drm: spacemit: Add spacemit DRM drivers 19/316919/2
authorMichal Wilczynski <m.wilczynski@samsung.com>
Wed, 28 Aug 2024 07:49:03 +0000 (09:49 +0200)
committerMichal Wilczynski <m.wilczynski@samsung.com>
Fri, 30 Aug 2024 16:07:12 +0000 (18:07 +0200)
Copy the drivers as is from the vendor kernel [1], since the vendor
already ported them to the kernel 6.6.

[1] - https://github.com/BPI-SINOVOIP/pi-linux/tree/linux-6.6.36-k1

Change-Id: I1329be3f40f6fabc5240cb42c61ee5a060d8d3da
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
66 files changed:
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/spacemit/Kconfig [new file with mode: 0644]
drivers/gpu/drm/spacemit/Makefile [new file with mode: 0644]
drivers/gpu/drm/spacemit/dphy/spacemit_dphy_drv.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/dpu_debug.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/dpu_debug.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/dpu_saturn.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/dpu_saturn.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/dpu_trace.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_fbcmem.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_fbcmem.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/cmdlist.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/cmps_x.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/dma_top.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_crg.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_ctl.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_intp.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_top.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/mmu.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/outctrl_proc_x.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/outctrl_top_x.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/prepipe_layer_proc_x.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/rdma_path_x.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/reg_map.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/scaler_x.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dpu/saturn_regs/wb_top.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dsi/spacemit_dptc_drv.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/dsi/spacemit_dptc_drv.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/dsi/spacemit_dsi_drv.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/dsi/spacemit_dsi_hw.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/lt8911exb.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/lt9711.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_bootloader.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_bootloader.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_cmdlist.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_cmdlist.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dmmu.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dmmu.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dphy.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dphy.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dpu.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dpu.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dpu_reg.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_drm.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_drm.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dsi.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_dsi.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_gem.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_gem.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_hdmi.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_hdmi.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_lib.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_lib.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_mipi_panel.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_mipi_panel.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_planes.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_wb.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/spacemit_wb.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/sysfs/sysfs_class.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/sysfs/sysfs_display.h [new file with mode: 0644]
drivers/gpu/drm/spacemit/sysfs/sysfs_dphy.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/sysfs/sysfs_dpu.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/sysfs/sysfs_dsi.c [new file with mode: 0644]
drivers/gpu/drm/spacemit/sysfs/sysfs_mipi_panel.c [new file with mode: 0644]
include/soc/spacemit/spacemit_panel.h [new file with mode: 0644]

index 120c9f3473bebc7b14f18363bbbdd35933105e19..958f9544e3a90b888f477edbfb29429a77c8995f 100644 (file)
@@ -390,6 +390,8 @@ source "drivers/gpu/drm/sprd/Kconfig"
 
 source "drivers/gpu/drm/img-rogue/Kconfig"
 
+source "drivers/gpu/drm/spacemit/Kconfig"
+
 config DRM_HYPERV
        tristate "DRM Support for Hyper-V synthetic video device"
        depends on DRM && PCI && MMU && HYPERV
index a088f8be0915a08ad34c09fb36b5a573d636602a..f98bc4bb20c56515a236320a26eec1b61f4eb068 100644 (file)
@@ -199,3 +199,4 @@ obj-y                       += solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
 obj-$(CONFIG_DRM_LOONGSON) += loongson/
 obj-$(CONFIG_POWERVR_ROGUE) += img-rogue/
+obj-$(CONFIG_DRM_SPACEMIT) += spacemit/
diff --git a/drivers/gpu/drm/spacemit/Kconfig b/drivers/gpu/drm/spacemit/Kconfig
new file mode 100644 (file)
index 0000000..67de8a3
--- /dev/null
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_SPACEMIT
+       tristate "DRM Support for Spacemit"
+       select DRM
+       select DRM_KMS_HELPER
+       select DRM_GEM_CMA_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_MIPI_DSI
+       select DRM_PANEL
+       select VIDEOMODE_HELPERS
+       select BACKLIGHT_CLASS_DEVICE
+       select GKI_FIX_WORKAROUND if DRM_SPACEMIT=m
+       default n
+       help
+         Choose this option if you have a Spacemit soc chipsets.
+         This driver provides Spacemit kernel mode
+         setting and buffer management. If M is selected the module will be called spacemit-drm.
+
+config SPACEMIT_MIPI_PANEL
+       tristate "MIPI Panel Support For Spacemit"
+       depends on DRM_SPACEMIT
+
+config SPACEMIT_HDMI
+       tristate "HDMI Support For Spacemit"
+       depends on DRM_SPACEMIT
+
+config DRM_LT8911EXB
+       tristate "Lontium LT8911EXB DSI to eDP"
+       default y
+       depends on DRM_SPACEMIT
+       select DRM_KMS_HELPER
+       select REGMAP_I2C
+       select DRM_PANEL
+       select DRM_MIPI_DSI
+       select AUXILIARY_BUS
+       select DRM_DP_AUX_BUS
+
+       help
+         Support for Lontium LT8911EXB DSI to eDP driver.
+
+config DRM_LT9711
+       tristate "Lontium LT9711 DSI to DP"
+       default y
+       depends on DRM_SPACEMIT
+       select DRM_KMS_HELPER
+       select REGMAP_I2C
+       select DRM_PANEL
+       select DRM_MIPI_DSI
+       select AUXILIARY_BUS
+       select DRM_DP_AUX_BUS
+
+       help
+         Support for Lontium LT9711 DSI to eDP driver.
\ No newline at end of file
diff --git a/drivers/gpu/drm/spacemit/Makefile b/drivers/gpu/drm/spacemit/Makefile
new file mode 100644 (file)
index 0000000..00582f9
--- /dev/null
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0
+
+spacemit-drm-y :=      spacemit_drm.o \
+                               spacemit_cmdlist.o \
+                               spacemit_dpu.o \
+                               spacemit_planes.o \
+                               spacemit_dsi.o \
+                               spacemit_wb.o \
+                               spacemit_dphy.o \
+                               spacemit_lib.o \
+                               spacemit_gem.o \
+                               spacemit_dmmu.o \
+                               spacemit_bootloader.o \
+                               sysfs/sysfs_class.o \
+                               sysfs/sysfs_dpu.o \
+                               sysfs/sysfs_dsi.o \
+                               sysfs/sysfs_dphy.o \
+                               sysfs/sysfs_mipi_panel.o \
+                               dpu/dpu_debug.o \
+                               dpu/dpu_saturn.o \
+                               dpu/saturn_fbcmem.o \
+                               dsi/spacemit_dsi_drv.o \
+                               dsi/spacemit_dptc_drv.o \
+                               dphy/spacemit_dphy_drv.o
+
+
+obj-$(CONFIG_DRM_SPACEMIT) += spacemit-drm.o
+
+obj-$(CONFIG_SPACEMIT_MIPI_PANEL) += spacemit_mipi_panel.o
+obj-$(CONFIG_SPACEMIT_HDMI) += spacemit_hdmi.o
+obj-$(CONFIG_DRM_LT8911EXB) += lt8911exb.o
+obj-$(CONFIG_DRM_LT9711) += lt9711.o
diff --git a/drivers/gpu/drm/spacemit/dphy/spacemit_dphy_drv.c b/drivers/gpu/drm/spacemit/dphy/spacemit_dphy_drv.c
new file mode 100644 (file)
index 0000000..079332e
--- /dev/null
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "../dsi/spacemit_dsi_hw.h"
+#include "../spacemit_dphy.h"
+#include "../dsi/spacemit_dptc_drv.h"
+
+static unsigned int spacemit_dphy_lane[5] = {0, 0x1, 0x3, 0x7, 0xf};
+
+static void dphy_ana_reset(void __iomem *base_addr)
+{
+       dsi_clear_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET);
+       udelay(5);
+       dsi_set_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET);
+}
+
+static void dphy_set_power(void __iomem *base_addr, bool poweron)
+{
+       if(poweron) {
+               dsi_set_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET);
+               dsi_set_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_PU);
+       } else {
+               dsi_clear_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_PU);
+               dsi_clear_bits(base_addr, DSI_PHY_ANA_PWR_CTRL, CFG_DPHY_ANA_RESET);
+       }
+}
+
+static void dphy_set_cont_clk(void __iomem *base_addr, bool cont_clk)
+{
+#ifdef DPTC_DPHY_TEST
+       uint32_t tmp;
+
+       if(cont_clk) {
+               tmp = dptc_dsi_read(0x04);
+               tmp |= CFG_DPHY_CONT_CLK;
+               //dptc_dsi_write(0x04, tmp);
+       } else {
+               tmp = dptc_dsi_read(0x04);
+               tmp &= (~CFG_DPHY_CONT_CLK);
+               //dptc_dsi_write(0x04, tmp);
+       }
+       dptc_dsi_write(0x04, 0x30001);
+#else
+       if(cont_clk)
+               dsi_set_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_CONT_CLK);
+       else
+               dsi_clear_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_CONT_CLK);
+
+       dsi_set_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_ADD_VALID);
+       dsi_set_bits(base_addr, DSI_PHY_CTRL_1, CFG_DPHY_VDD_VALID);
+#endif
+}
+
+static void dphy_set_lane_num(void __iomem *base_addr, uint32_t lane_num)
+{
+#ifdef DPTC_DPHY_TEST
+       uint32_t tmp;
+
+       tmp = dptc_dsi_read(0x08);
+       tmp &= ~CFG_DPHY_LANE_EN_MASK;
+       tmp |= spacemit_dphy_lane[lane_num] << CFG_DPHY_LANE_EN_SHIFT;
+       dptc_dsi_write(0x08, 0);
+       tmp = dptc_dsi_read(0x08);
+       dptc_dsi_write(0x08, 0x30);
+#endif
+       dsi_write_bits(base_addr, DSI_PHY_CTRL_2,
+               CFG_DPHY_LANE_EN_MASK, spacemit_dphy_lane[lane_num] << CFG_DPHY_LANE_EN_SHIFT);
+}
+
+static void dphy_set_bit_clk_src(void __iomem *base_addr, uint32_t bit_clk_src,
+       uint32_t half_pll5)
+{
+#ifdef DPTC_DPHY_TEST
+       uint32_t tmp;
+#endif
+
+       if(bit_clk_src >= DPHY_BIT_CLK_SRC_MAX) {
+               pr_err("%s: Invalid bit clk src (%d)\n", __func__, bit_clk_src);
+               return;
+       }
+
+#ifdef DPTC_DPHY_TEST
+       //if(bit_clk_src == DPHY_BIT_CLK_SRC_MUX) {
+       if(0) {
+               tmp = dptc_dsi_read(0x68);
+               tmp |= CFG_CLK_SEL;
+               dptc_dsi_write(0x68,tmp);
+       } else {
+               tmp = dptc_dsi_read(0x68);
+               tmp &= ~CFG_CLK_SEL;
+               dptc_dsi_write(0x68,tmp);
+       }
+
+       //if(1 == half_pll5) {
+       if(0) {
+               tmp = dptc_dsi_read(0x68);
+               tmp |= CFG_CLK_DIV2;
+               dptc_dsi_write(0x68,tmp);
+       } else {
+               tmp = dptc_dsi_read(0x68);
+               tmp &= ~CFG_CLK_DIV2;
+               dptc_dsi_write(0x68,tmp);
+       }
+#else
+#if 0
+       if(bit_clk_src == DPHY_BIT_CLK_SRC_MUX)
+               dsi_set_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_SEL);
+       else
+               dsi_clear_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_SEL);
+
+       if(1 == half_pll5)
+               dsi_set_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_DIV2);
+       else
+               dsi_clear_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_DIV2);
+#else
+       /*
+       dsi_set_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_SEL);
+       dsi_clear_bits(base_addr, DSI_PHY_ANA_CTRL1, CFG_CLK_DIV2);
+       */
+#endif
+#endif
+}
+
+static void dphy_set_timing(struct spacemit_dphy_ctx *dphy_ctx)
+{
+       uint32_t bitclk, lpx_clk, lpx_time, ta_get, ta_go;
+       int ui, wakeup, reg;
+       int hs_prep, hs_zero, hs_trail, hs_exit, ck_zero, ck_trail, ck_exit;
+       int esc_clk, esc_clk_t;
+       struct spacemit_dphy_timing *phy_timing;
+       uint32_t value;
+
+       if(NULL == dphy_ctx) {
+               pr_err("%s: Invalid param!\n", __func__);
+               return;
+       }
+
+       phy_timing = &(dphy_ctx->dphy_timing);
+
+       DRM_DEBUG("%s() phy_freq %d esc_clk %d \n", __func__, dphy_ctx->phy_freq, dphy_ctx->esc_clk);
+
+       esc_clk = dphy_ctx->esc_clk/1000;
+       esc_clk_t = 1000/esc_clk;
+
+       bitclk = dphy_ctx->phy_freq / 1000;
+       ui = 1000/bitclk + 1;
+
+       lpx_clk = (phy_timing->lpx_constant + phy_timing->lpx_ui * ui) / esc_clk_t + 1;
+       lpx_time = lpx_clk * esc_clk_t;
+
+       /* Below is for NT35451 */
+       ta_get = lpx_time * 5 / esc_clk_t - 1;
+       ta_go = lpx_time * 4 / esc_clk_t - 1;
+
+       wakeup = phy_timing->wakeup_constant;
+       wakeup = wakeup / esc_clk_t + 1;
+
+       hs_prep = phy_timing->hs_prep_constant + phy_timing->hs_prep_ui * ui;
+       hs_prep = hs_prep / esc_clk_t + 1;
+
+       /* Our hardware added 3-byte clk automatically.
+        * 3-byte 3 * 8 * ui.
+        */
+       hs_zero = phy_timing->hs_zero_constant + phy_timing->hs_zero_ui * ui -
+               (hs_prep + 1) * esc_clk_t;
+       hs_zero = (hs_zero - (3 * ui << 3)) / esc_clk_t + 4;
+       if (hs_zero < 0)
+               hs_zero = 0;
+
+       hs_trail = phy_timing->hs_trail_constant + phy_timing->hs_trail_ui * ui;
+       hs_trail = ((8 * ui) >= hs_trail) ? (8 * ui) : hs_trail;
+       hs_trail = hs_trail / esc_clk_t + 1;
+       if (hs_trail > 3)
+               hs_trail -= 3;
+       else
+               hs_trail = 0;
+
+       hs_exit = phy_timing->hs_exit_constant + phy_timing->hs_exit_ui * ui;
+       hs_exit = hs_exit / esc_clk_t + 1;
+
+       ck_zero = phy_timing->ck_zero_constant + phy_timing->ck_zero_ui * ui -
+               (hs_prep + 1) * esc_clk_t;
+       ck_zero = ck_zero / esc_clk_t + 1;
+
+       ck_trail = phy_timing->ck_trail_constant + phy_timing->ck_trail_ui * ui;
+       ck_trail = ck_trail / esc_clk_t + 1;
+
+       ck_exit = hs_exit;
+
+       reg = (hs_exit << CFG_DPHY_TIME_HS_EXIT_SHIFT)
+               | (hs_trail << CFG_DPHY_TIME_HS_TRAIL_SHIFT)
+               | (hs_zero << CFG_DPHY_TIME_HS_ZERO_SHIFT)
+               | (hs_prep << CFG_DPHY_TIME_HS_PREP_SHIFT);
+
+       DRM_DEBUG("%s dphy time0 hs_exit %d hs_trail %d hs_zero %d hs_prep %d reg 0x%x\n", __func__, hs_exit, hs_trail, hs_zero, hs_prep, reg);
+#ifdef DPTC_DPHY_TEST
+       dptc_dsi_write(0x40 , 0x01000000);
+#else
+       dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_0, reg);
+       // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_0, 0x06010603);
+#endif
+
+       reg = (ta_get << CFG_DPHY_TIME_TA_GET_SHIFT)
+               | (ta_go << CFG_DPHY_TIME_TA_GO_SHIFT)
+               | (wakeup << CFG_DPHY_TIME_WAKEUP_SHIFT);
+
+       DRM_DEBUG("%s dphy time1 ta_get %d ta_go %d wakeup %d reg 0x%x\n", __func__, ta_get, ta_go, wakeup, reg);
+#ifdef DPTC_DPHY_TEST
+       dptc_dsi_write(0x44, 0x0403001F);
+#else
+       dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_1, reg);
+       // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_1, 0x130fcd98);
+#endif
+       reg = (ck_exit << CFG_DPHY_TIME_CLK_EXIT_SHIFT)
+               | (ck_trail << CFG_DPHY_TIME_CLK_TRAIL_SHIFT)
+               | (ck_zero << CFG_DPHY_TIME_CLK_ZERO_SHIFT)
+               | (lpx_clk << CFG_DPHY_TIME_CLK_LPX_SHIFT);
+
+       DRM_DEBUG("%s dphy time2 ck_exit %d ck_trail %d ck_zero %d lpx_clk %d reg 0x%x\n", __func__, ck_exit, ck_trail, ck_zero, lpx_clk, reg);
+#ifdef DPTC_DPHY_TEST
+       dptc_dsi_write(0x48, 0x02010500);
+#else
+       dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_2, reg);
+       // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_2, 0x06040c04);
+#endif
+
+       reg = (lpx_clk << CFG_DPHY_TIME_LPX_SHIFT)
+               | phy_timing->req_ready << CFG_DPHY_TIME_REQRDY_SHIFT;
+
+       DRM_DEBUG("%s dphy time3 lpx_clk %d req_ready %d reg 0x%x\n", __func__, lpx_clk, phy_timing->req_ready, reg);
+#ifdef DPTC_DPHY_TEST
+       dptc_dsi_write(0x4c, 0x001F);
+#else
+       dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_3, reg);
+       // dsi_write(dphy_ctx->base_addr, DSI_PHY_TIME_3, 0x43c);
+#endif
+       /* calculated timing on brownstone:
+        * DSI_PHY_TIME_0 0x06080204
+        * DSI_PHY_TIME_1 0x6d2bfff0
+        * DSI_PHY_TIME_2 0x603130a
+        * DSI_PHY_TIME_3 0xa3c
+        */
+
+       value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_0);
+       DRM_DEBUG("%s() DSI_PHY_TIME_0 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_0, value);
+       value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_1);
+       DRM_DEBUG("%s() DSI_PHY_TIME_1 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_1, value);
+       value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_2);
+       DRM_DEBUG("%s() DSI_PHY_TIME_2 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_2, value);
+       value = dsi_read(dphy_ctx->base_addr, DSI_PHY_TIME_3);
+       DRM_DEBUG("%s() DSI_PHY_TIME_3 offset 0x%x value 0x%x\n", __func__, DSI_PHY_TIME_3, value);
+}
+
+static void dphy_get_setting(struct spacemit_dphy_ctx *dphy_ctx, struct device_node *np)
+{
+       struct spacemit_dphy_timing *dphy_timing = &dphy_ctx->dphy_timing;
+       int ret;
+
+       if(NULL == dphy_timing){
+               pr_err("%s: Invalid param\n",__func__);
+               return;
+       }
+
+       ret = of_property_read_u32(np, "hs_prep_constant", &dphy_timing->hs_prep_constant);
+       if(0 != ret)
+               dphy_timing->hs_prep_constant = HS_PREP_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_prep_ui", &dphy_timing->hs_prep_ui);
+       if(0 != ret)
+               dphy_timing->hs_prep_ui = HS_PREP_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_zero_constant", &dphy_timing->hs_zero_constant);
+       if(0 != ret)
+               dphy_timing->hs_zero_constant = HS_ZERO_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_zero_ui", &dphy_timing->hs_zero_ui);
+       if(0 != ret)
+               dphy_timing->hs_zero_ui = HS_ZERO_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_trail_constant", &dphy_timing->hs_trail_constant);
+       if(0 != ret)
+               dphy_timing->hs_trail_constant = HS_TRAIL_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_trail_ui", &dphy_timing->hs_trail_ui);
+       if(0 != ret)
+               dphy_timing->hs_trail_ui = HS_TRAIL_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_exit_constant", &dphy_timing->hs_exit_constant);
+       if(0 != ret)
+               dphy_timing->hs_exit_constant = HS_EXIT_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "hs_exit_ui", &dphy_timing->hs_exit_ui);
+       if(0 != ret)
+               dphy_timing->hs_exit_ui = HS_EXIT_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "ck_zero_constant", &dphy_timing->ck_zero_constant);
+       if(0 != ret)
+               dphy_timing->ck_zero_constant = CK_ZERO_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "ck_zero_ui", &dphy_timing->ck_zero_ui);
+       if(0 != ret)
+               dphy_timing->ck_zero_ui = CK_ZERO_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "ck_trail_constant", &dphy_timing->ck_trail_constant);
+       if(0 != ret)
+               dphy_timing->ck_trail_constant = CK_TRAIL_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "ck_zero_ui", &dphy_timing->ck_zero_ui);
+       if(0 != ret)
+               dphy_timing->ck_zero_ui = CK_TRAIL_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "req_ready", &dphy_timing->req_ready);
+       if(0 != ret)
+               dphy_timing->req_ready = REQ_READY_DEFAULT;
+
+       ret = of_property_read_u32(np, "wakeup_constant", &dphy_timing->wakeup_constant);
+       if(0 != ret)
+               dphy_timing->wakeup_constant = WAKEUP_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "wakeup_ui", &dphy_timing->wakeup_ui);
+       if(0 != ret)
+               dphy_timing->wakeup_ui = WAKEUP_UI_DEFAULT;
+
+       ret = of_property_read_u32(np, "lpx_constant", &dphy_timing->lpx_constant);
+       if(0 != ret)
+               dphy_timing->lpx_constant = LPX_CONSTANT_DEFAULT;
+
+       ret = of_property_read_u32(np, "lpx_ui", &dphy_timing->lpx_ui);
+       if(0 != ret)
+               dphy_timing->lpx_ui = LPX_UI_DEFAULT;
+}
+
+
+void spacemit_dphy_core_get_status(struct spacemit_dphy_ctx *dphy_ctx)
+{
+       pr_debug("%s\n", __func__);
+
+       if(NULL == dphy_ctx){
+               pr_err("%s: Invalid param\n", __func__);
+               return;
+       }
+
+       dphy_ctx->dphy_status0 = dsi_read(dphy_ctx->base_addr, DSI_PHY_STATUS_0);
+       dphy_ctx->dphy_status1 = dsi_read(dphy_ctx->base_addr, DSI_PHY_STATUS_1);
+       dphy_ctx->dphy_status2 = dsi_read(dphy_ctx->base_addr, DSI_PHY_STATUS_2);
+       pr_debug("%s: dphy_status0 = 0x%x\n", __func__, dphy_ctx->dphy_status0);
+       pr_debug("%s: dphy_status1 = 0x%x\n", __func__, dphy_ctx->dphy_status1);
+       pr_debug("%s: dphy_status2 = 0x%x\n", __func__, dphy_ctx->dphy_status2);
+}
+
+void spacemit_dphy_core_reset(struct spacemit_dphy_ctx *dphy_ctx)
+{
+       pr_debug("%s\n", __func__);
+
+       if(NULL == dphy_ctx){
+               pr_err("%s: Invalid param\n", __func__);
+               return;
+       }
+
+       dphy_ana_reset(dphy_ctx->base_addr);
+}
+
+/**
+ * spacemit_dphy_core_init - int spacemit dphy
+ *
+ * @dphy_ctx: pointer to the spacemit_dphy_ctx
+ *
+ * This function will be called by the dsi driver in order to init the dphy
+ * This function will do phy power on, enable continous clk, set dphy timing
+ * and set lane number.
+ *
+ * This function has no return value.
+ *
+ */
+void spacemit_dphy_core_init(struct spacemit_dphy_ctx *dphy_ctx)
+{
+       pr_debug("%s\n", __func__);
+
+       if(NULL == dphy_ctx){
+               pr_err("%s: Invalid param\n", __func__);
+               return;
+       }
+
+       if(DPHY_STATUS_UNINIT != dphy_ctx->status){
+               pr_warn("%s: dphy_ctx has been initialized (%d)\n",
+                       __func__, dphy_ctx->status);
+               return;
+       }
+
+       /*use DPHY_BIT_CLK_SRC_MUX as default clk src*/
+       dphy_set_bit_clk_src(dphy_ctx->base_addr, dphy_ctx->clk_src, dphy_ctx->half_pll5);
+
+       /* digital and analog power on */
+       dphy_set_power(dphy_ctx->base_addr, true);
+
+       /* turn on DSI continuous clock for HS */
+       dphy_set_cont_clk(dphy_ctx->base_addr, true);
+
+       /* set dphy */
+       dphy_set_timing(dphy_ctx);
+
+       /* enable data lanes */
+       dphy_set_lane_num(dphy_ctx->base_addr, dphy_ctx->lane_num);
+
+       dphy_ctx->status = DPHY_STATUS_INIT;
+}
+
+/**
+ * spacemit_dphy_core_uninit - unint spacemit dphy
+ *
+ * @dphy_ctx: pointer to the spacemit_dphy_ctx
+ *
+ * This function will be called by the dsi driver in order to unint the dphy
+ * This function will disable continous clk, reset dphy, power down dphy
+ *
+ * This function has no return value.
+ *
+ */
+void spacemit_dphy_core_uninit(struct spacemit_dphy_ctx *dphy_ctx)
+{
+       pr_debug("%s\n", __func__);
+
+       if(NULL == dphy_ctx){
+               pr_err("%s: Invalid param\n", __func__);
+               return;
+       }
+
+       if(DPHY_STATUS_INIT != dphy_ctx->status){
+               pr_warn("%s: dphy_ctx has not been initialized (%d)\n",
+                       __func__, dphy_ctx->status);
+               return;
+       }
+
+       dphy_set_cont_clk(dphy_ctx->base_addr, false);
+       dphy_ana_reset(dphy_ctx->base_addr);
+       dphy_set_power(dphy_ctx->base_addr, false);
+
+       dphy_ctx->status = DPHY_STATUS_UNINIT;
+}
+
+int spacemit_dphy_core_parse_dt(struct spacemit_dphy_ctx *dphy_ctx, struct device_node *np)
+{
+       if (!dphy_ctx) {
+               pr_err("%s: Param is NULL\n",__func__);
+               return -1;
+       }
+
+       dphy_get_setting(dphy_ctx, np);
+
+       return 0;
+}
+
+
+static struct dphy_core_ops dphy_core_ops = {
+       .parse_dt = spacemit_dphy_core_parse_dt,
+       .init = spacemit_dphy_core_init,
+       .uninit = spacemit_dphy_core_uninit,
+       .reset = spacemit_dphy_core_reset,
+       .get_status = spacemit_dphy_core_get_status,
+};
+
+static struct ops_entry entry = {
+       .ver = "spacemit-dphy",
+       .ops = &dphy_core_ops,
+};
+
+static int __init dphy_core_register(void)
+{
+       return dphy_core_ops_register(&entry);
+}
+
+subsys_initcall(dphy_core_register);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_debug.c b/drivers/gpu/drm/spacemit/dpu/dpu_debug.c
new file mode 100644 (file)
index 0000000..d3e262c
--- /dev/null
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/trace_events.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_framebuffer.h>
+#include "dpu_debug.h"
+#include "dpu_trace.h"
+#include "./../spacemit_dpu_reg.h"
+#include "./../spacemit_drm.h"
+
+dpu_reg_enum SATURN_LE_DPU_REG_ENUM_LISTS[] = {
+       E_DPU_TOP_REG,
+       E_DPU_CTRL_REG,
+       E_DPU_CTRL_REG,
+       E_DPU_CMDLIST_REG,
+       E_DPU_INT_REG,
+       E_DMA_TOP_CTRL_REG,
+       E_RDMA_LAYER0_REG,
+       E_RDMA_LAYER1_REG,
+       E_RDMA_LAYER2_REG,
+       E_RDMA_LAYER3_REG,
+       E_MMU_TBU0_REG,
+       E_MMU_TBU2_REG,
+       E_MMU_TBU4_REG,
+       E_MMU_TBU6_REG,
+       E_COMPOSER2_REG,
+       E_SCALER0_REG,
+       E_OUTCTRL2_REG
+};
+
+static dpu_reg_dump_t dpu_reg_dump_array[] = {
+       {E_DPU_TOP_REG, "DPU_TOP", DPU_TOP_BASE_ADDR, 218},
+
+       {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR, 5},
+       {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x24, 8},
+       {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x54, 8},
+       {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x84, 19},
+       {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0xe4, 8},
+       {E_DPU_CTRL_REG, "DPU_CTRL", DPU_CTRL_BASE_ADDR + 0x114, 25},
+
+       {E_DPU_CRG_REG, "DPU_CRG", DPU_CRG_BASE_ADDR, 5},
+
+       {E_DPU_CMDLIST_REG, "DPU_CMDLIST", CMDLIST_BASE_ADDR, 44},
+
+       {E_DPU_INT_REG, "DPU_INT", DPU_INT_BASE_ADDR, 40},
+
+       {E_DMA_TOP_CTRL_REG, "DMA_TOP_CTRL", DMA_TOP_BASE_ADDR, 25},
+
+       {E_RDMA_LAYER0_REG, "RDMA_LAYER0", RDMA0_BASE_ADDR, 31},
+       {E_RDMA_LAYER0_REG, "RDMA_LAYER0", RDMA0_BASE_ADDR + 0x80, 57},
+
+       {E_RDMA_LAYER1_REG, "RDMA_LAYER1", RDMA1_BASE_ADDR, 31},
+       {E_RDMA_LAYER1_REG, "RDMA_LAYER1", RDMA1_BASE_ADDR + 0x80, 57},
+
+       {E_RDMA_LAYER2_REG, "RDMA_LAYER2", RDMA2_BASE_ADDR, 31},
+       {E_RDMA_LAYER2_REG, "RDMA_LAYER2", RDMA2_BASE_ADDR + 0x80, 57},
+
+       {E_RDMA_LAYER3_REG, "RDMA_LAYER3" ,RDMA3_BASE_ADDR, 31},
+       {E_RDMA_LAYER3_REG, "RDMA_LAYER3", RDMA3_BASE_ADDR + 0x80, 57},
+
+       {E_RDMA_LAYER4_REG, "RDMA_LAYER4", RDMA4_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER5_REG, "RDMA_LAYER5", RDMA5_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER6_REG, "RDMA_LAYER6", RDMA6_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER7_REG, "RDMA_LAYER7", RDMA7_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER8_REG, "RDMA_LAYER8", RDMA8_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER9_REG, "RDMA_LAYER9", RDMA9_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER10_REG, "RDMA_LAYER10", RDMA10_BASE_ADDR, 46},
+
+       {E_RDMA_LAYER11_REG, "RDMA_LAYER11", RDMA11_BASE_ADDR, 46},
+
+       {E_MMU_TBU0_REG, "MMU_TBU0", MMU_TBU_BASE_ADDR + 0 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU1_REG, "MMU_TBU1", MMU_TBU_BASE_ADDR + 1 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU2_REG, "MMU_TBU2", MMU_TBU_BASE_ADDR + 2 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU3_REG, "MMU_TBU3", MMU_TBU_BASE_ADDR + 3 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU4_REG, "MMU_TBU4", MMU_TBU_BASE_ADDR + 4 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU5_REG, "MMU_TBU5", MMU_TBU_BASE_ADDR + 5 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU6_REG, "MMU_TBU6", MMU_TBU_BASE_ADDR + 6 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU7_REG, "MMU_TBU7", MMU_TBU_BASE_ADDR + 7 * MMU_TBU_SIZE, 13},
+       {E_MMU_TBU8_REG, "MMU_TBU8", MMU_TBU_BASE_ADDR + 8 * MMU_TBU_SIZE, 13},
+       {E_MMU_TOP_REG,  "MMU_TOP",  MMU_TOP_BASE_ADDR, 13},
+
+       {E_LP0_REG, "LP0", LP0_BASE_ADDR, 81},
+       {E_LP1_REG, "LP1", LP1_BASE_ADDR, 81},
+       {E_LP2_REG, "LP2", LP2_BASE_ADDR, 81},
+       {E_LP3_REG, "LP3", LP3_BASE_ADDR, 81},
+       {E_LP4_REG, "LP4", LP4_BASE_ADDR, 81},
+       {E_LP5_REG, "LP5", LP5_BASE_ADDR, 81},
+       {E_LP6_REG, "LP6", LP6_BASE_ADDR, 81},
+       {E_LP7_REG, "LP7", LP7_BASE_ADDR, 81},
+       {E_LP8_REG, "LP8", LP8_BASE_ADDR, 81},
+       {E_LP9_REG, "LP9", LP9_BASE_ADDR, 81},
+       {E_LP10_REG, "LP10", LP10_BASE_ADDR, 81},
+       {E_LP11_REG, "LP11", LP11_BASE_ADDR, 81},
+
+       {E_LM0_REG, "LMERGE0", LMERGE0_BASE_ADDR, 4},
+       {E_LM1_REG, "LMERGE1", LMERGE1_BASE_ADDR, 4},
+       {E_LM2_REG, "LMERGE2", LMERGE2_BASE_ADDR, 4},
+       {E_LM3_REG, "LMERGE3", LMERGE3_BASE_ADDR, 4},
+       {E_LM4_REG, "LMERGE4", LMERGE4_BASE_ADDR, 4},
+       {E_LM5_REG, "LMERGE5", LMERGE5_BASE_ADDR, 4},
+       {E_LM6_REG, "LMERGE6", LMERGE6_BASE_ADDR, 4},
+       {E_LM7_REG, "LMERGE7", LMERGE7_BASE_ADDR, 4},
+       {E_LM8_REG, "LMERGE8", LMERGE8_BASE_ADDR, 4},
+       {E_LM9_REG, "LMERGE9", LMERGE9_BASE_ADDR, 4},
+       {E_LM10_REG, "LMERGE10", LMERGE10_BASE_ADDR, 4},
+       {E_LM11_REG, "LMERGE11", LMERGE11_BASE_ADDR, 4},
+
+       {E_COMPOSER0_REG, "COMPOSER0", CMP0_BASE_ADDR, 146},
+       {E_COMPOSER1_REG, "COMPOSER1", CMP1_BASE_ADDR, 146},
+       {E_COMPOSER2_REG, "COMPOSER2", CMP2_BASE_ADDR, 146},
+       {E_COMPOSER3_REG, "COMPOSER3", CMP3_BASE_ADDR, 146},
+
+       {E_SCALER0_REG, "SCALER0", SCALER0_ONLINE_BASE_ADDR, 121},
+       {E_SCALER1_REG, "SCALER1", SCALER1_ONLINE_BASE_ADDR, 121},
+
+       {E_OUTCTRL0_REG, "OUTCTRL0", OUTCTRL0_BASE_ADDR, 55},
+       {E_PP0_REG, "PP0", PP0_BASE_ADDR, 86},
+       {E_OUTCTRL1_REG, "OUTCTRL1", OUTCTRL1_BASE_ADDR, 55},
+       {E_PP1_REG, "PP1", PP1_BASE_ADDR, 86},
+       {E_OUTCTRL2_REG, "OUTCTRL2", OUTCTRL2_BASE_ADDR, 55},
+       {E_PP2_REG, "PP2", PP2_BASE_ADDR, 86},
+       {E_OUTCTRL3_REG, "OUTCTRL3", OUTCTRL3_BASE_ADDR, 55},
+       {E_PP3_REG, "PP3", PP3_BASE_ADDR, 86},
+
+       {E_WB_TOP_0_REG, "WB_TOP_0", WB0_TOP_BASE_ADDR, 54},
+       {E_WB_TOP_1_REG, "WB_TOP_1", WB1_TOP_BASE_ADDR, 54},
+};
+
+static void dump_dpu_regs_by_enum(void __iomem *io_base, phys_addr_t phy_base, dpu_reg_enum reg_enum, u8 trace_dump)
+{
+       int i;
+       int j;
+       uint32_t reg_num;
+       void __iomem *io_addr;
+       phys_addr_t phy_addr;
+
+       dpu_reg_dump_t *tmp = &dpu_reg_dump_array[0];
+
+       for (i = 0; i < ARRAY_SIZE(dpu_reg_dump_array); i++) {
+               if (tmp->index == reg_enum) {
+                       reg_num = tmp->dump_reg_num;
+                       io_addr = io_base + tmp->module_offset;
+                       phy_addr = phy_base + tmp->module_offset;
+                       if (trace_dump) {
+                               trace_dpu_reg_info(tmp->module_name, phy_addr, reg_num);
+                               for (j = 0; j < reg_num; j++) {
+                                       trace_dpu_reg_dump(phy_addr + j * 4, readl(io_addr + j * 4));
+                               }
+                       } else {
+                               printk(KERN_DEBUG "%d-%s, address:0x%08llx, num:%d\n", tmp->index, tmp->module_name, phy_addr, reg_num);
+                               for (j = 0; j < reg_num; j++) {
+                                       printk(KERN_DEBUG "0x%08llx: 0x%08x\n", (phy_addr + j * 4), readl(io_addr + j * 4));
+                               }
+                       }
+               }
+               tmp++;
+       }
+}
+
+bool dpu_reg_enum_valid(dpu_reg_enum reg_enum)
+{
+       int size = ARRAY_SIZE(SATURN_LE_DPU_REG_ENUM_LISTS);
+       int i = 0;
+
+       for (i = 0; i < size; i++) {
+               if (reg_enum == SATURN_LE_DPU_REG_ENUM_LISTS[i])
+                       return true;
+       }
+
+       return false;
+}
+
+void dump_dpu_regs(struct spacemit_dpu *dpu, dpu_reg_enum reg_enum, u8 trace_dump)
+{
+       dpu_reg_enum tmp = E_DPU_TOP_REG;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       void __iomem* reg_io_base = hwdev->base;
+       phys_addr_t reg_phy_base = hwdev->phy_addr;
+
+       if (reg_enum > E_DPU_DUMP_ALL) {
+               pr_err("invalid dump regsiter enum\n");
+               return;
+       } else if (reg_enum == E_DPU_DUMP_ALL) {
+               for (; tmp < E_DPU_DUMP_ALL; tmp++) {
+                       if (dpu_reg_enum_valid(tmp))
+                               dump_dpu_regs_by_enum(reg_io_base, reg_phy_base, tmp, trace_dump);
+               }
+       } else {
+               dump_dpu_regs_by_enum(reg_io_base, reg_phy_base, reg_enum, trace_dump);
+       }
+}
+
+static void dpu_debug_mode(struct spacemit_hw_device *hwdev, int pipeline_id, bool enable)
+{
+       u32 base = DPU_CTRL_BASE_ADDR;
+
+       switch (pipeline_id) {
+       case ONLINE0:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl0_dbg_mod, enable ? 1 : 0);
+               break;
+       case ONLINE1:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl1_dbg_mod, enable ? 1 : 0);
+               break;
+       case ONLINE2:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl2_dbg_mod, enable ? 1 : 0);
+               break;
+       case OFFLINE0:
+       case OFFLINE1:
+       default:
+               DRM_ERROR("pipeline id is invalid!\n");
+               break;
+       }
+}
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+static struct file *gki_filp_open(const char *filename, int flags, umode_t mode)
+{
+       return 0;
+}
+static ssize_t gki_kernel_write(struct file *file, const void *buf, size_t count,
+                           loff_t *pos)
+{
+       return 0;
+}
+#endif
+#define DPU_BUFFER_DUMP_FILE "/mnt/dpu_buffer_dump"
+int dpu_buffer_dump(struct drm_plane *plane) {
+       unsigned int buffer_size = 0;
+       int i = 0;
+       void *mmu_tbl_vaddr = NULL;
+       phys_addr_t dpu_buffer_paddr = 0;
+       void __iomem *dpu_buffer_vaddr = NULL;
+       loff_t pos = 0;
+       static int dump_once = true;
+       struct file *filep = NULL;
+       struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(plane->state);
+
+       if (!dump_once)
+               return 0;
+
+       mmu_tbl_vaddr = spacemit_pstate->mmu_tbl.va;
+       buffer_size = plane->state->fb->obj[0]->size >> PAGE_SHIFT;
+
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+       filep = gki_filp_open(DPU_BUFFER_DUMP_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);
+#else
+       filep = filp_open(DPU_BUFFER_DUMP_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);
+#endif
+
+       if (IS_ERR(filep)) {
+               printk("Open file %s error\n", DPU_BUFFER_DUMP_FILE);
+               return -EINVAL;
+       }
+       for (i = 0; i < buffer_size; i++) {
+               dpu_buffer_paddr = *(volatile u32 __force *)mmu_tbl_vaddr;
+               dpu_buffer_paddr = dpu_buffer_paddr << PAGE_SHIFT;
+               if (dpu_buffer_paddr >= 0x80000000UL) {
+                       dpu_buffer_paddr += 0x80000000UL;
+               }
+               dpu_buffer_vaddr = phys_to_virt((unsigned long)dpu_buffer_paddr);
+               mmu_tbl_vaddr += 4;
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+               gki_kernel_write(filep, (void *)dpu_buffer_vaddr, PAGE_SIZE, &pos);
+#else
+               kernel_write(filep, (void *)dpu_buffer_vaddr, PAGE_SIZE, &pos);
+#endif
+       }
+
+       filp_close(filep, NULL);
+       filep = NULL;
+
+       dump_once = false;
+
+       return 0;
+}
+
+void dpu_dump_reg(struct spacemit_dpu *dpu)
+{
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       if (!dpu->enable_dump_reg)
+               return;
+
+       dpu_debug_mode(hwdev, ONLINE2, 1);
+       dump_dpu_regs(dpu, E_DPU_DUMP_ALL, 1);
+       dpu_debug_mode(hwdev, ONLINE2, 0);
+}
+
+void dpu_dump_fps(struct spacemit_dpu *dpu)
+{
+       struct timespec64 cur_tm, tmp_tm;
+
+       if (!dpu->enable_dump_fps)
+               return;
+
+       ktime_get_real_ts64(&cur_tm);
+       tmp_tm = timespec64_sub(cur_tm, dpu->last_tm);
+       dpu->last_tm.tv_sec = cur_tm.tv_sec;
+       dpu->last_tm.tv_nsec = cur_tm.tv_nsec;
+       if (tmp_tm.tv_sec == 0)
+               trace_printk("fps: %ld\n", 1000000000 / (tmp_tm.tv_nsec / 1000));
+}
+
+void dpu_underrun_wq_stop_trace(struct work_struct *work)
+{
+// #ifndef MODULE
+//     trace_set_clr_event("dpu", NULL, false);
+// #endif
+}
diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_debug.h b/drivers/gpu/drm/spacemit/dpu/dpu_debug.h
new file mode 100644 (file)
index 0000000..7aebb50
--- /dev/null
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _DPU_DEBUG_H_
+#define _DPU_DEBUG_H_
+
+#include <linux/types.h>
+#include "saturn_regs/reg_map.h"
+#include "./../spacemit_dpu.h"
+
+typedef enum {
+       E_DPU_TOP_REG = 0,
+       E_DPU_CTRL_REG,
+       E_DPU_CRG_REG,
+       E_DPU_CMDLIST_REG,
+       E_DPU_INT_REG,
+
+       E_DMA_TOP_CTRL_REG,
+       E_RDMA_LAYER0_REG,
+       E_RDMA_LAYER1_REG,
+       E_RDMA_LAYER2_REG,
+       E_RDMA_LAYER3_REG,
+       E_RDMA_LAYER4_REG,
+       E_RDMA_LAYER5_REG,
+       E_RDMA_LAYER6_REG,
+       E_RDMA_LAYER7_REG,
+       E_RDMA_LAYER8_REG,
+       E_RDMA_LAYER9_REG,
+       E_RDMA_LAYER10_REG,
+       E_RDMA_LAYER11_REG,
+
+       E_MMU_TBU0_REG,
+       E_MMU_TBU1_REG,
+       E_MMU_TBU2_REG,
+       E_MMU_TBU3_REG,
+       E_MMU_TBU4_REG,
+       E_MMU_TBU5_REG,
+       E_MMU_TBU6_REG,
+       E_MMU_TBU7_REG,
+       E_MMU_TBU8_REG,
+       E_MMU_TOP_REG,
+
+       E_LP0_REG,
+       E_LP1_REG,
+       E_LP2_REG,
+       E_LP3_REG,
+       E_LP4_REG,
+       E_LP5_REG,
+       E_LP6_REG,
+       E_LP7_REG,
+       E_LP8_REG,
+       E_LP9_REG,
+       E_LP10_REG,
+       E_LP11_REG,
+
+       E_LM0_REG,
+       E_LM1_REG,
+       E_LM2_REG,
+       E_LM3_REG,
+       E_LM4_REG,
+       E_LM5_REG,
+       E_LM6_REG,
+       E_LM7_REG,
+       E_LM8_REG,
+       E_LM9_REG,
+       E_LM10_REG,
+       E_LM11_REG,
+
+       E_COMPOSER0_REG,
+       E_COMPOSER1_REG,
+       E_COMPOSER2_REG,
+       E_COMPOSER3_REG,
+
+       E_SCALER0_REG,
+       E_SCALER1_REG,
+
+       E_OUTCTRL0_REG,
+       E_PP0_REG,
+       E_OUTCTRL1_REG,
+       E_PP1_REG,
+       E_OUTCTRL2_REG,
+       E_PP2_REG,
+       E_OUTCTRL3_REG,
+       E_PP3_REG,
+
+       E_WB_TOP_0_REG,
+       E_WB_TOP_1_REG,
+
+       E_DPU_DUMP_ALL
+}dpu_reg_enum;
+
+typedef struct dpu_reg_dump {
+       dpu_reg_enum    index;
+       u8*             module_name;
+       uint32_t        module_offset;
+       uint32_t        dump_reg_num;
+}dpu_reg_dump_t;
+
+void dump_dpu_regs(struct spacemit_dpu *dpu, dpu_reg_enum reg_enum, u8 trace_dump);
+void dpu_dump_reg(struct spacemit_dpu *dpu);
+void dpu_dump_fps(struct spacemit_dpu *dpu);
+int dpu_buffer_dump(struct drm_plane *plane);
+void dpu_underrun_wq_stop_trace(struct work_struct *work);
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_saturn.c b/drivers/gpu/drm/spacemit/dpu/dpu_saturn.c
new file mode 100644 (file)
index 0000000..a77ad3d
--- /dev/null
@@ -0,0 +1,1860 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pm_qos.h>
+#include <linux/regmap.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_writeback.h>
+#include "dpu_saturn.h"
+#include "saturn_fbcmem.h"
+#include "../spacemit_cmdlist.h"
+#include "../spacemit_dmmu.h"
+#include "../spacemit_dpu_reg.h"
+#include "../spacemit_drm.h"
+#include "../spacemit_wb.h"
+#include <video/display_timing.h>
+#include <dt-bindings/display/spacemit-dpu.h>
+
+#define CREATE_TRACE_POINTS
+#include "dpu_trace.h"
+#include "dpu_debug.h"
+
+#define TOTAL_RDMA_MEMSIZE     (68 * 1024) /* 68KB */
+#define YUV2RGB_COEFFS 12
+
+//RDMA_FMT_YUV_420_P1_8, RDMA_FMT_YUV_420_P1_10 not support by hardware, has checked with asic
+//rdma hardware support RDMA_FMT_BGRA_16161616/RDMA_FMT_RGBA_16161616 formats are not  support by fourcc
+static const struct dpu_format_id primary_fmts[] = {
+       { DRM_FORMAT_ABGR2101010, 1, 32 }, //RDMA_FMT_ABGR_2101010
+       { DRM_FORMAT_ARGB8888, 4, 32 }, //RDMA_FMT_ARGB_8888
+       { DRM_FORMAT_ABGR8888, 5, 32 }, //RDMA_FMT_ABGR_8888
+       { DRM_FORMAT_RGBA8888, 6, 32 }, //RDMA_FMT_RGBA_8888
+       { DRM_FORMAT_BGRA8888, 7, 32 }, //RDMA_FMT_BGRA_8888
+       { DRM_FORMAT_XRGB8888, 8, 32 }, //RDMA_FMT_XRGB_8888
+       { DRM_FORMAT_XBGR8888, 9, 32 }, //RDMA_FMT_XBGR_8888
+       { DRM_FORMAT_RGBX8888, 10, 32 }, //RDMA_FMT_RGBX_8888
+       { DRM_FORMAT_BGRX8888, 11, 32 }, //RDMA_FMT_BGRX_8888
+       { DRM_FORMAT_RGB565, 22, 16 }, //RDMA_FMT_RGB_565
+       { DRM_FORMAT_BGR565, 23, 16 }, //RDMA_FMT_BGR_565
+       /*
+       { DRM_FORMAT_ARGB2101010,        0 }, //RDMA_FMT_ARGB_2101010
+       { DRM_FORMAT_RGBA1010102,        2 }, //RDMA_FMT_RGBA_2101010
+       { DRM_FORMAT_BGRA1010102,        3 }, //RDMA_FMT_BGRA_2101010
+       { DRM_FORMAT_RGB888,            12 }, //RDMA_FMT_RGB_888
+       { DRM_FORMAT_BGR888,        13 }, //RDMA_FMT_BGR_888
+       { DRM_FORMAT_RGBA5551,      14 }, //RDMA_FMT_RGBA_5551
+       { DRM_FORMAT_BGRA5551,      15 }, //RDMA_FMT_BGRA_5551
+       { DRM_FORMAT_ABGR1555,      16 }, //RDMA_FMT_ABGR_1555
+       { DRM_FORMAT_ARGB1555,      17 }, //RDMA_FMT_ARGB_1555
+       { DRM_FORMAT_RGBX5551,      18 }, //RDMA_FMT_RGBX_5551
+       { DRM_FORMAT_BGRX5551,      19 }, //RDMA_FMT_BGRX_5551
+       { DRM_FORMAT_XBGR1555,      20 }, //RDMA_FMT_XBGR_1555
+       { DRM_FORMAT_XRGB1555,      21 }, //RDMA_FMT_XRGB_1555
+       { DRM_FORMAT_ARGB16161616F, 24 }, //RDMA_FMT_ARGB_16161616
+       { DRM_FORMAT_ABGR16161616F, 25 }, //RDMA_FMT_ABGR_16161616
+       { DRM_FORMAT_XYUV8888,      32 }, //RDMA_FMT_XYUV_444_P1_8, uv_swap has no corresponding fourcc format
+       { DRM_FORMAT_Y410,          33 }, //RDMA_FMT_XYUV_444_P1_10, uv_swap has no corresponding fourcc format
+       { DRM_FORMAT_YUYV,          34 }, //RDMA_FMT_VYUY_422_P1_8
+       { DRM_FORMAT_YVYU,          34 }, //RDMA_FMT_VYUY_422_P1_8, uv_swap = 1
+       { DRM_FORMAT_UYVY,          35 }, //RDMA_FMT_YVYU_422_P1_8
+       { DRM_FORMAT_VYUY,          35 }, //RDMA_FMT_YVYU_422_P1_8, uv_swap = 1
+       */
+       { DRM_FORMAT_YUV420_8BIT,   37, 12 }, //DRM_FORMAT_YUV420_8BIT for AFBC
+       { DRM_FORMAT_NV12,          37, 12 }, //RDMA_FMT_YUV_420_P2_8
+       /*
+       { DRM_FORMAT_NV21,          37 }, //RDMA_FMT_YUV_420_P2_8, uv_swap = 1
+       { DRM_FORMAT_YUV420,        38 }, //RDMA_FMT_YUV_420_P3_8
+       { DRM_FORMAT_YVU420,        38 }, //RDMA_FMT_YUV_420_P3_8, uv_swap = 1
+       { DRM_FORMAT_YUV420_10BIT,  39 }, //RDMA_FMT_YUV_420_P1_10 //DPU not support, DO NOT use
+       */
+       { DRM_FORMAT_P010,          40, 24 }, //RDMA_FMT_YUV_420_P2_10
+       /*
+       { DRM_FORMAT_Q410,          41 }, //DPU not support
+       { DRM_FORMAT_Q401,          41 }, //DPU not support
+       */
+};
+
+const struct spacemit_hw_rdma saturn_rdmas[] = {
+       {FORMAT_RGB | FORMAT_RAW_YUV | FORMAT_AFBC, ROTATE_COMMON | ROTATE_AFBC_90_270},
+       {FORMAT_RGB | FORMAT_RAW_YUV | FORMAT_AFBC, ROTATE_COMMON | ROTATE_AFBC_90_270},
+       {FORMAT_RGB | FORMAT_RAW_YUV | FORMAT_AFBC, ROTATE_COMMON | ROTATE_AFBC_90_270},
+       {FORMAT_RGB | FORMAT_RAW_YUV | FORMAT_AFBC, ROTATE_COMMON | ROTATE_AFBC_90_270},
+};
+
+const u32 saturn_fbcmem_sizes[] = {
+       68 * 1024,
+       68 * 1024,
+};
+EXPORT_SYMBOL(saturn_fbcmem_sizes);
+
+const u32 saturn_rdma_fixed_fbcmem_sizes[] = {
+       34 * 1024,
+       34 * 1024,
+       34 * 1024,
+       34 * 1024,
+};
+
+static const s16
+spacemit_yuv2rgb_coefs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][YUV2RGB_COEFFS] = {
+       [DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+               1192,    0, 1635, -223,
+               1192, -403, -833, 136,
+               1192, 2065,    0, -277,
+       },
+       [DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
+               1024,    0, 1436, -179,
+               1024, -354, -732,  136,
+               1024, 1814,    0, -227,
+       },
+       [DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+               1192,    0, 1836, -248,
+               1192, -218, -546,   77,
+               1192, 2163,    0, -289,
+       },
+       [DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
+               1024,    0, 1613, -202,
+               1024, -192, -479,   84,
+               1024, 1900,    0, -238,
+       },
+       [DRM_COLOR_YCBCR_BT2020][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+               1196,    0, 1724,  -937,
+               1196, -192, -668,   355,
+               1196, 2200,    0, -1175,
+       },
+       [DRM_COLOR_YCBCR_BT2020][DRM_COLOR_YCBCR_FULL_RANGE] = {
+               1024,    0, 1510, -755,
+               1024, -168, -585,  377,
+               1024, 1927,    0, -963,
+       }
+};
+
+const struct spacemit_hw_rdma saturn_le_rdmas[] = {
+       /* TODO: set max_yuv_height to 1088 instead of 1080 due to vpu fw issue */
+       {FORMAT_RGB | FORMAT_AFBC, ROTATE_COMMON},
+       {FORMAT_RGB | FORMAT_RAW_YUV | FORMAT_AFBC, ROTATE_COMMON | ROTATE_AFBC_90_270},
+       {FORMAT_RGB, ROTATE_COMMON},
+       {FORMAT_RGB | FORMAT_RAW_YUV | FORMAT_AFBC, ROTATE_COMMON | ROTATE_AFBC_90_270},
+};
+
+const u32 saturn_le_fbcmem_sizes[] = {
+       44544,  //43.5k
+       35712,  //34.875k
+};
+EXPORT_SYMBOL(saturn_le_fbcmem_sizes);
+
+const u32 saturn_le_rdma_fixed_fbcmem_sizes[] = {
+       11776,          //43.5k
+       32 * 1024,
+       2944,           //2.875k
+       32 * 1024,
+};
+
+static atomic_t mclk_cnt = ATOMIC_INIT(0);;
+bool dpu_mclk_exclusive_get(void)
+{
+       if (0 == atomic_cmpxchg(&mclk_cnt, 0, 1))
+               return true;
+       else
+               return false;
+}
+EXPORT_SYMBOL(dpu_mclk_exclusive_get);
+
+void dpu_mclk_exclusive_put(void)
+{
+       atomic_set(&mclk_cnt, 0);
+}
+EXPORT_SYMBOL(dpu_mclk_exclusive_put);
+
+struct spacemit_hw_device spacemit_dp_devices[DP_MAX_DEVICES] = {
+       [SATURN_HDMI] = {
+               .base = NULL,           /* Parsed by dts */
+               .phy_addr = 0x0,        /* Parsed by dts */
+               .plane_nums = 8,
+               .rdma_nums = ARRAY_SIZE(saturn_le_rdmas),
+               .rdmas = saturn_le_rdmas,
+               .n_formats = ARRAY_SIZE(primary_fmts),
+               .formats = primary_fmts,
+               .n_fbcmems = ARRAY_SIZE(saturn_le_fbcmem_sizes),
+               .fbcmem_sizes = saturn_le_fbcmem_sizes,
+               .rdma_fixed_fbcmem_sizes = saturn_le_rdma_fixed_fbcmem_sizes,
+               .solid_color_shift = 0,
+               .hdr_coef_size = 135,
+               .scale_coef_size = 48,
+               .is_hdmi = true,
+       },
+       [SATURN_LE] = {
+               .base = NULL,           /* Parsed by dts */
+               .phy_addr = 0x0,        /* Parsed by dts */
+               .plane_nums = 8,
+               .rdma_nums = ARRAY_SIZE(saturn_le_rdmas),
+               .rdmas = saturn_le_rdmas,
+               .n_formats = ARRAY_SIZE(primary_fmts),
+               .formats = primary_fmts,
+               .n_fbcmems = ARRAY_SIZE(saturn_le_fbcmem_sizes),
+               .fbcmem_sizes = saturn_le_fbcmem_sizes,
+               .rdma_fixed_fbcmem_sizes = saturn_le_rdma_fixed_fbcmem_sizes,
+               .solid_color_shift = 0,
+               .hdr_coef_size = 135,
+               .scale_coef_size = 48,
+               .is_hdmi = false,
+       },
+};
+EXPORT_SYMBOL(spacemit_dp_devices);
+
+static int dpu_parse_dt(struct spacemit_dpu *dpu, struct device_node *np)
+{
+       struct dpu_clk_context *clk_ctx = &dpu->clk_ctx;
+
+       clk_ctx->pxclk = of_clk_get_by_name(np, "pxclk");
+       if (IS_ERR(clk_ctx->pxclk)) {
+               pr_debug("%s, read pxclk failed from dts!\n", __func__);
+       }
+
+       clk_ctx->mclk = of_clk_get_by_name(np, "mclk");
+       if (IS_ERR(clk_ctx->mclk)) {
+               pr_debug("%s, read mclk failed from dts!\n", __func__);
+       }
+
+       clk_ctx->hclk = of_clk_get_by_name(np, "hclk");
+       if (IS_ERR(clk_ctx->hclk)) {
+               pr_debug("%s, read hclk failed from dts!\n", __func__);
+       }
+
+       clk_ctx->escclk = of_clk_get_by_name(np, "escclk");
+       if (IS_ERR(clk_ctx->escclk)) {
+               pr_debug("%s, read escclk failed from dts!\n", __func__);
+       }
+
+       clk_ctx->bitclk = of_clk_get_by_name(np, "bitclk");
+       if (IS_ERR(clk_ctx->bitclk)) {
+               pr_debug("%s, read bitclk failed from dts!\n", __func__);
+       }
+
+       clk_ctx->hmclk = of_clk_get_by_name(np, "hmclk");
+       if (IS_ERR(clk_ctx->hmclk)) {
+               pr_debug("%s, read hmclk failed from dts!\n", __func__);
+       }
+
+       if (of_property_read_u32(np, "spacemit-dpu-min-mclk", &dpu->min_mclk))
+               dpu->min_mclk = DPU_MCLK_DEFAULT;
+
+       if (of_property_read_bool(np, "spacemit-dpu-auto-fc"))
+               dpu->enable_auto_fc = 0;
+
+       if (of_property_read_u32(np, "spacemit-dpu-bitclk", &dpu->bitclk))
+               dpu->bitclk = DPU_BITCLK_DEFAULT;
+
+       if (of_property_read_u32(np, "spacemit-dpu-escclk", &dpu->escclk))
+               dpu->escclk = DPU_ESCCLK_DEFAULT;
+
+       return 0;
+}
+
+
+static unsigned int dpu_get_bpp(u32 format)
+{
+       unsigned int i = 0;
+
+       for(i = 0; i < ARRAY_SIZE(primary_fmts); i++) {
+               if (format == primary_fmts[i].format)
+                       return primary_fmts[i].bpp;
+       }
+
+       DRM_ERROR("format 0x%x is not supported!\n", format);
+       return SPACEMIT_DPU_INVALID_FORMAT_ID;
+}
+
+int dpu_calc_plane_mclk_bw(struct drm_plane *plane, \
+               struct drm_plane_state *new_state)
+{
+       /* For some platform without aclk, mclk = max(aclk, mclk) */
+       uint64_t calc_mclk = 0;
+       uint64_t Fpixclk_hblk = 0;
+       struct drm_plane_state *state = NULL;
+       struct drm_crtc *crtc = new_state->crtc;
+       struct drm_display_mode *mode = NULL;
+       struct spacemit_plane_state *spacemit_plane_state = NULL;
+       uint64_t tmp;
+       uint64_t hact = 0;
+       uint64_t fps = 0;
+       bool scl_en = 0;
+
+       struct drm_framebuffer *fb = new_state->fb;
+       const struct drm_format_info *format = fb->format;
+       unsigned int bpp = dpu_get_bpp(format->format);
+       uint64_t calc_bandwidth = 0;
+
+       struct spacemit_dpu *dpu = NULL;
+
+       state = new_state;
+       if (!crtc)
+               return 0;
+
+       dpu = crtc_to_dpu(crtc);
+       if (!dpu->enable_auto_fc)
+               return 0;
+
+       /* prepare calc mclk and bw */
+       mode = &crtc->mode;
+       hact = mode->hdisplay;
+       fps = mode->clock * 1000 / (mode->htotal * (u16)mode->vtotal);
+       spacemit_plane_state = to_spacemit_plane_state(state);
+       scl_en = spacemit_plane_state->use_scl;
+       Fpixclk_hblk = hact * (mode->vtotal) * fps; /* MHZ */
+
+       trace_dpu_fpixclk_hblk(hact, fps, (uint64_t)mode->vtotal, Fpixclk_hblk);
+
+       /* calculate mclk and bandwidth */
+       if (scl_en) {
+               unsigned int C = 0;
+               uint64_t width_ratio = 0;       /* (scl_in_width/hact) * 1000000 */
+               uint64_t height_ratio = 0;      /* roundup(scl_in_height/scl_out_height) */
+               uint64_t scl_in_width, scl_in_height, scl_out_width, scl_out_height;
+
+               if (state->rotation == DRM_MODE_ROTATE_90 || state->rotation == DRM_MODE_ROTATE_270) {
+                       scl_in_width  = state->src_h >> 16;
+                       scl_in_height = state->src_w >> 16;
+               } else {
+                       scl_in_width  = state->src_w >> 16;
+                       scl_in_height = state->src_h >> 16;
+               }
+
+               scl_out_width = state->crtc_w;
+               scl_out_height = state->crtc_h;
+
+               C = min(2 * scl_in_width / scl_out_width, 4ULL);        /* sclaer after rdma */
+               spacemit_plane_state->afbc_effc = 4 / C;
+
+               //width_ratio = scl_in_width * MHZ2HZ / hact;
+               tmp = scl_in_width * MHZ2HZ;
+               do_div(tmp, hact);
+               width_ratio = tmp;
+               height_ratio = DIV_ROUND_UP_ULL(scl_in_height, scl_out_height) * MHZ2HZ;
+               //calc_mclk = Fpixclk_hblk * max(width_ratio, MHZ2HZ) / MHZ2HZ * max(height_ratio, MHZ2HZ) / MHZ2HZ / C;
+               tmp = Fpixclk_hblk * max(width_ratio, MHZ2HZ);
+               do_div(tmp, MHZ2HZ);
+               tmp = tmp * max(height_ratio, MHZ2HZ);
+               do_div(tmp, MHZ2HZ);
+               do_div(tmp, C);
+               calc_mclk = tmp;
+
+               if (calc_mclk > DPU_MCLK_MAX) {
+                       DRM_INFO("plane:%d mclk too large %lld\n", state->zpos, calc_mclk);
+                       DRM_INFO("hact = %lld, fps = %lld, vtotal = %d, Fpixclk_hblk = %lld\n", hact, fps, (u16)mode->vtotal, Fpixclk_hblk);
+                       DRM_INFO("scl_in_width = %lld, scl_in_height = %lld, scl_out_width = %lld, scl_out_height = %lld, bpp = %d, C = %d\n", scl_in_width, scl_in_height, scl_out_width, scl_out_height, bpp, C);
+                       return -EINVAL;
+               }
+
+               //calc_bandwidth = Fpixclk_hblk * bpp * width_ratio / MHZ2HZ * height_ratio / MHZ2HZ / 8;
+               tmp = Fpixclk_hblk * bpp * width_ratio;
+               do_div(tmp, MHZ2HZ);
+               tmp = tmp * height_ratio;
+               do_div(tmp, MHZ2HZ);
+               tmp = tmp >> 3;
+               calc_bandwidth = tmp;
+               if (calc_bandwidth > (DPU_MAX_QOS_REQ * MHZ2KHZ)) {
+                       DRM_INFO("plane:%d bandwidth too large %lld\n", state->zpos, calc_bandwidth);
+                       DRM_INFO("hact = %lld, fps = %lld, vtotal = %d, Fpixclk_hblk = %lld\n", hact, fps, (u16)mode->vtotal, Fpixclk_hblk);
+                       DRM_INFO("scl_in_width = %lld, scl_in_height = %lld, scl_out_width = %lld, scl_out_height = %lld, bpp = %d, C = %d\n", scl_in_width, scl_in_height, scl_out_width, scl_out_height, bpp, C);
+                       return -EINVAL;
+               }
+               trace_dpu_mclk_scl(scl_in_width, scl_in_height, scl_out_width, scl_out_height, bpp, C, width_ratio, height_ratio);
+       } else {
+               unsigned long img_width = 0;
+
+               if (state->rotation == DRM_MODE_ROTATE_90 || state->rotation == DRM_MODE_ROTATE_270)
+                       img_width = state->src_h >> 16;
+               else
+                       img_width = state->src_w >> 16;
+
+               trace_u64_data("no scl img_width", img_width);
+               //calc_mclk = ( hact + 32 ) * MHZ2HZ / hact * Fpixclk_hblk / 2 / MHZ2HZ;
+               tmp = ( hact + 32 ) * MHZ2HZ;
+               do_div(tmp, hact);
+               tmp = tmp * Fpixclk_hblk;
+               tmp = tmp >> 1;
+               do_div(tmp, MHZ2HZ);
+               calc_mclk = tmp;
+               if (calc_mclk > DPU_MCLK_MAX) {
+                       DRM_INFO("plane:%d mclk too large %lld\n", state->zpos, calc_mclk);
+                       DRM_INFO("img_width = %ld\n", img_width);
+                       return -EINVAL;
+               }
+               //calc_bandwidth = Fpixclk_hblk * bpp * img_width / hact / 8;
+               tmp = Fpixclk_hblk * bpp * img_width;
+               do_div(tmp, hact);
+               do_div(tmp, 8);
+               calc_bandwidth = tmp;
+               if (calc_bandwidth > (DPU_MAX_QOS_REQ * MHZ2KHZ)) {
+                       DRM_INFO("plane:%d bandwidth too large %lld\n", state->zpos, calc_bandwidth);
+                       DRM_INFO("img_width = %ld\n", img_width);
+                       return -EINVAL;
+               }
+       }
+
+       dpu = crtc_to_dpu(crtc);
+       if (calc_mclk < dpu->min_mclk)
+               calc_mclk = dpu->min_mclk;
+
+       spacemit_plane_state->mclk = calc_mclk;
+
+       /* add some buffer for MMU and AFBC Header */
+       //calc_bandwidth = calc_bandwidth * 108 / 100;
+       tmp = calc_bandwidth * 108;
+       do_div(tmp, 100);
+       calc_bandwidth = tmp;
+       spacemit_plane_state->bw = calc_bandwidth;
+
+       trace_u64_data("plane calc_mclk", calc_mclk);
+       trace_u64_data("plane calc_bw", calc_bandwidth);
+       return 0;
+}
+
+static int dpu_update_clocks(struct spacemit_dpu *dpu, uint64_t mclk)
+{
+       struct dpu_clk_context *clk_ctx = &dpu->clk_ctx;
+       uint64_t cur_mclk = 0;
+       int ret = 0;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       if (!hwdev->is_hdmi) {
+               trace_u64_data("update mclk", mclk);
+               cur_mclk = clk_get_rate(clk_ctx->mclk);
+               if (cur_mclk == mclk)
+                       return 0;
+               if (dpu_mclk_exclusive_get()) {
+                       ret = clk_set_rate(clk_ctx->mclk, mclk);
+                       if (ret) {
+                               trace_u64_data("Failed to set mclk", mclk);
+                               DRM_ERROR("Failed to set DPU MCLK %lld %d\n", mclk, ret);
+                       } else {
+                               dpu_mclk_exclusive_put();
+                               dpu->cur_mclk = clk_get_rate(clk_ctx->mclk);
+                               trace_u64_data("Pass to set mclk", mclk);
+                       }
+               } else {
+                       if (mclk > dpu->cur_mclk) {
+                               trace_u64_data("MCLK using by other module", mclk);
+                               DRM_ERROR("Mclk using by other module %lld\n", mclk);
+                       } else
+                               dpu->cur_mclk = mclk;
+
+                       return 0;
+               }
+       }
+
+       return 0;
+}
+
+static int dpu_update_bw(struct spacemit_dpu *dpu, uint64_t bw)
+{
+       uint64_t __maybe_unused tmp;
+
+       trace_u64_data("update bw", bw);
+
+       if (dpu->cur_bw == bw)
+               return 0;
+
+       return 0;
+}
+
+static int dpu_finish_uboot(struct spacemit_dpu *dpu)
+{
+       void __iomem *base;
+       void __iomem *hdmi;
+       u32 value;
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       if (dpu->type == HDMI) {
+               base = (void __iomem *)ioremap(0xC0440000, 0x2A000);
+               hdmi = (void __iomem *)ioremap(0xC0400500, 0x200);
+
+               // hdmi dpu ctl regs
+               writel(0x00, base + 0x560);
+               writel(0x01, base + 0x56c);
+               // writel(0x00, base + 0x58c);
+
+               // hdmi dpu int regs
+               writel(0x00, base + 0x910);
+               writel(0x00, base + 0x938);
+               //writel(0x00, base + 0x960);
+
+               // hdmi close pll clock
+               writel(0x00, hdmi + 0xe4);
+
+               value = readl_relaxed(base + 0x910);
+               DRM_DEBUG("%s hdmi int reg4 0x910:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x938);
+               DRM_DEBUG("%s hdmi int reg14 0x938:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x960);
+               DRM_DEBUG("%s hdmi int reg24 0x960:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x560);
+               DRM_DEBUG("%s hdmi ctl reg24 0x560:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x56c);
+               DRM_DEBUG("%s hdmi ctl reg27 0x56c:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x58c);
+               DRM_DEBUG("%s hdmi ctl reg35 0x58c:0x%x\n", __func__, value);
+
+               udelay(100);
+               iounmap(base);
+               iounmap(hdmi);
+       } else if (dpu->type == DSI) {
+               base = (void __iomem *)ioremap(0xc0340000, 0x2A000);
+
+               // mipi dsi dpu ctl regs
+               writel(0x00, base + 0x560);
+               writel(0x01, base + 0x56c);
+               // writel(0x00, base + 0x58c);
+
+               // mipi dsi dpu int regs
+               writel(0x00, base + 0x910);
+               writel(0x00, base + 0x938);
+               //writel(0x00, base + 0x960);
+
+               value = readl_relaxed(base + 0x910);
+               DRM_DEBUG("%s mipi dsi int reg4 0x910:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x938);
+               DRM_DEBUG("%s mipi dsi int reg14 0x938:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x960);
+               DRM_DEBUG("%s mipi dsi int reg24 0x960:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x560);
+               DRM_DEBUG("%s mipi dsi ctl reg24 0x560:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x56c);
+               DRM_DEBUG("%s mipi dsi ctl reg27 0x56c:0x%x\n", __func__, value);
+               value = readl_relaxed(base + 0x58c);
+               DRM_DEBUG("%s mipi dsi ctl reg35 0x58c:0x%x\n", __func__, value);
+
+               udelay(100);
+               iounmap(base);
+       } else {
+               return 0;
+       }
+
+       return 0;
+}
+
+static int dpu_enable_clocks(struct spacemit_dpu *dpu)
+{
+       struct dpu_clk_context *clk_ctx = &dpu->clk_ctx;
+       struct drm_crtc *crtc = &dpu->crtc;
+       struct drm_display_mode *mode = &crtc->mode;
+       uint64_t clk_val;
+       uint64_t set_clk_val;
+       struct spacemit_drm_private *priv;
+       struct spacemit_hw_device *hwdev;
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       if (dpu->logo_booton) {
+               if (dpu->type == HDMI) {
+                       clk_prepare_enable(clk_ctx->hmclk);
+               } else if (dpu->type == DSI) {
+                       clk_prepare_enable(clk_ctx->pxclk);
+                       clk_prepare_enable(clk_ctx->mclk);
+                       clk_prepare_enable(clk_ctx->hclk);
+                       clk_prepare_enable(clk_ctx->escclk);
+                       clk_prepare_enable(clk_ctx->bitclk);
+               }
+               udelay(10);
+
+               return 0;
+       }
+
+       priv = dpu->crtc.dev->dev_private;
+       hwdev = priv->hwdev;
+
+       if (hwdev->is_hdmi) {
+               clk_prepare_enable(clk_ctx->hmclk);
+
+               clk_val = clk_get_rate(clk_ctx->hmclk);
+               if(clk_val != DPU_MCLK_DEFAULT){
+                       clk_val = clk_round_rate(clk_ctx->hmclk, DPU_MCLK_DEFAULT);
+                       if (dpu_mclk_exclusive_get()) {
+                               clk_set_rate(clk_ctx->hmclk, clk_val);
+                               DRM_DEBUG("set hdmi mclk=%lld\n", clk_val);
+                               dpu_mclk_exclusive_put();
+                       }
+               }
+
+               clk_val = clk_get_rate(clk_ctx->hmclk);
+               DRM_DEBUG("get hdmi mclk=%lld\n", clk_val);
+
+               udelay(10);
+       } else {
+               clk_prepare_enable(clk_ctx->pxclk);
+               clk_prepare_enable(clk_ctx->mclk);
+               clk_prepare_enable(clk_ctx->hclk);
+               clk_prepare_enable(clk_ctx->escclk);
+               clk_prepare_enable(clk_ctx->bitclk);
+
+               set_clk_val = mode->clock * 1000;
+               DRM_INFO("pxclk set_clk_val %lld\n", set_clk_val);
+
+               if (set_clk_val) {
+                       set_clk_val = clk_round_rate(clk_ctx->pxclk, set_clk_val);
+                       clk_val = clk_get_rate(clk_ctx->pxclk);
+                       if(clk_val != set_clk_val){
+                               clk_set_rate(clk_ctx->pxclk, set_clk_val);
+                               DRM_DEBUG("set pxclk=%lld\n", set_clk_val);
+                       }
+               }
+
+               clk_val = clk_get_rate(clk_ctx->mclk);
+               if(clk_val != DPU_MCLK_DEFAULT){
+                       clk_val = clk_round_rate(clk_ctx->mclk, DPU_MCLK_DEFAULT);
+                       if (dpu_mclk_exclusive_get()) {
+                               clk_set_rate(clk_ctx->mclk, clk_val);
+                               DRM_DEBUG("set mclk=%lld\n", clk_val);
+                               dpu_mclk_exclusive_put();
+                       }
+               }
+
+               clk_val = clk_get_rate(clk_ctx->escclk);
+               set_clk_val = dpu->escclk;
+               if(clk_val != set_clk_val){
+                       clk_val = clk_round_rate(clk_ctx->escclk, set_clk_val);
+                       clk_set_rate(clk_ctx->escclk, clk_val);
+                       DRM_DEBUG("set escclk=%lld\n", clk_val);
+               }
+
+               clk_val = clk_get_rate(clk_ctx->bitclk);
+               set_clk_val = dpu->bitclk;
+               if(clk_val != set_clk_val){
+                       clk_val = clk_round_rate(clk_ctx->bitclk, set_clk_val);
+                       clk_set_rate(clk_ctx->bitclk, clk_val);
+                       DRM_DEBUG("set bitclk=%lld\n", clk_val);
+               }
+
+               clk_val = clk_get_rate(clk_ctx->pxclk);
+               DRM_DEBUG("get pxclk=%lld\n", clk_val);
+               clk_val = clk_get_rate(clk_ctx->mclk);
+               DRM_DEBUG("get mclk=%lld\n", clk_val);
+               clk_val = clk_get_rate(clk_ctx->hclk);
+               DRM_DEBUG("get hclk=%lld\n", clk_val);
+               clk_val = clk_get_rate(clk_ctx->escclk);
+               DRM_DEBUG("get escclk=%lld\n", clk_val);
+               clk_val = clk_get_rate(clk_ctx->bitclk);
+               DRM_DEBUG("get bitclk=%lld\n", clk_val);
+
+               udelay(10);
+       }
+
+       trace_dpu_enable_clocks(dpu->dev_id);
+
+       return 0;
+}
+
+static int dpu_disable_clocks(struct spacemit_dpu *dpu)
+{
+       struct dpu_clk_context *clk_ctx = &dpu->clk_ctx;
+       struct spacemit_drm_private *priv;
+       struct spacemit_hw_device *hwdev;
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       trace_dpu_disable_clocks(dpu->dev_id);
+
+       if (dpu->logo_booton) {
+               dpu_finish_uboot(dpu);
+               if (dpu->type == HDMI) {
+                       clk_disable_unprepare(clk_ctx->hmclk);
+               } else if (dpu->type == DSI) {
+                       clk_disable_unprepare(clk_ctx->pxclk);
+                       clk_disable_unprepare(clk_ctx->mclk);
+                       clk_disable_unprepare(clk_ctx->hclk);
+                       clk_disable_unprepare(clk_ctx->escclk);
+                       clk_disable_unprepare(clk_ctx->bitclk);
+               }
+               return 0;
+       }
+
+       priv = dpu->crtc.dev->dev_private;
+       hwdev = priv->hwdev;
+
+       if (hwdev->is_hdmi) {
+               clk_disable_unprepare(clk_ctx->hmclk);
+       } else {
+               clk_disable_unprepare(clk_ctx->pxclk);
+               clk_disable_unprepare(clk_ctx->mclk);
+               clk_disable_unprepare(clk_ctx->hclk);
+               clk_disable_unprepare(clk_ctx->escclk);
+               clk_disable_unprepare(clk_ctx->bitclk);
+       }
+
+       return 0;
+}
+
+u8 spacemit_plane_hw_get_format_id(u32 format)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(primary_fmts); i++) {
+               if (primary_fmts[i].format == format)
+                       return primary_fmts[i].id;
+       }
+
+       return SPACEMIT_DPU_INVALID_FORMAT_ID;
+}
+
+static bool parsr_afbc_modifier(uint64_t modifier, uint8_t* tile_type, uint8_t* block_size, uint8_t* yuv_transform, uint8_t* split_mode)
+{
+       uint64_t super_block_size = modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK;
+
+       if (block_size && (super_block_size == AFBC_FORMAT_MOD_BLOCK_SIZE_16x16)) {
+               *block_size = 0;
+       } else if (block_size && (super_block_size == AFBC_FORMAT_MOD_BLOCK_SIZE_32x8)) {
+               *block_size = 1;
+       } else {
+               DRM_ERROR("unsupport modifier = 0x%llu, super_block_size = 0x%llu, dpu support only 16x16 and 32x8!\n", modifier, super_block_size);
+               return false;
+       }
+
+       if (tile_type && (modifier & AFBC_FORMAT_MOD_TILED)) {
+               *tile_type = 1;
+       }
+
+       if (yuv_transform && (modifier & AFBC_FORMAT_MOD_YTR)) {
+               *yuv_transform = 1;
+       }
+
+       if (split_mode && (modifier & AFBC_FORMAT_MOD_SPLIT)) {
+               *split_mode = 1;
+       }
+
+       return true;
+}
+
+void spacemit_update_csc_matrix(struct drm_plane *plane, struct drm_plane_state *old_state){
+       struct spacemit_plane_state *spacemit_plane_state = to_spacemit_plane_state(plane->state);
+       u32 rdma_id = spacemit_plane_state->rdma_id;
+       u32 module_base;
+       int color_encoding = plane->state->color_encoding;
+       int color_range = plane->state->color_range;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       int value;
+
+       module_base = RDMA0_BASE_ADDR + rdma_id * RDMA_SIZE;
+
+       if ((color_encoding != old_state->color_encoding) || (color_range != old_state->color_range)){
+               value = (spacemit_yuv2rgb_coefs[color_encoding][color_range][0] & 0x3FFF) | ((spacemit_yuv2rgb_coefs[color_encoding][color_range][1] & 0x3FFF) << 14);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, CSC_MATRIX0, value);
+               value = (spacemit_yuv2rgb_coefs[color_encoding][color_range][2] & 0x3FFF) | ((spacemit_yuv2rgb_coefs[color_encoding][color_range][3] & 0x3FFF) << 14);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, CSC_MATRIX1, value);
+               value = (spacemit_yuv2rgb_coefs[color_encoding][color_range][4] & 0x3FFF) | ((spacemit_yuv2rgb_coefs[color_encoding][color_range][5] & 0x3FFF) << 14);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, CSC_MATRIX2, value);
+               value = (spacemit_yuv2rgb_coefs[color_encoding][color_range][6] & 0x3FFF) | ((spacemit_yuv2rgb_coefs[color_encoding][color_range][7] & 0x3FFF) << 14);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, CSC_MATRIX3, value);
+               value = (spacemit_yuv2rgb_coefs[color_encoding][color_range][8] & 0x3FFF) | ((spacemit_yuv2rgb_coefs[color_encoding][color_range][9] & 0x3FFF) << 14);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, CSC_MATRIX4, value);
+               value = (spacemit_yuv2rgb_coefs[color_encoding][color_range][10] & 0x3FFF) | ((spacemit_yuv2rgb_coefs[color_encoding][color_range][11] & 0x3FFF) << 14);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, CSC_MATRIX5, value);
+       }
+}
+
+static void saturn_conf_scaler_x(struct drm_plane_state *state)
+{
+       struct spacemit_plane_state *spacemit_plane_state = to_spacemit_plane_state(state);
+       struct drm_plane *plane = state->plane;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       u32 in_width, in_height, out_width, out_height;
+       uint32_t hor_delta_phase, ver_delta_phase;
+       int64_t hor_init_phase, ver_init_phase;
+       u32 module_base;
+
+       trace_saturn_conf_scaler_x(spacemit_plane_state);
+
+       /* should never happen */
+       if (unlikely(spacemit_plane_state->scaler_id >= MAX_SCALER_NUMS))
+               panic("Invalid scaler id:%d\n", spacemit_plane_state->scaler_id);
+
+       if (state->rotation == DRM_MODE_ROTATE_90 || state->rotation == DRM_MODE_ROTATE_270) {
+               in_width  = state->src_h >> 16;
+               in_height = state->src_w >> 16;
+       } else {
+               in_width  = state->src_w >> 16;
+               in_height = state->src_h >> 16;
+       }
+
+       out_width = state->crtc_w;
+       out_height = state->crtc_h;
+
+       /* Config SCALER scaling regs */
+       module_base = SCALER0_ONLINE_BASE_ADDR + spacemit_plane_state->scaler_id * SCALER_SIZE;
+
+       hor_delta_phase = in_width * 65536 / out_width;
+       ver_delta_phase = in_height * 65536 / out_height;
+
+       write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_7, hor_delta_phase); //0x10000
+       write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_8, ver_delta_phase); //0x5555
+
+       //TODO: start cor
+       hor_init_phase = ((int64_t)hor_delta_phase - 65536) >> 1;
+       ver_init_phase = ((int64_t)ver_delta_phase - 65536) >> 1;
+
+       DRM_DEBUG("hor_delta:%d ver_delta:%d\n", hor_delta_phase, ver_delta_phase);
+       DRM_DEBUG("hor_init_phase:%lld ver_init_phase:%lld\n", hor_init_phase, ver_init_phase);
+
+       if (hor_init_phase >= 0) {
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_3, hor_init_phase & 0x00000000ffffffff);  //0x0
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_4, 0);
+       } else { //convert to positive value if negative
+               hor_init_phase += 8589934592;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_3, hor_init_phase & 0x00000000ffffffff);
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_4, (hor_init_phase & 0x0000000100000000) >> 32);
+       }
+
+       if (ver_init_phase >= 0) {
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_5, ver_init_phase & 0x00000000ffffffff);
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_6, 0);
+       } else { //convert to positive value if negative
+               ver_init_phase += 8589934592;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_5, ver_init_phase & 0x00000000ffffffff);
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_6, (ver_init_phase & 0x0000000100000000) >> 32);
+       }
+
+       // enable both ver and hor
+       write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_0, in_width << 8 | 0x1 << 1 | 0x1);
+       write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_1, out_width << 16 | in_height);
+       write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_2, out_height);
+
+       /* Config RDMA scaling regs */
+       module_base = RDMA0_BASE_ADDR + spacemit_plane_state->rdma_id * RDMA_SIZE;
+
+       ver_init_phase = ((int64_t)ver_delta_phase - 65536) / 2;
+       if (ver_init_phase >= 0) {
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_INIT_PHASE_V_LOW, ver_init_phase & 0x00000000ffffffff);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_INIT_PHASE_V_HIGH, 0);  //TODO: only 1 bit, read then write??
+       } else { //convert to positive value if negative
+               ver_init_phase += 8589934592;
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_INIT_PHASE_V_LOW, ver_init_phase & 0x00000000ffffffff);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_INIT_PHASE_V_HIGH, (ver_init_phase & 0x0000000100000000) >> 32);
+       }
+
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_SCL_RATIO_V, 0x1 << 20 | ver_delta_phase);
+}
+
+void saturn_conf_scaler_coefs(struct drm_plane *plane, struct spacemit_plane_state *spacemit_pstate){
+       struct drm_property_blob * blob = spacemit_pstate->scale_coefs_blob_prop;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       int scale_num = 192;
+       u32 module_base;
+       int val;
+       int *coef_data;
+
+       /* should never happen */
+       if (unlikely(spacemit_pstate->scaler_id >= MAX_SCALER_NUMS))
+               DRM_ERROR("Invalid scaler id:%d\n", spacemit_pstate->scaler_id);
+
+       /* Config SCALER scaling regs */
+       module_base = SCALER0_ONLINE_BASE_ADDR + spacemit_pstate->scaler_id * SCALER_SIZE;
+
+       if (blob){
+               /* should never happen */
+               if (unlikely(blob->length != scale_num))
+                       DRM_ERROR("The blob length %ld is not correct scale_num %d\n", blob->length, scale_num);
+
+               coef_data = (int *)blob->data;
+               if (unlikely(NULL == coef_data)){
+                       DRM_ERROR("The coef data is NULL\n");
+                       return;
+               }
+
+               val  = (coef_data[0] & 0xFFFF) | coef_data[1] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_9, val);
+               val  = (coef_data[2] & 0xFFFF) | coef_data[3] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_10, val);
+               val  = (coef_data[4] & 0xFFFF) | coef_data[5] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_11, val);
+               val  = (coef_data[6] & 0xFFFF) | coef_data[7] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_12, val);
+               val  = (coef_data[8] & 0xFFFF) | coef_data[9] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_13, val);
+               val  = (coef_data[10] & 0xFFFF) | coef_data[11] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_14, val);
+               val  = (coef_data[12] & 0xFFFF) | coef_data[13] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_15, val);
+               val  = (coef_data[14] & 0xFFFF) | coef_data[15] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_16, val);
+               val  = (coef_data[16] & 0xFFFF) | coef_data[17] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_17, val);
+               val  = (coef_data[18] & 0xFFFF) | coef_data[19] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_18, val);
+               val  = (coef_data[20] & 0xFFFF) | coef_data[21] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_19, val);
+               val  = (coef_data[22] & 0xFFFF) | coef_data[23] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_20, val);
+               val  = (coef_data[24] & 0xFFFF) | coef_data[25] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_21, val);
+               val  = (coef_data[26] & 0xFFFF) | coef_data[27] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_22, val);
+               val  = (coef_data[28] & 0xFFFF) | coef_data[29] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_23, val);
+               val  = (coef_data[30] & 0xFFFF) | coef_data[31] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_24, val);
+               val  = (coef_data[32] & 0xFFFF) | coef_data[33] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_25, val);
+               val  = (coef_data[34] & 0xFFFF) | coef_data[35] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_26, val);
+               val  = (coef_data[36] & 0xFFFF) | coef_data[37] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_27, val);
+               val  = (coef_data[38] & 0xFFFF) | coef_data[39] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_28, val);
+               val  = (coef_data[40] & 0xFFFF) | coef_data[41] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_29, val);
+               val  = (coef_data[42] & 0xFFFF) | coef_data[43] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_30, val);
+               val  = (coef_data[44] & 0xFFFF) | coef_data[45] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_31, val);
+               val  = (coef_data[46] & 0xFFFF) | coef_data[47] << 16;
+               write_to_cmdlist(priv, SCALER_X_REG, module_base, disp_scl_reg_32, val);
+       }
+}
+
+static void spacemit_set_afbc_info(struct spacemit_drm_private *priv, uint64_t modifier, u32 module_base)
+{
+       bool ret = false;
+       u8 tile_type = 0;
+       u8 block_size = 0;
+       u8 yuv_transform = 0;
+       u8 split_mode = 0;
+
+       ret = parsr_afbc_modifier(modifier, &tile_type, &block_size, &yuv_transform, &split_mode);
+       if (!ret) {
+               DRM_ERROR("Faild to set afbc plane\n");
+               return;
+       }
+
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, AFBC_CFG, tile_type << 3 | block_size << 2 | yuv_transform << 1 | split_mode);
+
+       return;
+}
+
+#define CONFIG_HW_COMPOSER_LAYER(id) \
+{\
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _rect_ltopx, crtc_x); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _rect_ltopy, crtc_y); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _rect_rbotx, crtc_x + crtc_w - 1); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _rect_rboty, crtc_y + crtc_h - 1); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _color_key_en, 0); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _blend_mode, blend_mode); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _alpha_sel, alpha_sel); \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _layer_alpha, alpha); \
+       if (unlikely(solid_en)) { \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _solid_color_R, solid_r); \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _solid_color_G, solid_g); \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _solid_color_B, solid_b); \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _solid_color_A, solid_a); \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _solid_en, 1); \
+               DRM_DEBUG("solid_r:0x%x, solid_g:0x%x, solid_b:0x%x, solid_a:0x%x\n", \
+                       solid_r, solid_g, solid_b, solid_a); \
+       } else { \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _solid_en, 0); \
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _layer_id, rdma_id); \
+       } \
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl ## id ## _en, 1); \
+}
+
+void spacemit_plane_update_hw_channel(struct drm_plane *plane)
+{
+       struct drm_plane_state *state = plane->state;
+       struct spacemit_plane *spacemit_plane = to_spacemit_plane(plane);
+       struct drm_framebuffer *fb = plane->state->fb;
+       struct spacemit_plane_state *spacemit_plane_state = to_spacemit_plane_state(state);
+       u32 rdma_id = spacemit_plane_state->rdma_id;
+       u8 alpha = state->alpha >> 8;
+       u16 pixel_alpha = state->pixel_blend_mode;
+       u32 src_w, src_h, src_x, src_y;
+       u32 crtc_w, crtc_h, crtc_x, crtc_y;
+       u32 alpha_sel, blend_mode = 0;
+       u8 uv_swap = 0;
+       uint32_t fccf = fb->format->format;
+       u32 module_base, base;
+       u32 val;
+       bool solid_en = false;
+       u32 solid_a, solid_r, solid_g, solid_b;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       bool is_afbc = (fb->modifier > 0);
+       u8 channel = crtc_to_dpu(state->crtc)->dev_id;
+
+       trace_spacemit_plane_update_hw_channel("rdma_id", rdma_id);
+
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+
+       crtc_w = state->crtc_w;
+       crtc_h = state->crtc_h;
+       crtc_x = state->crtc_x;
+       crtc_y = state->crtc_y;
+
+       if (rdma_id == RDMA_INVALID_ID)
+               solid_en = true;
+
+       spacemit_plane_state->is_offline = 0;
+
+       trace_dpu_plane_info(state, fb, rdma_id, alpha,
+                            state->rotation, is_afbc);
+
+       /* For solid color both src_w and src_h are 0 */
+       if (solid_en == false) {
+               /* Set RDMA regs */
+               module_base = RDMA0_BASE_ADDR + rdma_id * RDMA_SIZE;
+               val = 0x0 << 26 | 0x0 << 25 | 0xf << 17 | 0x0 << 15 | channel << 12 | 0x0 << 7 | 0xf << 2 | (is_afbc ? 1 : 0);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LAYER_CTRL, val);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, COMPSR_Y_OFST, crtc_y);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_SCL_RATIO_V, 0x0 << 20);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_IMG_SIZE, fb->height << 16 | fb->width);
+               val = src_y + src_h;
+
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_CROP_POS_START, src_y << 16 | src_x);
+               val = ((val - 1) << 16) | (src_x + src_w - 1);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_CROP_POS_END, val);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_ALPHA01, 0x0 << 16 | 0x0);
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, LEFT_ALPHA23, 0x0 << 16 | 0x0);
+
+               saturn_write_fbcmem_regs(state, rdma_id, module_base, NULL);
+
+               val = 0;
+               /* setup the rotation and axis flip bits */
+               if (state->rotation & DRM_MODE_ROTATE_MASK)
+                       val = ilog2(plane->state->rotation & DRM_MODE_ROTATE_MASK) & 0x3;
+               if (state->rotation & DRM_MODE_REFLECT_MASK)
+                       val = ilog2(plane->state->rotation & DRM_MODE_REFLECT_MASK);
+
+               if (fccf == DRM_FORMAT_YVYU || fccf == DRM_FORMAT_VYUY || fccf == DRM_FORMAT_NV21 || fccf == DRM_FORMAT_YVU420 || fccf == DRM_FORMAT_Q401) {
+                       uv_swap = 1;
+               }
+
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, ROT_MODE, val << 7 | uv_swap << 6 | spacemit_plane_state->format);
+
+               if (fb->modifier) {
+                       spacemit_set_afbc_info(priv, fb->modifier, module_base);
+               }
+
+               if (spacemit_plane_state->use_scl){
+                       saturn_conf_scaler_x(state);
+
+                       /*scaling mismatch gpu render result without these coefs*/
+                       saturn_conf_scaler_coefs(plane, spacemit_plane_state);
+               }
+       } else {
+               solid_r = (u8)spacemit_plane_state->solid_color;
+               solid_g = (u8)(spacemit_plane_state->solid_color >> 8);
+               solid_b = (u8)(spacemit_plane_state->solid_color >> 16);
+               solid_a = (u8)(spacemit_plane_state->solid_color >> 24);
+               solid_r = solid_r << hwdev->solid_color_shift;
+               solid_g = solid_g << hwdev->solid_color_shift;
+               solid_b = solid_b << hwdev->solid_color_shift;
+       }
+
+       /* Set layer regs */
+       if (state->fb->format->has_alpha)
+               alpha_sel = 0x1;
+       else
+               alpha_sel = 0x0;
+
+       switch (pixel_alpha) {
+       case DRM_MODE_BLEND_PIXEL_NONE:
+               /* k1x supports knone + layer alpha*/
+               alpha_sel = 0x0;
+               fallthrough;
+       case DRM_MODE_BLEND_COVERAGE:
+               blend_mode = 0x0;
+               break;
+       case DRM_MODE_BLEND_PREMULTI:
+               blend_mode = 0x1;
+               break;
+       default:
+               DRM_ERROR("Unsupported blend mode!\n");
+       }
+
+       /* enable composer and bind RDMA */
+       base = CMP_BASE_ADDR(channel);
+       switch(spacemit_plane->hw_pid) {
+       case 0:
+               CONFIG_HW_COMPOSER_LAYER(0);
+               break;
+       case 1:
+               CONFIG_HW_COMPOSER_LAYER(1);
+               break;
+       case 2:
+               CONFIG_HW_COMPOSER_LAYER(2);
+               break;
+       case 3:
+               CONFIG_HW_COMPOSER_LAYER(3);
+               break;
+       case 4:
+               CONFIG_HW_COMPOSER_LAYER(4);
+               break;
+       case 5:
+               CONFIG_HW_COMPOSER_LAYER(5);
+               break;
+       case 6:
+               CONFIG_HW_COMPOSER_LAYER(6);
+               break;
+       case 7:
+               CONFIG_HW_COMPOSER_LAYER(7);
+               break;
+       default:
+               DRM_ERROR("%s unsupported zpos:%d\n", __func__, state->zpos);
+               break;
+       }
+}
+
+void spacemit_plane_disable_hw_channel(struct drm_plane *plane, struct drm_plane_state *old_state)
+{
+       struct spacemit_plane *p = to_spacemit_plane(plane);
+       u8 channel = crtc_to_dpu(old_state->crtc)->dev_id;
+       u32 base = CMP_BASE_ADDR(channel);
+       u32 rdma_id = to_spacemit_plane_state(old_state)->rdma_id;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       DRM_DEBUG("%s() layer_id = %u rdma_id:%d\n", __func__, p->hw_pid, rdma_id);
+
+       trace_spacemit_plane_disable_hw_channel(p->hw_pid, rdma_id);
+
+       switch (p->hw_pid) {
+       case 0:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl0_en, 0);
+               break;
+       case 1:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl1_en, 0);
+               break;
+       case 2:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl2_en, 0);
+               break;
+       case 3:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl3_en, 0);
+               break;
+       case 4:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl4_en, 0);
+               break;
+       case 5:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl5_en, 0);
+               break;
+       case 6:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl6_en, 0);
+               break;
+       case 7:
+               dpu_write_reg(hwdev, CMPS_X_REG, base, m_nl7_en, 0);
+               break;
+       default:
+               DRM_ERROR("%s unsupported zpos:%d\n", __func__, old_state->zpos);
+               break;
+       }
+}
+
+static u32 saturn_conf_dpuctrl_scaling(struct spacemit_dpu *dpu)
+{
+       struct spacemit_dpu_scaler *scaler = NULL;
+       struct spacemit_crtc_state *ac = to_spacemit_crtc_state(dpu->crtc.state);
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u32 scl_en = 0;
+       u32 i;
+
+       for (i = 0; i < MAX_SCALER_NUMS; i++) {
+               scaler = &(ac->scalers[i]);
+               DRM_DEBUG("scaler%d: in_use:0x%x rdma_id:%d\n", i, scaler->in_use, scaler->rdma_id);
+               trace_dpuctrl_scaling_setting(i, scaler->in_use, scaler->rdma_id);
+               if (scaler->in_use) {
+                       scl_en |= (i + 1);
+                       //TODO: expand below code when more than 2 sclaers
+                       if (i == 0)
+                               dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl_nml_scl0_layer_id, scaler->rdma_id);
+                       if (i == 1)
+                               dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl_nml_scl1_layer_id, scaler->rdma_id);
+               }
+       }
+
+       DRM_DEBUG("scl_en:%d\n", scl_en);
+       trace_dpuctrl_scaling("scl_en", scl_en);
+       return scl_en;
+}
+
+void spacemit_update_hdr_matrix(struct drm_plane *plane, struct spacemit_plane_state *spacemit_pstate){
+
+}
+
+static u32 saturn_conf_dpuctrl_rdma(struct spacemit_dpu *dpu)
+{
+       struct drm_crtc *crtc = &dpu->crtc;
+       struct drm_plane *plane;
+       u32 rdma_en = 0;
+
+       /* Find out the active rdmas */
+       drm_atomic_crtc_for_each_plane(plane, crtc) {
+               u32 rdma_id = to_spacemit_plane_state(plane->state)->rdma_id;
+
+               if (rdma_id != RDMA_INVALID_ID)
+                       rdma_en |= (1 << rdma_id);
+       }
+
+       trace_dpuctrl("rdma_en", rdma_en);
+
+       return rdma_en;
+}
+
+void saturn_conf_dpuctrl_color_matrix(struct spacemit_dpu *dpu, struct drm_crtc_state *old_state)
+{
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       struct drm_crtc_state *state = dpu->crtc.state;
+       struct spacemit_crtc_state *spacemit_state = to_spacemit_crtc_state(state);
+       struct drm_property_blob * blob = spacemit_state->color_matrix_blob_prop;
+       int *color_matrix;
+
+       /*For color matrix, if no update from user space,
+       we keep the original configuration, do not change the value of any color matix register*/
+       if (blob){
+               color_matrix = (int*)blob->data;
+
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_npost_proc_en, 1);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_nendmatrix_en, 1);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_ngain_to_full_en, 0);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_nmatrix_en, 0);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_nfront_tmootf_en, 0);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_nend_tmootf_en, 0);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_neotf_en, 0);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_noetf_en, 0);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table0, color_matrix[0]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table1, color_matrix[1]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table2, color_matrix[2]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table3, color_matrix[3]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table4, color_matrix[4]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table5, color_matrix[5]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table6, color_matrix[6]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table7, color_matrix[7]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_table8, color_matrix[8]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_offset0, color_matrix[9]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_offset1, color_matrix[10]);
+               dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_pendmatrix_offset2, color_matrix[11]);
+       }
+
+       return;
+}
+
+static void saturn_conf_dpuctrl(struct drm_crtc *crtc,
+                   struct drm_crtc_state *old_state)
+{
+       u32 scl_en = 0;
+       u32 rdma_en = 0;
+       u32 base = DPU_CTRL_BASE_ADDR;
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       rdma_en = saturn_conf_dpuctrl_rdma(dpu);
+       scl_en = saturn_conf_dpuctrl_scaling(dpu);
+
+       cmdlist_sort_by_group(crtc);
+       cmdlist_atomic_commit(crtc, old_state);
+
+       switch (dpu->dev_id) {
+       case ONLINE2:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl2_nml_scl_en, scl_en);
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl2_nml_rch_en, rdma_en);
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl2_nml_outctl_en, 0x1);
+               break;
+       case OFFLINE0:
+               /* TODO */
+               break;
+       default:
+               DRM_ERROR("%s, dpu_id %d is invalid!\n", __func__, dpu->dev_id);
+               break;
+       }
+}
+
+static void saturn_irq_enable(struct spacemit_dpu *dpu, bool enable)
+{
+       u32 base = DPU_INT_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       trace_saturn_irq_enable("irq", enable);
+
+       if (dpu->dev_id == ONLINE2) {
+               /* enable online2 irq */
+               dpu_write_reg(hwdev, DPU_INTP_REG, base, b.onl2_nml_frm_timing_eof_int_msk, enable ? 1 : 0);
+               dpu_write_reg(hwdev, DPU_INTP_REG, base, b.onl2_nml_cfg_rdy_clr_int_msk, enable ? 1 : 0);
+               dpu_write_reg(hwdev, DPU_INTP_REG, base, b.onl2_nml_frm_timing_unflow_int_msk, enable ? 1 : 0);
+               dpu_write_reg(hwdev, DPU_INTP_REG, base, b.onl2_nml_cmdlist_ch_frm_cfg_done_int_msk, enable ? 0xF : 0);
+       }
+}
+
+static void saturn_enable_vsync(struct spacemit_dpu *dpu, bool enable)
+{
+       u32 base = DPU_INT_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       trace_saturn_enable_vsync("vsync", enable);
+       if (dpu->dev_id == ONLINE2)
+               dpu_write_reg(hwdev, DPU_INTP_REG, base, b.onl2_nml_frm_timing_vsync_int_msk, enable ? 1 : 0);
+}
+
+static void saturn_ctrl_cfg_ready(struct spacemit_dpu *dpu, bool enable)
+{
+       u32 base = DPU_CTRL_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       trace_saturn_ctrl_cfg_ready(dpu->dev_id, enable);
+
+       switch (dpu->dev_id) {
+       case ONLINE0:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl0_nml_cfg_rdy, enable ? 1 : 0);
+               break;
+       case ONLINE1:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl1_nml_cfg_rdy, enable ? 1 : 0);
+               break;
+       case ONLINE2:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl2_nml_cfg_rdy, enable ? 1 : 0);
+               break;
+       case OFFLINE0:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl3_nml_cfg_rdy, enable ? 1 : 0);
+               break;
+       case OFFLINE1:
+               dpu_write_reg(hwdev, DPU_CTL_REG, base, ctl4_nml_cfg_rdy, enable ? 1 : 0);
+               break;
+       default:
+               DRM_ERROR("id is invalid!\n");
+               break;
+       }
+}
+
+static void saturn_ctrl_sw_start(struct spacemit_dpu *dpu, bool enable)
+{
+       u32 base = DPU_CTRL_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       trace_saturn_ctrl_sw_start(dpu->dev_id, enable);
+
+       switch (dpu->dev_id) {
+       case ONLINE0:
+               dpu_write_reg_w1c(hwdev, DPU_CTL_REG, base, ctl0_sw_start, enable ? 1 : 0);
+               break;
+       case ONLINE1:
+               dpu_write_reg_w1c(hwdev, DPU_CTL_REG, base, ctl1_sw_start, enable ? 1 : 0);
+               break;
+       case ONLINE2:
+               dpu_write_reg_w1c(hwdev, DPU_CTL_REG, base, ctl2_sw_start, enable ? 1 : 0);
+               break;
+       case OFFLINE0:
+               dpu_write_reg_w1c(hwdev, DPU_CTL_REG, base, ctl3_sw_start, enable ? 1 : 0);
+               break;
+       default:
+               DRM_ERROR("id is invalid!\n");
+               break;
+       }
+}
+
+void saturn_wb_disable(struct spacemit_dpu *dpu)
+{
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_nml_wb_en, 0);
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_nml_cfg_rdy, 1);
+}
+
+void saturn_wb_config(struct spacemit_dpu *dpu)
+{
+
+}
+
+static u32 dpu_get_version(struct spacemit_dpu *dpu)
+{
+       return 0;
+}
+
+static void saturn_init_csc(struct spacemit_dpu *dpu){
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u32 module_base;
+       int i = 0;
+
+       for(i = 0; i < hwdev->rdma_nums; i++){
+               module_base = RDMA0_BASE_ADDR + i * RDMA_SIZE;
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix00, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][0] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix01, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][1] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix02, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][2] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix03, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][3] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix10, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][4] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix11, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][5] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix12, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][6] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix13, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][7] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix20, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][8] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix21, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][9] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix22, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][10] & 0x3FFF);
+               dpu_write_reg(hwdev, RDMA_PATH_X_REG, module_base, b.csc_matrix23, spacemit_yuv2rgb_coefs[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE][11] & 0x3FFF);
+       }
+}
+
+static void saturn_init_regs(struct spacemit_dpu *dpu)
+{
+       u32 base = 0;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       struct drm_crtc *crtc = &dpu->crtc;
+       struct drm_display_mode *mode = &crtc->mode;
+       u8 channel = dpu->dev_id;
+       u16 vfp, vbp, vsync, hfp, hbp, hsync;
+       // u32 value;
+
+       hsync = mode->hsync_end - mode->hsync_start;
+       hbp = mode->htotal - mode->hsync_end;
+       hfp = mode->hsync_start - mode->hdisplay;
+       vsync = mode->vsync_end - mode->vsync_start;
+       vbp = mode->vtotal - mode->vsync_end;
+       vfp = mode->vsync_start - mode->vdisplay;
+       trace_drm_display_mode_info(mode);
+
+       base = CMP_BASE_ADDR(channel);
+       /* set bg color to black */
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nbg_color_B, 0x0);
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nbg_color_R, 0x0);
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nbg_color_G, 0xFF);
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_nbg_color_A, 0xFF);
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_ncmps_en, 1);
+
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_noutput_width, mode->hdisplay);
+       dpu_write_reg(hwdev, CMPS_X_REG, base, m_noutput_height, mode->vdisplay);
+
+       base = OUTCTRL_BASE_ADDR(channel);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, m_n_inheight, mode->vdisplay);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, m_n_inwdith, mode->hdisplay);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, scale_en, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, dither_en, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, sbs_en, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, split_en, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, narrow_yuv_en, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, cmd_screen, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, frame_timing_en, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, split_overlap, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, hblank, 0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, back_ground_r, 0xfff);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, back_ground_g, 0x0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, back_ground_b, 0x0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, sof_pre_ln_num, 0x0);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, cfg_ln_num_intp, 0x0);
+
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, hbp, hbp);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, hfp, hfp);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, vfp, vfp);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, vbp, vbp);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, hsync_width, hsync);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, hsp, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, vsync_width, vsync);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, vsp, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, h_active, mode->hdisplay);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, v_active, mode->vdisplay);
+
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, user, 2); /* RGB888 */
+
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, eof_ln_dly, 16);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, eof0_irq_mask, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, underflow0_irq_mask, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, eof1_irq_mask, 1);
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, underflow1_irq_mask, 1);
+
+       // if (hwdev->is_hdmi) {
+       //      dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, base, disp_ready_man_en, 1);
+       //      value = dpu_read_reg(hwdev, OUTCTRL_TOP_X_REG, base, value32[31]);
+       //      DRM_INFO("%s read OUTCTRL_TOP_X_REG value32[31] 0x%x", __func__, value);
+       // }
+
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_video_mod, 0x1);
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_dbg_mod, 0x0);
+       /*
+        * ctl2_timing_inter0 use default value
+        * ctl2_timing_inter1 = 2 * ⌈fmclk / fdscclk⌉, set 0xf as max value
+        */
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_timing_inter1, DPU_CTRL_MAX_TIMING_INTER1);
+
+       dpu_write_reg(hwdev, MMU_REG, MMU_BASE_ADDR, b.cfg_dmac0_rd_outs_num, 16);
+       dpu_write_reg(hwdev, MMU_REG, MMU_BASE_ADDR, b.cfg_dmac0_axi_burst, 1);
+
+       /* totally disable post proc */
+       dpu_write_reg(hwdev, OUTCTRL_PROC_X_REG, PP2_BASE_ADDR, m_npost_proc_en, 0);
+}
+
+static void saturn_setup_dma_top(struct spacemit_dpu *dpu)
+{
+       u32 base = DMA_TOP_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, img_rr_ratio, 0x10);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, round_robin_mode, 0);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, pixel_num_th, 4);
+
+       //regnum: 2, offset: 0x08
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, rdma_timeout_limit, 0xFFFE);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, wdma_timeout_limit, 0xFFFF);
+
+       //regnum: 7, offset: 0x1C
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, online_rqos, DPU_QOS_NORMAL);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, offline_rqos, DPU_QOS_LOW);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, online_wqos, DPU_QOS_LOW);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, offline_wqos, DPU_QOS_LOW);
+
+       //regnum: 17, offset: 0x44
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, cmdlist_rqos, 4);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, dmac0_burst_length, 1);
+
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, dmac0_rd_outs_num, 8);
+       dpu_write_reg(hwdev, DMA_TOP_REG, base, dmac0_wr_outs_num, 16);
+
+       if (dpu_read_reg(hwdev, DMA_TOP_REG, base, cmdlist_rqos) < DPU_QOS_URGENT || \
+           dpu_read_reg(hwdev, DMA_TOP_REG, base, online_rqos) < dpu_read_reg(hwdev, DMA_TOP_REG, base, offline_rqos)) {
+               // panic("dma_top qos cfg failed!\n");
+       }
+}
+
+
+static void saturn_setup_mmu_top(struct spacemit_dpu *dpu)
+{
+       unsigned int val;
+       unsigned int rd_outs_num = 0;
+       u32 base = MMU_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       rd_outs_num = RD_OUTS_NUM / 2;
+
+       /* MMU TOP init setting */
+       dpu_write_reg(hwdev, MMU_REG, base, v.TBU_Timelimit, (0x1 << 16) | RDMA_TIMELIMIT);
+       dpu_write_reg(hwdev, MMU_REG, base, v.TBU_AXI_PORT_SEL, 0);
+
+       val = dpu_read_reg(hwdev, MMU_REG, base, v.MMU_Dmac0_Reg);
+       val &= ~(0xFF << 16);
+       val |= (rd_outs_num << 16);
+       dpu_write_reg(hwdev, MMU_REG, base, v.MMU_Dmac0_Reg, val);
+
+       val = dpu_read_reg(hwdev, MMU_REG, base, v.MMU_Dmac1_Reg);
+       val &= ~(0xFF << 16);
+       val |= (rd_outs_num << 16);
+       dpu_write_reg(hwdev, MMU_REG, base, v.MMU_Dmac1_Reg, val);
+
+       val = dpu_read_reg(hwdev, MMU_REG, base, v.MMU_Dmac2_Reg);
+       val &= ~(0xFF << 16);
+       val |= (rd_outs_num << 16);
+       dpu_write_reg(hwdev, MMU_REG, base, v.MMU_Dmac2_Reg, val);
+
+       val = dpu_read_reg(hwdev, MMU_REG, base, v.MMU_Dmac3_Reg);
+       val &= ~(0xFF << 16);
+       val |= (rd_outs_num << 16);
+       dpu_write_reg(hwdev, MMU_REG, base, v.MMU_Dmac3_Reg, val);
+}
+
+static int dpu_init(struct spacemit_dpu *dpu)
+{
+       unsigned int timeout = 1000;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+#ifdef CONFIG_SPACEMIT_FPGA
+       void __iomem *addr = (void __iomem *)ioremap(0xD4282800, 100);
+#endif
+       void __iomem *ciu_addr = (void __iomem *)ioremap(0xD4282C00, 0x200);
+       u32 value;
+
+       DRM_INFO("%s \n", __func__);
+       trace_dpu_init(dpu->dev_id);
+
+       while(timeout) {
+               if (dpu_read_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, value32[27]) == 0)
+                       break;
+               udelay(100);
+               timeout--;
+       };
+       if (timeout == 0)
+               DRM_ERROR("%s wait cfg ready done timeout\n", __func__);
+
+#ifdef CONFIG_SPACEMIT_FPGA
+       // for FPGA: enable PMU
+       writel(0xffa1ffff, addr + 0x44);
+       writel(0xFF65FF05, addr + 0x4c);
+#endif
+
+       // modified hdmi and mipi dsi qos
+       value = readl_relaxed(ciu_addr + 0x011c);
+       DRM_DEBUG("%s ciu offset 0x011c:0x%x\n", __func__, value);
+       value = readl_relaxed(ciu_addr + 0x0124);
+       DRM_DEBUG("%s ciu offset 0x0124:0x%x\n", __func__, value);
+       writel(value | 0xffff, ciu_addr + 0x0124);
+       udelay(2);
+       value = readl_relaxed(ciu_addr + 0x0124);
+       DRM_DEBUG("%s ciu offset 0x0124:0x%x\n", __func__, value);
+
+       saturn_init_regs(dpu);
+       saturn_setup_dma_top(dpu);
+       saturn_setup_mmu_top(dpu);
+       saturn_irq_enable(dpu, true);
+       saturn_init_csc(dpu);
+
+       return 0;
+}
+
+static void dpu_uninit(struct spacemit_dpu *dpu)
+{
+       trace_dpu_uninit(dpu->dev_id);
+       saturn_irq_enable(dpu, false);
+}
+
+static uint32_t dpu_isr(struct spacemit_dpu *dpu)
+{
+       uint32_t irq_raw, int_mask = 0;
+       u32 base = DPU_INT_BASE_ADDR;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       static bool flip_done[DP_MAX_DEVICES] = {false};
+       struct drm_writeback_connector *wb_conn = &dpu->wb_connector;
+       u8 channel = dpu->dev_id;
+       int flip_id;
+
+       if (hwdev->is_hdmi) {
+               flip_id = SATURN_HDMI;
+       } else {
+               flip_id = SATURN_LE;
+       }
+
+       trace_dpu_isr(dpu->dev_id);
+
+       if (dpu->dev_id == ONLINE2) {
+               irq_raw = dpu_read_reg(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_24);
+               trace_dpu_isr_status("ONLINE2", irq_raw );
+               /* underrun */
+               if (irq_raw & DPU_INT_FRM_TIMING_UNFLOW) {
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_14, DPU_INT_FRM_TIMING_UNFLOW);
+                       trace_dpu_isr_status("Under Run!", irq_raw & DPU_INT_FRM_TIMING_UNFLOW);
+                       trace_dpu_isr_ul_data("DPU Mclk", dpu->cur_mclk);
+                       trace_dpu_isr_ul_data("DPU BW", dpu->cur_bw);
+                       dpu_dump_reg(dpu);
+                       queue_work(dpu->dpu_underrun_wq, &dpu->work_stop_trace);
+                       DRM_ERROR_RATELIMITED("Under Run! DPU_Mclk = %ld, DPU BW = %ld\n", (unsigned long)dpu->cur_mclk, (unsigned long)dpu->cur_bw);
+                       int_mask |= DPU_INT_UNDERRUN;
+               }
+               /* eof */
+               if (irq_raw & DPU_INT_FRM_TIMING_EOF) {
+                       dpu_write_reg_w1c(hwdev, OUTCTRL_TOP_X_REG, OUTCTRL_BASE_ADDR(channel), eof0_irq_status, 1);
+                       dpu_write_reg_w1c(hwdev, OUTCTRL_TOP_X_REG, OUTCTRL_BASE_ADDR(channel), eof1_irq_status, 1);
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_14, DPU_INT_FRM_TIMING_EOF);
+                       trace_dpu_isr_status("eof", irq_raw & DPU_INT_FRM_TIMING_EOF);
+               }
+               /* cfg ready clear */
+               if (irq_raw & DPU_INT_CFG_RDY_CLR) {
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_14, DPU_INT_CFG_RDY_CLR);
+                       trace_dpu_isr_status("cfg_rdy_clr", irq_raw & DPU_INT_CFG_RDY_CLR);
+                       flip_done[flip_id] = false;
+
+                       trace_u64_data("irq crtc mclk cur", dpu->cur_mclk);
+                       trace_u64_data("irq crtc mclk new", dpu->new_mclk);
+                       trace_u64_data("irq crtc bw cur", dpu->cur_bw);
+                       trace_u64_data("irq crtc bw new", dpu->new_bw);
+                       if (dpu->enable_auto_fc && dpu->new_mclk < dpu->cur_mclk) {
+                               trace_u64_data("run wq to set mclk", dpu->new_mclk);
+                               queue_work(system_wq, &dpu->work_update_clk);
+                       }
+                       if (dpu->enable_auto_fc && dpu->new_bw < dpu->cur_bw) {
+                               trace_u64_data("run wq to set bw", dpu->new_bw);
+                               queue_work(system_wq, &dpu->work_update_bw);
+                       }
+               }
+               /* vsync */
+               if (irq_raw & DPU_INT_FRM_TIMING_VSYNC) {
+                       struct drm_crtc *crtc = &dpu->crtc;
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_14, DPU_INT_FRM_TIMING_VSYNC);
+                       trace_u64_data("dpu name", (u64)hwdev->is_hdmi);
+                       trace_dpu_isr_status("vsync", irq_raw & DPU_INT_FRM_TIMING_VSYNC);
+                       drm_crtc_handle_vblank(crtc);
+
+                       if (!flip_done[flip_id]) {
+                               struct drm_device *drm = dpu->crtc.dev;
+                               struct drm_pending_vblank_event *event = crtc->state->event;
+
+                               flip_done[flip_id] = true;
+
+                               spin_lock(&drm->event_lock);
+                               if (crtc->state->event) {
+                                       /*
+                                        * Set event to NULL first to ensure event is consumed
+                                        * before drm_atomic_helper_commit_hw_done.
+                                        */
+                                       crtc->state->event = NULL;
+                                       drm_crtc_send_vblank_event(crtc, event);
+                               }
+                               spin_unlock(&drm->event_lock);
+                       }
+                       dpu_dump_fps(dpu);
+               }
+               /* wb done */
+               if (irq_raw & DPU_INT_WB_DONE) {
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, b.onl2_nml_wb_done_int_sts, irq_raw);
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_14, DPU_INT_WB_DONE);
+                       saturn_wb_disable(dpu);
+                       drm_writeback_signal_completion(wb_conn, 0);
+               }
+               /* rest irq status */
+               if (irq_raw & DPU_REST_INT_BITS)
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, v.dpu_int_reg_14, DPU_REST_INT_BITS);
+
+       }
+       if (dpu->dev_id == OFFLINE0) {
+               /*cfg ready*/
+               irq_raw = dpu_read_reg(hwdev, DPU_INTP_REG, base, b.offl0_cfg_rdy_clr_int_raw);
+               if (irq_raw)
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, b.offl0_cfg_rdy_clr_int_sts, 1);
+
+               /*wb*/
+               irq_raw = dpu_read_reg(hwdev, DPU_INTP_REG, base, b.offl0_wb_frm_done_int_raw);
+               if (irq_raw)
+                       dpu_write_reg_w1c(hwdev, DPU_INTP_REG, base, b.offl0_wb_frm_done_int_sts, irq_raw);
+       }
+
+       return int_mask;
+}
+
+static void dpu_run(struct drm_crtc *crtc,
+                   struct drm_crtc_state *old_state)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       trace_dpu_run(dpu->dev_id);
+
+       /* config dpuctrl modules */
+       saturn_conf_dpuctrl(crtc, old_state);
+
+       //dsb(sy);
+       mb();
+       saturn_ctrl_cfg_ready(dpu, true);
+
+       if (unlikely(dpu->is_1st_f)) {
+               DRM_INFO("DPU type %d id %d Start!\n", dpu->type, dpu->dev_id);
+               dpu->is_1st_f = false;
+               saturn_ctrl_sw_start(dpu, true);
+       }
+
+#ifdef CONFIG_ARM64
+       __iomb();
+#else
+       dma_rmb();
+#endif
+}
+
+static void dpu_stop(struct spacemit_dpu *dpu)
+{
+       unsigned int timeout = DPU_STOP_TIMEOUT;
+       struct spacemit_drm_private *priv = dpu->crtc.dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u8 channel = dpu->dev_id;
+
+       flush_workqueue(dpu->dpu_underrun_wq);
+
+       trace_dpu_stop(dpu->dev_id);
+
+       while(timeout) {
+               if (dpu_read_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, value32[27]) == 0)
+                       break;
+               udelay(10);
+               timeout--;
+       };
+       if (timeout == 0)
+               DRM_ERROR("%s wait cfg ready done timeout\n", __func__);
+       else
+               DRM_DEBUG("%s wait cfg ready done %d\n", __func__, timeout);
+
+       dpu_write_reg(hwdev, OUTCTRL_TOP_X_REG, OUTCTRL_BASE_ADDR(channel), value32[6], 0x0);
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_nml_rch_en, 0x0);
+       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, ctl2_nml_cfg_rdy, 0x1);
+
+       timeout = DPU_STOP_TIMEOUT;
+       while(timeout) {
+               if (dpu_read_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR, value32[27]) & 1) {
+                       udelay(10);
+                       timeout--;
+                       continue;
+               } else
+                       break;
+       };
+       if (timeout == 0)
+               DRM_ERROR("%s stop timeout\n", __func__);
+       else
+               DRM_DEBUG("%s stop Done %d\n", __func__, timeout);
+}
+
+static int dpu_modeset(struct spacemit_dpu *dpu, struct drm_mode_modeinfo *mode)
+{
+       return 0;
+}
+
+static void dpu_enable_vsync(struct spacemit_dpu *dpu)
+{
+       saturn_enable_vsync(dpu, true);
+}
+
+static void dpu_disable_vsync(struct spacemit_dpu *dpu)
+{
+       saturn_enable_vsync(dpu, false);
+}
+
+
+static struct dpu_core_ops dpu_saturn_ops = {
+       .parse_dt = dpu_parse_dt,
+       .version = dpu_get_version,
+       .init = dpu_init,
+       .uninit = dpu_uninit,
+       .run = dpu_run,
+       .stop = dpu_stop,
+       .isr = dpu_isr,
+       .modeset = dpu_modeset,
+       .enable_clk = dpu_enable_clocks,
+       .disable_clk = dpu_disable_clocks,
+       .update_clk = dpu_update_clocks,
+       .update_bw = dpu_update_bw,
+       .enable_vsync = dpu_enable_vsync,
+       .disable_vsync = dpu_disable_vsync,
+       .cal_layer_fbcmem_size = saturn_cal_layer_fbcmem_size,
+       .calc_plane_mclk_bw = dpu_calc_plane_mclk_bw,
+       .adjust_rdma_fbcmem = saturn_adjust_rdma_fbcmem,
+       .wb_config = saturn_wb_config,
+};
+
+static struct ops_entry entry = {
+       .ver = "spacemit-saturn",
+       .ops = &dpu_saturn_ops,
+};
+
+#ifndef MODULE
+static int __init dpu_core_register(void)
+#else
+int dpu_core_register(void)
+#endif
+{
+       return dpu_core_ops_register(&entry);
+}
+#ifndef MODULE
+subsys_initcall(dpu_core_register);
+#endif
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_saturn.h b/drivers/gpu/drm/spacemit/dpu/dpu_saturn.h
new file mode 100644 (file)
index 0000000..e43a7de
--- /dev/null
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _DPU_SATURN_H_
+#define _DPU_SATURN_H_
+
+#include "saturn_regs/reg_map.h"
+#include "../spacemit_dpu.h"
+
+#ifndef min
+#define min(x, y) (((x)<(y))?(x):(y))
+#endif
+
+#ifndef max
+#define max(x, y) (((x)>(y))?(x):(y))
+#endif
+
+#ifndef clip
+#define clip(x, a, b) (max(a, min(x, b)))
+#endif
+
+/* Supported variants of the hardware */
+enum {
+       SATURN_HDMI = 0,
+       SATURN_LE,
+       /* keep the next entry last */
+       DP_MAX_DEVICES
+};
+
+struct dpu_format_id {
+       u32 format;             /* DRM fourcc */
+       u8 id;                  /* used internally */
+       u8 bpp;                 /* bit per pixel */
+};
+
+#define SPACEMIT_DPU_INVALID_FORMAT_ID 0xff
+
+enum format_features {
+       FORMAT_RGB  = BIT(0),
+       FORMAT_RAW_YUV  = BIT(1),
+       FORMAT_AFBC = BIT(2),
+};
+
+enum rotation_features {
+       ROTATE_COMMON = BIT(0),         /* supports rotation on raw and afbc 0/180, x and y */
+       ROTATE_RAW_90_270 = BIT(1),     /* supports rotation on raw 90/270 */
+       ROTATE_AFBC_90_270 = BIT(2),    /* supports rotation on afbc 90/270 */
+};
+
+struct spacemit_hw_rdma {
+       u16 formats;
+       u16 rots;
+};
+
+extern const u32 saturn_fbcmem_sizes[2];
+extern const u32 saturn_le_fbcmem_sizes[2];
+
+void spacemit_plane_update_hw_channel(struct drm_plane *plane);
+void spacemit_update_hdr_matrix(struct drm_plane *plane, struct spacemit_plane_state *spacemit_pstate);
+void spacemit_update_csc_matrix(struct drm_plane *plane, struct drm_plane_state *old_state);
+void spacemit_plane_disable_hw_channel(struct drm_plane *plane, struct drm_plane_state *old_state);
+void saturn_conf_dpuctrl_color_matrix(struct spacemit_dpu *dpu, struct drm_crtc_state *old_state);
+u8 spacemit_plane_hw_get_format_id(u32 format);
+extern struct spacemit_hw_device spacemit_dp_devices[DP_MAX_DEVICES];
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_trace.h b/drivers/gpu/drm/spacemit/dpu/dpu_trace.h
new file mode 100644 (file)
index 0000000..51f5621
--- /dev/null
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM dpu
+
+#if !defined(_DPU_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _DPU_TRACE_H_
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#include "dpu_saturn.h"
+
+TRACE_EVENT(drm_display_mode_info,
+       TP_PROTO(struct drm_display_mode *mode),
+       TP_ARGS(mode),
+       TP_STRUCT__entry(
+               __field(u16, hdisplay)
+               __field(u16, hbp)
+               __field(u16, hfp)
+               __field(u16, hsync)
+               __field(u16, vdisplay)
+               __field(u16, vbp)
+               __field(u16, vfp)
+               __field(u16, vsync)
+       ),
+       TP_fast_assign(
+               __entry->hdisplay = mode->hdisplay;
+               __entry->hbp = mode->htotal - mode->hsync_end;
+               __entry->hfp = mode->hsync_start - mode->hdisplay;
+               __entry->hsync = mode->hsync_end - mode->hsync_start;
+               __entry->vdisplay = mode->vdisplay;
+               __entry->vbp = mode->vtotal - mode->vsync_end;
+               __entry->vfp = mode->vsync_start - mode->vdisplay;
+               __entry->vsync = mode->vsync_end - mode->vsync_start;
+       ),
+       TP_printk("drm_display_mode: hdisplay=%d vdisplay=%d hsync=%d vsync=%d hbp=%d hfp=%d vbp=%d vfp=%d",
+                 __entry->hdisplay, __entry->vdisplay,  __entry->hsync, __entry->vsync,
+                 __entry->hbp, __entry->hfp,  __entry->vbp, __entry->vfp
+       )
+);
+
+TRACE_EVENT(dpu_plane_info,
+       TP_PROTO(struct drm_plane_state *state, struct drm_framebuffer *fb,
+               u32 rdma_id, u32 alpha, u32 rotation, u32 afbc),
+       TP_ARGS(state, fb, rdma_id, alpha, rotation, afbc),
+       TP_STRUCT__entry(
+                       __field(u32, rdma_id)
+                       __field(u32, src_w)
+                       __field(u32, src_h)
+                       __field(u32, src_x)
+                       __field(u32, src_y)
+                       __field(u32, crtc_w)
+                       __field(u32, crtc_h)
+                       __field(u32, crtc_x)
+                       __field(u32, crtc_y)
+                       __field(u32, width)
+                       __field(u32, height)
+                       __field(u32, format)
+                       __field(u32, blend_mode)
+                       __field(u32, alpha)
+                       __field(u32, zpos)
+                       __field(u32, rotation)
+                       __field(u32, afbc)
+       ),
+       TP_fast_assign(
+                       __entry->rdma_id = rdma_id;
+                       __entry->src_w = state->src_w >> 16;
+                       __entry->src_h = state->src_h >> 16;
+                       __entry->src_x = state->src_x >> 16;
+                       __entry->src_y = state->src_y >> 16;
+                       __entry->crtc_w = state->crtc_w;
+                       __entry->crtc_h = state->crtc_h;
+                       __entry->crtc_x = state->crtc_x;
+                       __entry->crtc_y = state->crtc_y;
+                       __entry->width = fb->width;
+                       __entry->height = fb->height;
+                       __entry->format = fb->format->format;
+                       __entry->blend_mode = state->pixel_blend_mode;
+                       __entry->alpha = alpha;
+                       __entry->zpos = state->zpos;
+                       __entry->rotation = rotation;
+                       __entry->afbc = afbc;
+       ),
+       TP_printk("rdma_id=%d src: w=%d h=%d x=%d y=%d crtc: w=%d h=%d x=%d y=%d "
+                 "width=%d height=%d fmt=0x%x blend=%d alpha:%d "
+                 "zpos=%d rot:%d afbc:%d",
+                 __entry->rdma_id, __entry->src_w,
+                 __entry->src_h, __entry->src_x,
+                 __entry->src_y, __entry->crtc_w,
+                 __entry->crtc_h, __entry->crtc_x,
+                 __entry->crtc_y, __entry->width,
+                 __entry->height, __entry->format,
+                 __entry->blend_mode, __entry->alpha,
+                 __entry->zpos, __entry->rotation,
+                 __entry->afbc)
+);
+
+DECLARE_EVENT_CLASS(dpu_status_template,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status),
+       TP_STRUCT__entry(
+               __string(name_str, name)
+               __field(int, status)
+       ),
+       TP_fast_assign(
+               __assign_str(name_str, name);
+               __entry->status = status;
+       ),
+       TP_printk("%s: 0x%x",
+                 __get_str(name_str), __entry->status)
+);
+
+DEFINE_EVENT(dpu_status_template, dpuctrl_scaling,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status)
+);
+DEFINE_EVENT(dpu_status_template, dpuctrl,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status)
+);
+DEFINE_EVENT(dpu_status_template, spacemit_plane_update_hw_channel,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status)
+);
+DEFINE_EVENT(dpu_status_template, saturn_irq_enable,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status)
+);
+DEFINE_EVENT(dpu_status_template, saturn_enable_vsync,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status)
+);
+DEFINE_EVENT(dpu_status_template, dpu_isr_status,
+       TP_PROTO(const char *name, int status),
+       TP_ARGS(name, status)
+);
+
+DECLARE_EVENT_CLASS(dpu_ul_data_template,
+       TP_PROTO(const char *name, unsigned long ul_data),
+       TP_ARGS(name, ul_data),
+       TP_STRUCT__entry(
+               __string(name_str, name)
+               __field(unsigned long, ul_data)
+       ),
+       TP_fast_assign(
+               __assign_str(name_str, name);
+               __entry->ul_data = ul_data;
+       ),
+       TP_printk("%s: %ld",
+                 __get_str(name_str), __entry->ul_data)
+);
+
+DEFINE_EVENT(dpu_ul_data_template, dpu_isr_ul_data,
+       TP_PROTO(const char *name, unsigned long ul_data),
+       TP_ARGS(name, ul_data)
+);
+
+DECLARE_EVENT_CLASS(dpu_uint64_t_data_template,
+       TP_PROTO(const char *name, uint64_t data),
+       TP_ARGS(name, data),
+       TP_STRUCT__entry(
+               __string(name_str, name)
+               __field(uint64_t, data)
+       ),
+       TP_fast_assign(
+               __assign_str(name_str, name);
+               __entry->data = data;
+       ),
+       TP_printk("%s: %lld",
+                 __get_str(name_str), __entry->data)
+);
+
+DEFINE_EVENT(dpu_uint64_t_data_template, u64_data,
+        TP_PROTO(const char *name, uint64_t data),
+        TP_ARGS(name, data)
+);
+
+TRACE_EVENT(spacemit_plane_disable_hw_channel,
+       TP_PROTO(uint32_t layer_id, uint32_t rdma_id),
+       TP_ARGS(layer_id, rdma_id),
+       TP_STRUCT__entry(
+               __field(uint32_t, layer_id)
+               __field(uint32_t, rdma_id)
+       ),
+       TP_fast_assign(
+               __entry->layer_id = layer_id;
+               __entry->rdma_id = rdma_id;
+       ),
+       TP_printk("layer_id:%d rdma_id:%d",
+                 __entry->layer_id, __entry->rdma_id)
+);
+
+TRACE_EVENT(dpu_mclk_scl,
+       TP_PROTO(uint64_t in_width, uint64_t in_height, uint64_t out_width, uint64_t out_height, uint64_t bpp, uint64_t C, uint64_t width_ratio, uint64_t height_ratio),
+       TP_ARGS(in_width, in_height, out_width, out_height, bpp, C, width_ratio, height_ratio),
+       TP_STRUCT__entry(
+               __field(uint64_t, in_width)
+               __field(uint64_t, in_height)
+               __field(uint64_t, out_width)
+               __field(uint64_t, out_height)
+               __field(uint64_t, bpp)
+               __field(uint64_t, C)
+               __field(uint64_t, width_ratio)
+               __field(uint64_t, height_ratio)
+       ),
+       TP_fast_assign(
+               __entry->in_width = in_width;
+               __entry->in_height = in_height;
+               __entry->out_width = out_width;
+               __entry->out_height = out_height;
+               __entry->bpp = bpp;
+               __entry->C = C;
+               __entry->width_ratio = width_ratio;
+               __entry->height_ratio = height_ratio;
+       ),
+       TP_printk("scl_in_width = %lld, scl_in_height = %lld, scl_out_width = %lld, scl_out_height = %lld, bpp = %lld, C = %lld, width_ratio = %lld, height_ratio = %lld \n",
+               __entry->in_width, __entry->in_height, __entry->out_width,
+               __entry->out_height, __entry->bpp, __entry->C,
+               __entry->width_ratio, __entry->height_ratio)
+);
+
+TRACE_EVENT(dpu_fpixclk_hblk,
+       TP_PROTO(uint64_t hact, uint64_t fps, uint64_t vtotal, uint64_t fpixclk_hblk),
+       TP_ARGS(hact, fps, vtotal, fpixclk_hblk),
+       TP_STRUCT__entry(
+               __field(uint64_t, hact)
+               __field(uint64_t, fps)
+               __field(uint64_t, vtotal)
+               __field(uint64_t, fpixclk_hblk)
+       ),
+       TP_fast_assign(
+               __entry->hact = hact;
+               __entry->fps = fps;
+               __entry->vtotal = vtotal;
+               __entry->fpixclk_hblk = fpixclk_hblk;
+       ),
+       TP_printk("hact = %lld, fps = %lld, vtotal = %lld, Fpixclk_hblk = %lld\n",
+                 __entry->hact, __entry->fps, __entry->vtotal, __entry->fpixclk_hblk)
+);
+
+TRACE_EVENT(dpuctrl_scaling_setting,
+       TP_PROTO(uint32_t id, int in_use, int rdma_id),
+       TP_ARGS(id, in_use, rdma_id),
+       TP_STRUCT__entry(
+               __field(uint32_t, id)
+               __field(int, in_use)
+               __field(int, rdma_id)
+       ),
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->in_use = in_use;
+               __entry->rdma_id = rdma_id;
+       ),
+       TP_printk("scaler%d: in_use:0x%x rdma_id:%d",
+                 __entry->id, __entry->in_use, __entry->rdma_id)
+);
+
+TRACE_EVENT(dpu_reg_info,
+       TP_PROTO(u8* mod_name, phys_addr_t p_addr, uint32_t reg_num),
+       TP_ARGS(mod_name, p_addr, reg_num),
+       TP_STRUCT__entry(
+               __string(name_str, mod_name)
+               __field(phys_addr_t, p_addr)
+               __field(uint32_t, reg_num)
+       ),
+       TP_fast_assign(
+               __assign_str(name_str, mod_name);
+               __entry->p_addr = p_addr;
+               __entry->reg_num = reg_num;
+       ),
+       TP_printk("%s(0x%08llx), num:%d",
+               __get_str(name_str), __entry->p_addr, __entry->reg_num)
+);
+
+TRACE_EVENT(dpu_reg_dump,
+       TP_PROTO(uint32_t reg, uint32_t val),
+       TP_ARGS(reg, val),
+       TP_STRUCT__entry(
+               __field(uint32_t, reg)
+               __field(uint32_t, val)
+       ),
+       TP_fast_assign(
+               __entry->reg = reg;
+               __entry->val = val;
+       ),
+       TP_printk("0x%08x: 0x%08x",
+                 __entry->reg, __entry->val)
+);
+
+DECLARE_EVENT_CLASS(dpu_func_template,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id),
+       TP_STRUCT__entry(
+               __field(uint32_t, dev_id)
+       ),
+       TP_fast_assign(
+               __entry->dev_id = dev_id;
+       ),
+       TP_printk("dev_id=%d", __entry->dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_check_rdma,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_check,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_update,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_reset,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_duplicate_state,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_destroy_state,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_plane_init,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_enable_clocks,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_disable_clocks,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, saturn_wait_for_cfg_ready,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, saturn_wait_for_eof,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, saturn_wait_for_wb,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, saturn_run_display,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_init,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_uninit,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_isr,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_run,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, dpu_stop,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_mode_set_nofb,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_enable,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_disable,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_check,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_begin,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_flush,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_enable_vblank,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_crtc_disable_vblank,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_dpu_run,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_dpu_stop,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_dpu_init,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+DEFINE_EVENT(dpu_func_template, spacemit_dpu_uninit,
+       TP_PROTO(uint32_t dev_id),
+       TP_ARGS(dev_id)
+);
+
+DECLARE_EVENT_CLASS(dpu_ctrl_template,
+       TP_PROTO(int id, int enable),
+       TP_ARGS(id, enable),
+       TP_STRUCT__entry(
+               __field(uint32_t, id)
+               __field(uint32_t, enable)
+       ),
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->enable = enable;
+       ),
+       TP_printk("id=%d enable:%d",
+                 __entry->id, __entry->enable)
+);
+DEFINE_EVENT(dpu_ctrl_template, saturn_ctrl_cfg_ready,
+       TP_PROTO(int id, int enable),
+       TP_ARGS(id, enable)
+);
+DEFINE_EVENT(dpu_ctrl_template, saturn_ctrl_sw_start,
+       TP_PROTO(int id, int enable),
+       TP_ARGS(id, enable)
+);
+
+
+TRACE_EVENT(saturn_conf_scaler_x,
+       TP_PROTO(struct spacemit_plane_state *state),
+       TP_ARGS(state),
+       TP_STRUCT__entry(
+               __field(int, id)
+       ),
+       TP_fast_assign(
+               __entry->id = state->scaler_id;
+       ),
+       TP_printk("scaler_id:%d", __entry->id)
+);
+
+
+#endif /* _DPU_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_FILE
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/spacemit/dpu/
+
+#define TRACE_INCLUDE_FILE dpu_trace
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_fbcmem.c b/drivers/gpu/drm/spacemit/dpu/saturn_fbcmem.c
new file mode 100644 (file)
index 0000000..d759c9a
--- /dev/null
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/stddef.h>
+#include <linux/export.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_mode.h>
+#include <drm/drm_print.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_fourcc.h>
+#include "../spacemit_cmdlist.h"
+#include "saturn_fbcmem.h"
+
+static int get_fbc_block_size_by_modifier(uint64_t modifier, u32* out_fbc_block_size) {
+       int ret_val = 0;
+       uint64_t super_block_size = modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK;
+
+       if (super_block_size == AFBC_FORMAT_MOD_BLOCK_SIZE_16x16) {
+               *out_fbc_block_size = FBC_BLOCK_SIZE_16x16;
+       } else if (super_block_size == AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) {
+               *out_fbc_block_size = FBC_BLOCK_SIZE_32x8;
+       } else {
+               DRM_ERROR("FBC_MEM: failed to get fbc block size info for modifier = 0x%llx\n", super_block_size);
+               ret_val = -1;
+       }
+
+       return ret_val;
+}
+
+static bool is_bpp32_in_fbc_mem_cal(uint32_t drm_4cc_fmt) {
+       bool is_taken_as_bpp32 = false;
+       const struct drm_format_info *info = NULL;
+
+       info = drm_format_info(drm_4cc_fmt);
+       if (info->num_planes == 1 && info->cpp[0] == 4) {
+               is_taken_as_bpp32 = true;
+       } else {
+               switch (drm_4cc_fmt) {
+                       case DRM_FORMAT_RGB888:   //RDMA_FMT_RGB_888:
+                       case DRM_FORMAT_BGR888:   //RDMA_FMT_BGR_888:
+                               is_taken_as_bpp32 = true;
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       return is_taken_as_bpp32;
+}
+
+static u32 adjust_afbc_layer_mem_size(u8 rdma_work_mode, u32 drm_4cc_fmt, u32 fbc_block_size, bool rot_90_or_270, u32 fbcmem_size) {
+       u32 ret_size = fbcmem_size;
+       bool is_bpp_32 = is_bpp32_in_fbc_mem_cal(drm_4cc_fmt);
+
+       //if left_right mode fbc_layout 32x8, is_bpp_32 and not rotation 90/270, hw request double mem_size.
+       if ((rdma_work_mode == LEFT_RIGHT) && (fbc_block_size == FBC_BLOCK_SIZE_32x8) && is_bpp_32 && (rot_90_or_270 == false)) {
+               ret_size *= 2;
+       }
+       DRM_DEBUG("FBC_MEM: %s, rdma_mode = %u, is_bpp_32 = %d, fbc_block_size = %u, rot_90_or_270 = %d, input = %u, ret = %u\n",
+               __func__, rdma_work_mode, is_bpp_32, fbc_block_size, rot_90_or_270, fbcmem_size, ret_size);
+
+       return ret_size;
+}
+
+int get_raw_data_plane_rdma_mem_size(u32 drm_4cc_fmt, bool rot_90_or_270, u32 plane_crop_width, u32* output_mem_size) {
+       u8 index = 0;
+       u32 ret_mem_size = 0;
+       u32 data_plane_mem_size[3] = {0}; //max 3 plane, YUV data
+       const struct drm_format_info *info = NULL;
+       //struct drm_format_name_buf fmt_name = {0};
+
+       //drm_get_format_name(drm_4cc_fmt, &fmt_name);
+       if (rot_90_or_270) {
+               ret_mem_size = 1024; //dpu hardware request 32K fix size, 32 byte unit has considered
+       } else {
+               info = drm_format_info(drm_4cc_fmt);
+               if (info->cpp[0] == 0) {
+                       DRM_ERROR("FBC_MEM: not support format %d\n", drm_4cc_fmt);
+                       return -1;
+               }
+               if (info->num_planes == 1 && info->is_yuv == false) {
+                       data_plane_mem_size[0] = plane_crop_width * info->cpp[0];
+               } else if (info->num_planes >= 2 && info->is_yuv) {
+                       data_plane_mem_size[0] = plane_crop_width * info->cpp[0];
+                       data_plane_mem_size[1] = plane_crop_width * info->cpp[1]/info->hsub;
+                       if (info->num_planes == 3) {
+                               data_plane_mem_size[2] = plane_crop_width * info->cpp[2]/info->vsub;
+                       }
+               } else {
+                       DRM_ERROR("FBC_MEM: not considered drm format %d\n", drm_4cc_fmt);
+                       return -1;
+               }
+               for (index = 0; index < info->num_planes; index++) { //max 3 plane, YUV data
+                       data_plane_mem_size[index] = roundup(data_plane_mem_size[index], 64); //dpu hardware request
+                       ret_mem_size += data_plane_mem_size[index];
+               }
+               ret_mem_size = roundup(ret_mem_size, 64);
+               ret_mem_size = ret_mem_size/32;
+       }
+       if (output_mem_size) {
+               *output_mem_size = ret_mem_size;
+               DRM_DEBUG("FBC_MEM: raw layer, fmt = %d, rot_90_270 = %d, crop_w = %u, cal memsize = %u\n",
+               drm_4cc_fmt, rot_90_or_270, plane_crop_width, *output_mem_size);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(get_raw_data_plane_rdma_mem_size);
+
+int get_afbc_data_plane_min_rdma_mem_size(u8 rdma_work_mode, u32 drm_4cc_fmt,
+               u32 crop_start_x, u32 crop_start_y, u32 crop_width, u32 crop_height,
+               u32 fbc_block_size, bool rot_90_or_270, u8 min_lines, u32* output_mem_size) {
+       bool is_bpp_32 = false;
+       uint32_t ret_mem_size = 0;
+       uint32_t crop_start_align = 0;
+       uint32_t crop_end_align = 0;
+       uint32_t bbox_width = 0;
+       uint32_t num_addr_per_line = 0;
+       uint32_t num_addr_frac4 = 0;
+       uint32_t num_addr_4line = 0;
+       uint32_t num_addr_frac8 = 0;
+       uint32_t num_addr_8line = 0;
+       uint32_t align_num = (fbc_block_size == FBC_BLOCK_SIZE_16x16) ? 16 : 32;
+       //struct drm_format_name_buf fmt_name = {0};
+
+       //drm_get_format_name(drm_4cc_fmt, &fmt_name);
+       DRM_DEBUG("FBC_MEM: afbc layer, fmt = %d, \
+               crop_x = %u, crop_y = %u, crop_w = %u, crop_h = %u, \
+               fbc_block_size = %u, rot_90_270 = %u, min_lines = %u, rdma_mode = %u\n",
+               drm_4cc_fmt, crop_start_x, crop_start_y, crop_width,
+               crop_height, fbc_block_size, rot_90_or_270, min_lines, rdma_work_mode);
+
+       if (rot_90_or_270) {
+               crop_start_align = rounddown(crop_start_y, align_num);
+               crop_end_align = roundup(crop_start_y + crop_height, align_num) - 1;
+       } else {
+               crop_start_align = rounddown(crop_start_x, align_num);
+               crop_end_align = roundup(crop_start_x + crop_width, align_num) - 1;
+       }
+
+       bbox_width = crop_end_align - crop_start_align + 1;
+       is_bpp_32 = is_bpp32_in_fbc_mem_cal(drm_4cc_fmt);
+       num_addr_per_line = is_bpp_32 ? bbox_width/8 : bbox_width/16;
+
+       if (min_lines == 4) {
+               num_addr_frac4 = (num_addr_per_line % 8 == 0) ? num_addr_per_line/8 : (num_addr_per_line/8 + 1);
+               num_addr_4line = num_addr_frac4 * 8 * 4;
+               ret_mem_size = num_addr_4line;
+       } else if (min_lines == 8) {
+               num_addr_frac8 = (num_addr_per_line % 16 == 0) ? num_addr_per_line/16 : (num_addr_per_line/16 + 1);
+               num_addr_8line = num_addr_frac8 * 16 * 8;
+               ret_mem_size = num_addr_8line;
+       }
+
+       ret_mem_size = adjust_afbc_layer_mem_size(rdma_work_mode, drm_4cc_fmt, fbc_block_size, rot_90_or_270, ret_mem_size);
+       *output_mem_size = ret_mem_size;
+
+       DRM_DEBUG("FBC_MEM: afbc layer, fmt = %d, \
+               crop_x = %u, crop_y = %u, crop_w = %u, crop_h = %u, \
+               fbc_block_size = %u, rot_90_270 = %u, min_lines = %u, rdma_mode = %u, cal memsize = %u\n",
+               drm_4cc_fmt, crop_start_x, crop_start_y, crop_width,
+               crop_height, fbc_block_size, rot_90_or_270, min_lines, rdma_work_mode, *output_mem_size);
+
+       return 0;
+}
+EXPORT_SYMBOL(get_afbc_data_plane_min_rdma_mem_size);
+
+void inline saturn_write_fbcmem_regs(struct drm_plane_state *state, u32 rdma_id,
+                                    u32 module_base, volatile RDMA_PATH_X_REG *rdma_regs)
+{
+       struct drm_crtc_state *crtc_state = state->crtc->state;
+       struct drm_crtc *crtc = crtc_state->crtc;
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+       const struct spacemit_dpu_rdma *rdmas = to_spacemit_crtc_state(crtc_state)->rdmas;
+       u32 size  = rdmas[rdma_id].fbcmem.size;// / FBCMEM_UNIT;
+       u32 start = rdmas[rdma_id].fbcmem.start;// / FBCMEM_UNIT;
+       bool map   = rdmas[rdma_id].fbcmem.map;
+
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, module_base, FBC_MEM_SIZE, map << 28 | start << 16 | size);
+
+       return;
+}
+
+int saturn_cal_layer_fbcmem_size(struct drm_plane *plane, \
+                                  struct drm_plane_state *state)
+{
+       int ret = 0;
+       //u32 adjust_fbcmem_size = 0;
+       struct spacemit_plane_state *pstate = to_spacemit_plane_state(state);
+       uint64_t modifier = pstate->state.fb->modifier;
+
+       u32 drm_4cc_fmt = pstate->state.fb->format->format;
+       unsigned int rotation = pstate->state.rotation;
+       bool rot_90_or_270 = (rotation & DRM_MODE_ROTATE_90) || (rotation & DRM_MODE_ROTATE_270);
+       u32 crop_x = pstate->state.src_x >> 16; //check
+       u32 crop_y = pstate->state.src_y >> 16; //check
+       u32 crop_w = pstate->state.src_w >> 16; //check
+       u32 crop_h = pstate->state.src_h >> 16; //check
+       u32 fbc_block_size = FBC_BLOCK_SIZE_LIMIT;
+       struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
+       const struct spacemit_dpu_rdma *rdmas = to_spacemit_crtc_state(crtc_state)->rdmas;
+       u8 rdma_work_mode = rdmas[pstate->rdma_id].mode;
+       u8 min_lines = 4; //TODO
+
+       if (modifier == 0) { //raw data layer
+               ret = get_raw_data_plane_rdma_mem_size(drm_4cc_fmt, rot_90_or_270, crop_w, &(pstate->fbcmem_size));
+       } else { //afbc data layer
+               ret = get_fbc_block_size_by_modifier(modifier, &fbc_block_size);
+               if (ret < 0) {
+                       DRM_ERROR("FBC_MEM: failed to get fbc block size info for modifier = 0x%llx\n", modifier);
+                       return -1;
+               }
+               if (modifier && drm_4cc_fmt == DRM_FORMAT_YUV420_8BIT) { //video layer
+                       min_lines = 8; //dpu hardware request 8 line at least
+               }
+               ret = get_afbc_data_plane_min_rdma_mem_size(rdma_work_mode, drm_4cc_fmt, crop_x, crop_y,
+                       crop_w, crop_h, fbc_block_size, rot_90_or_270, min_lines, &(pstate->fbcmem_size));
+       }
+       if (ret < 0) {
+               DRM_ERROR("FBC_MEM: faied to get plane mem size for plane: src_x = %u, scr_y = %u, src_h = %u, src_w = %u\n",
+                               pstate->state.src_x, pstate->state.src_y, pstate->state.src_h, pstate->state.src_w);
+       }
+
+       return ret;
+}
+
+int saturn_adjust_rdma_fbcmem(struct spacemit_hw_device *hwdev, \
+                                    struct spacemit_dpu_rdma *rdmas)
+{
+       int ret = -1;
+       u8 index = 0;
+       u8 sec_fbcmem_index = 0;
+       u32 pri_fbcmem_size = 0;
+       u32 sec_fbcmem_size = 0;
+       u32* fbc_mems_left = NULL;
+       u32 cur_rdma_fbcmem_size = 0;
+       u8 rdma_nums = hwdev->rdma_nums;
+       bool pre_odd_rdma_use_shared_fbc_mem = false;
+
+       struct spacemit_dpu_fbcmem * fbcmem = NULL;
+       for (index = 0; index < rdma_nums; index++) {
+               fbcmem = &(rdmas[index].fbcmem);
+               DRM_DEBUG("input rdmas[%u/%u]: mode = %d, start = %d, size = %d, map = %d\n",
+                       index, rdma_nums, rdmas[index].mode, fbcmem->start, fbcmem->size, fbcmem->map);
+       }
+
+       fbc_mems_left = kzalloc(sizeof(u32) * (rdma_nums/2), GFP_KERNEL);
+       if (fbc_mems_left == NULL) {
+               DRM_ERROR("FBC_MEM: No memory left!\n");
+               goto free;
+       }
+       for (index = 0; index < rdma_nums/2; index++) {
+               fbc_mems_left[index] = hwdev->fbcmem_sizes[index] / FBCMEM_UNIT;
+               DRM_DEBUG("fbcmem_sizes[%u/%u] = %u, total fbc_mems_left = %u\n",
+                       index, rdma_nums/2, hwdev->fbcmem_sizes[index], fbc_mems_left[index]);
+               if (fbc_mems_left[index] == 0) {
+                       DRM_ERROR("FBC_MEM: error fbcmem_sizes[%d] = %u\n", index, hwdev->fbcmem_sizes[index]);
+                       goto free;
+               }
+       }
+
+       for (index = 0; index < rdma_nums; index++) {
+               cur_rdma_fbcmem_size = rdmas[index].fbcmem.size;
+               if (cur_rdma_fbcmem_size == 0) {
+                       DRM_DEBUG("return directly for rmda %u fbcmem size is 0\n", index);
+                       continue;
+               }
+               pri_fbcmem_size = hwdev->fbcmem_sizes[index/2] / FBCMEM_UNIT;
+
+               if (index % 2 == 0) { //dma_id is even: 0, 2, 4, 6...
+                       DRM_DEBUG("rdma%u, cur_rdma_fbcmem_size = %u, pri_fbcmem_size = %u\n",
+                                                                       index, cur_rdma_fbcmem_size, pri_fbcmem_size);
+                       if (cur_rdma_fbcmem_size > pri_fbcmem_size) {
+                               DRM_ERROR("FBC_MEM: rdma %d use %d byte, excess the size %d\n",
+                                       index, cur_rdma_fbcmem_size, pri_fbcmem_size);
+                               goto free;
+                       }
+                       if (index > 0) {
+                               pre_odd_rdma_use_shared_fbc_mem = rdmas[index - 1].fbcmem.map;
+                               if (pre_odd_rdma_use_shared_fbc_mem) {
+                                       DRM_ERROR("FBC_MEM: both rdma %d %d use fbc memory\n", index, index - 1);
+                                       goto free;
+                               }
+                       }
+                       rdmas[index].fbcmem.start = 0;
+                       rdmas[index].fbcmem.map = true;
+                       fbc_mems_left[index/2] -= cur_rdma_fbcmem_size;
+                       DRM_DEBUG("FBC_MEM: rdma: id = %d, size = %u, start = %u, map = %u, fbc_mems_left[%u] = %d\n",
+                               index, cur_rdma_fbcmem_size, rdmas[index].fbcmem.start, rdmas[index].fbcmem.map, index/2, fbc_mems_left[index/2]);
+               } else { //odd rdma: rdma 1, 3, 5...
+                       if (cur_rdma_fbcmem_size <= fbc_mems_left[index/2]) { //not share fbc mem
+                               DRM_DEBUG("rdma%u, cur_rdma_fbcmem_size = %u, fbc_mems_left[%u] = %u\n",
+                                       index, cur_rdma_fbcmem_size, index/2, fbc_mems_left[index/2]);
+                               rdmas[index].fbcmem.map = false;
+                               rdmas[index].fbcmem.start = pri_fbcmem_size - fbc_mems_left[index/2];
+                               rdmas[index].fbcmem.size = fbc_mems_left[index/2];//use all the mem left
+                               fbc_mems_left[index/2] = 0;
+                               DRM_DEBUG("FBC_MEM: rdma: id = %d, size = %u, actually size = %u, start = %u, map = %u, fbc_mems_left[%u] = 0\n",
+                                       index, cur_rdma_fbcmem_size, rdmas[index].fbcmem.size, rdmas[index].fbcmem.start, rdmas[index].fbcmem.map, index/2);
+                       } else { //need share fbc mem
+                               sec_fbcmem_index = (index/2 + 1)%(rdma_nums/2);
+                               sec_fbcmem_size = hwdev->fbcmem_sizes[sec_fbcmem_index] / FBCMEM_UNIT;
+                               DRM_DEBUG("rdma%u, cur_rdma_fbcmem_size = %u, fbc_mems_left[%u] = %d, sec_fbcmem_size = %u\n",
+                                       index, cur_rdma_fbcmem_size, index/2, fbc_mems_left[index/2], sec_fbcmem_size);
+                               if (cur_rdma_fbcmem_size > fbc_mems_left[index/2] + sec_fbcmem_size) {
+                                       DRM_ERROR("FBC_MEM: rdma %d use %d mem size, but left %d\n",
+                                               index, cur_rdma_fbcmem_size, fbc_mems_left[index/2] + sec_fbcmem_size);
+                                       goto free;
+                               } else {
+                                       if (index == rdma_nums - 1) { //last rdma id
+                                               if (fbc_mems_left[0] != hwdev->fbcmem_sizes[0] / FBCMEM_UNIT) { //fbc mem0 has used
+                                                       DRM_ERROR("FBC_MEM: rdma %d can not use fbc mem 0 for it has been used\n", index);
+                                                       goto free;
+                                               } else {
+                                                       rdmas[index].fbcmem.size = fbc_mems_left[index/2] + sec_fbcmem_size;
+                                                       cur_rdma_fbcmem_size = rdmas[index].fbcmem.size;
+                                               }
+                                       }
+                                       fbc_mems_left[sec_fbcmem_index] -= (cur_rdma_fbcmem_size - fbc_mems_left[index/2]);
+                                       DRM_DEBUG("rmda%u, fbc_mems_left[%u] = %d, fbc_mems_left[%u] = %u\n",
+                                               index, index/2, fbc_mems_left[index/2], sec_fbcmem_index, fbc_mems_left[sec_fbcmem_index]);
+                                       rdmas[index].fbcmem.start = pri_fbcmem_size - fbc_mems_left[index/2];
+                                       rdmas[index].fbcmem.map = true;
+                                       fbc_mems_left[index/2] = 0;
+
+                                       fbcmem = &(rdmas[index].fbcmem);
+                                       DRM_DEBUG("FBC_MEM: rdma: id = %d, actually size = %u, start = %u, map = %u\n",
+                                               index, fbcmem->size, fbcmem->start, fbcmem->map);
+                               }
+                       }
+               }
+       }
+       ret = 0;
+
+free:
+       if (fbc_mems_left) {
+               kfree(fbc_mems_left);
+       }
+
+       return ret;
+}
+
+EXPORT_SYMBOL(saturn_adjust_rdma_fbcmem);
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_fbcmem.h b/drivers/gpu/drm/spacemit/dpu/saturn_fbcmem.h
new file mode 100644 (file)
index 0000000..738e50a
--- /dev/null
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SATURN_FBCMEM_H_
+#define _SATURN_FBCMEM_H_
+
+#include <drm/drm_plane.h>
+#include "../spacemit_dpu.h"
+#include "../spacemit_dpu_reg.h"
+#include "dpu_saturn.h"
+#include "saturn_regs/reg_map.h"
+
+#define FBCMEM_UNIT    (32)    //fbcmem is 32 bytes per unit
+
+typedef enum FBC_BLOCK_SIZE_E {
+       FBC_BLOCK_SIZE_16x16, //0
+       FBC_BLOCK_SIZE_32x8,  //1
+       FBC_BLOCK_SIZE_LIMIT, //error block size
+} FBC_BLOCK_SIZE_T;
+
+int get_raw_data_plane_rdma_mem_size(u32 drm_4cc_fmt, bool rot_90_or_270,
+                               u32 plane_crop_width, u32* output_mem_size);
+
+int get_afbc_data_plane_min_rdma_mem_size(u8 rdma_work_mode, u32 drm_4cc_fmt,
+       u32 crop_start_x, u32 crop_start_y, u32 crop_width, u32 crop_height,
+       u32 fbc_block_size, bool rot_90_or_270, u8 min_lines, u32* output_mem_size);
+
+int saturn_cal_layer_fbcmem_size(struct drm_plane *plane, \
+                                struct drm_plane_state *state);
+
+int saturn_adjust_rdma_fbcmem(struct spacemit_hw_device *hwdev, \
+                             struct spacemit_dpu_rdma *rdmas);
+
+void inline saturn_write_fbcmem_regs(struct drm_plane_state *state, u32 rdma_id,
+                                    u32 module_base, volatile RDMA_PATH_X_REG *rdma_regs);
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/cmdlist.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/cmdlist.h
new file mode 100644 (file)
index 0000000..aad6552
--- /dev/null
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef CMDLIST_REG_H
+#define CMDLIST_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER cmdlist_reg_0
+       struct
+       {
+       UINT32                                 : 4 ;
+       UINT32 cmdlist_ch_start_addrl          : 28;
+       }cmdlist_reg_0[14];
+
+       //REGISTER cmdlist_reg_14
+       struct
+       {
+       UINT32 cmdlist_ch_start_addrh          : 2 ;
+       UINT32                                 : 6 ;
+       UINT32 cmdlist_ch_y                    : 16;
+       UINT32                                 : 8 ;
+       }cmdlist_reg_14[14];
+
+       //REGISTER cmdlist_reg_28
+       UINT32 cmdlist_burst_len               : 5 ;
+       UINT32 axi_port_sel                    : 2 ;
+       UINT32 onl_arb_ratio                   : 3 ;
+       UINT32                                 : 22;
+
+
+       //REGISTER cmdlist_reg_29
+       UINT32 cmdlist_clr                     : 1 ;
+       UINT32                                 : 31;
+
+
+       //REGISTER cmdlist_reg_30
+       UINT32                                 : 16;
+       UINT32 cmdlist_clr_timeout_th          : 16;
+
+
+       //REGISTER cmdlist_reg_31
+       UINT32                                 : 4 ;
+       UINT32 cmdlist_ch_cfg_timeout_int_msk  : 14;
+       UINT32 cmdlist_ch_clr_timeout_int_msk  : 14;
+
+
+       //REGISTER cmdlist_reg_32
+       UINT32                                 : 32;
+
+
+       //REGISTER cmdlist_reg_33
+       UINT32                                 : 4 ;
+       UINT32 cmdlist_ch_cfg_timeout_ints     : 14;
+       UINT32 cmdlist_ch_clr_timeout_ints     : 14;
+
+
+       //REGISTER cmdlist_reg_34
+       UINT32                                 : 32;
+
+
+       //REGISTER cmdlist_reg_35
+       UINT32                                 : 4 ;
+       UINT32 cmdlist_ch_cfg_timeout_int_raw  : 14;
+       UINT32 cmdlist_ch_clr_timeout_int_raw  : 14;
+
+
+       //REGISTER cmdlist_reg_36
+       struct
+       {
+       UINT32 cmdlist_ch_dbg0                 : 11;
+       UINT32                                 : 5 ;
+       UINT32 cmdlist_ch_dbg1                 : 11;
+       UINT32                                 : 5 ;
+       }cmdlist_reg_36[7];
+
+       //REGISTER cmdlist_reg_43
+       UINT32 cmdlist_dbg                     : 6 ;
+       UINT32                                 : 26;
+
+
+       };
+
+       INT32 value32[44];
+
+}CMDLIST_REG;
+
+#define CMDLIST_ADDRL_ALIGN_BITS               (4) //From cmdlist_reg_0[] in CMDLIST_REG
+#define CMDLIST_ADDRL_ALIGN_MASK               ((u32)(~(BIT(CMDLIST_ADDRL_ALIGN_BITS) - 1)))
+#endif
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/cmps_x.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/cmps_x.h
new file mode 100644 (file)
index 0000000..bb98343
--- /dev/null
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef CMPS_X_REG_H
+#define CMPS_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER dpu_cmps_reg_0
+       UINT32 m_ncmps_en            : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_noutput_width       : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_1
+       UINT32 m_noutput_height      : 16;
+       UINT32                       : 16;
+
+
+       //REGISTER dpu_cmps_reg_2
+       UINT32 m_nbg_color_R         : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_3
+       UINT32 m_nbg_color_G         : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_4
+       UINT32 m_nbg_color_B         : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_5
+       UINT32 m_nbg_color_A         : 8 ;
+       UINT32                       : 24;
+
+
+       //REGISTER dpu_cmps_reg_6
+       UINT32 m_nl0_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl1_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_7
+       UINT32 m_nl2_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl3_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_8
+       UINT32 m_nl4_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl5_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_9
+       UINT32 m_nl6_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl7_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_10
+       UINT32 m_nl8_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl9_solid_color_A   : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_11
+       UINT32 m_nl10_solid_color_A  : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl11_solid_color_A  : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_12
+       UINT32 m_nl12_solid_color_A  : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl13_solid_color_A  : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_13
+       UINT32 m_nl14_solid_color_A  : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl15_solid_color_A  : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_14
+       UINT32 m_nl0_en              : 1 ;
+       UINT32 m_nl0_layer_id        : 4 ;
+       UINT32 m_nl0_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_15
+       UINT32 m_nl0_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_16
+       UINT32 m_nl0_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_17
+       UINT32 m_nl0_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_18
+       UINT32 m_nl0_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl0_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_19
+       UINT32 m_nl0_rect_ltopy      : 16;
+       UINT32 m_nl0_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_20
+       UINT32 m_nl0_rect_rboty      : 16;
+       UINT32 m_nl0_blend_mode      : 2 ;
+       UINT32 m_nl0_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_21
+       UINT32 m_nl0_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl0_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_22
+       UINT32 m_nl1_en              : 1 ;
+       UINT32 m_nl1_layer_id        : 4 ;
+       UINT32 m_nl1_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_23
+       UINT32 m_nl1_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_24
+       UINT32 m_nl1_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_25
+       UINT32 m_nl1_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_26
+       UINT32 m_nl1_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl1_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_27
+       UINT32 m_nl1_rect_ltopy      : 16;
+       UINT32 m_nl1_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_28
+       UINT32 m_nl1_rect_rboty      : 16;
+       UINT32 m_nl1_blend_mode      : 2 ;
+       UINT32 m_nl1_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_29
+       UINT32 m_nl1_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl1_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_30
+       UINT32 m_nl2_en              : 1 ;
+       UINT32 m_nl2_layer_id        : 4 ;
+       UINT32 m_nl2_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_31
+       UINT32 m_nl2_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_32
+       UINT32 m_nl2_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_33
+       UINT32 m_nl2_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_34
+       UINT32 m_nl2_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl2_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_35
+       UINT32 m_nl2_rect_ltopy      : 16;
+       UINT32 m_nl2_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_36
+       UINT32 m_nl2_rect_rboty      : 16;
+       UINT32 m_nl2_blend_mode      : 2 ;
+       UINT32 m_nl2_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_37
+       UINT32 m_nl2_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl2_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_38
+       UINT32 m_nl3_en              : 1 ;
+       UINT32 m_nl3_layer_id        : 4 ;
+       UINT32 m_nl3_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_39
+       UINT32 m_nl3_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_40
+       UINT32 m_nl3_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_41
+       UINT32 m_nl3_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_42
+       UINT32 m_nl3_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl3_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_43
+       UINT32 m_nl3_rect_ltopy      : 16;
+       UINT32 m_nl3_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_44
+       UINT32 m_nl3_rect_rboty      : 16;
+       UINT32 m_nl3_blend_mode      : 2 ;
+       UINT32 m_nl3_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_45
+       UINT32 m_nl3_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl3_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_46
+       UINT32 m_nl4_en              : 1 ;
+       UINT32 m_nl4_layer_id        : 4 ;
+       UINT32 m_nl4_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_47
+       UINT32 m_nl4_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_48
+       UINT32 m_nl4_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_49
+       UINT32 m_nl4_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_50
+       UINT32 m_nl4_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl4_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_51
+       UINT32 m_nl4_rect_ltopy      : 16;
+       UINT32 m_nl4_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_52
+       UINT32 m_nl4_rect_rboty      : 16;
+       UINT32 m_nl4_blend_mode      : 2 ;
+       UINT32 m_nl4_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_53
+       UINT32 m_nl4_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl4_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_54
+       UINT32 m_nl5_en              : 1 ;
+       UINT32 m_nl5_layer_id        : 4 ;
+       UINT32 m_nl5_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_55
+       UINT32 m_nl5_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_56
+       UINT32 m_nl5_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_57
+       UINT32 m_nl5_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_58
+       UINT32 m_nl5_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl5_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_59
+       UINT32 m_nl5_rect_ltopy      : 16;
+       UINT32 m_nl5_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_60
+       UINT32 m_nl5_rect_rboty      : 16;
+       UINT32 m_nl5_blend_mode      : 2 ;
+       UINT32 m_nl5_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_61
+       UINT32 m_nl5_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl5_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_62
+       UINT32 m_nl6_en              : 1 ;
+       UINT32 m_nl6_layer_id        : 4 ;
+       UINT32 m_nl6_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_63
+       UINT32 m_nl6_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_64
+       UINT32 m_nl6_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_65
+       UINT32 m_nl6_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_66
+       UINT32 m_nl6_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl6_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_67
+       UINT32 m_nl6_rect_ltopy      : 16;
+       UINT32 m_nl6_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_68
+       UINT32 m_nl6_rect_rboty      : 16;
+       UINT32 m_nl6_blend_mode      : 2 ;
+       UINT32 m_nl6_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_69
+       UINT32 m_nl6_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl6_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_70
+       UINT32 m_nl7_en              : 1 ;
+       UINT32 m_nl7_layer_id        : 4 ;
+       UINT32 m_nl7_solid_en        : 1 ;
+       UINT32                       : 26;
+
+
+       //REGISTER dpu_cmps_reg_71
+       UINT32 m_nl7_solid_color_R   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_72
+       UINT32 m_nl7_solid_color_G   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_73
+       UINT32 m_nl7_solid_color_B   : 12;
+       UINT32                       : 20;
+
+
+       //REGISTER dpu_cmps_reg_74
+       UINT32 m_nl7_color_key_en    : 1 ;
+       UINT32                       : 7 ;
+       UINT32 m_nl7_rect_ltopx      : 16;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_75
+       UINT32 m_nl7_rect_ltopy      : 16;
+       UINT32 m_nl7_rect_rbotx      : 16;
+
+
+       //REGISTER dpu_cmps_reg_76
+       UINT32 m_nl7_rect_rboty      : 16;
+       UINT32 m_nl7_blend_mode      : 2 ;
+       UINT32 m_nl7_alpha_sel       : 1 ;
+       UINT32                       : 13;
+
+
+       //REGISTER dpu_cmps_reg_77
+       UINT32 m_nl7_alpha_factor    : 8 ;
+       UINT32                       : 8 ;
+       UINT32 m_nl7_layer_alpha     : 8 ;
+       UINT32                       : 8 ;
+
+
+       //REGISTER dpu_cmps_reg_78
+       struct
+       {
+       UINT32                       : 32;
+       }dpu_cmps_reg_78[64];
+
+       //REGISTER dpu_cmps_reg_142
+       UINT32 dbug_bus0             : 32;
+
+
+       //REGISTER dpu_cmps_reg_143
+       UINT32 dbg_bus1              : 32;
+
+
+       //REGISTER dpu_cmps_reg_144
+       UINT32 dbg_bus2              : 32;
+
+
+       //REGISTER dpu_cmps_reg_145
+       UINT32 dbg_bus3              : 32;
+
+
+       };
+
+       INT32 value32[146];
+
+}CMPS_X_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/dma_top.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/dma_top.h
new file mode 100644 (file)
index 0000000..7fbfee6
--- /dev/null
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef DMA_TOP_REG_H
+#define DMA_TOP_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER DBG_EN
+       UINT32 dbg_en                            : 1 ;
+       UINT32                                   : 31;
+
+
+       //REGISTER DMA_ARB_OPTION
+       UINT32 img_rr_ratio                      : 8 ;
+       UINT32                                   : 4 ;
+       UINT32 round_robin_mode                  : 1 ;
+       UINT32                                   : 3 ;
+       UINT32 pixel_num_th                      : 6 ;
+       UINT32                                   : 10;
+
+
+       //REGISTER DMA_TIMEOUT_NUM
+       UINT32 rdma_timeout_limit                : 16;
+       UINT32 wdma_timeout_limit                : 16;
+
+
+       //REGISTER dmac0_ctrl
+       UINT32 dmac0_rstn_pwr                    : 1 ;
+       UINT32 dmac0_rst_req                     : 1 ;
+       UINT32                                   : 1 ;
+       UINT32 dmac0_burst_length                : 3 ;
+       UINT32 dmac0_arcache                     : 4 ;
+       UINT32 dmac0_awcache                     : 4 ;
+       UINT32 dmac0_arregion                    : 4 ;
+       UINT32 dmac0_awregion                    : 4 ;
+       UINT32                                   : 10;
+
+
+       //REGISTER dmac1_ctrl
+       UINT32 dmac1_rstn_pwr                    : 1 ;
+       UINT32 dmac1_rst_req                     : 1 ;
+       UINT32                                   : 1 ;
+       UINT32 dmac1_burst_length                : 3 ;
+       UINT32 dmac1_arcache                     : 4 ;
+       UINT32 dmac1_awcache                     : 4 ;
+       UINT32 dmac1_arregion                    : 4 ;
+       UINT32 dmac1_awregion                    : 4 ;
+       UINT32                                   : 10;
+
+
+       //REGISTER dmac2_ctrl
+       UINT32 dmac2_rstn_pwr                    : 1 ;
+       UINT32 dmac2_rst_req                     : 1 ;
+       UINT32                                   : 1 ;
+       UINT32 dmac2_burst_length                : 3 ;
+       UINT32 dmac2_arcache                     : 4 ;
+       UINT32 dmac2_awcache                     : 4 ;
+       UINT32 dmac2_arregion                    : 4 ;
+       UINT32 dmac2_awregion                    : 4 ;
+       UINT32                                   : 10;
+
+
+       //REGISTER dmac3_ctrl
+       UINT32 dmac3_rstn_pwr                    : 1 ;
+       UINT32 dmac3_rst_req                     : 1 ;
+       UINT32                                   : 1 ;
+       UINT32 dmac3_burst_length                : 3 ;
+       UINT32 dmac3_arcache                     : 4 ;
+       UINT32 dmac3_awcache                     : 4 ;
+       UINT32 dmac3_arregion                    : 4 ;
+       UINT32 dmac3_awregion                    : 4 ;
+       UINT32                                   : 10;
+
+
+       //REGISTER DMA_QOS
+       UINT32 online_rqos                       : 4 ;
+       UINT32 offline_rqos                      : 4 ;
+       UINT32 online_wqos                       : 4 ;
+       UINT32 offline_wqos                      : 4 ;
+       UINT32 cmdlist_rqos                      : 4 ;
+       UINT32                                   : 12;
+
+
+       //REGISTER DMAC0_OUTS_NUM
+       UINT32 dmac0_rd_outs_num                 : 8 ;
+       UINT32 dmac0_wr_outs_num                 : 8 ;
+       UINT32                                   : 16;
+
+
+       //REGISTER DMAC1_OUTS_NUM
+       UINT32 dmac1_rd_outs_num                 : 8 ;
+       UINT32 dmac1_wr_outs_num                 : 8 ;
+       UINT32                                   : 16;
+
+
+       //REGISTER DMAC2_OUTS_NUM
+       UINT32 dmac2_rd_outs_num                 : 8 ;
+       UINT32 dmac2_wr_outs_num                 : 8 ;
+       UINT32                                   : 16;
+
+
+       //REGISTER DMAC3_OUTS_NUM
+       UINT32 dmac3_rd_outs_num                 : 8 ;
+       UINT32 dmac3_wr_outs_num                 : 8 ;
+       UINT32                                   : 16;
+
+
+       //REGISTER CMDLIST0_IRQ_RAW
+       UINT32                                   : 6 ;
+       UINT32 cmdlist0_rdma_timeout_irq_raw     : 1 ;
+       UINT32 cmdlist0_rdma_rsp_decerr_raw      : 1 ;
+       UINT32 cmdlist0_rdma_rsp_slverr_raw      : 1 ;
+       UINT32 cmdlist0_rdma_rsp_exok_raw        : 1 ;
+       UINT32 cmdlist0_va_mismatch_raw          : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER WB0_IRQ_RAW
+       UINT32 wb0_tlb_miss_irq_raw              : 1 ;
+       UINT32 wb0_tbu_size_err_irq_raw          : 1 ;
+       UINT32 wb0_mmu_rdma_timeout_raw          : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_decerr_raw       : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_slverr_raw       : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_exok_raw         : 1 ;
+       UINT32 wb0_wdma_timeout_irq_raw          : 1 ;
+       UINT32 wb0_wdma_rsp_decerr_raw           : 1 ;
+       UINT32 wb0_wdma_rsp_slverr_raw           : 1 ;
+       UINT32 wb0_wdma_rsp_exok_raw             : 1 ;
+       UINT32 wb0_va_mismatch_raw               : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER WB1_IRQ_RAW
+       UINT32 wb1_tlb_miss_irq_raw              : 1 ;
+       UINT32 wb1_tbu_size_err_irq_raw          : 1 ;
+       UINT32 wb1_mmu_rdma_timeout_raw          : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_decerr_raw       : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_slverr_raw       : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_exok_raw         : 1 ;
+       UINT32 wb1_wdma_timeout_irq_raw          : 1 ;
+       UINT32 wb1_wdma_rsp_decerr_raw           : 1 ;
+       UINT32 wb1_wdma_rsp_slverr_raw           : 1 ;
+       UINT32 wb1_wdma_rsp_exok_raw             : 1 ;
+       UINT32 wb1_va_mismatch_raw               : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER CMDLIST0_IRQ_MASK
+       UINT32                                   : 6 ;
+       UINT32 cmdlist0_rdma_timeout_irq_mask    : 1 ;
+       UINT32 cmdlist0_rdma_rsp_decerr_mask     : 1 ;
+       UINT32 cmdlist0_rdma_rsp_slverr_mask     : 1 ;
+       UINT32 cmdlist0_rdma_rsp_exok_mask       : 1 ;
+       UINT32 cmdlist0_va_mismatch_mask         : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER WB0_IRQ_MASK
+       UINT32 wb0_tlb_miss_irq_mask             : 1 ;
+       UINT32 wb0_tbu_size_err_irq_mask         : 1 ;
+       UINT32 wb0_mmu_rdma_timeout_mask         : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_decerr_mask      : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_slverr_mask      : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_exok_mask        : 1 ;
+       UINT32 wb0_wdma_timeout_irq_mask         : 1 ;
+       UINT32 wb0_wdma_rsp_decerr_mask          : 1 ;
+       UINT32 wb0_wdma_rsp_slverr_mask          : 1 ;
+       UINT32 wb0_wdma_rsp_exok_mask            : 1 ;
+       UINT32 wb0_va_mismatch_mask              : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER WB1_IRQ_MASK
+       UINT32 wb1_tlb_miss_irq_mask             : 1 ;
+       UINT32 wb1_tbu_size_err_irq_mask         : 1 ;
+       UINT32 wb1_mmu_rdma_timeout_mask         : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_decerr_mask      : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_slverr_mask      : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_exok_mask        : 1 ;
+       UINT32 wb1_wdma_timeout_irq_mask         : 1 ;
+       UINT32 wb1_wdma_rsp_decerr_mask          : 1 ;
+       UINT32 wb1_wdma_rsp_slverr_mask          : 1 ;
+       UINT32 wb1_wdma_rsp_exok_mask            : 1 ;
+       UINT32 wb1_va_mismatch_mask              : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER CMDLIST0_IRQ_STATUS
+       UINT32                                   : 6 ;
+       UINT32 cmdlist0_rdma_timeout_irq_status  : 1 ;
+       UINT32 cmdlist0_rdma_rsp_decerr_status   : 1 ;
+       UINT32 cmdlist0_rdma_rsp_slverr_status   : 1 ;
+       UINT32 cmdlist0_rdma_rsp_exok_status     : 1 ;
+       UINT32 cmdlist0_va_mistach_status        : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER WB0_IRQ_STATUS
+       UINT32 wb0_tlb_miss_irq_status           : 1 ;
+       UINT32 wb0_tbu_size_err_irq_status       : 1 ;
+       UINT32 wb0_mmu_rdma_timeout_status       : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_decerr_status    : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_slverr_status    : 1 ;
+       UINT32 wb0_mmu_rdma_rsp_exok_status      : 1 ;
+       UINT32 wb0_wdma_timeout_irq_status       : 1 ;
+       UINT32 wb0_wdma_rsp_decerr_status        : 1 ;
+       UINT32 wb0_wdma_rsp_slverr_status        : 1 ;
+       UINT32 wb0_wdma_rsp_exok_status          : 1 ;
+       UINT32 wb0_va_mismatch_status            : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER WB1_IRQ_STATUS
+       UINT32 wb1_tlb_miss_irq_status           : 1 ;
+       UINT32 wb1_tbu_size_err_irq_status       : 1 ;
+       UINT32 wb1_mmu_rdma_timeout_status       : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_decerr_status    : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_slverr_status    : 1 ;
+       UINT32 wb1_mmu_rdma_rsp_exok_status      : 1 ;
+       UINT32 wb1_wdma_timeout_irq_status       : 1 ;
+       UINT32 wb1_wdma_rsp_decerr_status        : 1 ;
+       UINT32 wb1_wdma_rsp_slverr_status        : 1 ;
+       UINT32 wb1_wdma_rsp_exok_staus           : 1 ;
+       UINT32 wb1_va_mismatch_status            : 1 ;
+       UINT32                                   : 21;
+
+
+       //REGISTER ARB_DEBUG_INFO0
+       UINT32 arb_debug_info_axi0               : 32;
+
+
+       //REGISTER ARB_DEBUG_INFO1
+       UINT32 arb_debug_info_axi1               : 32;
+
+
+       //REGISTER ARB_DEBUG_INFO2
+       UINT32 arb_debug_info_axi2               : 32;
+
+
+       //REGISTER ARB_DEBUG_INFO3
+       UINT32 arb_debug_info_axi3               : 32;
+
+
+       };
+
+       INT32 value32[25];
+
+}DMA_TOP_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_crg.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_crg.h
new file mode 100644 (file)
index 0000000..5bb92ca
--- /dev/null
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef DPU_CRG_REG_H
+#define DPU_CRG_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER crg_reg_0
+       UINT32 mclk_wb_auto_en        : 1 ;
+       UINT32 aclk_wb_auto_en        : 1 ;
+       UINT32 aclk_rdma_auto_en      : 1 ;
+       UINT32 mclk_layer_auto_en     : 1 ;
+       UINT32 mclk_scl_auto_en       : 1 ;
+       UINT32 mclk_cmps_auto_en      : 1 ;
+       UINT32 mclk_outctl_auto_en    : 1 ;
+       UINT32 dscclk_outctl_auto_en  : 1 ;
+       UINT32 pixclk_outctl_auto_en  : 1 ;
+       UINT32 aclk_outctl_auto_en    : 1 ;
+       UINT32 aclk_cmdlist_auto_en   : 1 ;
+       UINT32 pclk_cmdlist_auto_en   : 1 ;
+       UINT32 aclk_dma_top_sw_en     : 1 ;
+       UINT32                        : 19;
+
+
+       //REGISTER crg_reg_1
+       UINT32 crg_dma_auto_en        : 5 ;
+       UINT32                        : 11;
+       UINT32 crg_wb_auto_en         : 6 ;
+       UINT32                        : 10;
+
+
+       //REGISTER crg_reg_2
+       UINT32 crg_scl_auto_en        : 2 ;
+       UINT32                        : 6 ;
+       UINT32 crg_pre_auto_en        : 17;
+       UINT32                        : 7 ;
+
+
+       //REGISTER crg_reg_3
+       UINT32 crg_outctl_auto_en     : 18;
+       UINT32                        : 14;
+
+
+       //REGISTER crg_reg_4
+       UINT32 crg_ctl_auto_en        : 2 ;
+       UINT32                        : 14;
+       UINT32 crg_cmdlist_auto_en    : 2 ;
+       UINT32                        : 14;
+
+
+       };
+
+       INT32 value32[5];
+
+}DPU_CRG_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_ctl.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_ctl.h
new file mode 100644 (file)
index 0000000..cc594d3
--- /dev/null
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef DPU_CTL_REG_H
+#define DPU_CTL_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER dpu_ctl_reg_0
+       UINT32 ctl0_nml_rch_en              : 12;
+       UINT32 ctl0_nml_scl_en              : 4 ;
+       UINT32 ctl0_nml_wb_en               : 2 ;
+       UINT32 ctl0_nml_outctl_en           : 1 ;
+       UINT32                              : 13;
+
+
+       //REGISTER dpu_ctl_reg_1
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_2
+       UINT32 ctl0_nml_cmd_updt_en         : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_3
+       UINT32 ctl0_nml_cfg_rdy             : 1 ;
+       UINT32 ctl0_sw_clr                  : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_4
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_5
+       UINT32 ctl0_secu_rch_en             : 12;
+       UINT32 ctl0_secu_scl_en             : 4 ;
+       UINT32 ctl0_secu_wb_en              : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_6
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_7
+       UINT32 ctl0_secu_cmd_updt_en        : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_8
+       UINT32 ctl0_secu_cfg_rdy            : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_9
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_10
+       UINT32 ctl0_video_mod               : 1 ;
+       UINT32 ctl0_dbg_mod                 : 1 ;
+       UINT32 ctl0_timing_inter0           : 4 ;
+       UINT32                              : 2 ;
+       UINT32 ctl0_timing_inter1           : 4 ;
+       UINT32                              : 20;
+
+
+       //REGISTER dpu_ctl_reg_11
+       UINT32 ctl0_sw_start                : 1 ;
+       UINT32 ctl0_dbg_unflow_clr          : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_12
+       UINT32 ctl1_nml_rch_en              : 12;
+       UINT32 ctl1_nml_scl_en              : 4 ;
+       UINT32 ctl1_nml_wb_en               : 2 ;
+       UINT32 ctl1_nml_outctl_en           : 1 ;
+       UINT32                              : 13;
+
+
+       //REGISTER dpu_ctl_reg_13
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_14
+       UINT32 ctl1_nml_cmd_updt_en         : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_15
+       UINT32 ctl1_nml_cfg_rdy             : 1 ;
+       UINT32 ctl1_sw_clr                  : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_16
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_17
+       UINT32 ctl1_secu_rch_en             : 12;
+       UINT32 ctl1_secu_scl_en             : 4 ;
+       UINT32 ctl1_secu_wb_en              : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_18
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_19
+       UINT32 ctl1_secu_cmd_updt_en        : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_20
+       UINT32 ctl1_secu_cfg_rdy            : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_21
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_22
+       UINT32 ctl1_video_mod               : 1 ;
+       UINT32 ctl1_dbg_mod                 : 1 ;
+       UINT32 ctl1_timing_inter0           : 4 ;
+       UINT32                              : 2 ;
+       UINT32 ctl1_timing_inter1           : 4 ;
+       UINT32                              : 20;
+
+
+       //REGISTER dpu_ctl_reg_23
+       UINT32 ctl1_sw_start                : 1 ;
+       UINT32 ctl1_dbg_unflow_clr          : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_24
+       UINT32 ctl2_nml_rch_en              : 12;
+       UINT32 ctl2_nml_scl_en              : 4 ;
+       UINT32 ctl2_nml_wb_en               : 2 ;
+       UINT32 ctl2_nml_outctl_en           : 1 ;
+       UINT32                              : 13;
+
+
+       //REGISTER dpu_ctl_reg_25
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_26
+       UINT32 ctl2_nml_cmd_updt_en         : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_27
+       UINT32 ctl2_nml_cfg_rdy             : 1 ;
+       UINT32 ctl2_sw_clr                  : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_28
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_29
+       UINT32 ctl2_secu_rch_en             : 12;
+       UINT32 ctl2_secu_scl_en             : 4 ;
+       UINT32 ctl2_secu_wb_en              : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_30
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_31
+       UINT32 ctl2_secu_cmd_updt_en        : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_32
+       UINT32 ctl2_secu_cfg_rdy            : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_33
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_34
+       UINT32 ctl2_video_mod               : 1 ;
+       UINT32 ctl2_dbg_mod                 : 1 ;
+       UINT32 ctl2_timing_inter0           : 4 ;
+       UINT32                              : 2 ;
+       UINT32 ctl2_timing_inter1           : 4 ;
+       UINT32                              : 20;
+
+
+       //REGISTER dpu_ctl_reg_35
+       UINT32 ctl2_sw_start                : 1 ;
+       UINT32 ctl2_dbg_unflow_clr          : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_36
+       UINT32 ctl3_nml_rch_en              : 12;
+       UINT32 ctl3_nml_scl_en              : 4 ;
+       UINT32 ctl3_nml_wb_en               : 2 ;
+       UINT32 ctl3_nml_outctl_en           : 1 ;
+       UINT32                              : 13;
+
+
+       //REGISTER dpu_ctl_reg_37
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_38
+       UINT32 ctl3_nml_cmd_updt_en         : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_39
+       UINT32 ctl3_nml_cfg_rdy             : 1 ;
+       UINT32 ctl3_sw_clr                  : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_40
+       UINT32 ctl3_video_mod               : 1 ;
+       UINT32 ctl3_dbg_mod                 : 1 ;
+       UINT32 ctl3_timing_inter0           : 4 ;
+       UINT32                              : 2 ;
+       UINT32 ctl3_timing_inter1           : 4 ;
+       UINT32                              : 20;
+
+
+       //REGISTER dpu_ctl_reg_41
+       UINT32 ctl3_sw_start                : 1 ;
+       UINT32 ctl3_dbg_unflow_clr          : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_42
+       UINT32 ctl4_nml_rch_en              : 12;
+       UINT32 ctl4_nml_scl_en              : 4 ;
+       UINT32 ctl4_nml_wb_en               : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_43
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_44
+       UINT32 ctl4_nml_cmd_updt_en         : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_45
+       UINT32 ctl4_nml_cfg_rdy             : 1 ;
+       UINT32 ctl4_sw_clr                  : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_46
+       UINT32                              : 32;
+
+
+       //REGISTER dpu_ctl_reg_47
+       UINT32 ctl4_timing_inter0           : 4 ;
+       UINT32 ctl4_timing_inter1           : 4 ;
+       UINT32                              : 24;
+
+
+       //REGISTER dpu_ctl_reg_48
+       UINT32 ctl_nml_scl0_layer_id        : 4 ;
+       UINT32 ctl_nml_scl0_layer_right     : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_49
+       UINT32 ctl_nml_scl1_layer_id        : 4 ;
+       UINT32 ctl_nml_scl1_layer_right     : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_50
+       UINT32 ctl_nml_scl2_layer_id        : 4 ;
+       UINT32 ctl_nml_scl2_layer_right     : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_51
+       UINT32 ctl_nml_scl3_layer_id        : 4 ;
+       UINT32 ctl_nml_scl3_layer_right     : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_52
+       UINT32 ctl_secu_scl0_layer_id       : 4 ;
+       UINT32 ctl_secu_scl0_layer_right    : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_53
+       UINT32 ctl_secu_scl1_layer_id       : 4 ;
+       UINT32 ctl_secu_scl1_layer_right    : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_54
+       UINT32 ctl_secu_scl2_layer_id       : 4 ;
+       UINT32 ctl_secu_scl2_layer_right    : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_55
+       UINT32 ctl_secu_scl3_layer_id       : 4 ;
+       UINT32 ctl_secu_scl3_layer_right    : 1 ;
+       UINT32                              : 27;
+
+
+       //REGISTER dpu_ctl_reg_56
+       UINT32 outctl_secu                  : 3 ;
+       UINT32 cmps_secu                    : 3 ;
+       UINT32 prc_curve_secu               : 1 ;
+       UINT32                              : 25;
+
+
+       //REGISTER dpu_ctl_reg_57
+       UINT32 ctl_rd_shadow                : 1 ;
+       UINT32                              : 31;
+
+
+       //REGISTER dpu_ctl_reg_58
+       UINT32 rch_conflict_ints            : 12;
+       UINT32 scl_conflict_ints            : 4 ;
+       UINT32 wb_timeout_ints              : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_59
+       UINT32 rch_conflict_ints_msk        : 12;
+       UINT32 scl_conflict_int_msk         : 4 ;
+       UINT32 wb_timeout_int_msk           : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_60
+       UINT32 rch_conflict_int_raw         : 12;
+       UINT32 scl_conflict_int_raw         : 4 ;
+       UINT32 wb_timeout_int_raw           : 2 ;
+       UINT32                              : 14;
+
+
+       //REGISTER dpu_ctl_reg_61
+       UINT32 ctl_nml_reuse_scl0_en        : 1 ;
+       UINT32 ctl_nml_cmps_scl0_en         : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_62
+       UINT32 ctl_nml_reuse_scl1_en        : 1 ;
+       UINT32 ctl_nml_cmps_scl1_en         : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_63
+       UINT32 ctl_nml_reuse_scl2_en        : 1 ;
+       UINT32 ctl_nml_cmps_scl2_en         : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_64
+       UINT32 ctl_nml_reuse_scl3_en        : 1 ;
+       UINT32 ctl_nml_cmps_scl3_en         : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_65
+       UINT32 ctl_secu_reuse_scl0_en       : 1 ;
+       UINT32 ctl_secu_cmps_scl0_en        : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_66
+       UINT32 ctl_secu_reuse_scl1_en       : 1 ;
+       UINT32 ctl_secu_cmps_scl1_en        : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_67
+       UINT32 ctl_secu_reuse_scl2_en       : 1 ;
+       UINT32 ctl_secu_cmps_scl2_en        : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_68
+       UINT32 ctl_secu_reuse_scl3_en       : 1 ;
+       UINT32 ctl_secu_cmps_scl3_en        : 1 ;
+       UINT32                              : 30;
+
+
+       //REGISTER dpu_ctl_reg_69
+       struct
+       {
+       UINT32 ctl_nml_cmdlist_rch_en       : 1 ;
+       UINT32                              : 31;
+       }dpu_ctl_reg_69[12];
+
+       //REGISTER dpu_ctl_reg_81
+       struct
+       {
+       UINT32 ctl_nml_cmdlist_wb_en        : 1 ;
+       UINT32                              : 31;
+       }dpu_ctl_reg_81[2];
+
+       //REGISTER dpu_ctl_reg_83
+       UINT32 ctl_nml_cmdlist_rch_cfg_rdy  : 12;
+       UINT32 ctl_nml_cmdlist_wb_cfg_rdy   : 2 ;
+       UINT32                              : 18;
+
+
+       //REGISTER dpu_ctl_reg_84
+       UINT32 ctl_wb0_sel_id               : 5 ;
+       UINT32 ctl_wb0_sel_right            : 1 ;
+       UINT32                              : 26;
+
+
+       //REGISTER dpu_ctl_reg_85
+       UINT32 ctl_wb1_sel_id               : 5 ;
+       UINT32 ctl_wb1_sel_right            : 1 ;
+       UINT32                              : 26;
+
+
+       //REGISTER dpu_ctl_reg_86
+       UINT32 ctl_rdma_act                 : 12;
+       UINT32 ctl_scl_act                  : 4 ;
+       UINT32 ctl_layer_act                : 12;
+       UINT32                              : 4 ;
+
+
+       //REGISTER dpu_ctl_reg_87
+       UINT32 ctl_cmps_outctl_act          : 3 ;
+       UINT32                              : 5 ;
+       UINT32 ctl_wb_act                   : 2 ;
+       UINT32                              : 6 ;
+       UINT32 ctl_wb_slice_cnt             : 10;
+       UINT32                              : 6 ;
+
+
+       //REGISTER dpu_ctl_reg_88
+       UINT32 cmdlist_rch_act              : 12;
+       UINT32 cmdlist_wb_act               : 2 ;
+       UINT32                              : 2 ;
+       UINT32 scene_ctl_dbg0               : 15;
+       UINT32                              : 1 ;
+
+
+       //REGISTER dpu_ctl_reg_89
+       UINT32 scene_ctl_dbg1               : 15;
+       UINT32                              : 1 ;
+       UINT32 scene_ctl_dbg2               : 15;
+       UINT32                              : 1 ;
+
+
+       //REGISTER dpu_ctl_reg_90
+       UINT32 scene_ctl_dbg3               : 15;
+       UINT32                              : 1 ;
+       UINT32 scene_ctl_dbg4               : 15;
+       UINT32                              : 1 ;
+
+
+       //REGISTER dpu_ctl_reg_91
+       UINT32 rdma_clr_req_aclk            : 12;
+       UINT32 wb_clr_req_aclk              : 2 ;
+       UINT32                              : 2 ;
+       UINT32 rdma_clr_ack_aclk            : 12;
+       UINT32 wb_clr_ack_aclk              : 2 ;
+       UINT32                              : 2 ;
+
+
+       //REGISTER dpu_ctl_reg_92
+       UINT32 outctl_clr_req_aclk          : 3 ;
+       UINT32                              : 1 ;
+       UINT32 outctl_clr_ack_aclk          : 3 ;
+       UINT32                              : 1 ;
+       UINT32 wb_conflict_hld              : 2 ;
+       UINT32                              : 22;
+
+
+       //REGISTER dpu_ctl_reg_93
+       UINT32 cmdlist_clr_req_aclk         : 14;
+       UINT32                              : 2 ;
+       UINT32 cmdlist_clr_ack_aclk         : 14;
+       UINT32                              : 2 ;
+
+
+       };
+
+       INT32 value32[94];
+
+}DPU_CTL_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_intp.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_intp.h
new file mode 100644 (file)
index 0000000..9d6d41b
--- /dev/null
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef DPU_INTP_REG_H
+#define DPU_INTP_REG_H
+
+
+#define DPU_INT_CMDLIST_CH_FRM_CFG_DONE_MASK   (0x3fff)
+#define DPU_INT_CMDLIST_CH_FRM_CFG_DONE                (DPU_INT_CMDLIST_CH_FRM_CFG_DONE_MASK << 12)
+#define DPU_INT_WB_OVFLOW_MASK                 (0x3)
+#define DPU_INT_WB_OVFLOW                      (DPU_INT_WB_OVFLOW_MASK << 10)
+#define DPU_INT_FRM_TIMING_UNFLOW              BIT(9)
+#define DPU_INT_WB_DONE_MASK                   (0x3)
+#define DPU_INT_WB_DONE                                (DPU_INT_WB_DONE_MASK << 7)
+#define DPU_INT_CURVE_DONE                     BIT(6)
+#define DPU_INT_HIST_DONE                      BIT(5)
+#define DPU_INT_CFG_RDY_CLR                    BIT(4)
+#define DPU_INT_FRM_TIMING_CFG_LINE            BIT(3)
+#define DPU_INT_FRM_TIMING_CFG_EOF             BIT(2)
+#define DPU_INT_FRM_TIMING_EOF                 BIT(1)
+#define DPU_INT_FRM_TIMING_VSYNC               BIT(0)
+
+#define DPU_REST_INT_BITS                      (DPU_INT_FRM_TIMING_CFG_EOF | \
+                                                DPU_INT_FRM_TIMING_CFG_LINE | \
+                                                DPU_INT_HIST_DONE | \
+                                                DPU_INT_CURVE_DONE | \
+                                                DPU_INT_CMDLIST_CH_FRM_CFG_DONE)
+
+typedef union
+{
+       struct
+       {
+       //REGISTER dpu_int_reg_0
+       UINT32 onl0_nml_frm_timing_vsync_int_msk          : 1 ;
+       UINT32 onl0_nml_frm_timing_eof_int_msk            : 1 ;
+       UINT32 onl0_nml_frm_timing_cfg_eof_int_msk        : 1 ;
+       UINT32 onl0_nml_frm_timing_cfg_line_int_msk       : 1 ;
+       UINT32 onl0_nml_cfg_rdy_clr_int_msk               : 1 ;
+       UINT32 onl0_nml_hist_done_int_msk                 : 1 ;
+       UINT32 onl0_nml_curve_done_int_msk                : 1 ;
+       UINT32 onl0_nml_wb_done_int_msk                   : 2 ;
+       UINT32 onl0_nml_frm_timing_unflow_int_msk         : 1 ;
+       UINT32 onl0_nml_wb_ovflow_int_msk                 : 2 ;
+       UINT32 onl0_nml_cmdlist_ch_frm_cfg_done_int_msk   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_1
+       UINT32 onl0_nml_dma_dbg_int_msk                   : 16;
+       UINT32 onl0_nml_outctl_dbg_int_msk                : 1 ;
+       UINT32 onl0_nml_ctl_dbg_int_msk                   : 1 ;
+       UINT32 onl0_nml_cmdlist_dbg_int_msk               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_2
+       UINT32 onl1_nml_frm_timing_vsync_int_msk          : 1 ;
+       UINT32 onl1_nml_frm_timing_eof_int_msk            : 1 ;
+       UINT32 onl1_nml_frm_timing_cfg_eof_int_msk        : 1 ;
+       UINT32 onl1_nml_frm_timing_cfg_line_int_msk       : 1 ;
+       UINT32 onl1_nml_cfg_rdy_clr_int_msk               : 1 ;
+       UINT32 onl1_nml_hist_done_int_msk                 : 1 ;
+       UINT32 onl1_nml_curve_done_int_msk                : 1 ;
+       UINT32 onl1_nml_wb_done_int_msk                   : 2 ;
+       UINT32 onl1_nml_frm_timing_unflow_int_msk         : 1 ;
+       UINT32 onl1_nml_wb_ovflow_int_msk                 : 2 ;
+       UINT32 onl1_nml_cmdlist_ch_frm_cfg_done_int_msk   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_3
+       UINT32 onl1_nml_dma_dbg_int_msk                   : 16;
+       UINT32 onl1_nml_outctl_dbg_int_msk                : 1 ;
+       UINT32 onl1_nml_ctl_dbg_int_msk                   : 1 ;
+       UINT32 onl1_nml_cmdlist_dbg_int_msk               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_4
+       UINT32 onl2_nml_frm_timing_vsync_int_msk          : 1 ;
+       UINT32 onl2_nml_frm_timing_eof_int_msk            : 1 ;
+       UINT32 onl2_nml_frm_timing_cfg_eof_int_msk        : 1 ;
+       UINT32 onl2_nml_frm_timing_cfg_line_int_msk       : 1 ;
+       UINT32 onl2_nml_cfg_rdy_clr_int_msk               : 1 ;
+       UINT32 onl2_nml_hist_done_int_msk                 : 1 ;
+       UINT32 onl2_nml_curve_done_int_msk                : 1 ;
+       UINT32 onl2_nml_wb_done_int_msk                   : 2 ;
+       UINT32 onl2_nml_frm_timing_unflow_int_msk         : 1 ;
+       UINT32 onl2_nml_wb_ovflow_int_msk                 : 2 ;
+       UINT32 onl2_nml_cmdlist_ch_frm_cfg_done_int_msk   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_5
+       UINT32 onl2_nml_dma_dbg_int_msk                   : 16;
+       UINT32 onl2_nml_outctl_dbg_int_msk                : 1 ;
+       UINT32 onl2_nml_ctl_dbg_int_msk                   : 1 ;
+       UINT32 onl2_nml_cmdlist_dbg_int_msk               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_6
+       UINT32 offl0_cfg_rdy_clr_int_msk                  : 1 ;
+       UINT32 offl0_wb_frm_done_int_msk                  : 2 ;
+       UINT32 offl0_wb_slice_done_int_msk                : 2 ;
+       UINT32 offl0_cmdlist_ch_frm_cfg_done_int_msk      : 14;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_7
+       UINT32 offl0_nml_dma_dbg_int_msk                  : 16;
+       UINT32 offl0_nml_ctl_dbg_int_msk                  : 1 ;
+       UINT32 offl0_nml_cmdlist_dbg_int_msk              : 1 ;
+       UINT32                                            : 14;
+
+
+       //REGISTER dpu_int_reg_8
+       UINT32 offl1_cfg_rdy_clr_int_msk                  : 1 ;
+       UINT32 offl1_wb_frm_done_int_msk                  : 2 ;
+       UINT32 offl1_wb_slice_done_int_msk                : 2 ;
+       UINT32 offl1_cmdlist_ch_frm_cfg_done_int_msk      : 14;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_9
+       UINT32 offl1_nml_dma_dbg_int_msk                  : 16;
+       UINT32 offl1_nml_ctl_dbg_int_msk                  : 1 ;
+       UINT32 offl1_nml_cmdlist_dbg_int_msk              : 1 ;
+       UINT32                                            : 14;
+
+
+       //REGISTER dpu_int_reg_10
+       UINT32 onl0_nml_frm_timing_vsync_int_sts          : 1 ;
+       UINT32 onl0_nml_frm_timing_eof_int_sts            : 1 ;
+       UINT32 onl0_nml_frm_timing_cfg_eof_int_sts        : 1 ;
+       UINT32 onl0_nml_frm_timing_cfg_line_int_sts       : 1 ;
+       UINT32 onl0_nml_cfg_rdy_clr_int_sts               : 1 ;
+       UINT32 onl0_nml_hist_done_int_sts                 : 1 ;
+       UINT32 onl0_nml_curve_done_int_sts                : 1 ;
+       UINT32 onl0_nml_wb_done_int_sts                   : 2 ;
+       UINT32 onl0_nml_frm_timing_unflow_int_sts         : 1 ;
+       UINT32 onl0_nml_wb_ovflow_int_sts                 : 2 ;
+       UINT32 onl0_nml_cmdlist_ch_frm_cfg_done_int_sts   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_11
+       UINT32 onl0_nml_dma_dbg_int_sts                   : 16;
+       UINT32 onl0_nml_outctl_dbg_int_sts                : 1 ;
+       UINT32 onl0_nml_ctl_dbg_int_sts                   : 1 ;
+       UINT32 onl0_nml_cmdlist_dbg_int_sts               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_12
+       UINT32 onl1_nml_frm_timing_vsync_int_sts          : 1 ;
+       UINT32 onl1_nml_frm_timing_eof_int_sts            : 1 ;
+       UINT32 onl1_nml_frm_timing_cfg_eof_int_sts        : 1 ;
+       UINT32 onl1_nml_frm_timing_cfg_line_int_sts       : 1 ;
+       UINT32 onl1_nml_cfg_rdy_clr_int_sts               : 1 ;
+       UINT32 onl1_nml_hist_done_int_sts                 : 1 ;
+       UINT32 onl1_nml_curve_done_int_sts                : 1 ;
+       UINT32 onl1_nml_wb_done_int_sts                   : 2 ;
+       UINT32 onl1_nml_frm_timing_unflow_int_sts         : 1 ;
+       UINT32 onl1_nml_wb_ovflow_int_sts                 : 2 ;
+       UINT32 onl1_nml_cmdlist_ch_frm_cfg_done_int_sts   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_13
+       UINT32 onl1_nml_dma_dbg_int_sts                   : 16;
+       UINT32 onl1_nml_outctl_dbg_int_sts                : 1 ;
+       UINT32 onl1_nml_ctl_dbg_int_sts                   : 1 ;
+       UINT32 onl1_nml_cmdlist_dbg_int_sts               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_14
+       UINT32 onl2_nml_frm_timing_vsync_int_sts          : 1 ;
+       UINT32 onl2_nml_frm_timing_eof_int_sts            : 1 ;
+       UINT32 onl2_nml_frm_timing_cfg_eof_int_sts        : 1 ;
+       UINT32 onl2_nml_frm_timing_cfg_line_int_sts       : 1 ;
+       UINT32 onl2_nml_cfg_rdy_clr_int_sts               : 1 ;
+       UINT32 onl2_nml_hist_done_int_sts                 : 1 ;
+       UINT32 onl2_nml_curve_done_int_sts                : 1 ;
+       UINT32 onl2_nml_wb_done_int_sts                   : 2 ;
+       UINT32 onl2_nml_frm_timing_unflow_int_sts         : 1 ;
+       UINT32 onl2_nml_wb_ovflow_int_sts                 : 2 ;
+       UINT32 onl2_nml_cmdlist_ch_frm_cfg_done_int_sts   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_15
+       UINT32 onl2_nml_dma_dbg_int_sts                   : 16;
+       UINT32 onl2_nml_outctl_dbg_int_sts                : 1 ;
+       UINT32 onl2_nml_ctl_dbg_int_sts                   : 1 ;
+       UINT32 onl2_nml_cmdlist_dbg_int_sts               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_16
+       UINT32 offl0_cfg_rdy_clr_int_sts                  : 1 ;
+       UINT32 offl0_wb_frm_done_int_sts                  : 2 ;
+       UINT32 offl0_wb_slice_done_int_sts                : 2 ;
+       UINT32 offl0_cmdlist_ch_frm_cfg_done_int_sts      : 14;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_17
+       UINT32 offl0_nml_dma_dbg_int_sts                  : 16;
+       UINT32 offl0_nml_ctl_dbg_int_sts                  : 1 ;
+       UINT32 offl0_nml_cmdlist_dbg_int_sts              : 1 ;
+       UINT32                                            : 14;
+
+
+       //REGISTER dpu_int_reg_18
+       UINT32 offl1_cfg_rdy_clr_int_sts                  : 1 ;
+       UINT32 offl1_wb_frm_done_int_sts                  : 2 ;
+       UINT32 offl1_wb_slice_done_int_sts                : 2 ;
+       UINT32 offl1_cmdlist_ch_frm_cfg_done_int_sts      : 14;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_19
+       UINT32 offl1_nml_dma_dbg_int_sts                  : 16;
+       UINT32 offl1_nml_ctl_dbg_int_sts                  : 1 ;
+       UINT32 offl1_nml_cmdlist_dbg_int_sts              : 1 ;
+       UINT32                                            : 14;
+
+
+       //REGISTER dpu_int_reg_20
+       UINT32 onl0_nml_frm_timing_vsync_int_raw          : 1 ;
+       UINT32 onl0_nml_frm_timing_eof_int_raw            : 1 ;
+       UINT32 onl0_nml_frm_timing_cfg_eof_int_raw        : 1 ;
+       UINT32 onl0_nml_frm_timing_cfg_line_int_raw       : 1 ;
+       UINT32 onl0_nml_cfg_rdy_clr_int_raw               : 1 ;
+       UINT32 onl0_nml_hist_done_int_raw                 : 1 ;
+       UINT32 onl0_nml_curve_done_int_raw                : 1 ;
+       UINT32 onl0_nml_wb_done_int_raw                   : 2 ;
+       UINT32 onl0_nml_frm_timing_unflow_int_raw         : 1 ;
+       UINT32 onl0_nml_wb_ovflow_int_raw                 : 2 ;
+       UINT32 onl0_nml_cmdlist_ch_frm_cfg_done_int_raw   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_21
+       UINT32 onl0_nml_dma_dbg_int_raw                   : 16;
+       UINT32 onl0_nml_outctl_dbg_int_raw                : 1 ;
+       UINT32 onl0_nml_ctl_dbg_int_raw                   : 1 ;
+       UINT32 onl0_nml_cmdlist_dbg_int_raw               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_22
+       UINT32 onl1_nml_frm_timing_vsync_int_raw          : 1 ;
+       UINT32 onl1_nml_frm_timing_eof_int_raw            : 1 ;
+       UINT32 onl1_nml_frm_timing_cfg_eof_int_raw        : 1 ;
+       UINT32 onl1_nml_frm_timing_cfg_line_int_raw       : 1 ;
+       UINT32 onl1_nml_cfg_rdy_clr_int_raw               : 1 ;
+       UINT32 onl1_nml_hist_done_int_raw                 : 1 ;
+       UINT32 onl1_nml_curve_done_int_raw                : 1 ;
+       UINT32 onl1_nml_wb_done_int_raw                   : 2 ;
+       UINT32 onl1_nml_frm_timing_unflow_int_raw         : 1 ;
+       UINT32 onl1_nml_wb_ovflow_int_raw                 : 2 ;
+       UINT32 onl1_nml_cmdlist_ch_frm_cfg_done_int_raw   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_23
+       UINT32 onl1_nml_dma_dbg_int_raw                   : 16;
+       UINT32 onl1_nml_outctl_dbg_int_raw                : 1 ;
+       UINT32 onl1_nml_ctl_dbg_int_raw                   : 1 ;
+       UINT32 onl1_nml_cmdlist_dbg_int_raw               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_24
+       UINT32 onl2_nml_frm_timing_vsync_int_raw          : 1 ;
+       UINT32 onl2_nml_frm_timing_eof_int_raw            : 1 ;
+       UINT32 onl2_nml_frm_timing_cfg_eof_int_raw        : 1 ;
+       UINT32 onl2_nml_frm_timing_cfg_line_int_raw       : 1 ;
+       UINT32 onl2_nml_cfg_rdy_clr_int_raw               : 1 ;
+       UINT32 onl2_nml_hist_done_int_raw                 : 1 ;
+       UINT32 onl2_nml_curve_done_int_raw                : 1 ;
+       UINT32 onl2_nml_wb_done_int_raw                   : 2 ;
+       UINT32 onl2_nml_frm_timing_unflow_int_raw         : 1 ;
+       UINT32 onl2_nml_wb_ovflow_int_raw                 : 2 ;
+       UINT32 onl2_nml_cmdlist_ch_frm_cfg_done_int_raw   : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_25
+       UINT32 onl2_nml_dma_dbg_int_raw                   : 16;
+       UINT32 onl2_nml_outctl_dbg_int_raw                : 1 ;
+       UINT32 onl2_nml_ctl_dbg_int_raw                   : 1 ;
+       UINT32 onl2_nml_cmdlist_dbg_int_raw               : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_26
+       UINT32 offl0_cfg_rdy_clr_int_raw                  : 1 ;
+       UINT32 offl0_wb_frm_done_int_raw                  : 2 ;
+       UINT32 offl0_wb_slice_done_int_raw                : 2 ;
+       UINT32 offl0_cmdlist_ch_frm_cfg_done_int_raw      : 14;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_27
+       UINT32 offl0_nml_dma_dbg_int_raw                  : 16;
+       UINT32 offl0_nml_ctl_dbg_int_raw                  : 1 ;
+       UINT32 offl0_nml_cmdlist_dbg_int_raw              : 1 ;
+       UINT32                                            : 14;
+
+
+       //REGISTER dpu_int_reg_28
+       UINT32 offl1_cfg_rdy_clr_int_raw                  : 1 ;
+       UINT32 offl1_wb_frm_done_int_raw                  : 2 ;
+       UINT32 offl1_wb_slice_done_int_raw                : 2 ;
+       UINT32 offl1_cmdlist_ch_frm_cfg_done_int_raw      : 14;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_29
+       UINT32 offl1_nml_dma_dbg_int_raw                  : 16;
+       UINT32 offl1_nml_ctl_dbg_int_raw                  : 1 ;
+       UINT32 offl1_nml_cmdlist_dbg_int_raw              : 1 ;
+       UINT32                                            : 14;
+
+
+       //REGISTER dpu_int_reg_30
+       struct
+       {
+       UINT32                                            : 32;
+       }dpu_int_reg_30[10];
+
+       //REGISTER dpu_int_reg_40
+       UINT32 onl0_secu_frm_timing_vsync_int_msk         : 1 ;
+       UINT32 onl0_secu_frm_timing_eof_int_msk           : 1 ;
+       UINT32 onl0_secu_frm_timing_cfg_eof_int_msk       : 1 ;
+       UINT32 onl0_secu_frm_timing_cfg_line_int_msk      : 1 ;
+       UINT32 onl0_secu_cfg_rdy_clr_int_msk              : 1 ;
+       UINT32 onl0_secu_hist_done_int_msk                : 1 ;
+       UINT32 onl0_secu_curve_done_int_msk               : 1 ;
+       UINT32 onl0_secu_wb_done_int_msk                  : 2 ;
+       UINT32 onl0_secu_frm_timing_unflow_int_msk        : 1 ;
+       UINT32 onl0_secu_wb_ovflow_int_msk                : 2 ;
+       UINT32 onl0_secu_cmdlist_ch_frm_cfg_done_int_msk  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_41
+       UINT32 onl0_secu_dma_dbg_int_msk                  : 16;
+       UINT32 onl0_secu_outctl_dbg_int_msk               : 1 ;
+       UINT32 onl0_secu_ctl_dbg_int_msk                  : 1 ;
+       UINT32 onl0_secu_cmdlist_dbg_int_msk              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_42
+       UINT32 onl1_secu_frm_timing_vsync_int_msk         : 1 ;
+       UINT32 onl1_secu_frm_timing_eof_int_msk           : 1 ;
+       UINT32 onl1_secu_frm_timing_cfg_eof_int_msk       : 1 ;
+       UINT32 onl1_secu_frm_timing_cfg_line_int_msk      : 1 ;
+       UINT32 onl1_secu_cfg_rdy_clr_int_msk              : 1 ;
+       UINT32 onl1_secu_hist_done_int_msk                : 1 ;
+       UINT32 onl1_secu_curve_done_int_msk               : 1 ;
+       UINT32 onl1_secu_wb_done_int_msk                  : 2 ;
+       UINT32 onl1_secu_frm_timing_unflow_int_msk        : 1 ;
+       UINT32 onl1_secu_wb_ovflow_int_msk                : 2 ;
+       UINT32 onl1_secu_cmdlist_ch_frm_cfg_done_int_msk  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_43
+       UINT32 onl1_secu_dma_dbg_int_msk                  : 16;
+       UINT32 onl1_secu_outctl_dbg_int_msk               : 1 ;
+       UINT32 onl1_secu_ctl_dbg_int_msk                  : 1 ;
+       UINT32 onl1_secu_cmdlist_dbg_int_msk              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_44
+       UINT32 onl2_secu_frm_timing_vsync_int_msk         : 1 ;
+       UINT32 onl2_secu_frm_timing_eof_int_msk           : 1 ;
+       UINT32 onl2_secu_frm_timing_cfg_eof_int_msk       : 1 ;
+       UINT32 onl2_secu_frm_timing_cfg_line_int_msk      : 1 ;
+       UINT32 onl2_secu_cfg_rdy_clr_int_msk              : 1 ;
+       UINT32 onl2_secu_hist_done_int_msk                : 1 ;
+       UINT32 onl2_secu_curve_done_int_msk               : 1 ;
+       UINT32 onl2_secu_wb_done_int_msk                  : 2 ;
+       UINT32 onl2_secu_frm_timing_unflow_int_msk        : 1 ;
+       UINT32 onl2_secu_wb_ovflow_int_msk                : 2 ;
+       UINT32 onl2_secu_cmdlist_ch_frm_cfg_done_int_msk  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_45
+       UINT32 onl2_secu_dma_dbg_int_msk                  : 16;
+       UINT32 onl2_secu_outctl_dbg_int_msk               : 1 ;
+       UINT32 onl2_secu_ctl_dbg_int_msk                  : 1 ;
+       UINT32 onl2_secu_cmdlist_dbg_int_msk              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_46
+       UINT32 onl0_secu_frm_timing_vsync_int_sts         : 1 ;
+       UINT32 onl0_secu_frm_timing_eof_int_sts           : 1 ;
+       UINT32 onl0_secu_frm_timing_cfg_eof_int_sts       : 1 ;
+       UINT32 onl0_secu_frm_timing_cfg_line_int_sts      : 1 ;
+       UINT32 onl0_secu_cfg_rdy_clr_int_sts              : 1 ;
+       UINT32 onl0_secu_hist_done_int_sts                : 1 ;
+       UINT32 onl0_secu_curve_done_int_sts               : 1 ;
+       UINT32 onl0_secu_wb_done_int_sts                  : 2 ;
+       UINT32 onl0_secu_frm_timing_unflow_int_sts        : 1 ;
+       UINT32 onl0_secu_wb_ovflow_int_sts                : 2 ;
+       UINT32 onl0_secu_cmdlist_ch_frm_cfg_done_int_sts  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_47
+       UINT32 onl0_secu_dma_dbg_int_sts                  : 16;
+       UINT32 onl0_secu_outctl_dbg_int_sts               : 1 ;
+       UINT32 onl0_secu_ctl_dbg_int_sts                  : 1 ;
+       UINT32 onl0_secu_cmdlist_dbg_int_sts              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_48
+       UINT32 onl1_secu_frm_timing_vsync_int_sts         : 1 ;
+       UINT32 onl1_secu_frm_timing_eof_int_sts           : 1 ;
+       UINT32 onl1_secu_frm_timing_cfg_eof_int_sts       : 1 ;
+       UINT32 onl1_secu_frm_timing_cfg_line_int_sts      : 1 ;
+       UINT32 onl1_secu_cfg_rdy_clr_int_sts              : 1 ;
+       UINT32 onl1_secu_hist_done_int_sts                : 1 ;
+       UINT32 onl1_secu_curve_done_int_sts               : 1 ;
+       UINT32 onl1_secu_wb_done_int_sts                  : 2 ;
+       UINT32 onl1_secu_frm_timing_unflow_int_sts        : 1 ;
+       UINT32 onl1_secu_wb_ovflow_int_sts                : 2 ;
+       UINT32 onl1_secu_cmdlist_ch_frm_cfg_done_int_sts  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_49
+       UINT32 onl1_secu_dma_dbg_int_sts                  : 16;
+       UINT32 onl1_secu_outctl_dbg_int_sts               : 1 ;
+       UINT32 onl1_secu_ctl_dbg_int_sts                  : 1 ;
+       UINT32 onl1_secu_cmdlist_dbg_int_sts              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_50
+       UINT32 onl2_secu_frm_timing_vsync_int_sts         : 1 ;
+       UINT32 onl2_secu_frm_timing_eof_int_sts           : 1 ;
+       UINT32 onl2_secu_frm_timing_cfg_eof_int_sts       : 1 ;
+       UINT32 onl2_secu_frm_timing_cfg_line_int_sts      : 1 ;
+       UINT32 onl2_secu_cfg_rdy_clr_int_sts              : 1 ;
+       UINT32 onl2_secu_hist_done_int_sts                : 1 ;
+       UINT32 onl2_secu_curve_done_int_sts               : 1 ;
+       UINT32 onl2_secu_wb_done_int_sts                  : 2 ;
+       UINT32 onl2_secu_frm_timing_unflow_int_sts        : 1 ;
+       UINT32 onl2_secu_wb_ovflow_int_sts                : 2 ;
+       UINT32 onl2_secu_cmdlist_ch_frm_cfg_done_int_sts  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_51
+       UINT32 onl2_secu_dma_dbg_int_sts                  : 16;
+       UINT32 onl2_secu_outctl_dbg_int_sts               : 1 ;
+       UINT32 onl2_secu_ctl_dbg_int_sts                  : 1 ;
+       UINT32 onl2_secu_cmdlist_dbg_int_sts              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_52
+       UINT32 onl0_secu_frm_timing_vsync_int_raw         : 1 ;
+       UINT32 onl0_secu_frm_timing_eof_int_raw           : 1 ;
+       UINT32 onl0_secu_frm_timing_cfg_eof_int_raw       : 1 ;
+       UINT32 onl0_secu_frm_timing_cfg_line_int_raw      : 1 ;
+       UINT32 onl0_secu_cfg_rdy_clr_int_raw              : 1 ;
+       UINT32 onl0_secu_hist_done_int_raw                : 1 ;
+       UINT32 onl0_secu_curve_done_int_raw               : 1 ;
+       UINT32 onl0_secu_wb_done_int_raw                  : 2 ;
+       UINT32 onl0_secu_frm_timing_unflow_int_raw        : 1 ;
+       UINT32 onl0_secu_wb_ovflow_int_raw                : 2 ;
+       UINT32 onl0_secu_cmdlist_ch_frm_cfg_done_int_raw  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_53
+       UINT32 onl0_secu_dma_dbg_int_raw                  : 16;
+       UINT32 onl0_secu_outctl_dbg_int_raw               : 1 ;
+       UINT32 onl0_secu_ctl_dbg_int_raw                  : 1 ;
+       UINT32 onl0_secu_cmdlist_dbg_int_raw              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_54
+       UINT32 onl1_secu_frm_timing_vsync_int_raw         : 1 ;
+       UINT32 onl1_secu_frm_timing_eof_int_raw           : 1 ;
+       UINT32 onl1_secu_frm_timing_cfg_eof_int_raw       : 1 ;
+       UINT32 onl1_secu_frm_timing_cfg_line_int_raw      : 1 ;
+       UINT32 onl1_secu_cfg_rdy_clr_int_raw              : 1 ;
+       UINT32 onl1_secu_hist_done_int_raw                : 1 ;
+       UINT32 onl1_secu_curve_done_int_raw               : 1 ;
+       UINT32 onl1_secu_wb_done_int_raw                  : 2 ;
+       UINT32 onl1_secu_frm_timing_unflow_int_raw        : 1 ;
+       UINT32 onl1_secu_wb_ovflow_int_raw                : 2 ;
+       UINT32 onl1_secu_cmdlist_ch_frm_cfg_done_int_raw  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_55
+       UINT32 onl1_secu_dma_dbg_int_raw                  : 16;
+       UINT32 onl1_secu_outctl_dbg_int_raw               : 1 ;
+       UINT32 onl1_secu_ctl_dbg_int_raw                  : 1 ;
+       UINT32 onl1_secu_cmdlist_dbg_int_raw              : 1 ;
+       UINT32                                            : 13;
+
+
+       //REGISTER dpu_int_reg_56
+       UINT32 onl2_secu_frm_timing_vsync_int_raw         : 1 ;
+       UINT32 onl2_secu_frm_timing_eof_int_raw           : 1 ;
+       UINT32 onl2_secu_frm_timing_cfg_eof_int_raw       : 1 ;
+       UINT32 onl2_secu_frm_timing_cfg_line_int_raw      : 1 ;
+       UINT32 onl2_secu_cfg_rdy_clr_int_raw              : 1 ;
+       UINT32 onl2_secu_hist_done_int_raw                : 1 ;
+       UINT32 onl2_secu_curve_done_int_raw               : 1 ;
+       UINT32 onl2_secu_wb_done_int_raw                  : 2 ;
+       UINT32 onl2_secu_frm_timing_unflow_int_raw        : 1 ;
+       UINT32 onl2_secu_wb_ovflow_int_raw                : 2 ;
+       UINT32 onl2_secu_cmdlist_ch_frm_cfg_done_int_raw  : 14;
+       UINT32                                            : 6 ;
+
+
+       //REGISTER dpu_int_reg_57
+       UINT32 onl2_secu_dma_dbg_int_raw                  : 16;
+       UINT32 onl2_secu_outctl_dbg_int_raw               : 1 ;
+       UINT32 onl2_secu_ctl_dbg_int_raw                  : 1 ;
+       UINT32 onl2_secu_cmdlist_dbg_int_raw              : 1 ;
+       UINT32                                            : 13;
+
+
+       } b;
+
+       struct
+       {
+               UINT32 dpu_int_reg_0;
+               UINT32 dpu_int_reg_1;
+               UINT32 dpu_int_reg_2;
+               UINT32 dpu_int_reg_3;
+               UINT32 dpu_int_reg_4;
+               UINT32 dpu_int_reg_5;
+               UINT32 dpu_int_reg_6;
+               UINT32 dpu_int_reg_7;
+               UINT32 dpu_int_reg_8;
+               UINT32 dpu_int_reg_9;
+               UINT32 dpu_int_reg_10;
+               UINT32 dpu_int_reg_11;
+               UINT32 dpu_int_reg_12;
+               UINT32 dpu_int_reg_13;
+               UINT32 dpu_int_reg_14;
+               UINT32 dpu_int_reg_15;
+               UINT32 dpu_int_reg_16;
+               UINT32 dpu_int_reg_17;
+               UINT32 dpu_int_reg_18;
+               UINT32 dpu_int_reg_19;
+               UINT32 dpu_int_reg_20;
+               UINT32 dpu_int_reg_21;
+               UINT32 dpu_int_reg_22;
+               UINT32 dpu_int_reg_23;
+               UINT32 dpu_int_reg_24;
+               UINT32 dpu_int_reg_25;
+               UINT32 dpu_int_reg_26;
+               UINT32 dpu_int_reg_27;
+               UINT32 dpu_int_reg_28;
+               UINT32 dpu_int_reg_29;
+               UINT32 dpu_int_reg_30;
+               UINT32 dpu_int_reg_31;
+               UINT32 dpu_int_reg_32;
+               UINT32 dpu_int_reg_33;
+               UINT32 dpu_int_reg_34;
+               UINT32 dpu_int_reg_35;
+               UINT32 dpu_int_reg_36;
+               UINT32 dpu_int_reg_37;
+               UINT32 dpu_int_reg_38;
+               UINT32 dpu_int_reg_39;
+               UINT32 dpu_int_reg_40;
+               UINT32 dpu_int_reg_41;
+               UINT32 dpu_int_reg_42;
+               UINT32 dpu_int_reg_43;
+               UINT32 dpu_int_reg_44;
+               UINT32 dpu_int_reg_45;
+               UINT32 dpu_int_reg_46;
+               UINT32 dpu_int_reg_47;
+               UINT32 dpu_int_reg_48;
+               UINT32 dpu_int_reg_49;
+               UINT32 dpu_int_reg_50;
+               UINT32 dpu_int_reg_51;
+               UINT32 dpu_int_reg_52;
+               UINT32 dpu_int_reg_53;
+               UINT32 dpu_int_reg_54;
+               UINT32 dpu_int_reg_55;
+               UINT32 dpu_int_reg_56;
+               UINT32 dpu_int_reg_57;
+       } v;
+
+}DPU_INTP_REG;
+#endif
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_top.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/dpu_top.h
new file mode 100644 (file)
index 0000000..e01ae14
--- /dev/null
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef DPU_TOP_REG_H
+#define DPU_TOP_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER dpu_top_reg_0
+       UINT32 Minor_number                 : 8 ;
+       UINT32 Major_number                 : 8 ;
+       UINT32 Product_ID                   : 16;
+
+
+       //REGISTER dpu_top_reg_1
+       struct
+       {
+       UINT32                              : 32;
+       }dpu_top_reg_1[213];
+
+       //REGISTER dpu_top_reg_214
+       UINT32 dma0_to_layer0_valid         : 1 ;
+       UINT32 layer0_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer1_valid         : 1 ;
+       UINT32 layer1_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer2_valid         : 1 ;
+       UINT32 layer2_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer3_valid         : 1 ;
+       UINT32 layer3_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer4_valid         : 1 ;
+       UINT32 layer4_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer5_valid         : 1 ;
+       UINT32 layer5_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer6_valid         : 1 ;
+       UINT32 layer6_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer7_valid         : 1 ;
+       UINT32 layer7_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer8_valid         : 1 ;
+       UINT32 layer8_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer9_valid         : 1 ;
+       UINT32 layer9_to_dma0_ready         : 1 ;
+       UINT32 dma0_to_layer10_valid        : 1 ;
+       UINT32 layer10_to_dma0_ready        : 1 ;
+       UINT32 dma0_to_layer11_valid        : 1 ;
+       UINT32 layer11_to_dma0_ready        : 1 ;
+       UINT32                              : 8 ;
+
+
+       //REGISTER dpu_top_reg_215
+       UINT32 dma1_to_layer0_valid         : 1 ;
+       UINT32 layer0_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer1_valid         : 1 ;
+       UINT32 layer1_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer2_valid         : 1 ;
+       UINT32 layer2_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer3_valid         : 1 ;
+       UINT32 layer3_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer4_valid         : 1 ;
+       UINT32 layer4_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer5_valid         : 1 ;
+       UINT32 layer5_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer6_valid         : 1 ;
+       UINT32 layer6_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer7_valid         : 1 ;
+       UINT32 layer7_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer8_valid         : 1 ;
+       UINT32 layer8_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer9_valid         : 1 ;
+       UINT32 layer9_to_dma1_ready         : 1 ;
+       UINT32 dma1_to_layer10_valid        : 1 ;
+       UINT32 layer10_to_dma1_ready        : 1 ;
+       UINT32 dma1_to_layer11_valid        : 1 ;
+       UINT32 layer11_to_dma1_ready        : 1 ;
+       UINT32                              : 8 ;
+
+
+       //REGISTER dpu_top_reg_216
+       UINT32 core0_onl_pre_to_post_valid  : 1 ;
+       UINT32 core0_onl_post_to_pre_ready  : 1 ;
+       UINT32 core1_onl_pre_to_post_valid  : 1 ;
+       UINT32 core1_onl_post_to_pre_ready  : 1 ;
+       UINT32 core2_cmb_pre_to_post_valid  : 1 ;
+       UINT32 core2_cmb_post_to_pre_ready  : 1 ;
+       UINT32 core3_cmb_pre_to_post_valid  : 1 ;
+       UINT32 core3_cmb_post_to_pre_ready  : 1 ;
+       UINT32                              : 8 ;
+       UINT32 prepipe_wb0_valid            : 1 ;
+       UINT32 wb0_prepipe_ready            : 1 ;
+       UINT32 prepipe_wb1_valid            : 1 ;
+       UINT32 wb1_prepipe_ready            : 1 ;
+       UINT32                              : 12;
+
+
+       //REGISTER dpu_top_reg_217
+       UINT32 prepipe_to_scale0_valid      : 1 ;
+       UINT32 scale0_to_prepipe_ready      : 1 ;
+       UINT32 prepipe_to_scale1_valid      : 1 ;
+       UINT32 scale1_to_prepipe_ready      : 1 ;
+       UINT32 scale0_to_prepipe_valid      : 1 ;
+       UINT32 prepipe_to_scale0_ready      : 1 ;
+       UINT32 scale1_to_prepipe_valid      : 1 ;
+       UINT32 prepipe_to_scale1_ready      : 1 ;
+       UINT32 postpipe_to_scale0_valid     : 1 ;
+       UINT32 scale0_to_postpipe_ready     : 1 ;
+       UINT32 postpipe_to_scale1_valid     : 1 ;
+       UINT32 scale1_to_postpipe_ready     : 1 ;
+       UINT32 scale0_to_post_valid         : 1 ;
+       UINT32 postpipe_to_scale0_ready     : 1 ;
+       UINT32 scale1_to_postpipe_valid     : 1 ;
+       UINT32 postpipe_to_scale1_ready     : 1 ;
+       UINT32                              : 16;
+
+
+       };
+
+       INT32 value32[218];
+
+}DPU_TOP_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/mmu.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/mmu.h
new file mode 100644 (file)
index 0000000..e87a5ab
--- /dev/null
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef MMU_REG_H
+#define MMU_REG_H
+
+typedef union
+{
+       struct
+       {
+       struct
+       {
+       //REGISTER TBU_Timelimit
+       UINT32 rdma_timelimit         : 16;
+       UINT32 mmu_cg_en              : 1 ;
+       UINT32                        : 15;
+
+
+       //REGISTER TBU_AXI_PORT_SEL
+       UINT32 axi_port_sel0          : 2 ;
+       UINT32 axi_port_sel1          : 2 ;
+       UINT32 axi_port_sel2          : 2 ;
+       UINT32 axi_port_sel3          : 2 ;
+       UINT32                        : 24;
+
+
+       //REGISTER TLB_Miss_stat
+       UINT32 tlb_miss_num_clr       : 1 ;
+       UINT32 tlb_miss_num_sel       : 7 ;
+       UINT32                        : 8 ;
+       UINT32 tlb_miss_num           : 16;
+
+
+       //REGISTER MMU_Dmac0_Reg
+       UINT32                        : 4 ;
+       UINT32 cfg_dmac0_arcache      : 4 ;
+       UINT32 cfg_dmac0_arregion     : 4 ;
+       UINT32 cfg_dmac0_aruser       : 4 ;
+       UINT32 cfg_dmac0_rd_outs_num  : 8 ;
+       UINT32 cfg_dmac0_axi_burst    : 3 ;
+       UINT32                        : 5 ;
+
+
+       //REGISTER MMU_Dmac1_Reg
+       UINT32                        : 4 ;
+       UINT32 cfg_dmac1_arcache      : 4 ;
+       UINT32 cfg_dmac1_arregion     : 4 ;
+       UINT32 cfg_dmac1_aruser       : 4 ;
+       UINT32 cfg_dmac1_rd_outs_num  : 8 ;
+       UINT32 cfg_dmac1_axi_burst    : 3 ;
+       UINT32                        : 5 ;
+
+
+       //REGISTER MMU_Dmac2_Reg
+       UINT32                        : 4 ;
+       UINT32 cfg_dmac2_arcache      : 4 ;
+       UINT32 cfg_dmac2_arregion     : 4 ;
+       UINT32 cfg_dmac2_aruser       : 4 ;
+       UINT32 cfg_dmac2_rd_outs_num  : 8 ;
+       UINT32 cfg_dmac2_axi_burst    : 3 ;
+       UINT32                        : 5 ;
+
+
+       //REGISTER MMU_Dmac3_Reg
+       UINT32                        : 4 ;
+       UINT32 cfg_dmac3_arcache      : 4 ;
+       UINT32 cfg_dmac3_arregion     : 4 ;
+       UINT32 cfg_dmac3_aruser       : 4 ;
+       UINT32 cfg_dmac3_rd_outs_num  : 8 ;
+       UINT32 cfg_dmac3_axi_burst    : 3 ;
+       UINT32                        : 5 ;
+
+
+       //REGISTER MMU_axi0_ar_debug_Reg
+       UINT32 axi0_ar_debug          : 32;
+
+
+       //REGISTER MMU_axi0_aw_debug_Reg
+       UINT32 axi0_aw_debug          : 32;
+
+
+       //REGISTER MMU_axi1_ar_debug_Reg
+       UINT32 axi1_ar_debug          : 32;
+
+
+       //REGISTER MMU_axi1_aw_debug_Reg
+       UINT32 axi1_aw_debug          : 32;
+
+
+       //REGISTER MMU_axi2_ar_debug_Reg
+       UINT32 axi2_ar_debug          : 32;
+
+
+       //REGISTER MMU_axi2_aw_debug_Reg
+       UINT32 axi2_aw_debug          : 32;
+
+
+       //REGISTER MMU_axi3_ar_debug_Reg
+       UINT32 axi3_ar_debug          : 32;
+
+
+       //REGISTER MMU_axi3_aw_debug_Reg
+       UINT32 axi3_aw_debug          : 32;
+
+
+       //REGISTER TLB_CMD_NUM
+       UINT32 tlb_cmd_num_sel        : 7 ;
+       UINT32                        : 9 ;
+       UINT32 tlb_cmd_num            : 16;
+
+
+       //REGISTER TLB_CMD_NUM_TOTAL
+       UINT32 tlb_cmd_num_total      : 32;
+
+
+       //REGISTER TLB_WAIT_CYCLE
+       UINT32 tlb_wait_cycle_sel     : 7 ;
+       UINT32                        : 9 ;
+       UINT32 tlb_wait_cycle         : 16;
+
+
+       //REGISTER TLB_WAIT_CYCLE_TOTAL
+       UINT32 tlb_wait_cycle_total   : 32;
+
+
+       //REGISTER TLB_MISS_NUM_TOTAL
+       UINT32 tlb_miss_num_total     : 32;
+
+
+       struct {
+               UINT32                    : 32;
+       } reserve[44];
+
+       struct
+       {
+       //REGISTER TBU_Ctrl
+       UINT32 tbu_en               : 1 ;
+       UINT32 tbu_fbc_mode         : 1 ;
+       UINT32 tbu_plane_num        : 2 ;
+       UINT32 tbu_burst_limit_en   : 1 ;
+       UINT32 tlb_fetch_active_en  : 1 ;
+       UINT32                      : 2 ;
+       UINT32 tbu_qos              : 4 ;
+       UINT32                      : 20;
+
+
+       //REGISTER TBU_Base_Addr0_Low
+       UINT32 tbu_base_addr0_low   : 32;
+
+
+       //REGISTER TBU_Base_Addr0_High
+       UINT32 tbu_base_addr0_high  : 2 ;
+       UINT32                      : 30;
+
+
+       //REGISTER TBU_Base_Addr1_Low
+       UINT32 tbu_base_addr1_low   : 32;
+
+
+       //REGISTER TBU_Base_Addr1_High
+       UINT32 tbu_base_addr1_high  : 2 ;
+       UINT32                      : 30;
+
+
+       //REGISTER TBU_Base_Addr2_Low
+       UINT32 tbu_base_addr2_low   : 32;
+
+
+       //REGISTER TBU_Base_Addr2_High
+       UINT32 tbu_base_addr2_high  : 2 ;
+       UINT32                      : 30;
+
+
+       //REGISTER TBU_VA0
+       UINT32 tbu_va0              : 22;
+       UINT32                      : 10;
+
+
+       //REGISTER TBU_VA1
+       UINT32 tbu_va1              : 22;
+       UINT32                      : 10;
+
+
+       //REGISTER TBU_VA2
+       UINT32 tbu_va2              : 22;
+       UINT32                      : 10;
+
+
+       //REGISTER TBU_SIZE0
+       UINT32 tbu_size0            : 16;
+       UINT32                      : 16;
+
+
+       //REGISTER TBU_SIZE1
+       UINT32 tbu_size1            : 16;
+       UINT32                      : 16;
+
+
+       //REGISTER TBU_SIZE2
+       UINT32 tbu_size2            : 16;
+       UINT32                      : 16;
+
+       struct {
+               UINT32                  : 32;
+       } reserve[3];
+
+       }TBU[9];
+
+       };
+
+       INT32 value32[208];
+       } b;
+
+       struct
+       {
+       //REGISTER TBU_Timelimit
+       UINT32 TBU_Timelimit;
+
+
+       //REGISTER TBU_AXI_PORT_SEL
+       UINT32 TBU_AXI_PORT_SEL;
+
+
+       //REGISTER TLB_Miss_stat
+       UINT32 TLB_Miss_stat;
+
+
+       //REGISTER MMU_Dmac0_Reg
+       UINT32 MMU_Dmac0_Reg;
+
+
+       //REGISTER MMU_Dmac1_Reg
+       UINT32 MMU_Dmac1_Reg;
+
+
+       //REGISTER MMU_Dmac2_Reg
+       UINT32 MMU_Dmac2_Reg;
+
+
+       //REGISTER MMU_Dmac3_Reg
+       UINT32 MMU_Dmac3_Reg;
+
+       //REGISTER MMU_axi0_ar_debug_Reg
+       UINT32 MMU_axi0_ar_debug_Reg;
+
+
+       //REGISTER MMU_axi0_aw_debug_Reg
+       UINT32 MMU_axi0_aw_debug_Reg;
+
+
+       //REGISTER MMU_axi1_ar_debug_Reg
+       UINT32 MMU_axi1_ar_debug_Reg;
+
+
+       //REGISTER MMU_axi1_aw_debug_Reg
+       UINT32 MMU_axi1_aw_debug_Reg;
+
+
+       //REGISTER MMU_axi2_ar_debug_Reg
+       UINT32 axi2_ar_debug;
+
+
+       //REGISTER MMU_axi2_aw_debug_Reg
+       UINT32 MMU_axi2_aw_debug_Reg;
+
+
+       //REGISTER MMU_axi3_ar_debug_Reg
+       UINT32 MMU_axi3_ar_debug_Reg;
+
+
+       //REGISTER MMU_axi3_aw_debug_Reg
+       UINT32 axi3_aw_debug;
+
+
+       //REGISTER TLB_CMD_NUM
+       UINT32 TLB_CMD_NUM;
+
+
+       //REGISTER TLB_CMD_NUM_TOTAL
+       UINT32 TLB_CMD_NUM_TOTAL;
+
+
+       //REGISTER TLB_WAIT_CYCLE
+       UINT32 TLB_WAIT_CYCLE;
+
+
+       //REGISTER TLB_WAIT_CYCLE_TOTAL
+       UINT32 TLB_WAIT_CYCLE_TOTAL;
+
+
+       //REGISTER TLB_MISS_NUM_TOTAL
+       UINT32 TLB_MISS_NUM_TOTAL;
+
+       //Reserved
+       struct {
+               UINT32 RESERVED;
+       } reserve[44];
+
+       struct
+       {
+       //REGISTER TBU_Ctrl
+       UINT32  TBU_Ctrl;
+
+
+       //REGISTER TBU_Base_Addr0_Low
+       UINT32 TBU_Base_Addr0_Low;
+
+
+       //REGISTER TBU_Base_Addr0_High
+       UINT32 TBU_Base_Addr0_High;
+
+
+       //REGISTER TBU_Base_Addr1_Low
+       UINT32 TBU_Base_Addr1_Low;
+
+
+       //REGISTER TBU_Base_Addr1_High
+       UINT32 TBU_Base_Addr1_High;
+
+
+       //REGISTER TBU_Base_Addr2_Low
+       UINT32 TBU_Base_Addr2_Low;
+
+
+       //REGISTER TBU_Base_Addr2_High
+       UINT32 TBU_Base_Addr2_High;
+
+
+       //REGISTER TBU_VA0
+       UINT32 TBU_VA0;
+
+
+       UINT32 TBU_VA1;
+
+
+       //REGISTER TBU_VA2
+       UINT32 TBU_VA2;
+
+
+       //REGISTER TBU_SIZE0
+       UINT32 TBU_SIZE0;
+
+
+       //REGISTER TBU_SIZE1
+       UINT32 TBU_SIZE1;
+
+
+       //REGISTER TBU_SIZE2
+       UINT32 TBU_SIZE2;
+
+       struct {
+               UINT32                  : 32;
+       } reserve[3];
+
+       }TBU[9];
+
+       } v;
+
+}MMU_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/outctrl_proc_x.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/outctrl_proc_x.h
new file mode 100644 (file)
index 0000000..8822f07
--- /dev/null
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef OUTCTRL_PROC_X_REG_H
+#define OUTCTRL_PROC_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER Post_proc_reg_0
+       UINT32 m_npost_proc_en               : 1 ;
+       UINT32 m_ngain_to_full_en            : 1 ;
+       UINT32 m_nmatrix_en                  : 1 ;
+       UINT32 m_nendmatrix_en               : 1 ;
+       UINT32 m_nfront_tmootf_en            : 1 ;
+       UINT32 m_nend_tmootf_en              : 1 ;
+       UINT32 m_neotf_en                    : 1 ;
+       UINT32 m_noetf_en                    : 1 ;
+       UINT32                               : 24;
+
+
+       //REGISTER Post_proc_reg_1
+       UINT32 m_neotf_mode                  : 3 ;
+       UINT32                               : 29;
+
+
+       //REGISTER Post_proc_reg_2
+       UINT32 m_noetf_mode                  : 3 ;
+       UINT32                               : 5 ;
+       UINT32 m_noetf_max                   : 12;
+       UINT32                               : 12;
+
+
+       //REGISTER Post_proc_reg_3
+       UINT32 m_pfront_tmootf_gain_table0   : 16;
+       UINT32 m_pfront_tmootf_gain_table1   : 16;
+
+
+       //REGISTER Post_proc_reg_4
+       UINT32 m_pfront_tmootf_gain_table2   : 16;
+       UINT32 m_pfront_tmootf_gain_table3   : 16;
+
+
+       //REGISTER Post_proc_reg_5
+       UINT32 m_pfront_tmootf_gain_table4   : 16;
+       UINT32 m_pfront_tmootf_gain_table5   : 16;
+
+
+       //REGISTER Post_proc_reg_6
+       UINT32 m_pfront_tmootf_gain_table6   : 16;
+       UINT32 m_pfront_tmootf_gain_table7   : 16;
+
+
+       //REGISTER Post_proc_reg_7
+       UINT32 m_pfront_tmootf_gain_table8   : 16;
+       UINT32 m_pfront_tmootf_gain_table9   : 16;
+
+
+       //REGISTER Post_proc_reg_8
+       UINT32 m_pfront_tmootf_gain_table10  : 16;
+       UINT32 m_pfront_tmootf_gain_table11  : 16;
+
+
+       //REGISTER Post_proc_reg_9
+       UINT32 m_pfront_tmootf_gain_table12  : 16;
+       UINT32 m_pfront_tmootf_gain_table13  : 16;
+
+
+       //REGISTER Post_proc_reg_10
+       UINT32 m_pfront_tmootf_gain_table14  : 16;
+       UINT32 m_pfront_tmootf_gain_table15  : 16;
+
+
+       //REGISTER Post_proc_reg_11
+       UINT32 m_pfront_tmootf_gain_table16  : 16;
+       UINT32 m_pfront_tmootf_gain_table17  : 16;
+
+
+       //REGISTER Post_proc_reg_12
+       UINT32 m_pfront_tmootf_gain_table18  : 16;
+       UINT32 m_pfront_tmootf_gain_table19  : 16;
+
+
+       //REGISTER Post_proc_reg_13
+       UINT32 m_pfront_tmootf_gain_table20  : 16;
+       UINT32 m_pfront_tmootf_gain_table21  : 16;
+
+
+       //REGISTER Post_proc_reg_14
+       UINT32 m_pfront_tmootf_gain_table22  : 16;
+       UINT32 m_pfront_tmootf_gain_table23  : 16;
+
+
+       //REGISTER Post_proc_reg_15
+       UINT32 m_pfront_tmootf_gain_table24  : 16;
+       UINT32 m_pfront_tmootf_gain_table25  : 16;
+
+
+       //REGISTER Post_proc_reg_16
+       UINT32 m_pfront_tmootf_gain_table26  : 16;
+       UINT32 m_pfront_tmootf_gain_table27  : 16;
+
+
+       //REGISTER Post_proc_reg_17
+       UINT32 m_pfront_tmootf_gain_table28  : 16;
+       UINT32 m_pfront_tmootf_gain_table29  : 16;
+
+
+       //REGISTER Post_proc_reg_18
+       UINT32 m_pfront_tmootf_gain_table30  : 16;
+       UINT32 m_pfront_tmootf_gain_table31  : 16;
+
+
+       //REGISTER Post_proc_reg_19
+       UINT32 m_pfront_tmootf_gain_table32  : 16;
+       UINT32 m_pfront_tmootf_gain_table33  : 16;
+
+
+       //REGISTER Post_proc_reg_20
+       UINT32 m_pfront_tmootf_gain_table34  : 16;
+       UINT32 m_pfront_tmootf_gain_table35  : 16;
+
+
+       //REGISTER Post_proc_reg_21
+       UINT32 m_pfront_tmootf_gain_table36  : 16;
+       UINT32 m_pfront_tmootf_gain_table37  : 16;
+
+
+       //REGISTER Post_proc_reg_22
+       UINT32 m_pfront_tmootf_gain_table38  : 16;
+       UINT32 m_pfront_tmootf_gain_table39  : 16;
+
+
+       //REGISTER Post_proc_reg_23
+       UINT32 m_pfront_tmootf_gain_table40  : 16;
+       UINT32 m_pfront_tmootf_gain_table41  : 16;
+
+
+       //REGISTER Post_proc_reg_24
+       UINT32 m_pfront_tmootf_gain_table42  : 16;
+       UINT32 m_pfront_tmootf_gain_table43  : 16;
+
+
+       //REGISTER Post_proc_reg_25
+       UINT32 m_pfront_tmootf_gain_table44  : 16;
+       UINT32 m_pfront_tmootf_gain_table45  : 16;
+
+
+       //REGISTER Post_proc_reg_26
+       UINT32 m_pfront_tmootf_gain_table46  : 16;
+       UINT32 m_pfront_tmootf_gain_table47  : 16;
+
+
+       //REGISTER Post_proc_reg_27
+       UINT32 m_pfront_tmootf_gain_table48  : 16;
+       UINT32 m_pfront_tmootf_gain_table49  : 16;
+
+
+       //REGISTER Post_proc_reg_28
+       UINT32 m_pfront_tmootf_gain_table50  : 16;
+       UINT32 m_pfront_tmootf_gain_table51  : 16;
+
+
+       //REGISTER Post_proc_reg_29
+       UINT32 m_pfront_tmootf_gain_table52  : 16;
+       UINT32 m_pfront_tmootf_gain_table53  : 16;
+
+
+       //REGISTER Post_proc_reg_30
+       UINT32 m_pfront_tmootf_gain_table54  : 16;
+       UINT32 m_pfront_tmootf_gain_table55  : 16;
+
+
+       //REGISTER Post_proc_reg_31
+       UINT32 m_pfront_tmootf_gain_table56  : 16;
+       UINT32 m_pfront_tmootf_gain_table57  : 16;
+
+
+       //REGISTER Post_proc_reg_32
+       UINT32 m_pfront_tmootf_gain_table58  : 16;
+       UINT32 m_pfront_tmootf_gain_table59  : 16;
+
+
+       //REGISTER Post_proc_reg_33
+       UINT32 m_pfront_tmootf_gain_table60  : 16;
+       UINT32 m_pfront_tmootf_gain_table61  : 16;
+
+
+       //REGISTER Post_proc_reg_34
+       UINT32 m_pfront_tmootf_gain_table62  : 16;
+       UINT32 m_pfront_tmootf_gain_table63  : 16;
+
+
+       //REGISTER Post_proc_reg_35
+       UINT32 m_pfront_tmootf_gain_table64  : 16;
+       UINT32 m_nfront_tmootf_shift_bits    : 5 ;
+       UINT32 m_nfront_tmootf_rgb_mode      : 2 ;
+       UINT32                               : 9 ;
+
+
+       //REGISTER Post_proc_reg_36
+       UINT32 m_pend_tmootf_gain_table0     : 16;
+       UINT32 m_pend_tmootf_gain_table1     : 16;
+
+
+       //REGISTER Post_proc_reg_37
+       UINT32 m_pend_tmootf_gain_table2     : 16;
+       UINT32 m_pend_tmootf_gain_table3     : 16;
+
+
+       //REGISTER Post_proc_reg_38
+       UINT32 m_pend_tmootf_gain_table4     : 16;
+       UINT32 m_pend_tmootf_gain_table5     : 16;
+
+
+       //REGISTER Post_proc_reg_39
+       UINT32 m_pend_tmootf_gain_table6     : 16;
+       UINT32 m_pend_tmootf_gain_table7     : 16;
+
+
+       //REGISTER Post_proc_reg_40
+       UINT32 m_pend_tmootf_gain_table8     : 16;
+       UINT32 m_pend_tmootf_gain_table9     : 16;
+
+
+       //REGISTER Post_proc_reg_41
+       UINT32 m_pend_tmootf_gain_table10    : 16;
+       UINT32 m_pend_tmootf_gain_table11    : 16;
+
+
+       //REGISTER Post_proc_reg_42
+       UINT32 m_pend_tmootf_gain_table12    : 16;
+       UINT32 m_pend_tmootf_gain_table13    : 16;
+
+
+       //REGISTER Post_proc_reg_43
+       UINT32 m_pend_tmootf_gain_table14    : 16;
+       UINT32 m_pend_tmootf_gain_table15    : 16;
+
+
+       //REGISTER Post_proc_reg_44
+       UINT32 m_pend_tmootf_gain_table16    : 16;
+       UINT32 m_pend_tmootf_gain_table17    : 16;
+
+
+       //REGISTER Post_proc_reg_45
+       UINT32 m_pend_tmootf_gain_table18    : 16;
+       UINT32 m_pend_tmootf_gain_table19    : 16;
+
+
+       //REGISTER Post_proc_reg_46
+       UINT32 m_pend_tmootf_gain_table20    : 16;
+       UINT32 m_pend_tmootf_gain_table21    : 16;
+
+
+       //REGISTER Post_proc_reg_47
+       UINT32 m_pend_tmootf_gain_table22    : 16;
+       UINT32 m_pend_tmootf_gain_table23    : 16;
+
+
+       //REGISTER Post_proc_reg_48
+       UINT32 m_pend_tmootf_gain_table24    : 16;
+       UINT32 m_pend_tmootf_gain_table25    : 16;
+
+
+       //REGISTER Post_proc_reg_49
+       UINT32 m_pend_tmootf_gain_table26    : 16;
+       UINT32 m_pend_tmootf_gain_table27    : 16;
+
+
+       //REGISTER Post_proc_reg_50
+       UINT32 m_pend_tmootf_gain_table28    : 16;
+       UINT32 m_pend_tmootf_gain_table29    : 16;
+
+
+       //REGISTER Post_proc_reg_51
+       UINT32 m_pend_tmootf_gain_table30    : 16;
+       UINT32 m_pend_tmootf_gain_table31    : 16;
+
+
+       //REGISTER Post_proc_reg_52
+       UINT32 m_pend_tmootf_gain_table32    : 16;
+       UINT32 m_pend_tmootf_gain_table33    : 16;
+
+
+       //REGISTER Post_proc_reg_53
+       UINT32 m_pend_tmootf_gain_table34    : 16;
+       UINT32 m_pend_tmootf_gain_table35    : 16;
+
+
+       //REGISTER Post_proc_reg_54
+       UINT32 m_pend_tmootf_gain_table36    : 16;
+       UINT32 m_pend_tmootf_gain_table37    : 16;
+
+
+       //REGISTER Post_proc_reg_55
+       UINT32 m_pend_tmootf_gain_table38    : 16;
+       UINT32 m_pend_tmootf_gain_table39    : 16;
+
+
+       //REGISTER Post_proc_reg_56
+       UINT32 m_pend_tmootf_gain_table40    : 16;
+       UINT32 m_pend_tmootf_gain_table41    : 16;
+
+
+       //REGISTER Post_proc_reg_57
+       UINT32 m_pend_tmootf_gain_table42    : 16;
+       UINT32 m_pend_tmootf_gain_table43    : 16;
+
+
+       //REGISTER Post_proc_reg_58
+       UINT32 m_pend_tmootf_gain_table44    : 16;
+       UINT32 m_pend_tmootf_gain_table45    : 16;
+
+
+       //REGISTER Post_proc_reg_59
+       UINT32 m_pend_tmootf_gain_table46    : 16;
+       UINT32 m_pend_tmootf_gain_table47    : 16;
+
+
+       //REGISTER Post_proc_reg_60
+       UINT32 m_pend_tmootf_gain_table48    : 16;
+       UINT32 m_pend_tmootf_gain_table49    : 16;
+
+
+       //REGISTER Post_proc_reg_61
+       UINT32 m_pend_tmootf_gain_table50    : 16;
+       UINT32 m_pend_tmootf_gain_table51    : 16;
+
+
+       //REGISTER Post_proc_reg_62
+       UINT32 m_pend_tmootf_gain_table52    : 16;
+       UINT32 m_pend_tmootf_gain_table53    : 16;
+
+
+       //REGISTER Post_proc_reg_63
+       UINT32 m_pend_tmootf_gain_table54    : 16;
+       UINT32 m_pend_tmootf_gain_table55    : 16;
+
+
+       //REGISTER post_proc_reg_64
+       UINT32 m_pend_tmootf_gain_table56    : 16;
+       UINT32 m_pend_tmootf_gain_table57    : 16;
+
+
+       //REGISTER Post_proc_reg_65
+       UINT32 m_pend_tmootf_gain_table58    : 16;
+       UINT32 m_pend_tmootf_gain_table59    : 16;
+
+
+       //REGISTER Post_proc_reg_66
+       UINT32 m_pend_tmootf_gain_table60    : 16;
+       UINT32 m_pend_tmootf_gain_table61    : 16;
+
+
+       //REGISTER Post_proc_reg_67
+       UINT32 m_pend_tmootf_gain_table62    : 16;
+       UINT32 m_pend_tmootf_gain_table63    : 16;
+
+
+       //REGISTER Post_proc_reg_68
+       UINT32 m_pend_tmootf_gain_table64    : 16;
+       UINT32 m_nend_tmootf_shift_bits      : 5 ;
+       UINT32 m_nend_tmootf_rgb_mode        : 2 ;
+       UINT32                               : 9 ;
+
+
+       //REGISTER Post_proc_reg_69
+       UINT32 m_pmatrix_table0              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table1              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_70
+       UINT32 m_pmatrix_table2              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table3              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_71
+       UINT32 m_pmatrix_table4              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table5              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_72
+       UINT32 m_pmatrix_table6              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table7              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_73
+       UINT32 m_pmatrix_table8              : 14;
+       UINT32                               : 18;
+
+
+       //REGISTER Post_proc_reg_74
+       UINT32 m_pmatrix_offset0             : 25;
+       UINT32                               : 7 ;
+
+
+       //REGISTER Post_proc_reg_75
+       UINT32 m_pmatrix_offset1             : 25;
+       UINT32                               : 7 ;
+
+
+       //REGISTER Post_proc_reg_76
+       UINT32 m_pmatrix_offset2             : 25;
+       UINT32                               : 7 ;
+
+
+       //REGISTER Post_proc_reg_77
+       UINT32 m_ngain_to_full               : 16;
+       UINT32                               : 16;
+
+
+       //REGISTER Post_proc_reg_78
+       UINT32 m_pendmatrix_table0           : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pendmatrix_table1           : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_79
+       UINT32 m_pendmatrix_table2           : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pendmatrix_table3           : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_80
+       UINT32 m_pendmatrix_table4           : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pendmatrix_table5           : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_81
+       UINT32 m_pendmatrix_table6           : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pendmatrix_table7           : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER Post_proc_reg_82
+       UINT32 m_pendmatrix_table8           : 14;
+       UINT32                               : 18;
+
+
+       //REGISTER Post_proc_reg_83
+       UINT32 m_pendmatrix_offset0          : 13;
+       UINT32                               : 19;
+
+
+       //REGISTER Post_proc_reg_84
+       UINT32 m_pendmatrix_offset1          : 13;
+       UINT32                               : 19;
+
+
+       //REGISTER Post_proc_reg_85
+       UINT32 m_pendmatrix_offset2          : 13;
+       UINT32                               : 19;
+
+
+       };
+
+       INT32 value32[86];
+
+}OUTCTRL_PROC_X_REG;
+
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/outctrl_top_x.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/outctrl_top_x.h
new file mode 100644 (file)
index 0000000..3c443cb
--- /dev/null
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef OUTCTRL_TOP_X_REG_H
+#define OUTCTRL_TOP_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER Out_ctrl_reg_0
+       UINT32 m_n_inwdith               : 13;
+       UINT32                           : 3 ;
+       UINT32 m_n_inheight              : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_1
+       UINT32 scale_wdith               : 13;
+       UINT32                           : 3 ;
+       UINT32 scale_height              : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_2
+       UINT32                           : 32;
+
+
+       //REGISTER Out_ctrl_reg_3
+       UINT32                           : 32;
+
+
+       //REGISTER Out_ctrl_reg_4
+       UINT32 acad_en                   : 1 ;
+       UINT32 dsc_dfc_switch_en         : 1 ;
+       UINT32                           : 30;
+
+
+       //REGISTER Out_ctrl_reg_5
+       UINT32 scale_en                  : 1 ;
+       UINT32 dither_en                 : 1 ;
+       UINT32 dither_mode               : 1 ;
+       UINT32 dither_auto_temp          : 1 ;
+       UINT32 dither_out_dpth0          : 4 ;
+       UINT32 dither_out_dpth1          : 4 ;
+       UINT32 dither_out_dpth2          : 4 ;
+       UINT32 dither_temp_value         : 8 ;
+       UINT32 dither_rotate_mode        : 2 ;
+       UINT32 dither_pattern_bit        : 3 ;
+       UINT32                           : 3 ;
+
+
+       //REGISTER Out_ctrl_reg_6
+       UINT32 sbs_en                    : 1 ;
+       UINT32 split_en                  : 1 ;
+       UINT32 rgb2yuv_en                : 1 ;
+       UINT32 narrow_yuv_en             : 1 ;
+       UINT32 cmd_screen                : 1 ;
+       UINT32 frame_timing_en           : 1 ;
+       UINT32 cmd_wait_en               : 1 ;
+       UINT32 cmd_wait_te               : 1 ;
+       UINT32 reg06_bit8_dmy            : 1 ;
+       UINT32 tm_ctrl_reload_option     : 1 ;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_7
+       UINT32 hblank                    : 12;
+       UINT32                           : 4 ;
+       UINT32 split_overlap             : 10;
+       UINT32                           : 6 ;
+
+
+       //REGISTER Out_ctrl_reg_8
+       UINT32 rgb2yuv_matrix00          : 14;
+       UINT32                           : 2 ;
+       UINT32 rgb2yuv_matrix01          : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_9
+       UINT32 rgb2yuv_matrix02          : 14;
+       UINT32                           : 2 ;
+       UINT32 rgb2yuv_matrix03          : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_10
+       UINT32 rgb2yuv_matrix10          : 14;
+       UINT32                           : 2 ;
+       UINT32 rgb2yuv_matrix11          : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_11
+       UINT32 rgb2yuv_matrix12          : 14;
+       UINT32                           : 2 ;
+       UINT32 rgb2yuv_matrix13          : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_12
+       UINT32 rgb2yuv_matrix20          : 14;
+       UINT32                           : 2 ;
+       UINT32 rgb2yuv_matrix21          : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_13
+       UINT32 rgb2yuv_matrix22          : 14;
+       UINT32                           : 2 ;
+       UINT32 rgb2yuv_matrix23          : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_14
+       UINT32 dither_bayer_map00        : 8 ;
+       UINT32 dither_bayer_map01        : 8 ;
+       UINT32 dither_bayer_map02        : 8 ;
+       UINT32 dither_bayer_map03        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_15
+       UINT32 dither_bayer_map04        : 8 ;
+       UINT32 dither_bayer_map05        : 8 ;
+       UINT32 dither_bayer_map06        : 8 ;
+       UINT32 dither_bayer_map07        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_16
+       UINT32 dither_bayer_map10        : 8 ;
+       UINT32 dither_bayer_map11        : 8 ;
+       UINT32 dither_bayer_map12        : 8 ;
+       UINT32 dither_bayer_map13        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_17
+       UINT32 dither_bayer_map14        : 8 ;
+       UINT32 dither_bayer_map15        : 8 ;
+       UINT32 dither_bayer_map16        : 8 ;
+       UINT32 dither_bayer_map17        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_18
+       UINT32 dither_bayer_map20        : 8 ;
+       UINT32 dither_bayer_map21        : 8 ;
+       UINT32 dither_bayer_map22        : 8 ;
+       UINT32 dither_bayer_map23        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_19
+       UINT32 dither_bayer_map24        : 8 ;
+       UINT32 dither_bayer_map25        : 8 ;
+       UINT32 dither_bayer_map26        : 8 ;
+       UINT32 dither_bayer_map27        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_20
+       UINT32 dither_bayer_map30        : 8 ;
+       UINT32 dither_bayer_map31        : 8 ;
+       UINT32 dither_bayer_map32        : 8 ;
+       UINT32 dither_bayer_map33        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_21
+       UINT32 dither_bayer_map34        : 8 ;
+       UINT32 dither_bayer_map35        : 8 ;
+       UINT32 dither_bayer_map36        : 8 ;
+       UINT32 dither_bayer_map37        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_22
+       UINT32 dither_bayer_map40        : 8 ;
+       UINT32 dither_bayer_map41        : 8 ;
+       UINT32 dither_bayer_map42        : 8 ;
+       UINT32 dither_bayer_map43        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_23
+       UINT32 dither_bayer_map44        : 8 ;
+       UINT32 dither_bayer_map45        : 8 ;
+       UINT32 dither_bayer_map46        : 8 ;
+       UINT32 dither_bayer_map47        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_24
+       UINT32 dither_bayer_map50        : 8 ;
+       UINT32 dither_bayer_map51        : 8 ;
+       UINT32 dither_bayer_map52        : 8 ;
+       UINT32 dither_bayer_map53        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_25
+       UINT32 dither_bayer_map54        : 8 ;
+       UINT32 dither_bayer_map55        : 8 ;
+       UINT32 dither_bayer_map56        : 8 ;
+       UINT32 dither_bayer_map57        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_26
+       UINT32 dither_bayer_map60        : 8 ;
+       UINT32 dither_bayer_map61        : 8 ;
+       UINT32 dither_bayer_map62        : 8 ;
+       UINT32 dither_bayer_map63        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_27
+       UINT32 dither_bayer_map64        : 8 ;
+       UINT32 dither_bayer_map65        : 8 ;
+       UINT32 dither_bayer_map66        : 8 ;
+       UINT32 dither_bayer_map67        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_28
+       UINT32 dither_bayer_map70        : 8 ;
+       UINT32 dither_bayer_map71        : 8 ;
+       UINT32 dither_bayer_map72        : 8 ;
+       UINT32 dither_bayer_map73        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_29
+       UINT32 dither_bayer_map74        : 8 ;
+       UINT32 dither_bayer_map75        : 8 ;
+       UINT32 dither_bayer_map76        : 8 ;
+       UINT32 dither_bayer_map77        : 8 ;
+
+
+       //REGISTER Out_ctrl_reg_30
+       UINT32 dfc_low_thre              : 12;
+       UINT32                           : 4 ;
+       UINT32 dfc_high_thre             : 12;
+       UINT32                           : 4 ;
+
+
+       //REGISTER Out_ctrl_reg_31
+       UINT32 split_cfg_manual_en       : 1 ;
+       UINT32                           : 7 ;
+       UINT32 disp_ready_man_en         : 1 ;
+       UINT32 disp_ready_1_non_active   : 1 ;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_32
+       UINT32 hfp                       : 12;
+       UINT32                           : 4 ;
+       UINT32 hbp                       : 12;
+       UINT32                           : 4 ;
+
+
+       //REGISTER Out_ctrl_reg_33
+       UINT32 vfp                       : 12;
+       UINT32                           : 4 ;
+       UINT32 vbp                       : 12;
+       UINT32                           : 4 ;
+
+
+       //REGISTER Out_ctrl_reg_34
+       UINT32 hsync_width               : 10;
+       UINT32                           : 2 ;
+       UINT32 hsp                       : 1 ;
+       UINT32                           : 3 ;
+       UINT32 vsync_width               : 10;
+       UINT32                           : 2 ;
+       UINT32 vsp                       : 1 ;
+       UINT32                           : 3 ;
+
+
+       //REGISTER Out_ctrl_reg_35
+       UINT32 h_active                  : 14;
+       UINT32                           : 2 ;
+       UINT32 v_active                  : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_36
+       UINT32 user                      : 4 ;
+       UINT32                           : 28;
+
+
+       //REGISTER Out_ctrl_reg_37
+       UINT32 fm_cmd_timeout_num        : 24;
+       UINT32 fm_cmd_timeout_eq_eof     : 1 ;
+       UINT32                           : 7 ;
+
+
+       //REGISTER Out_ctrl_reg_38
+       UINT32 back_ground_r             : 12;
+       UINT32                           : 4 ;
+       UINT32 back_ground_g             : 12;
+       UINT32                           : 4 ;
+
+
+       //REGISTER Out_ctrl_reg_39
+       UINT32 back_ground_b             : 12;
+       UINT32                           : 20;
+
+
+       //REGISTER Out_ctrl_reg_40
+       UINT32 drift_timeout             : 12;
+       UINT32                           : 20;
+
+
+       //REGISTER Out_ctrl_reg_41
+       UINT32 eof_ln_dly                : 10;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_42
+       UINT32 sof_pre_ln_num            : 10;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_43
+       UINT32 cfg_ln_num_intp           : 14;
+       UINT32                           : 18;
+
+
+       //REGISTER Out_ctrl_reg_44
+       UINT32 sof0_irq_raw              : 1 ;
+       UINT32 eof0_irq_raw              : 1 ;
+       UINT32 cfg_eof0_irq_raw          : 1 ;
+       UINT32 cfg_eol0_irq_raw          : 1 ;
+       UINT32 underflow0_irq_raw        : 1 ;
+       UINT32 sof1_irq_raw              : 1 ;
+       UINT32 eof1_irq_raw              : 1 ;
+       UINT32 cfg_eof1_irq_raw          : 1 ;
+       UINT32 cfg_eol1_irq_raw          : 1 ;
+       UINT32 underflow1_irq_raw        : 1 ;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_45
+       UINT32 line_irq_raw              : 1 ;
+       UINT32 drift_timeout_irq_raw     : 1 ;
+       UINT32 acad_irq_raw              : 1 ;
+       UINT32 dsc0_overflow_irq_raw     : 1 ;
+       UINT32 dsc1_overflow_irq_raw     : 1 ;
+       UINT32                           : 27;
+
+
+       //REGISTER Out_ctrl_reg_46
+       UINT32 sof0_irq_mask             : 1 ;
+       UINT32 eof0_irq_mask             : 1 ;
+       UINT32 cfg_eof0_irq_mask         : 1 ;
+       UINT32 cfg_eol0_irq_mask         : 1 ;
+       UINT32 underflow0_irq_mask       : 1 ;
+       UINT32 sof1_irq_mask             : 1 ;
+       UINT32 eof1_irq_mask             : 1 ;
+       UINT32 cfg_eof1_irq_mask         : 1 ;
+       UINT32 cfg_eol1_irq_mask         : 1 ;
+       UINT32 underflow1_irq_mask       : 1 ;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_47
+       UINT32 line_irq_mask             : 1 ;
+       UINT32 drift_timeout_irq_mask    : 1 ;
+       UINT32 acad_irq_mask             : 1 ;
+       UINT32 dsc0_overflow_mask        : 1 ;
+       UINT32 dsc1_overflow_mask        : 1 ;
+       UINT32                           : 27;
+
+
+       //REGISTER Out_ctrl_reg_48
+       UINT32 sof0_irq_status           : 1 ;
+       UINT32 eof0_irq_status           : 1 ;
+       UINT32 cfg_eof0_irq_status       : 1 ;
+       UINT32 cfg_eol0_irq_status       : 1 ;
+       UINT32 underflow0_irq_status     : 1 ;
+       UINT32 sof1_irq_status           : 1 ;
+       UINT32 eof1_irq_status           : 1 ;
+       UINT32 cfg_eof1_irq_status       : 1 ;
+       UINT32 cfg_eol1_irq_status       : 1 ;
+       UINT32 underflow1_irq_status     : 1 ;
+       UINT32                           : 22;
+
+
+       //REGISTER Out_ctrl_reg_49
+       UINT32 line0_irq_status          : 1 ;
+       UINT32 drift_timeout_irq_status  : 1 ;
+       UINT32 acad_irq_status           : 1 ;
+       UINT32 dsc0_overflow_irq_sta     : 1 ;
+       UINT32 dsc1_overflow_irq_sta     : 1 ;
+       UINT32                           : 27;
+
+
+       //REGISTER Out_ctrl_reg_50
+       UINT32 postproc_ln_cnt           : 14;
+       UINT32                           : 2 ;
+       UINT32 postproc_pix_cnt          : 13;
+       UINT32                           : 3 ;
+
+
+       //REGISTER Out_ctrl_reg_51
+       UINT32 dither_ln_cnt             : 14;
+       UINT32                           : 2 ;
+       UINT32 dither_pix_cnt            : 13;
+       UINT32                           : 3 ;
+
+
+       //REGISTER Out_ctrl_reg_52
+       UINT32 dat_convert_ln_cnt        : 14;
+       UINT32                           : 2 ;
+       UINT32 dat_convert_pix_cnt       : 13;
+       UINT32                           : 3 ;
+
+
+       //REGISTER Out_ctrl_reg_53
+       UINT32 merge_ln_cnt              : 14;
+       UINT32                           : 2 ;
+       UINT32 merge_pix_cnt             : 14;
+       UINT32                           : 2 ;
+
+
+       //REGISTER Out_ctrl_reg_54
+       UINT32 split_ln_cnt              : 14;
+       UINT32                           : 2 ;
+       UINT32 split_pix_cnt             : 14;
+       UINT32                           : 2 ;
+
+
+       };
+
+       INT32 value32[55];
+
+}OUTCTRL_TOP_X_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/prepipe_layer_proc_x.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/prepipe_layer_proc_x.h
new file mode 100644 (file)
index 0000000..5f97561
--- /dev/null
@@ -0,0 +1,780 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef PREPIPE_LAYER_PROC_X_REG_H
+#define PREPIPE_LAYER_PROC_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER layer_proc_reg_0
+       UINT32 m_nlayer_pro_en               : 1 ;
+       UINT32 m_ngain_to_full_en            : 1 ;
+       UINT32 m_nmatrix_en                  : 1 ;
+       UINT32 m_nfront_tmootf_en            : 1 ;
+       UINT32 m_nend_tmootf_en              : 1 ;
+       UINT32 m_ncolor_key_en               : 1 ;
+       UINT32 m_neotf_en                    : 1 ;
+       UINT32 m_noetf_en                    : 1 ;
+       UINT32                               : 24;
+
+
+       //REGISTER layer_proc_reg_1
+       UINT32 rsvt                          : 32;
+
+
+       //REGISTER layer_proc_reg_2
+       UINT32 m_neotf_mode                  : 3 ;
+       UINT32 m_noetf_mode                  : 3 ;
+       UINT32                               : 2 ;
+       UINT32 m_noetf_max                   : 12;
+       UINT32                               : 12;
+
+
+       //REGISTER layer_proc_reg_3
+       UINT32 m_pfront_tmootf_gain_table0   : 16;
+       UINT32 m_pfront_tmootf_gain_table1   : 16;
+
+
+       //REGISTER layer_proc_reg_4
+       UINT32 m_pfront_tmootf_gain_table2   : 16;
+       UINT32 m_pfront_tmootf_gain_table3   : 16;
+
+
+       //REGISTER layer_proc_reg_5
+       UINT32 m_pfront_tmootf_gain_table4   : 16;
+       UINT32 m_pfront_tmootf_gain_table5   : 16;
+
+
+       //REGISTER layer_proc_reg_6
+       UINT32 m_pfront_tmootf_gain_table6   : 16;
+       UINT32 m_pfront_tmootf_gain_table7   : 16;
+
+
+       //REGISTER layer_proc_reg_7
+       UINT32 m_pfront_tmootf_gain_table8   : 16;
+       UINT32 m_pfront_tmootf_gain_table9   : 16;
+
+
+       //REGISTER layer_proc_reg_8
+       UINT32 m_pfront_tmootf_gain_table10  : 16;
+       UINT32 m_pfront_tmootf_gain_table11  : 16;
+
+
+       //REGISTER layer_proc_reg_9
+       UINT32 m_pfront_tmootf_gain_table12  : 16;
+       UINT32 m_pfront_tmootf_gain_table13  : 16;
+
+
+       //REGISTER layer_proc_reg_10
+       UINT32 m_pfront_tmootf_gain_table14  : 16;
+       UINT32 m_pfront_tmootf_gain_table15  : 16;
+
+
+       //REGISTER layer_proc_reg_11
+       UINT32 m_pfront_tmootf_gain_table16  : 16;
+       UINT32 m_pfront_tmootf_gain_table17  : 16;
+
+
+       //REGISTER layer_proc_reg_12
+       UINT32 m_pfront_tmootf_gain_table18  : 16;
+       UINT32 m_pfront_tmootf_gain_table19  : 16;
+
+
+       //REGISTER layer_proc_reg_13
+       UINT32 m_pfront_tmootf_gain_table20  : 16;
+       UINT32 m_pfront_tmootf_gain_table21  : 16;
+
+
+       //REGISTER layer_proc_reg_14
+       UINT32 m_pfront_tmootf_gain_table22  : 16;
+       UINT32 m_pfront_tmootf_gain_table23  : 16;
+
+
+       //REGISTER layer_proc_reg_15
+       UINT32 m_pfront_tmootf_gain_table24  : 16;
+       UINT32 m_pfront_tmootf_gain_table25  : 16;
+
+
+       //REGISTER layer_proc_reg_16
+       UINT32 m_pfront_tmootf_gain_table26  : 16;
+       UINT32 m_pfront_tmootf_gain_table27  : 16;
+
+
+       //REGISTER layer_proc_reg_17
+       UINT32 m_pfront_tmootf_gain_table28  : 16;
+       UINT32 m_pfront_tmootf_gain_table29  : 16;
+
+
+       //REGISTER layer_proc_reg_18
+       UINT32 m_pfront_tmootf_gain_table30  : 16;
+       UINT32 m_pfront_tmootf_gain_table31  : 16;
+
+
+       //REGISTER layer_proc_reg_19
+       UINT32 m_pfront_tmootf_gain_table32  : 16;
+       UINT32 m_pfront_tmootf_gain_table33  : 16;
+
+
+       //REGISTER layer_proc_reg_20
+       UINT32 m_pfront_tmootf_gain_table34  : 16;
+       UINT32 m_pfront_tmootf_gain_table35  : 16;
+
+
+       //REGISTER layer_proc_reg_21
+       UINT32 m_pfront_tmootf_gain_table36  : 16;
+       UINT32 m_pfront_tmootf_gain_table37  : 16;
+
+
+       //REGISTER layer_proc_reg_22
+       UINT32 m_pfront_tmootf_gain_table38  : 16;
+       UINT32 m_pfront_tmootf_gain_table39  : 16;
+
+
+       //REGISTER layer_proc_reg_23
+       UINT32 m_pfront_tmootf_gain_table40  : 16;
+       UINT32 m_pfront_tmootf_gain_table41  : 16;
+
+
+       //REGISTER layer_proc_reg_24
+       UINT32 m_pfront_tmootf_gain_table42  : 16;
+       UINT32 m_pfront_tmootf_gain_table43  : 16;
+
+
+       //REGISTER layer_proc_reg_25
+       UINT32 m_pfront_tmootf_gain_table44  : 16;
+       UINT32 m_pfront_tmootf_gain_table45  : 16;
+
+
+       //REGISTER layer_proc_reg_26
+       UINT32 m_pfront_tmootf_gain_table46  : 16;
+       UINT32 m_pfront_tmootf_gain_table47  : 16;
+
+
+       //REGISTER layer_proc_reg_27
+       UINT32 m_pfront_tmootf_gain_table48  : 16;
+       UINT32 m_pfront_tmootf_gain_table49  : 16;
+
+
+       //REGISTER layer_proc_reg_28
+       UINT32 m_pfront_tmootf_gain_table50  : 16;
+       UINT32 m_pfront_tmootf_gain_table51  : 16;
+
+
+       //REGISTER layer_proc_reg_29
+       UINT32 m_pfront_tmootf_gain_table52  : 16;
+       UINT32 m_pfront_tmootf_gain_table53  : 16;
+
+
+       //REGISTER layer_proc_reg_30
+       UINT32 m_pfront_tmootf_gain_table54  : 16;
+       UINT32 m_pfront_tmootf_gain_table55  : 16;
+
+
+       //REGISTER layer_proc_reg_31
+       UINT32 m_pfront_tmootf_gain_table56  : 16;
+       UINT32 m_pfront_tmootf_gain_table57  : 16;
+
+
+       //REGISTER layer_proc_reg_32
+       UINT32 m_pfront_tmootf_gain_table58  : 16;
+       UINT32 m_pfront_tmootf_gain_table59  : 16;
+
+
+       //REGISTER layer_proc_reg_33
+       UINT32 m_pfront_tmootf_gain_table60  : 16;
+       UINT32 m_pfront_tmootf_gain_table61  : 16;
+
+
+       //REGISTER layer_proc_reg_34
+       UINT32 m_pfront_tmootf_gain_table62  : 16;
+       UINT32 m_pfront_tmootf_gain_table63  : 16;
+
+
+       //REGISTER layer_proc_reg_35
+       UINT32 m_pfront_tmootf_gain_table64  : 16;
+       UINT32 m_nfront_tmootf_shift_bits    : 5 ;
+       UINT32 m_nfront_tmootf_rgb_mode      : 2 ;
+       UINT32                               : 9 ;
+
+
+       //REGISTER layer_proc_reg_36
+       UINT32 m_pend_tmootf_gain_table0     : 16;
+       UINT32 m_pend_tmootf_gain_table1     : 16;
+
+
+       //REGISTER layer_proc_reg_37
+       UINT32 m_pend_tmootf_gain_table2     : 16;
+       UINT32 m_pend_tmootf_gain_table3     : 16;
+
+
+       //REGISTER layer_proc_reg_38
+       UINT32 m_pend_tmootf_gain_table4     : 16;
+       UINT32 m_pend_tmootf_gain_table5     : 16;
+
+
+       //REGISTER layer_proc_reg_39
+       UINT32 m_pend_tmootf_gain_table6     : 16;
+       UINT32 m_pend_tmootf_gain_table7     : 16;
+
+
+       //REGISTER layer_proc_reg_40
+       UINT32 m_pend_tmootf_gain_table8     : 16;
+       UINT32 m_pend_tmootf_gain_table9     : 16;
+
+
+       //REGISTER layer_proc_reg_41
+       UINT32 m_pend_tmootf_gain_table10    : 16;
+       UINT32 m_pend_tmootf_gain_table11    : 16;
+
+
+       //REGISTER layer_proc_reg_42
+       UINT32 m_pend_tmootf_gain_table12    : 16;
+       UINT32 m_pend_tmootf_gain_table13    : 16;
+
+
+       //REGISTER layer_proc_reg_43
+       UINT32 m_pend_tmootf_gain_table14    : 16;
+       UINT32 m_pend_tmootf_gain_table15    : 16;
+
+
+       //REGISTER layer_proc_reg_44
+       UINT32 m_pend_tmootf_gain_table16    : 16;
+       UINT32 m_pend_tmootf_gain_table17    : 16;
+
+
+       //REGISTER layer_proc_reg_45
+       UINT32 m_pend_tmootf_gain_table18    : 16;
+       UINT32 m_pend_tmootf_gain_table19    : 16;
+
+
+       //REGISTER layer_proc_reg_46
+       UINT32 m_pend_tmootf_gain_table20    : 16;
+       UINT32 m_pend_tmootf_gain_table21    : 16;
+
+
+       //REGISTER layer_proc_reg_47
+       UINT32 m_pend_tmootf_gain_table22    : 16;
+       UINT32 m_pend_tmootf_gain_table23    : 16;
+
+
+       //REGISTER layer_proc_reg_48
+       UINT32 m_pend_tmootf_gain_table24    : 16;
+       UINT32 m_pend_tmootf_gain_table25    : 16;
+
+
+       //REGISTER layer_proc_reg_49
+       UINT32 m_pend_tmootf_gain_table26    : 16;
+       UINT32 m_pend_tmootf_gain_table27    : 16;
+
+
+       //REGISTER layer_proc_reg_50
+       UINT32 m_pend_tmootf_gain_table28    : 16;
+       UINT32 m_pend_tmootf_gain_table29    : 16;
+
+
+       //REGISTER layer_proc_reg_51
+       UINT32 m_pend_tmootf_gain_table30    : 16;
+       UINT32 m_pend_tmootf_gain_table31    : 16;
+
+
+       //REGISTER layer_proc_reg_52
+       UINT32 m_pend_tmootf_gain_table32    : 16;
+       UINT32 m_pend_tmootf_gain_table33    : 16;
+
+
+       //REGISTER layer_proc_reg_53
+       UINT32 m_pend_tmootf_gain_table34    : 16;
+       UINT32 m_pend_tmootf_gain_table35    : 16;
+
+
+       //REGISTER layer_proc_reg_54
+       UINT32 m_pend_tmootf_gain_table36    : 16;
+       UINT32 m_pend_tmootf_gain_table37    : 16;
+
+
+       //REGISTER layer_proc_reg_55
+       UINT32 m_pend_tmootf_gain_table38    : 16;
+       UINT32 m_pend_tmootf_gain_table39    : 16;
+
+
+       //REGISTER layer_proc_reg_56
+       UINT32 m_pend_tmootf_gain_table40    : 16;
+       UINT32 m_pend_tmootf_gain_table41    : 16;
+
+
+       //REGISTER layer_proc_reg_57
+       UINT32 m_pend_tmootf_gain_table42    : 16;
+       UINT32 m_pend_tmootf_gain_table43    : 16;
+
+
+       //REGISTER layer_proc_reg_58
+       UINT32 m_pend_tmootf_gain_table44    : 16;
+       UINT32 m_pend_tmootf_gain_table45    : 16;
+
+
+       //REGISTER layer_proc_reg_59
+       UINT32 m_pend_tmootf_gain_table46    : 16;
+       UINT32 m_pend_tmootf_gain_table47    : 16;
+
+
+       //REGISTER layer_proc_reg_60
+       UINT32 m_pend_tmootf_gain_table48    : 16;
+       UINT32 m_pend_tmootf_gain_table49    : 16;
+
+
+       //REGISTER layer_proc_reg_61
+       UINT32 m_pend_tmootf_gain_table50    : 16;
+       UINT32 m_pend_tmootf_gain_table51    : 16;
+
+
+       //REGISTER layer_proc_reg_62
+       UINT32 m_pend_tmootf_gain_table52    : 16;
+       UINT32 m_pend_tmootf_gain_table53    : 16;
+
+
+       //REGISTER layer_proc_reg_63
+       UINT32 m_pend_tmootf_gain_table54    : 16;
+       UINT32 m_pend_tmootf_gain_table55    : 16;
+
+
+       //REGISTER layer_proc_reg_64
+       UINT32 m_pend_tmootf_gain_table56    : 16;
+       UINT32 m_pend_tmootf_gain_table57    : 16;
+
+
+       //REGISTER layer_proc_reg_65
+       UINT32 m_pend_tmootf_gain_table58    : 16;
+       UINT32 m_pend_tmootf_gain_table59    : 16;
+
+
+       //REGISTER layer_proc_reg_66
+       UINT32 m_pend_tmootf_gain_table60    : 16;
+       UINT32 m_pend_tmootf_gain_table61    : 16;
+
+
+       //REGISTER layer_proc_reg_67
+       UINT32 m_pend_tmootf_gain_table62    : 16;
+       UINT32 m_pend_tmootf_gain_table63    : 16;
+
+
+       //REGISTER layer_proc_reg_68
+       UINT32 m_pend_tmootf_gain_table64    : 16;
+       UINT32 m_nend_tmootf_shift_bits      : 5 ;
+       UINT32 m_nend_tmootf_rgb_mode        : 2 ;
+       UINT32                               : 9 ;
+
+
+       //REGISTER layer_proc_reg_69
+       UINT32 m_pmatrix_table0              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table1              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER layer_proc_reg_70
+       UINT32 m_pmatrix_table2              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table3              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER layer_proc_reg_71
+       UINT32 m_pmatrix_table4              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table5              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER layer_proc_reg_72
+       UINT32 m_pmatrix_table6              : 14;
+       UINT32                               : 2 ;
+       UINT32 m_pmatrix_table7              : 14;
+       UINT32                               : 2 ;
+
+
+       //REGISTER layer_proc_reg_73
+       UINT32 m_pmatrix_table8              : 14;
+       UINT32                               : 18;
+
+
+       //REGISTER layer_proc_reg_74
+       UINT32 m_pmatrix_offset0             : 25;
+       UINT32                               : 7 ;
+
+
+       //REGISTER layer_proc_reg_75
+       UINT32 m_pmatrix_offset1             : 25;
+       UINT32                               : 7 ;
+
+
+       //REGISTER layer_proc_reg_76
+       UINT32 m_pmatrix_offset2             : 25;
+       UINT32                               : 7 ;
+
+
+       //REGISTER layer_proc_reg_77
+       UINT32 m_ngain_to_full               : 16;
+       UINT32 m_pcolor_key_R_thr0           : 12;
+       UINT32                               : 4 ;
+
+
+       //REGISTER layer_proc_reg_78
+       UINT32 m_pcolor_key_R_thr1           : 12;
+       UINT32                               : 4 ;
+       UINT32 m_pcolor_key_G_thr0           : 12;
+       UINT32                               : 4 ;
+
+
+       //REGISTER layer_proc_reg_79
+       UINT32 m_pcolor_key_G_thr1           : 12;
+       UINT32                               : 4 ;
+       UINT32 m_pcolor_key_B_thr0           : 12;
+       UINT32                               : 4 ;
+
+
+       //REGISTER layer_proc_reg_80
+       UINT32 m_pcolor_key_B_thr1           : 12;
+       UINT32                               : 20;
+
+
+       } b;
+
+       struct
+       {
+       //REGISTER layer_proc_reg_0
+       UINT32 layer_proc_reg_0;
+
+
+       //REGISTER layer_proc_reg_1
+       UINT32 layer_proc_reg_1;
+
+
+       //REGISTER layer_proc_reg_2
+       UINT32 layer_proc_reg_2;
+
+
+       //REGISTER layer_proc_reg_3
+       UINT32 layer_proc_reg_3;
+
+
+       //REGISTER layer_proc_reg_4
+       UINT32 layer_proc_reg_4;
+
+
+       //REGISTER layer_proc_reg_5
+       UINT32 layer_proc_reg_5;
+
+
+       //REGISTER layer_proc_reg_6
+       UINT32 layer_proc_reg_6;
+
+
+       //REGISTER layer_proc_reg_7
+       UINT32 layer_proc_reg_7;
+
+
+       //REGISTER layer_proc_reg_8
+       UINT32 layer_proc_reg_8;
+
+
+       //REGISTER layer_proc_reg_9
+       UINT32 layer_proc_reg_9;
+
+
+       //REGISTER layer_proc_reg_10
+       UINT32 layer_proc_reg_10;
+
+
+       //REGISTER layer_proc_reg_11
+       UINT32 layer_proc_reg_11;
+
+
+       //REGISTER layer_proc_reg_12
+       UINT32 layer_proc_reg_12;
+
+
+       //REGISTER layer_proc_reg_13
+       UINT32 layer_proc_reg_13;
+
+
+       //REGISTER layer_proc_reg_14
+       UINT32 layer_proc_reg_14;
+
+
+       //REGISTER layer_proc_reg_15
+       UINT32 layer_proc_reg_15;
+
+
+       //REGISTER layer_proc_reg_16
+       UINT32 layer_proc_reg_16;
+
+
+       //REGISTER layer_proc_reg_17
+       UINT32 layer_proc_reg_17;
+
+
+       //REGISTER layer_proc_reg_18
+       UINT32 layer_proc_reg_18;
+
+
+       //REGISTER layer_proc_reg_19
+       UINT32 layer_proc_reg_19;
+
+
+       //REGISTER layer_proc_reg_20
+       UINT32 layer_proc_reg_20;
+
+
+       //REGISTER layer_proc_reg_21
+       UINT32 layer_proc_reg_21;
+
+
+       //REGISTER layer_proc_reg_22
+       UINT32 layer_proc_reg_22;
+
+
+       //REGISTER layer_proc_reg_23
+       UINT32 layer_proc_reg_23;
+
+
+       //REGISTER layer_proc_reg_24
+       UINT32 layer_proc_reg_24;
+
+
+       //REGISTER layer_proc_reg_25
+       UINT32 layer_proc_reg_25;
+
+
+       //REGISTER layer_proc_reg_26
+       UINT32 layer_proc_reg_26;
+
+
+       //REGISTER layer_proc_reg_27
+       UINT32 layer_proc_reg_27;
+
+
+       //REGISTER layer_proc_reg_28
+       UINT32 layer_proc_reg_28;
+
+
+       //REGISTER layer_proc_reg_29
+       UINT32 layer_proc_reg_29;
+
+
+       //REGISTER layer_proc_reg_30
+       UINT32 layer_proc_reg_30;
+
+
+       //REGISTER layer_proc_reg_31
+       UINT32 layer_proc_reg_31;
+
+
+       //REGISTER layer_proc_reg_32
+       UINT32 layer_proc_reg_32;
+
+
+       //REGISTER layer_proc_reg_33
+       UINT32 layer_proc_reg_33;
+
+
+       //REGISTER layer_proc_reg_34
+       UINT32 layer_proc_reg_34;
+
+
+       //REGISTER layer_proc_reg_35
+       UINT32 layer_proc_reg_35;
+
+
+       //REGISTER layer_proc_reg_36
+       UINT32 layer_proc_reg_36;
+
+
+       //REGISTER layer_proc_reg_37
+       UINT32 layer_proc_reg_37;
+
+
+       //REGISTER layer_proc_reg_38
+       UINT32 layer_proc_reg_38;
+
+
+       //REGISTER layer_proc_reg_39
+       UINT32 layer_proc_reg_39;
+
+
+       //REGISTER layer_proc_reg_40
+       UINT32 layer_proc_reg_40;
+
+
+       //REGISTER layer_proc_reg_41
+       UINT32 layer_proc_reg_41;
+
+
+       //REGISTER layer_proc_reg_42
+       UINT32 layer_proc_reg_42;
+
+
+       //REGISTER layer_proc_reg_43
+       UINT32 layer_proc_reg_43;
+
+
+       //REGISTER layer_proc_reg_44
+       UINT32 layer_proc_reg_44;
+
+
+       //REGISTER layer_proc_reg_45
+       UINT32 layer_proc_reg_45;
+
+
+       //REGISTER layer_proc_reg_46
+       UINT32 layer_proc_reg_46;
+
+
+       //REGISTER layer_proc_reg_47
+       UINT32 layer_proc_reg_47;
+
+
+       //REGISTER layer_proc_reg_48
+       UINT32 layer_proc_reg_48;
+
+
+       //REGISTER layer_proc_reg_49
+       UINT32 layer_proc_reg_49;
+
+
+       //REGISTER layer_proc_reg_50
+       UINT32 layer_proc_reg_50;
+
+
+       //REGISTER layer_proc_reg_51
+       UINT32 layer_proc_reg_51;
+
+
+       //REGISTER layer_proc_reg_52
+       UINT32 layer_proc_reg_52;
+
+
+       //REGISTER layer_proc_reg_53
+       UINT32 layer_proc_reg_53;
+
+
+       //REGISTER layer_proc_reg_54
+       UINT32 layer_proc_reg_54;
+
+
+       //REGISTER layer_proc_reg_55
+       UINT32 layer_proc_reg_55;
+
+
+       //REGISTER layer_proc_reg_56
+       UINT32 layer_proc_reg_56;
+
+
+       //REGISTER layer_proc_reg_57
+       UINT32 layer_proc_reg_57;
+
+
+       //REGISTER layer_proc_reg_58
+       UINT32 layer_proc_reg_58;
+
+
+       //REGISTER layer_proc_reg_59
+       UINT32 layer_proc_reg_59;
+
+
+       //REGISTER layer_proc_reg_60
+       UINT32 layer_proc_reg_60;
+
+
+       //REGISTER layer_proc_reg_61
+       UINT32 layer_proc_reg_61;
+
+
+       //REGISTER layer_proc_reg_62
+       UINT32 layer_proc_reg_62;
+
+
+       //REGISTER layer_proc_reg_63
+       UINT32 layer_proc_reg_63;
+
+
+       //REGISTER layer_proc_reg_64
+       UINT32 layer_proc_reg_64;
+
+
+       //REGISTER layer_proc_reg_65
+       UINT32 layer_proc_reg_65;
+
+
+       //REGISTER layer_proc_reg_66
+       UINT32 layer_proc_reg_66;
+
+
+       //REGISTER layer_proc_reg_67
+       UINT32 layer_proc_reg_67;
+
+
+       //REGISTER layer_proc_reg_68
+       UINT32 layer_proc_reg_68;
+
+
+       //REGISTER layer_proc_reg_69
+       UINT32 layer_proc_reg_69;
+
+
+       //REGISTER layer_proc_reg_70
+       UINT32 layer_proc_reg_70;
+
+
+       //REGISTER layer_proc_reg_71
+       UINT32 layer_proc_reg_71;
+
+
+       //REGISTER layer_proc_reg_72
+       UINT32 layer_proc_reg_72;
+
+
+       //REGISTER layer_proc_reg_73
+       UINT32 layer_proc_reg_73;
+
+
+       //REGISTER layer_proc_reg_74
+       UINT32 layer_proc_reg_74;
+
+
+       //REGISTER layer_proc_reg_75
+       UINT32 layer_proc_reg_75;
+
+
+       //REGISTER layer_proc_reg_76
+       UINT32 layer_proc_reg_76;
+
+
+       //REGISTER layer_proc_reg_77
+       UINT32 layer_proc_reg_77;
+
+
+       //REGISTER layer_proc_reg_78
+       UINT32 layer_proc_reg_78;
+
+
+       //REGISTER layer_proc_reg_79
+       UINT32 layer_proc_reg_79;
+
+
+       //REGISTER layer_proc_reg_80
+       UINT32 layer_proc_reg_80;
+
+
+       } v;
+
+       //INT32 value32[81];
+
+}PREPIPE_LAYER_PROC_X_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/rdma_path_x.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/rdma_path_x.h
new file mode 100644 (file)
index 0000000..05d27bd
--- /dev/null
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef RDMA_PATH_X_REG_H
+#define RDMA_PATH_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER LAYER_CTRL
+       UINT32 layer_mode                  : 2 ;
+       UINT32 outstanding_num             : 5 ;
+       UINT32 req_conti_num               : 5 ;
+       UINT32 layer_cmpsr_id              : 3 ;
+       UINT32 axi_port_sel                : 2 ;
+       UINT32 rdma_burst_len              : 8 ;
+       UINT32 is_two_layers               : 1 ;
+       UINT32 is_offline                  : 1 ;
+       UINT32 hdr_osd_num                 : 4 ;
+       UINT32                             : 1 ;
+
+
+       //REGISTER COMPSR_Y_OFST
+       UINT32 compsr_y_offset0            : 16;
+       UINT32                             : 16;
+
+
+       //REGISTER LEFT_SCL_RATIO_V
+       UINT32                             : 32;
+
+
+       //REGISTER LEFT_INIT_PHASE_V_LOW
+       UINT32                             : 32;
+
+
+       //REGISTER LEFT_INIT_PHASE_V_HIGH
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_SCL_RATIO_V
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_INIT_PHASE_V_LOW
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_INIT_PHASE_V_HIGH
+       UINT32                             : 32;
+
+
+       //REGISTER LEFT_BASE_ADDR0_LOW
+       UINT32 base_addr0_low_ly0          : 32;
+
+
+       //REGISTER LEFT_BASE_ADDR0_HIGH
+       UINT32 base_addr0_high_ly0         : 2 ;
+       UINT32                             : 30;
+
+
+       //REGISTER LEFT_BASE_ADDR1_LOW
+       UINT32 base_addr1_low_ly0          : 32;
+
+
+       //REGISTER LEFT_BASE_ADDR1_HIGH
+       UINT32 base_addr1_high_ly0         : 2 ;
+       UINT32                             : 30;
+
+
+       //REGISTER LEFT_BASE_ADDR2_LOW
+       UINT32 base_addr2_low_ly0          : 32;
+
+
+       //REGISTER LEFT_BASE_ADDR2_HIGH
+       UINT32 base_addr2_high_ly0         : 2 ;
+       UINT32                             : 30;
+
+
+       //REGISTER LEFT_RDMA_STRIDE0
+       UINT32 rdma_stride0_layer0         : 16;
+       UINT32 rdma_stride1_layer0         : 16;
+
+
+       //REGISTER LEFT_IMG_SIZE
+       UINT32 img_width_ly0               : 16;
+       UINT32 img_height_ly0              : 16;
+
+
+       //REGISTER LEFT_CROP_POS_START
+       UINT32 bbox_start_x_ly0            : 16;
+       UINT32 bbox_start_y_ly0            : 16;
+
+
+       //REGISTER LEFT_CROP_POS_END
+       UINT32 bbox_end_x_ly0              : 16;
+       UINT32 bbox_end_y_ly0              : 16;
+
+
+       //REGISTER RIGHT_BASE_ADDR0_LOW
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_BASE_ADDR0_HIGH
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_BASE_ADDR1_LOW
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_BASE_ADDR1_HIGH
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_BASE_ADDR2_LOW
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_BASE_ADDR2_HIGH
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_RDMA_STRIDE0
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_IMG_SIZE
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_CROP_POS_START
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_CROP_POS_END
+       UINT32                             : 32;
+
+
+       //REGISTER ROT_MODE
+       UINT32 pixel_format                : 6 ;
+       UINT32 uv_swap                     : 1 ;
+       UINT32 rot_mode_ly0                : 3 ;
+       UINT32 rot_mode_ly1                : 3 ;
+       UINT32                             : 19;
+
+
+       //REGISTER AFBC_CFG
+       UINT32 fbc_split_mode              : 1 ;
+       UINT32 fbc_yuv_transform           : 1 ;
+       UINT32 fbc_sb_layout               : 1 ;
+       UINT32 fbc_tile_type               : 1 ;
+       UINT32                             : 28;
+
+
+       //REGISTER FBC_MEM_SIZE
+       UINT32 fbc_mem_size                : 16;
+       UINT32 fbc_mem_base_addr           : 12;
+       UINT32 fbc_mem_map                 : 1 ;
+       UINT32 dec_line_num_sw             : 1 ;
+       UINT32 sw_dec_line_nnum            : 2 ;
+
+
+       //REGISTER LAYER_NSAID
+       UINT32 nsaid                       : 4 ;
+       UINT32                             : 28;
+
+
+       //REGISTER CSC_MATRIX0
+       UINT32 csc_matrix00                : 14;
+       UINT32 csc_matrix01                : 14;
+       UINT32                             : 4 ;
+
+
+       //REGISTER CSC_MATRIX1
+       UINT32 csc_matrix02                : 14;
+       UINT32 csc_matrix03                : 14;
+       UINT32                             : 4 ;
+
+
+       //REGISTER CSC_MATRIX2
+       UINT32 csc_matrix10                : 14;
+       UINT32 csc_matrix11                : 14;
+       UINT32                             : 4 ;
+
+
+       //REGISTER CSC_MATRIX3
+       UINT32 csc_matrix12                : 14;
+       UINT32 csc_matrix13                : 14;
+       UINT32                             : 4 ;
+
+
+       //REGISTER CSC_MATRIX4
+       UINT32 csc_matrix20                : 14;
+       UINT32 csc_matrix21                : 14;
+       UINT32                             : 4 ;
+
+
+       //REGISTER CSC_MATRIX5
+       UINT32 csc_matrix22                : 14;
+       UINT32 csc_matrix23                : 14;
+       UINT32                             : 4 ;
+
+
+       //REGISTER LEFT_ALPHA01
+       UINT32 alpha0_ly0                  : 12;
+       UINT32                             : 4 ;
+       UINT32 alpha1_ly0                  : 12;
+       UINT32                             : 4 ;
+
+
+       //REGISTER LEFT_ALPHA23
+       UINT32 alpha2_ly0                  : 12;
+       UINT32                             : 4 ;
+       UINT32 alpha3_ly0                  : 12;
+       UINT32                             : 4 ;
+
+
+       //REGISTER RIGHT_ALPHA01
+       UINT32                             : 32;
+
+
+       //REGISTER RIGHT_ALPHA23
+       UINT32                             : 32;
+
+
+       //REGISTER DBG_IRQ_RAW
+       UINT32 tlc_miss_irq_raw            : 1 ;
+       UINT32 tbu_size_err_irq_raw        : 1 ;
+       UINT32 mmu_rdma_timeout_raw        : 1 ;
+       UINT32 mmu_rdma_rsp_decerr_raw     : 1 ;
+       UINT32 mmu_rdma_rsp_slverr_raw     : 1 ;
+       UINT32 mmu_rdma_rsp_exok_raw       : 1 ;
+       UINT32 rdma_timeout_irq_raw        : 1 ;
+       UINT32 rdma_rsp_decerr_raw         : 1 ;
+       UINT32 rdma_rsp_slverr_raw         : 1 ;
+       UINT32 rdma_rsp_exok_raw           : 1 ;
+       UINT32 dma_mem_err_hw_raw          : 1 ;
+       UINT32 rdma_eof_ly0_raw            : 1 ;
+       UINT32 rdma_eof_ly1_raw            : 1 ;
+       UINT32 rdma_output_eof_ly0_raw     : 1 ;
+       UINT32 rdma_output_eof_ly1_raw     : 1 ;
+       UINT32 fbcdec_eof_ly0_raw          : 1 ;
+       UINT32 fbcdec_eof_ly1_raw          : 1 ;
+       UINT32 fbcdec_err_ly0_raw          : 1 ;
+       UINT32 fbcdec_err_ly1_raw          : 1 ;
+       UINT32 rdma_all_out_eof_raw        : 1 ;
+       UINT32 rmda_err_sw_raw             : 1 ;
+       UINT32 mem_conflict_raw            : 1 ;
+       UINT32 rdma_sof_raw                : 1 ;
+       UINT32 rdma_va_mismatch_raw        : 1 ;
+       UINT32                             : 8 ;
+
+
+       //REGISTER DBG_IRQ_MASK
+       UINT32 tlc_miss_irq_mask           : 1 ;
+       UINT32 tbu_size_err_irq_mask       : 1 ;
+       UINT32 mmu_rdma_timeout_mask       : 1 ;
+       UINT32 mmu_rdma_rsp_decerr_mask    : 1 ;
+       UINT32 mmu_rdma_rsp_slverr_mask    : 1 ;
+       UINT32 mmu_rdma_rsp_exok_mask      : 1 ;
+       UINT32 rdma_timeout_irq_mask       : 1 ;
+       UINT32 rdma_rsp_decerr_mask        : 1 ;
+       UINT32 rdma_rsp_slverr_mask        : 1 ;
+       UINT32 rdma_rsp_exok_mask          : 1 ;
+       UINT32 dma_mem_err_hw_mask         : 1 ;
+       UINT32 rdma_eof_ly0_mask           : 1 ;
+       UINT32 rdma_eof_ly1_mask           : 1 ;
+       UINT32 rdma_output_eof_ly0_mask    : 1 ;
+       UINT32 rdma_output_eof_ly1_mask    : 1 ;
+       UINT32 fbcdec_eof_ly0_mask         : 1 ;
+       UINT32 fbcdec_eof_ly1_mask         : 1 ;
+       UINT32 fbcdec_err_ly0_mask         : 1 ;
+       UINT32 fbcdec_err_ly1_mask         : 1 ;
+       UINT32 rdma_all_out_eof_mask       : 1 ;
+       UINT32 rdma_err_sw_mask            : 1 ;
+       UINT32 mem_conflict_mask           : 1 ;
+       UINT32 rdma_sof_mask               : 1 ;
+       UINT32 rdma_va_mismatch_mask       : 1 ;
+       UINT32                             : 8 ;
+
+
+       //REGISTER DBG_IRQ_STATUS
+       UINT32 tlc_miss_irq_status         : 1 ;
+       UINT32 tbu_size_err_irq_status     : 1 ;
+       UINT32 mmu_rdma_timeout_status     : 1 ;
+       UINT32 mmu_rdma_rsp_decerr_status  : 1 ;
+       UINT32 mmu_rdma_rsp_slverr_status  : 1 ;
+       UINT32 mmu_rdma_rsp_exok_status    : 1 ;
+       UINT32 rdma_timeout_irq_status     : 1 ;
+       UINT32 rdma_rsp_decerr_status      : 1 ;
+       UINT32 rdma_rsp_slverr_status      : 1 ;
+       UINT32 rdma_rsp_exok_status        : 1 ;
+       UINT32 dma_mem_err_status          : 1 ;
+       UINT32 rdma_eof_ly0_status         : 1 ;
+       UINT32 rdma_eof_ly1_status         : 1 ;
+       UINT32 rdma_output_eof_ly0_status  : 1 ;
+       UINT32 rdma_output_eof_ly1_status  : 1 ;
+       UINT32 fbcdec_eof_ly0_status       : 1 ;
+       UINT32 fbcdec_eof_ly1_status       : 1 ;
+       UINT32 fbcdec_err_ly0_status       : 1 ;
+       UINT32 fbcdec_err_ly1_status       : 1 ;
+       UINT32 rdma_all_out_eof_status     : 1 ;
+       UINT32 rdma_mem_err_sw             : 1 ;
+       UINT32 mem_conflict_status         : 1 ;
+       UINT32 rdma_eof_status             : 1 ;
+       UINT32 rdma_va_mismatch_status     : 1 ;
+       UINT32                             : 8 ;
+
+
+       //REGISTER PREFETCH_OSD_CTRL
+       UINT32 prefetch_osd_num            : 5 ;
+       UINT32 prefetch_hdr_osd_num        : 4 ;
+       UINT32 osd_sub_step                : 2 ;
+       UINT32 osd_num_min                 : 5 ;
+       UINT32 hdr_osd_num_min             : 4 ;
+       UINT32 pp_buf_switch_num           : 3 ;
+       UINT32                             : 9 ;
+       } b;
+
+       struct
+       {
+       //REGISTER LAYER_CTRL
+       UINT32 LAYER_CTRL;
+
+
+       //REGISTER COMPSR_Y_OFST
+       UINT32 COMPSR_Y_OFST;
+
+
+       //REGISTER LEFT_SCL_RATIO_V
+       UINT32 LEFT_SCL_RATIO_V;
+
+
+       //REGISTER LEFT_INIT_PHASE_V_LOW
+       UINT32 LEFT_INIT_PHASE_V_LOW;
+
+
+       //REGISTER LEFT_INIT_PHASE_V_HIGH
+       UINT32 LEFT_INIT_PHASE_V_HIGH;
+
+
+       //REGISTER RIGHT_SCL_RATIO_V
+       UINT32 RIGHT_SCL_RATIO_V;
+
+
+       //REGISTER RIGHT_INIT_PHASE_V_LOW
+       UINT32 RIGHT_INIT_PHASE_V_LOW;
+
+
+       //REGISTER RIGHT_INIT_PHASE_V_HIGH
+       UINT32 RIGHT_INIT_PHASE_V_HIGH;
+
+
+       //REGISTER LEFT_BASE_ADDR0_LOW
+       UINT32 LEFT_BASE_ADDR0_LOW;
+
+
+       //REGISTER LEFT_BASE_ADDR0_HIGH
+       UINT32 LEFT_BASE_ADDR0_HIGH;
+
+
+       //REGISTER LEFT_BASE_ADDR1_LOW
+       UINT32 LEFT_BASE_ADDR1_LOW;
+
+
+       //REGISTER LEFT_BASE_ADDR1_HIGH
+       UINT32 LEFT_BASE_ADDR1_HIGH;
+
+
+       //REGISTER LEFT_BASE_ADDR2_LOW
+       UINT32 LEFT_BASE_ADDR2_LOW;
+
+
+       //REGISTER LEFT_BASE_ADDR2_HIGH
+       UINT32 LEFT_BASE_ADDR2_HIGH;
+
+
+       //REGISTER LEFT_RDMA_STRIDE0
+       UINT32 LEFT_RDMA_STRIDE0;
+
+
+       //REGISTER LEFT_IMG_SIZE
+       UINT32 LEFT_IMG_SIZE;
+
+
+       //REGISTER LEFT_CROP_POS_START
+       UINT32 LEFT_CROP_POS_START;
+
+
+       //REGISTER LEFT_CROP_POS_END
+       UINT32 LEFT_CROP_POS_END;
+
+
+       //REGISTER RIGHT_BASE_ADDR0_LOW
+       UINT32 RIGHT_BASE_ADDR0_LOW;
+
+
+       //REGISTER RIGHT_BASE_ADDR0_HIGH
+       UINT32 RIGHT_BASE_ADDR0_HIGH;
+
+
+       //REGISTER RIGHT_BASE_ADDR1_LOW
+       UINT32 RIGHT_BASE_ADDR1_LOW;
+
+
+       //REGISTER RIGHT_BASE_ADDR1_HIGH
+       UINT32 RIGHT_BASE_ADDR1_HIGH;
+
+
+       //REGISTER RIGHT_BASE_ADDR2_LOW
+       UINT32 RIGHT_BASE_ADDR2_LOW;
+
+
+       //REGISTER RIGHT_BASE_ADDR2_HIGH
+       UINT32 RIGHT_BASE_ADDR2_HIGH;
+
+
+       //REGISTER RIGHT_RDMA_STRIDE0
+       UINT32 RIGHT_RDMA_STRIDE0;
+
+
+       //REGISTER RIGHT_IMG_SIZE
+       UINT32 RIGHT_IMG_SIZE;
+
+
+       //REGISTER RIGHT_CROP_POS_START
+       UINT32 RIGHT_CROP_POS_START;
+
+
+       //REGISTER RIGHT_CROP_POS_END
+       UINT32 RIGHT_CROP_POS_END;
+
+
+       //REGISTER ROT_MODE
+       UINT32 ROT_MODE;
+
+
+       //REGISTER AFBC_CFG
+       UINT32 AFBC_CFG;
+
+
+       //REGISTER FBC_MEM_SIZE
+       UINT32 FBC_MEM_SIZE;
+
+
+       //REGISTER LAYER_NSAID
+       UINT32 LAYER_NSAID;
+
+
+       //REGISTER CSC_MATRIX0
+       UINT32 CSC_MATRIX0;
+
+
+       //REGISTER CSC_MATRIX1
+       UINT32 CSC_MATRIX1;
+
+
+       //REGISTER CSC_MATRIX2
+       UINT32 CSC_MATRIX2;
+
+
+       //REGISTER CSC_MATRIX3
+       UINT32 CSC_MATRIX3;
+
+
+       //REGISTER CSC_MATRIX4
+       UINT32 CSC_MATRIX4;
+
+
+       //REGISTER CSC_MATRIX5
+       UINT32 CSC_MATRIX5;
+
+
+       //REGISTER LEFT_ALPHA01
+       UINT32 LEFT_ALPHA01;
+
+
+       //REGISTER LEFT_ALPHA23
+       UINT32 LEFT_ALPHA23;
+
+
+       //REGISTER RIGHT_ALPHA01
+       UINT32 RIGHT_ALPHA01;
+
+
+       //REGISTER RIGHT_ALPHA23
+       UINT32 RIGHT_ALPHA23;
+
+
+       //REGISTER DBG_IRQ_RAW
+       UINT32 DBG_IRQ_RAW;
+
+
+       //REGISTER DBG_IRQ_MASK
+       UINT32 DBG_IRQ_MASK;
+
+
+       //REGISTER DBG_IRQ_STATUS
+       UINT32 DBG_IRQ_STATUS;
+
+
+       //PREFETCH_OSD_CTRL
+       UINT32 PREFETCH_OSD_CTRL;
+       } v;
+
+
+//     INT32 value32[46];
+
+}RDMA_PATH_X_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/reg_map.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/reg_map.h
new file mode 100644 (file)
index 0000000..88f8c18
--- /dev/null
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SATURN_REG_MAP_A0_H_
+#define _SATURN_REG_MAP_A0_H_
+
+#ifndef UINT32
+typedef uint32_t UINT32;
+#endif
+
+#ifndef INT32
+typedef uint32_t INT32;
+#endif
+
+#define SATURN_BASE_ADDR 0x0
+
+#define DPU_TOP_BASE_ADDR 0x0
+#define DPU_CTRL_BASE_ADDR 0x500
+#define DPU_CRG_BASE_ADDR 0x700
+#define CMDLIST_BASE_ADDR 0x800
+#define DPU_INT_BASE_ADDR 0x900
+
+#define RDMA_SIZE 0x100
+#define DMA_TOP_BASE_ADDR 0xA00
+#define RDMA0_BASE_ADDR (DMA_TOP_BASE_ADDR + 0x80)
+#define RDMA1_BASE_ADDR (RDMA0_BASE_ADDR + RDMA_SIZE)
+#define RDMA2_BASE_ADDR (RDMA1_BASE_ADDR + RDMA_SIZE)
+#define RDMA3_BASE_ADDR (RDMA2_BASE_ADDR + RDMA_SIZE)
+#define RDMA4_BASE_ADDR (RDMA3_BASE_ADDR + RDMA_SIZE)
+#define RDMA5_BASE_ADDR (RDMA4_BASE_ADDR + RDMA_SIZE)
+#define RDMA6_BASE_ADDR (RDMA5_BASE_ADDR + RDMA_SIZE)
+#define RDMA7_BASE_ADDR (RDMA6_BASE_ADDR + RDMA_SIZE)
+#define RDMA8_BASE_ADDR (RDMA7_BASE_ADDR + RDMA_SIZE)
+#define RDMA9_BASE_ADDR (RDMA8_BASE_ADDR + RDMA_SIZE)
+#define RDMA10_BASE_ADDR (RDMA9_BASE_ADDR + RDMA_SIZE)
+#define RDMA11_BASE_ADDR (RDMA10_BASE_ADDR + RDMA_SIZE)
+#define RDMA_BASE_ADDR(rdma_id) (RDMA0_BASE_ADDR + rdma_id * RDMA_SIZE)
+
+#define MMU_TBU_SIZE 0x40
+#define MMU_TOP_SIZE 0x100
+#define MMU_BASE_ADDR (RDMA11_BASE_ADDR + RDMA_SIZE)
+#define MMU_TOP_BASE_ADDR (MMU_BASE_ADDR)
+#define MMU_TBU_BASE_ADDR (MMU_BASE_ADDR + 0x100)
+
+#define LP_SIZE 0x300
+#define LMERGE_SIZE 0x20
+#define LP0_BASE_ADDR 0x2000
+#define LP1_BASE_ADDR (LP0_BASE_ADDR + LP_SIZE)
+#define LP2_BASE_ADDR (LP1_BASE_ADDR + LP_SIZE)
+#define LP3_BASE_ADDR (LP2_BASE_ADDR + LP_SIZE)
+#define LP4_BASE_ADDR (LP3_BASE_ADDR + LP_SIZE)
+#define LP5_BASE_ADDR (LP4_BASE_ADDR + LP_SIZE)
+#define LP6_BASE_ADDR (LP5_BASE_ADDR + LP_SIZE)
+#define LP7_BASE_ADDR (LP6_BASE_ADDR + LP_SIZE)
+#define LP8_BASE_ADDR (LP7_BASE_ADDR + LP_SIZE)
+#define LP9_BASE_ADDR (LP8_BASE_ADDR + LP_SIZE)
+#define LP10_BASE_ADDR (LP9_BASE_ADDR + LP_SIZE)
+#define LP11_BASE_ADDR (LP10_BASE_ADDR + LP_SIZE)
+#define LMERGE0_BASE_ADDR (LP11_BASE_ADDR + LP_SIZE)
+#define LMERGE1_BASE_ADDR (LMERGE0_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE2_BASE_ADDR (LMERGE1_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE3_BASE_ADDR (LMERGE2_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE4_BASE_ADDR (LMERGE3_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE5_BASE_ADDR (LMERGE4_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE6_BASE_ADDR (LMERGE5_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE7_BASE_ADDR (LMERGE6_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE8_BASE_ADDR (LMERGE7_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE9_BASE_ADDR (LMERGE8_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE10_BASE_ADDR (LMERGE9_BASE_ADDR + LMERGE_SIZE)
+#define LMERGE11_BASE_ADDR (LMERGE10_BASE_ADDR + LMERGE_SIZE)
+
+#define CMP_SIZE 0x300
+#define CMP0_BASE_ADDR 0x4600
+#define CMP1_BASE_ADDR (CMP0_BASE_ADDR + CMP_SIZE)
+#define CMP2_BASE_ADDR (CMP1_BASE_ADDR + CMP_SIZE)
+#define CMP3_BASE_ADDR (CMP2_BASE_ADDR + CMP_SIZE)
+#define CMP_BASE_ADDR(cmp_id) (CMP0_BASE_ADDR + cmp_id * CMP_SIZE)
+
+#define SCALER_SIZE 0x200
+#define SCALER0_ONLINE_BASE_ADDR 0x6000
+#define SCALER1_ONLINE_BASE_ADDR (SCALER0_ONLINE_BASE_ADDR + SCALER_SIZE)
+
+#define OUTCTRL_SIZE 0x8800
+#define OUTCTRL0_BASE_ADDR 0x7000
+#define PP0_BASE_ADDR (OUTCTRL0_BASE_ADDR + 0x100)
+#define ACAD0_BASE_ADDR (OUTCTRL0_BASE_ADDR + 0x280)
+#define LUT3D0_BASE_ADDR (OUTCTRL0_BASE_ADDR + 0x600)
+#define DSCA0_BASE_ADDR (OUTCTRL0_BASE_ADDR + 0x8000)
+#define DSCB0_BASE_ADDR (OUTCTRL0_BASE_ADDR + 0x8100)
+
+#define OUTCTRL1_BASE_ADDR (OUTCTRL0_BASE_ADDR + OUTCTRL_SIZE)
+#define PP1_BASE_ADDR (OUTCTRL1_BASE_ADDR + 0x100)
+#define ACAD1_BASE_ADDR (OUTCTRL1_BASE_ADDR + 0x280)
+#define LUT3D1_BASE_ADDR (OUTCTRL1_BASE_ADDR + 0x600)
+#define DSCA1_BASE_ADDR (OUTCTRL1_BASE_ADDR + 0x8000)
+#define DSCB1_BASE_ADDR (OUTCTRL1_BASE_ADDR + 0x8100)
+
+#define OUTCTRL2_BASE_ADDR (OUTCTRL1_BASE_ADDR + OUTCTRL_SIZE)
+#define PP2_BASE_ADDR (OUTCTRL2_BASE_ADDR + 0x100)
+#define ACAD2_BASE_ADDR (OUTCTRL2_BASE_ADDR + 0x280)
+#define LUT3D2_BASE_ADDR (OUTCTRL2_BASE_ADDR + 0x600)
+#define DSCA2_BASE_ADDR (OUTCTRL2_BASE_ADDR + 0x8000)
+#define DSCB2_BASE_ADDR (OUTCTRL2_BASE_ADDR + 0x8100)
+
+#define OUTCTRL3_BASE_ADDR (OUTCTRL2_BASE_ADDR + OUTCTRL_SIZE)
+#define PP3_BASE_ADDR (OUTCTRL3_BASE_ADDR + 0x100)
+#define ACAD3_BASE_ADDR (OUTCTRL3_BASE_ADDR + 0x280)
+#define LUT3D3_BASE_ADDR (OUTCTRL3_BASE_ADDR + 0x600)
+#define DSCA3_BASE_ADDR (OUTCTRL3_BASE_ADDR + 0x8000)
+#define DSCB3_BASE_ADDR (OUTCTRL3_BASE_ADDR + 0x8100)
+#define OUTCTRL_BASE_ADDR(ctrl_id) (OUTCTRL0_BASE_ADDR + OUTCTRL_SIZE * ctrl_id)
+
+#define WB_SIZE 0x400
+#define WB0_TOP_BASE_ADDR 0x29000
+#define WB0_SCALER_BASE_ADDR (WB0_TOP_BASE_ADDR + 0x200)
+#define WB1_TOP_BASE_ADDR (WB0_TOP_BASE_ADDR + WB_SIZE)
+#define WB1_SCALER_BASE_ADDR (WB1_TOP_BASE_ADDR + 0x200)
+
+#define SATURN_SIZE 0x2A000
+
+#include "cmdlist.h"
+#include "cmps_x.h"
+#include "dma_top.h"
+#include "dpu_crg.h"
+#include "dpu_ctl.h"
+#include "dpu_intp.h"
+#include "dpu_top.h"
+#include "mmu.h"
+#include "outctrl_top_x.h"
+#include "outctrl_proc_x.h"
+#include "rdma_path_x.h"
+#include "scaler_x.h"
+#include "wb_top.h"
+#include "prepipe_layer_proc_x.h"
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/scaler_x.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/scaler_x.h
new file mode 100644 (file)
index 0000000..30da4f1
--- /dev/null
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef SCALER_X_REG_H
+#define SCALER_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER disp_scl_reg_0
+       UINT32 m_nscl_hor_enable           : 1 ;
+       UINT32 m_nscl_ver_enable           : 1 ;
+       UINT32                             : 6 ;
+       UINT32 m_nscl_input_width          : 16;
+       UINT32                             : 8 ;
+
+
+       //REGISTER disp_scl_reg_1
+       UINT32 m_nscl_input_height         : 16;
+       UINT32 m_nscl_output_width         : 16;
+
+
+       //REGISTER disp_scl_reg_2
+       UINT32 m_nscl_output_height        : 16;
+       UINT32                             : 16;
+
+
+       //REGISTER disp_scl_reg_3
+       UINT32 m_nscl_hor_init_phase_l32b  : 32;
+
+
+       //REGISTER disp_scl_reg_4
+       UINT32 m_nscl_hor_init_phase_h1b   : 1 ;
+       UINT32                             : 31;
+
+
+       //REGISTER disp_scl_reg_5
+       UINT32 m_nscl_ver_init_phase_l32b  : 32;
+
+
+       //REGISTER disp_scl_reg_6
+       UINT32 m_nscl_ver_init_phase_h1b   : 1 ;
+       UINT32                             : 31;
+
+
+       //REGISTER disp_scl_reg_7
+       UINT32 m_nscl_hor_delta_phase      : 20;
+       UINT32                             : 12;
+
+
+       //REGISTER disp_scl_reg_8
+       UINT32 m_nscl_ver_delta_phase      : 20;
+       UINT32                             : 12;
+
+
+       //REGISTER disp_scl_reg_9
+       UINT32 m_nscl_hor_coef0            : 16;
+       UINT32 m_nscl_hor_coef1            : 16;
+
+
+       //REGISTER disp_scl_reg_10
+       UINT32 m_nscl_hor_coef2            : 16;
+       UINT32 m_nscl_hor_coef3            : 16;
+
+
+       //REGISTER disp_scl_reg_11
+       UINT32 m_nscl_hor_coef4            : 16;
+       UINT32 m_nscl_hor_coef5            : 16;
+
+
+       //REGISTER disp_scl_reg_12
+       UINT32 m_nscl_hor_coef6            : 16;
+       UINT32 m_nscl_hor_coef7            : 16;
+
+
+       //REGISTER disp_scl_reg_13
+       UINT32 m_nscl_hor_coef8            : 16;
+       UINT32 m_nscl_hor_coef9            : 16;
+
+
+       //REGISTER disp_scl_reg_14
+       UINT32 m_nscl_hor_coef10           : 16;
+       UINT32 m_nscl_hor_coef11           : 16;
+
+
+       //REGISTER disp_scl_reg_15
+       UINT32 m_nscl_hor_coef12           : 16;
+       UINT32 m_nscl_hor_coef13           : 16;
+
+
+       //REGISTER disp_scl_reg_16
+       UINT32 m_nscl_hor_coef14           : 16;
+       UINT32 m_nscl_hor_coef15           : 16;
+
+
+       //REGISTER disp_scl_reg_17
+       UINT32 m_nscl_hor_coef16           : 16;
+       UINT32 m_nscl_hor_coef17           : 16;
+
+
+       //REGISTER disp_scl_reg_18
+       UINT32 m_nscl_hor_coef18           : 16;
+       UINT32 m_nscl_hor_coef19           : 16;
+
+
+       //REGISTER disp_scl_reg_19
+       UINT32 m_nscl_hor_coef20           : 16;
+       UINT32 m_nscl_hor_coef21           : 16;
+
+
+       //REGISTER disp_scl_reg_20
+       UINT32 m_nscl_hor_coef22           : 16;
+       UINT32 m_nscl_hor_coef23           : 16;
+
+
+       //REGISTER disp_scl_reg_21
+       UINT32 m_nscl_hor_coef24           : 16;
+       UINT32 m_nscl_hor_coef25           : 16;
+
+
+       //REGISTER disp_scl_reg_22
+       UINT32 m_nscl_hor_coef26           : 16;
+       UINT32 m_nscl_hor_coef27           : 16;
+
+
+       //REGISTER disp_scl_reg_23
+       UINT32 m_nscl_hor_coef28           : 16;
+       UINT32 m_nscl_hor_coef29           : 16;
+
+
+       //REGISTER disp_scl_reg_24
+       UINT32 m_nscl_hor_coef30           : 16;
+       UINT32 m_nscl_hor_coef31           : 16;
+
+
+       //REGISTER disp_scl_reg_25
+       UINT32 m_nscl_hor_coef32           : 16;
+       UINT32 m_nscl_hor_coef33           : 16;
+
+
+       //REGISTER disp_scl_reg_26
+       UINT32 m_nscl_hor_coef34           : 16;
+       UINT32 m_nscl_hor_coef35           : 16;
+
+
+       //REGISTER disp_scl_reg_27
+       UINT32 m_nscl_hor_coef36           : 16;
+       UINT32 m_nscl_hor_coef37           : 16;
+
+
+       //REGISTER disp_scl_reg_28
+       UINT32 m_nscl_hor_coef38           : 16;
+       UINT32 m_nscl_hor_coef39           : 16;
+
+
+       //REGISTER disp_scl_reg_29
+       UINT32 m_nscl_hor_coef40           : 16;
+       UINT32 m_nscl_hor_coef41           : 16;
+
+
+       //REGISTER disp_scl_reg_30
+       UINT32 m_nscl_hor_coef42           : 16;
+       UINT32 m_nscl_hor_coef43           : 16;
+
+
+       //REGISTER disp_scl_reg_31
+       UINT32 m_nscl_hor_coef44           : 16;
+       UINT32 m_nscl_hor_coef45           : 16;
+
+
+       //REGISTER disp_scl_reg_32
+       UINT32 m_nscl_hor_coef46           : 16;
+       UINT32 m_nscl_hor_coef47           : 16;
+
+
+       } b;
+
+       struct
+       {
+       UINT32 disp_scl_reg_0;
+       UINT32 disp_scl_reg_1;
+       UINT32 disp_scl_reg_2;
+       UINT32 disp_scl_reg_3;
+       UINT32 disp_scl_reg_4;
+       UINT32 disp_scl_reg_5;
+       UINT32 disp_scl_reg_6;
+       UINT32 disp_scl_reg_7;
+       UINT32 disp_scl_reg_8;
+       UINT32 disp_scl_reg_9;
+       UINT32 disp_scl_reg_10;
+       UINT32 disp_scl_reg_11;
+       UINT32 disp_scl_reg_12;
+       UINT32 disp_scl_reg_13;
+       UINT32 disp_scl_reg_14;
+       UINT32 disp_scl_reg_15;
+       UINT32 disp_scl_reg_16;
+       UINT32 disp_scl_reg_17;
+       UINT32 disp_scl_reg_18;
+       UINT32 disp_scl_reg_19;
+       UINT32 disp_scl_reg_20;
+       UINT32 disp_scl_reg_21;
+       UINT32 disp_scl_reg_22;
+       UINT32 disp_scl_reg_23;
+       UINT32 disp_scl_reg_24;
+       UINT32 disp_scl_reg_25;
+       UINT32 disp_scl_reg_26;
+       UINT32 disp_scl_reg_27;
+       UINT32 disp_scl_reg_28;
+       UINT32 disp_scl_reg_29;
+       UINT32 disp_scl_reg_30;
+       UINT32 disp_scl_reg_31;
+       UINT32 disp_scl_reg_32;
+       } v;
+
+}SCALER_X_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dpu/saturn_regs/wb_top.h b/drivers/gpu/drm/spacemit/dpu/saturn_regs/wb_top.h
new file mode 100644 (file)
index 0000000..87d960f
--- /dev/null
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef WB_TOP_X_REG_H
+#define WB_TOP_X_REG_H
+
+typedef union
+{
+       struct
+       {
+       //REGISTER wb_top_reg_0
+       UINT32 wb_in_crop_en            : 1 ;
+       UINT32 m_nwb_proc_en            : 1 ;
+       UINT32 m_nmatrix_en             : 1 ;
+       UINT32 m_noetf_en               : 1 ;
+       UINT32 wb_en                    : 1 ;
+       UINT32 wb_scl_en                : 1 ;
+       UINT32                          : 2 ;
+       UINT32 wb_rot_mode              : 3 ;
+       UINT32 wb_wdma_fbc_en           : 1 ;
+       UINT32 rsvd_unuse               : 1 ;
+       UINT32                          : 3 ;
+       UINT32 wb_out_format            : 5 ;
+       UINT32                          : 11;
+
+
+       //REGISTER wb_top_reg_1
+       UINT32 wb_in_height             : 16;
+       UINT32 wb_in_width              : 16;
+
+
+       //REGISTER wb_top_reg_2
+       UINT32 wb_out_ori_height        : 16;
+       UINT32 wb_out_ori_width         : 16;
+
+
+       //REGISTER wb_top_reg_3
+       UINT32 wb_out_crop_ltopx        : 16;
+       UINT32 wb_out_crop_ltopy        : 16;
+
+
+       //REGISTER wb_top_reg_4
+       UINT32 wb_out_crop_height       : 16;
+       UINT32 wb_out_crop_width        : 16;
+
+
+       //REGISTER wb_top_reg_5
+       UINT32 wb_wdma_base_addr0_low   : 32;
+
+
+       //REGISTER wb_top_reg_6
+       UINT32 wb_wdma_base_addr0_high  : 2 ;
+       UINT32                          : 30;
+
+
+       //REGISTER wb_top_reg_7
+       UINT32 wb_wdma_base_addr1_low   : 32;
+
+
+       //REGISTER wb_top_reg_8
+       UINT32 wb_wdma_base_addr1_high  : 2 ;
+       UINT32                          : 6 ;
+       UINT32 wb_wdma_stride           : 16;
+       UINT32 wb_wdma_outstanding_num  : 5 ;
+       UINT32                          : 3 ;
+
+
+       //REGISTER wb_top_reg_9
+       UINT32 wb_wdma_burst_len        : 5 ;
+       UINT32 wb_wdma_axi_port         : 2 ;
+       UINT32                          : 5 ;
+       UINT32 wb_wdma_cache            : 4 ;
+       UINT32 wb_wdma_region           : 4 ;
+       UINT32 wb_wdma_qos              : 4 ;
+       UINT32                          : 8 ;
+
+
+       //REGISTER wb_top_reg_10
+       UINT32 copy_mode_enable         : 1 ;
+       UINT32 default_color_enable     : 1 ;
+       UINT32 yuv_transform_en         : 1 ;
+       UINT32 fbc_split_en             : 1 ;
+       UINT32 fbc_tile_hd_mode_en      : 1 ;
+       UINT32                          : 27;
+
+
+       //REGISTER wb_top_reg_11
+       UINT32 fmt_cvt_A0               : 16;
+       UINT32 fmt_cvt_A1               : 16;
+
+
+       //REGISTER wb_top_reg_12
+       UINT32 fmt_cvt_A2               : 16;
+       UINT32 fmt_cvt_narrow_mode      : 1 ;
+       UINT32                          : 15;
+
+
+       //REGISTER wb_top_reg_13
+       UINT32 fmt_cvt_matrix00         : 14;
+       UINT32                          : 2 ;
+       UINT32 fmt_cvt_matrix01         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_14
+       UINT32 fmt_cvt_matrix02         : 14;
+       UINT32                          : 2 ;
+       UINT32 fmt_cvt_matrix03         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_15
+       UINT32 fmt_cvt_matrix10         : 14;
+       UINT32                          : 2 ;
+       UINT32 fmt_cvt_matrix11         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_16
+       UINT32 fmt_cvt_matrix12         : 14;
+       UINT32                          : 2 ;
+       UINT32 fmt_cvt_matrix13         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_17
+       UINT32 fmt_cvt_matrix20         : 14;
+       UINT32                          : 2 ;
+       UINT32 fmt_cvt_matrix21         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_18
+       UINT32 fmt_cvt_matrix22         : 14;
+       UINT32                          : 2 ;
+       UINT32 fmt_cvt_matrix23         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_19
+       UINT32 wb_dither_en             : 1 ;
+       UINT32 wb_dither_auto_temp_en   : 1 ;
+       UINT32 wb_dither_rot_mode       : 2 ;
+       UINT32 wb_dither_mode           : 1 ;
+       UINT32                          : 3 ;
+       UINT32 wb_dither_out_dpth0      : 4 ;
+       UINT32 wb_dither_out_dpth1      : 4 ;
+       UINT32 wb_dither_out_dpth2      : 4 ;
+       UINT32                          : 4 ;
+       UINT32 wb_dither_tmp_value      : 8 ;
+
+
+       //REGISTER wb_top_reg_20
+       UINT32 wb_dither_pattern_bits   : 3 ;
+       UINT32                          : 29;
+
+
+       //REGISTER wb_top_reg_21
+       UINT32 wb_dither_bayer_map0     : 8 ;
+       UINT32 wb_dither_bayer_map1     : 8 ;
+       UINT32 wb_dither_bayer_map2     : 8 ;
+       UINT32 wb_dither_bayer_map3     : 8 ;
+
+
+       //REGISTER wb_top_reg_22
+       UINT32 wb_dither_bayer_map4     : 8 ;
+       UINT32 wb_dither_bayer_map5     : 8 ;
+       UINT32 wb_dither_bayer_map6     : 8 ;
+       UINT32 wb_dither_bayer_map7     : 8 ;
+
+
+       //REGISTER wb_top_reg_23
+       UINT32 wb_dither_bayer_map8     : 8 ;
+       UINT32 wb_dither_bayer_map9     : 8 ;
+       UINT32 wb_dither_bayer_map10    : 8 ;
+       UINT32 wb_dither_bayer_map11    : 8 ;
+
+
+       //REGISTER wb_top_reg_24
+       UINT32 wb_dither_bayer_map12    : 8 ;
+       UINT32 wb_dither_bayer_map13    : 8 ;
+       UINT32 wb_dither_bayer_map14    : 8 ;
+       UINT32 wb_dither_bayer_map15    : 8 ;
+
+
+       //REGISTER wb_top_reg_25
+       UINT32 wb_dither_bayer_map16    : 8 ;
+       UINT32 wb_dither_bayer_map17    : 8 ;
+       UINT32 wb_dither_bayer_map18    : 8 ;
+       UINT32 wb_dither_bayer_map19    : 8 ;
+
+
+       //REGISTER wb_top_reg_26
+       UINT32 wb_dither_bayer_map20    : 8 ;
+       UINT32 wb_dither_bayer_map21    : 8 ;
+       UINT32 wb_dither_bayer_map22    : 8 ;
+       UINT32 wb_dither_bayer_map23    : 8 ;
+
+
+       //REGISTER wb_top_reg_27
+       UINT32 wb_dither_bayer_map24    : 8 ;
+       UINT32 wb_dither_bayer_map25    : 8 ;
+       UINT32 wb_dither_bayer_map26    : 8 ;
+       UINT32 wb_dither_bayer_map27    : 8 ;
+
+
+       //REGISTER wb_top_reg_28
+       UINT32 wb_dither_bayer_map28    : 8 ;
+       UINT32 wb_dither_bayer_map29    : 8 ;
+       UINT32 wb_dither_bayer_map30    : 8 ;
+       UINT32 wb_dither_bayer_map31    : 8 ;
+
+
+       //REGISTER wb_top_reg_29
+       UINT32 wb_dither_bayer_map32    : 8 ;
+       UINT32 wb_dither_bayer_map33    : 8 ;
+       UINT32 wb_dither_bayer_map34    : 8 ;
+       UINT32 wb_dither_bayer_map35    : 8 ;
+
+
+       //REGISTER wb_top_reg_30
+       UINT32 wb_dither_bayer_map36    : 8 ;
+       UINT32 wb_dither_bayer_map37    : 8 ;
+       UINT32 wb_dither_bayer_map38    : 8 ;
+       UINT32 wb_dither_bayer_map39    : 8 ;
+
+
+       //REGISTER wb_top_reg_31
+       UINT32 wb_dither_bayer_map40    : 8 ;
+       UINT32 wb_dither_bayer_map41    : 8 ;
+       UINT32 wb_dither_bayer_map42    : 8 ;
+       UINT32 wb_dither_bayer_map43    : 8 ;
+
+
+       //REGISTER wb_top_reg_32
+       UINT32 wb_dither_bayer_map44    : 8 ;
+       UINT32 wb_dither_bayer_map45    : 8 ;
+       UINT32 wb_dither_bayer_map46    : 8 ;
+       UINT32 wb_dither_bayer_map47    : 8 ;
+
+
+       //REGISTER wb_top_reg_33
+       UINT32 wb_dither_bayer_map48    : 8 ;
+       UINT32 wb_dither_bayer_map49    : 8 ;
+       UINT32 wb_dither_bayer_map50    : 8 ;
+       UINT32 wb_dither_bayer_map51    : 8 ;
+
+
+       //REGISTER wb_top_reg_34
+       UINT32 wb_dither_bayer_map52    : 8 ;
+       UINT32 wb_dither_bayer_map53    : 8 ;
+       UINT32 wb_dither_bayer_map54    : 8 ;
+       UINT32 wb_dither_bayer_map55    : 8 ;
+
+
+       //REGISTER wb_top_reg_35
+       UINT32 wb_dither_bayer_map56    : 8 ;
+       UINT32 wb_dither_bayer_map57    : 8 ;
+       UINT32 wb_dither_bayer_map58    : 8 ;
+       UINT32 wb_dither_bayer_map59    : 8 ;
+
+
+       //REGISTER wb_top_reg_36
+       UINT32 wb_dither_bayer_map60    : 8 ;
+       UINT32 wb_dither_bayer_map61    : 8 ;
+       UINT32 wb_dither_bayer_map62    : 8 ;
+       UINT32 wb_dither_bayer_map63    : 8 ;
+
+
+       //REGISTER wb_top_reg_37
+       UINT32 wb_in_crop_ltopx         : 16;
+       UINT32 wb_in_crop_ltopy         : 16;
+
+
+       //REGISTER wb_top_reg_38
+       UINT32 wb_in_crop_rbotx         : 16;
+       UINT32 wb_in_crop_rboty         : 16;
+
+
+       //REGISTER wb_top_reg_39
+       UINT32 m_noetf_mode             : 3 ;
+       UINT32                          : 5 ;
+       UINT32 m_noetf_max              : 12;
+       UINT32                          : 12;
+
+
+       //REGISTER wb_top_reg_40
+       UINT32 m_pmatrix_table0         : 14;
+       UINT32                          : 2 ;
+       UINT32 m_pmatrix_table1         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_41
+       UINT32 m_pmatrix_table2         : 14;
+       UINT32                          : 2 ;
+       UINT32 m_pmatrix_table3         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_42
+       UINT32 m_pmatrix_table4         : 14;
+       UINT32                          : 2 ;
+       UINT32 m_pmatrix_table5         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_43
+       UINT32 m_pmatrix_table6         : 14;
+       UINT32                          : 2 ;
+       UINT32 m_pmatrix_table7         : 14;
+       UINT32                          : 2 ;
+
+
+       //REGISTER wb_top_reg_44
+       UINT32 m_pmatrix_table8         : 14;
+       UINT32                          : 18;
+
+
+       //REGISTER wb_top_reg_45
+       UINT32 m_pmatrix_offset0        : 25;
+       UINT32                          : 7 ;
+
+
+       //REGISTER wb_top_reg_46
+       UINT32 m_pmatrix_offset1        : 25;
+       UINT32                          : 7 ;
+
+
+       //REGISTER wb_top_reg_47
+       UINT32 m_pmatrix_offset2        : 25;
+       UINT32                          : 7 ;
+
+
+       //REGISTER wb_top_reg_48
+       UINT32 dbug_bus0                : 32;
+
+
+       //REGISTER wb_top_reg_49
+       UINT32 dbug_bus1                : 32;
+
+
+       //REGISTER wb_top_reg_50
+       UINT32 dbug_bus2                : 32;
+
+
+       //REGISTER wb_top_reg_51
+       UINT32 dbug_bus3                : 32;
+
+
+       //REGISTER wb_top_reg_52
+       UINT32 dbug_bus4                : 32;
+
+
+       //REGISTER wb_top_reg_53
+       UINT32 dbug_bus5                : 32;
+
+
+       //REGISTER wb_top_reg_54
+       UINT32 wb_wdma_nsaid            : 4 ;
+       UINT32                          : 28;
+
+
+       };
+
+       INT32 value32[55];
+
+}WB_TOP_REG;
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dsi/spacemit_dptc_drv.c b/drivers/gpu/drm/spacemit/dsi/spacemit_dptc_drv.c
new file mode 100644 (file)
index 0000000..66e239a
--- /dev/null
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include "spacemit_dptc_drv.h"
+
+#define I2C_NO 0
+#define TOP_SLAVE_ADDR 0x30
+#define DSI_SLAVE_ADDR 0x33
+
+#ifdef DPTC_DPHY_TEST
+
+static struct twsi_data dptc_i2c_data;
+static DEFINE_MUTEX(cmd_mutex);
+
+static int twsi_write_i2c(struct twsi_data *data)
+{
+       int ret = 0;
+       struct i2c_adapter *adapter;
+       struct i2c_msg msg;
+       u8 val[8];
+       int i ,j =0;
+
+       if (!data || !data->addr || !data->reg_len || !data->val_len) {
+               pr_err("Error: %s, %d", __func__, __LINE__);
+               return -EINVAL;
+       }
+
+       msg.addr = data->addr;
+       msg.flags = 0;
+       msg.len = data->reg_len + data->val_len;
+       msg.buf = val;
+
+       adapter = i2c_get_adapter(data->twsi_no);
+       if (adapter == NULL)
+               return -1;
+
+       mutex_lock(&cmd_mutex);
+       for (i = 0; i < data->reg_len; i++)
+               val[j++]=((u8*)(&data->reg))[i];
+       for (i = 0; i < data->val_len; i++)
+               val[j++]=((u8*)(&data->val))[i];
+       ret = i2c_transfer(adapter, &msg, 1);
+       if (ret < 0) {
+               mutex_unlock(&cmd_mutex);
+               return ret;
+       }
+       mutex_unlock(&cmd_mutex);
+
+       return ret;
+}
+
+static int twsi_read_i2c(struct twsi_data *data)
+{
+       struct i2c_adapter *adapter;
+       struct i2c_msg msg;
+       int ret = 0;
+       u8 val[4];
+
+       if (!data || !data->addr || !data->reg_len || !data->val_len) {
+               pr_err("%s, error param", __func__);
+               return -EINVAL;
+       }
+
+       msg.addr = data->addr;
+       msg.flags = 0;
+       msg.len = data->reg_len;
+       msg.buf = val;
+
+       adapter = i2c_get_adapter(data->twsi_no);
+       if (adapter == NULL)
+               return -1;
+
+       mutex_lock(&cmd_mutex);
+       if (data->reg_len == I2C_8BIT)
+               val[0] = data->reg & 0xff;
+       else if (data->reg_len == I2C_16BIT) {
+               val[0] = (data->reg >> 8) & 0xff;
+               val[1] = data->reg & 0xff;
+       }
+       msg.len = data->reg_len;
+       ret = i2c_transfer(adapter, &msg, 1);
+       if (ret < 0) {
+               mutex_unlock(&cmd_mutex);
+               goto err;
+       }
+
+       msg.flags = I2C_M_RD;
+       msg.len = data->val_len;
+       ret = i2c_transfer(adapter, &msg, 1);
+       if (ret < 0) {
+               mutex_unlock(&cmd_mutex);
+               goto err;
+       }
+
+       if (data->val_len == I2C_8BIT)
+               data->val = val[0];
+       else if (data->val_len == I2C_16BIT)
+               data->val = (val[0] << 8) + val[1];
+       else if (data->val_len == I2C_32BIT)
+               data->val = (val[3] << 24) + (val[2] << 16) + (val[1] << 8) + val[0];
+       //pr_info("twsi_read_i2c: val[0]=0x%x,val[1]=0x%x,val[2]=0x%x,val[3]=0x%x\n",val[0],val[1],val[2],val[3]);
+       mutex_unlock(&cmd_mutex);
+
+       return 0;
+
+err:
+       pr_info("Failed reading register 0x%02x!", data->reg);
+       return ret;
+}
+
+static void TWSI_Init(I2C_FAST_MODE mode, unsigned int i2c_no)
+{
+       dptc_i2c_data.twsi_no = i2c_no;
+       dptc_i2c_data.addr = 0x6c;
+       dptc_i2c_data.reg_len = 1;//I2C_8BIT;
+       dptc_i2c_data.val_len = 4;//I2C_32BIT;
+}
+
+static int TWSI_REG_WRITE_DPTC(uint8_t i2c_no, uint8_t slaveaddress, uint8_t addr, uint32_t data)
+{
+       int ret=0;
+
+       dptc_i2c_data.twsi_no = i2c_no;
+       dptc_i2c_data.addr = slaveaddress;
+       dptc_i2c_data.reg = addr;
+       dptc_i2c_data.val = data;
+       ret = twsi_write_i2c(&dptc_i2c_data);
+       if(0==ret)
+               return 1;
+       else
+               return -1;
+}
+
+static int TWSI_REG_READ_DPTC(uint8_t i2c_no, uint8_t slaveaddress, uint8_t addr)
+{
+       int ret=0;
+
+       dptc_i2c_data.twsi_no = i2c_no;
+       dptc_i2c_data.addr = slaveaddress;
+       dptc_i2c_data.reg = addr;
+       dptc_i2c_data.val = 0x00;
+       ret = twsi_read_i2c(&dptc_i2c_data);
+       if(0==ret)
+               return dptc_i2c_data.val;
+       else
+               return -1;
+}
+
+
+void dptc_top_write(uint32_t reg, uint32_t data)
+{
+       int ret = 0;
+       uint32_t rd_data;
+
+       ret = TWSI_REG_WRITE_DPTC(I2C_NO, TOP_SLAVE_ADDR, reg >> 2, data);
+       rd_data = TWSI_REG_READ_DPTC(I2C_NO, TOP_SLAVE_ADDR, reg >> 2);
+       if(rd_data != data)
+               pr_err("fb: %s failed,  [0x%x] = 0x%x\n", __func__, reg, data);
+}
+
+uint32_t dptc_top_read(uint32_t reg)
+{
+       uint32_t data = 0;
+
+       data = TWSI_REG_READ_DPTC(I2C_NO, TOP_SLAVE_ADDR, reg >> 2);
+       pr_debug("fb: %s [0x%x] = 0x%x\n", __func__, reg, data);
+
+       return data;
+}
+
+void dptc_dsi_write(uint32_t reg, uint32_t data)
+{
+       int ret = 0;
+       uint32_t rd_data;
+
+       ret = TWSI_REG_WRITE_DPTC(I2C_NO, DSI_SLAVE_ADDR, reg >> 2, data);
+       rd_data = TWSI_REG_READ_DPTC(I2C_NO, DSI_SLAVE_ADDR, reg >> 2);
+       if(rd_data != data)
+               pr_err("fb: %s failed,  [0x%x] = 0x%x\n", __func__, reg, data);
+}
+
+uint32_t dptc_dsi_read(uint32_t reg)
+{
+       uint32_t data = 0;
+
+       data = TWSI_REG_READ_DPTC(I2C_NO, DSI_SLAVE_ADDR, reg >> 2);
+
+       pr_debug("fb: %s [0x%x] = 0x%x\n", __func__, reg, data);
+       return data;
+}
+
+void dptc_board_init(void)
+{
+       uint32_t data;
+
+       TWSI_Init(STANDARD_MODE, I2C_NO);
+       msleep(100);
+
+       data = dptc_top_read(0x04);    // enter function 4
+       data |= 0x4;
+       dptc_top_write(0x4, data);
+
+       data = dptc_top_read(0x8);    // pll control
+       data |= 0x1;
+       dptc_top_write(0x8, data);
+
+       data = dptc_top_read(0x18);    // clk reset
+       data |= 0x4;
+       dptc_top_write(0x18, data);
+
+       dptc_top_write(0x0c, 0x3e627627);// setting pll   1.62G // success
+       dptc_top_write(0x10, 0x428D4420);
+
+       data = dptc_top_read(0x14);    // enable ck_dsi
+       data |= (0xff00);            // ???
+       dptc_top_write(0x14, data);
+
+       data = dptc_top_read(0x08);    // power up PLL
+       data |= 0x02;
+}
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/dsi/spacemit_dptc_drv.h b/drivers/gpu/drm/spacemit/dsi/spacemit_dptc_drv.h
new file mode 100644 (file)
index 0000000..527e6c1
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _DPTC_DRV_H_
+#define _DPTC_DRV_H_
+
+
+#ifdef CONFIG_SPACEMIT_FPGA
+#define DPTC_DPHY_TEST 1
+#endif
+
+typedef enum
+{
+       STANDARD_MODE = 0,  /*100Kbps*/
+       FAST_MODE = 1,      /*400Kbps*/
+       HS_MODE = 2,        /*3.4 Mbps slave/3.3 Mbps master,standard mode when not doing a high speed transfer*/
+       HS_MODE_FAST = 3,   /*3.4 Mbps slave/3.3 Mbps master,fast mode when not doing a high speed transfer*/
+} I2C_FAST_MODE;
+
+struct twsi_data {
+       u8 twsi_no;
+       u8 reg_len;/* byte num*/
+       u8 val_len;/* byte num*/
+       u8 addr; /* 7 bit i2c address*/
+       u16 reg;
+       u32 val;
+};
+
+enum i2c_len {
+       I2C_8BIT = 1,
+       I2C_16BIT = 2,
+       I2C_24BIT = 3,
+       I2C_32BIT = 4,
+};
+
+void dptc_dsi_write(uint32_t reg, uint32_t data);
+uint32_t dptc_dsi_read(uint32_t reg);
+void dptc_board_init(void);
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/dsi/spacemit_dsi_drv.c b/drivers/gpu/drm/spacemit/dsi/spacemit_dsi_drv.c
new file mode 100644 (file)
index 0000000..8ef2eff
--- /dev/null
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/clk-provider.h>
+
+#include "spacemit_dsi_hw.h"
+#include "../spacemit_dphy.h"
+#include "../spacemit_dsi.h"
+
+#include "spacemit_dptc_drv.h"
+
+#define SPACEMIT_DSI_MAX_TX_FIFO_BYTES 256
+#define SPACEMIT_DSI_MAX_RX_FIFO_BYTES 64
+#define SPACEMIT_DSI_MAX_CMD_FIFO_BYTES        1024
+
+#define to_dsi_bcnt(timing, bpp)        (((timing) * (bpp)) >> 3)
+
+static unsigned int spacemit_dsi_lane[5] = {0, 0x1, 0x3, 0x7, 0xf};
+
+static unsigned char dsi_bit(unsigned int index, unsigned char *pdata)
+{
+       unsigned char ret;
+       unsigned int cindex, bindex;
+       cindex = index / 8;
+       bindex = index % 8;
+
+       if (pdata[cindex] & (0x1 << bindex))
+               ret = (unsigned char) 0x1;
+       else
+               ret = (unsigned char) 0x0;
+
+       return ret;
+}
+
+static unsigned char calculate_ecc(unsigned char *pdata)
+{
+       unsigned char ret;
+       unsigned char p[8];
+
+       p[7] = (unsigned char) 0x0;
+       p[6] = (unsigned char) 0x0;
+
+       p[5] = (unsigned char) (
+       (
+               dsi_bit(10, pdata) ^
+               dsi_bit(11, pdata) ^
+               dsi_bit(12, pdata) ^
+               dsi_bit(13, pdata) ^
+               dsi_bit(14, pdata) ^
+               dsi_bit(15, pdata) ^
+               dsi_bit(16, pdata) ^
+               dsi_bit(17, pdata) ^
+               dsi_bit(18, pdata) ^
+               dsi_bit(19, pdata) ^
+               dsi_bit(21, pdata) ^
+               dsi_bit(22, pdata) ^
+               dsi_bit(23, pdata)
+               )
+       );
+       p[4] = (unsigned char) (
+               dsi_bit(4, pdata) ^
+               dsi_bit(5, pdata) ^
+               dsi_bit(6, pdata) ^
+               dsi_bit(7, pdata) ^
+               dsi_bit(8, pdata) ^
+               dsi_bit(9, pdata) ^
+               dsi_bit(16, pdata) ^
+               dsi_bit(17, pdata) ^
+               dsi_bit(18, pdata) ^
+               dsi_bit(19, pdata) ^
+               dsi_bit(20, pdata) ^
+               dsi_bit(22, pdata) ^
+               dsi_bit(23, pdata)
+       );
+       p[3] = (unsigned char) (
+       (
+               dsi_bit(1, pdata) ^
+               dsi_bit(2, pdata) ^
+               dsi_bit(3, pdata) ^
+               dsi_bit(7, pdata) ^
+               dsi_bit(8, pdata) ^
+               dsi_bit(9, pdata) ^
+               dsi_bit(13, pdata) ^
+               dsi_bit(14, pdata) ^
+               dsi_bit(15, pdata) ^
+               dsi_bit(19, pdata) ^
+               dsi_bit(20, pdata) ^
+               dsi_bit(21, pdata) ^
+               dsi_bit(23, pdata)
+               )
+       );
+       p[2] = (unsigned char) (
+       (
+               dsi_bit(0, pdata) ^
+               dsi_bit(2, pdata) ^
+               dsi_bit(3, pdata) ^
+               dsi_bit(5, pdata) ^
+               dsi_bit(6, pdata) ^
+               dsi_bit(9, pdata) ^
+               dsi_bit(11, pdata) ^
+               dsi_bit(12, pdata) ^
+               dsi_bit(15, pdata) ^
+               dsi_bit(18, pdata) ^
+               dsi_bit(20, pdata) ^
+               dsi_bit(21, pdata) ^
+               dsi_bit(22, pdata)
+               )
+       );
+       p[1] = (unsigned char) (
+               (
+               dsi_bit(0, pdata) ^
+               dsi_bit(1, pdata) ^
+               dsi_bit(3, pdata) ^
+               dsi_bit(4, pdata) ^
+               dsi_bit(6, pdata) ^
+               dsi_bit(8, pdata) ^
+               dsi_bit(10, pdata) ^
+               dsi_bit(12, pdata) ^
+               dsi_bit(14, pdata) ^
+               dsi_bit(17, pdata) ^
+               dsi_bit(20, pdata) ^
+               dsi_bit(21, pdata) ^
+               dsi_bit(22, pdata) ^
+               dsi_bit(23, pdata)
+               )
+       );
+       p[0] = (unsigned char) (
+               (
+               dsi_bit(0, pdata) ^
+               dsi_bit(1, pdata) ^
+               dsi_bit(2, pdata) ^
+               dsi_bit(4, pdata) ^
+               dsi_bit(5, pdata) ^
+               dsi_bit(7, pdata) ^
+               dsi_bit(10, pdata) ^
+               dsi_bit(11, pdata) ^
+               dsi_bit(13, pdata) ^
+               dsi_bit(16, pdata) ^
+               dsi_bit(20, pdata) ^
+               dsi_bit(21, pdata) ^
+               dsi_bit(22, pdata) ^
+               dsi_bit(23, pdata)
+               )
+       );
+       ret = (unsigned char)(
+               p[0] |
+               (p[1] << 0x1) |
+               (p[2] << 0x2) |
+               (p[3] << 0x3) |
+               (p[4] << 0x4) |
+               (p[5] << 0x5)
+       );
+       return   ret;
+}
+
+static unsigned short gs_crc16_generation_code = 0x8408;
+static unsigned short calculate_crc16(unsigned char *pdata, unsigned
+               short count)
+{
+       unsigned short byte_counter;
+       unsigned char bit_counter;
+       unsigned char data;
+       unsigned short crc16_result = 0xFFFF;
+       if (count > 0) {
+               for (byte_counter = 0; byte_counter < count;
+                       byte_counter++) {
+                       data = *(pdata + byte_counter);
+                       for (bit_counter = 0; bit_counter < 8; bit_counter++) {
+                               if (((crc16_result & 0x0001) ^ ((0x0001 *
+                                       data) & 0x0001)) > 0)
+                                       crc16_result = ((crc16_result >> 1)
+                                       & 0x7FFF) ^ gs_crc16_generation_code;
+                               else
+                                       crc16_result = (crc16_result >> 1)
+                                       & 0x7FFF;
+                               data = (data >> 1) & 0x7F;
+                       }
+               }
+       }
+       return crc16_result;
+}
+
+static void dsi_reset(void __iomem *base_addr)
+{
+       uint32_t reg;
+
+       reg = CFG_SOFT_RST | CFG_SOFT_RST_REG | CFG_CLR_PHY_FIFO | CFG_RST_TXLP |
+               CFG_RST_CPU | CFG_RST_CPN | CFG_RST_VPN | CFG_DSI_PHY_RST;
+
+       /* software reset DSI module */
+       dsi_write(base_addr, DSI_CTRL_0, reg);
+       /* Note: there need some delay after set CFG_SOFT_RST */
+       udelay(1000);
+       dsi_write(base_addr, DSI_CTRL_0, 0);
+
+       dsi_write(base_addr, DSI_IRQ_ST, 0xFFFFFFFF);
+}
+
+static void dsi_enable_video_mode(void __iomem *base_addr, bool enable)
+{
+       if(enable)
+               dsi_set_bits(base_addr, DSI_CTRL_0, CFG_VPN_TX_EN | CFG_VPN_SLV | CFG_VPN_EN);
+       else
+               dsi_clear_bits(base_addr, DSI_CTRL_0, CFG_VPN_TX_EN | CFG_VPN_EN);
+}
+
+static void dsi_enable_cmd_mode(void __iomem *base_addr, bool enable)
+{
+       if(enable)
+               dsi_set_bits(base_addr, DSI_CTRL_0, CFG_CPN_EN);
+       else
+               dsi_clear_bits(base_addr, DSI_CTRL_0, CFG_CPN_EN);
+}
+
+static void dsi_enable_eotp(void __iomem *base_addr, bool enable)
+{
+       if(enable)
+               dsi_set_bits(base_addr, DSI_CTRL_1, CFG_EOTP_EN);
+       else
+               dsi_clear_bits(base_addr, DSI_CTRL_1, CFG_EOTP_EN);
+}
+
+static void dsi_enable_lptx_lanes(void __iomem *base_addr, uint32_t lane_num)
+{
+       dsi_write_bits(base_addr, DSI_CPU_CMD_1,
+               CFG_TXLP_LPDT_MASK, lane_num << CFG_TXLP_LPDT_SHIFT);
+}
+
+static void dsi_enable_split_mode(void __iomem *base_addr, bool splite_mode)
+{
+       if(splite_mode){
+               dsi_set_bits(base_addr, DSI_LCD_BDG_CTRL0, CFG_SPLIT_EN);
+       } else {
+               dsi_clear_bits(base_addr, DSI_LCD_BDG_CTRL0, CFG_SPLIT_EN);
+       }
+}
+
+static void dsi_enable_irq(void __iomem *base_addr, bool enable)
+{
+       if (enable)
+               dsi_set_bits(base_addr, DSI_IRQ_MASK, DSI_IRQ_MASK_BITS);
+       else
+               dsi_clear_bits(base_addr, DSI_IRQ_MASK, DSI_IRQ_MASK_BITS);
+}
+
+static int dsi_write_cmd(void __iomem *base_addr, uint8_t *parameter, uint8_t count, bool lp)
+{
+       uint32_t send_data = 0, reg, timeout, tmp, i;
+       bool turnaround;
+       uint32_t len;
+
+       if(lp)
+               pr_debug("%s: %d packet data will be write in lp mode \n", __func__, count);
+       else
+               pr_debug("%s: %d data will be write in hs mode\n", __func__, count);
+
+       /* write all packet bytes to packet data buffer */
+       for (i = 0; i < count; i++) {
+               send_data |= parameter[i] << ((i % 4) * 8);
+               if (0 ==((i + 1) % 4)) {
+                       dsi_write(base_addr, DSI_CPU_WDAT, send_data);
+                       reg = CFG_CPU_DAT_REQ | CFG_CPU_DAT_RW |((i - 3) << CFG_CPU_DAT_ADDR_SHIFT);
+                       dsi_write(base_addr, DSI_CPU_CMD_3, reg);
+                       /* wait write operation done */
+                       timeout = 1000;
+                       do {
+                               timeout--;
+                               tmp = dsi_read(base_addr, DSI_CPU_CMD_3);
+                       } while ((tmp & CFG_CPU_DAT_REQ) && timeout);
+                       if (timeout == 0)
+                               pr_err("DSI write data to the packet data buffer not done.\n");
+                       send_data = 0;
+               }
+       }
+
+       /* handle last none 4Byte align data */
+       if (0 != i % 4) {
+               dsi_write(base_addr, DSI_CPU_WDAT, send_data);
+               reg = CFG_CPU_DAT_REQ | CFG_CPU_DAT_RW |((4 * (i / 4)) << CFG_CPU_DAT_ADDR_SHIFT);
+               dsi_write(base_addr, DSI_CPU_CMD_3, reg);
+               /* wait write operation done */
+               timeout = 1000;
+               do {
+                       timeout--;
+                       tmp = dsi_read(base_addr, DSI_CPU_CMD_3);
+               } while ((tmp & CFG_CPU_DAT_REQ) && timeout);
+               if (timeout == 0)
+                       pr_err("DSI write data to the packet data buffer not done.\n");
+               send_data = 0;
+       }
+
+       if (parameter[0] == SPACEMIT_DSI_DCS_READ ||
+               parameter[0] == SPACEMIT_DSI_GENERIC_READ1)
+               turnaround = true;
+       else
+               turnaround = false;
+
+       len = count;
+#if 0
+       /* The packet length should contain  CRC_bytes_length in Aquilac_DSI version */
+       if ((parameter[0] == SPACEMIT_DSI_DCS_LWRITE ||
+               parameter[0] == SPACEMIT_DSI_GENERIC_LWRITE) && !lp)
+               len = count - 6;
+#endif
+       reg = CFG_CPU_CMD_REQ |
+               ((count == 4) ? CFG_CPU_SP : 0) |
+               (turnaround ? CFG_CPU_TURN : 0) |
+               (lp ? CFG_CPU_TXLP : 0) |
+               (len << CFG_CPU_WC_SHIFT);
+
+       /* send out the packet */
+       dsi_write(base_addr, DSI_CPU_CMD_0, reg);
+       /* wait packet be sent out */
+       timeout = 1000;
+       do {
+               timeout--;
+               tmp = dsi_read(base_addr, DSI_CPU_CMD_0);
+               udelay(50);
+       } while ((tmp & CFG_CPU_CMD_REQ) && timeout);
+       if (0 == timeout) {
+               pr_info("%s: DSI send out packet maybe failed.\n", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void dsi_config_video_mode(struct spacemit_dsi_device *dsi_ctx, struct spacemit_mipi_info *mipi_info)
+{
+       uint32_t hsync_b, hbp_b, hact_b, hex_b, hfp_b, httl_b;
+       uint32_t hsync, hbp, hact, httl, v_total;
+       uint32_t hsa_wc, hbp_wc, hact_wc, hex_wc, hfp_wc, hlp_wc;
+       uint32_t bpp, hss_bcnt = 4, hse_bct = 4, lgp_over_head = 6, reg;
+       uint32_t slot_cnt0, slot_cnt1;
+       uint32_t dsi_ex_pixel_cnt = 0;
+       uint32_t dsi_hex_en = 0;
+       uint32_t width, lane_number;
+       void __iomem *base_addr = dsi_ctx->base_addr;
+       struct spacemit_dsi_advanced_setting *adv_setting = &dsi_ctx->adv_setting;
+
+       switch(mipi_info->rgb_mode){
+       case DSI_INPUT_DATA_RGB_MODE_565:
+               bpp = 16;
+               break;
+       case DSI_INPUT_DATA_RGB_MODE_666PACKET:
+               bpp = 18;
+               break;
+       case DSI_INPUT_DATA_RGB_MODE_666UNPACKET:
+               bpp = 18;
+               break;
+       case DSI_INPUT_DATA_RGB_MODE_888:
+               bpp = 24;
+               break;
+       default:
+               bpp = 24;
+       }
+
+       v_total = mipi_info->height + mipi_info->vfp + mipi_info->vbp + mipi_info->vsync;
+
+       if(mipi_info->split_enable) {
+               if(( 0 != (mipi_info->width & 0x1)) || (0 != (mipi_info->lane_number & 0x1))){
+                       pr_err("%s: warning:Invalid split config(lane = %d, width = %d)\n",
+                               __func__, mipi_info->lane_number, mipi_info->width);
+               }
+               width = mipi_info->width >> 1;
+               lane_number = mipi_info->lane_number >> 1;
+       } else {
+               width = mipi_info->width;
+               lane_number = mipi_info->lane_number;
+       }
+
+       hact_b = to_dsi_bcnt(width, bpp);
+       hfp_b = to_dsi_bcnt(mipi_info->hfp, bpp);
+       hbp_b = to_dsi_bcnt(mipi_info->hbp, bpp);
+       hsync_b = to_dsi_bcnt(mipi_info->hsync, bpp);
+       hex_b = to_dsi_bcnt(dsi_ex_pixel_cnt, bpp);
+       httl_b = hact_b + hsync_b + hfp_b + hbp_b + hex_b;
+       slot_cnt0 = (httl_b - hact_b) / lane_number + 3;
+       slot_cnt1 = slot_cnt0;
+
+       hact = hact_b / lane_number;
+       hbp = hbp_b / lane_number;
+       hsync = hsync_b / lane_number;
+       httl = (hact_b + hfp_b + hbp_b + hsync_b) / lane_number;
+
+       /* word count in the unit of byte */
+       hsa_wc = (mipi_info->burst_mode == DSI_BURST_MODE_NON_BURST_SYNC_PULSE) ?
+               (hsync_b - hss_bcnt - lgp_over_head) : 0;
+
+       /* Hse is with backporch */
+       hbp_wc = (mipi_info->burst_mode == DSI_BURST_MODE_NON_BURST_SYNC_PULSE) ?
+               (hbp_b - hse_bct - lgp_over_head)
+               : (hsync_b + hbp_b - hss_bcnt - lgp_over_head);
+
+       hfp_wc = ((mipi_info->burst_mode == DSI_BURST_MODE_BURST) && (dsi_hex_en == 0)) ?
+               (hfp_b + hex_b - lgp_over_head - lgp_over_head) :
+               (hfp_b - lgp_over_head - lgp_over_head);
+
+       hact_wc =  (width * bpp) >> 3;
+
+       /* disable Hex currently */
+       hex_wc = 0;
+
+       /*  There is no hlp with active data segment.  */
+       hlp_wc = (mipi_info->burst_mode == DSI_BURST_MODE_NON_BURST_SYNC_PULSE) ?
+               (httl_b - hsync_b - hse_bct - lgp_over_head) :
+               (httl_b - hss_bcnt - lgp_over_head);
+
+       /* FIXME - need to double check the (*3) is bytes_per_pixel
+       * from input data or output to panel
+       */
+
+       /*Jessica: need be calculated by really case*/
+       dsi_write(base_addr, DSI_VPN_CTRL_0, (0x50<<16) | 0xc08);
+
+    /* SET UP LCD1 TIMING REGISTERS FOR DSI BUS */
+       dsi_write(base_addr, DSI_VPN_TIMING_0, (hact << 16) | httl);
+       dsi_write(base_addr, DSI_VPN_TIMING_1, (hsync << 16) | hbp);
+       dsi_write(base_addr, DSI_VPN_TIMING_2, ((mipi_info->height)<<16) | (v_total));
+       dsi_write(base_addr, DSI_VPN_TIMING_3, ((mipi_info->vsync) << 16) | (mipi_info->vbp));
+
+    /* SET UP LCD1 WORD COUNT REGISTERS FOR DSI BUS */
+       dsi_write(base_addr, DSI_VPN_WC_0, (hbp_wc << 16) | hsa_wc);
+       dsi_write(base_addr, DSI_VPN_WC_1, (hfp_wc << 16) | hact_wc);
+       dsi_write(base_addr, DSI_VPN_WC_2, (hex_wc << 16) | hlp_wc);
+
+       dsi_write(base_addr, DSI_VPN_SLOT_CNT_0, (slot_cnt0 << 16) | slot_cnt0);
+       dsi_write(base_addr, DSI_VPN_SLOT_CNT_1, (slot_cnt1 << 16) | slot_cnt1);
+
+    /* Configure LCD control register 1 FOR DSI BUS */
+#ifdef DPTC_DPHY_TEST
+       adv_setting->hact_wc_en = 0;
+       adv_setting->timing_check_dis = 1;
+       adv_setting->auto_dly_dis = 1;
+#endif
+
+       reg = adv_setting->vsync_rst_en << CFG_VPN_VSYNC_RST_EN_SHIFT |
+                       adv_setting->auto_wc_dis << CFG_VPN_AUTO_WC_DIS_SHIFT |
+                       adv_setting->hact_wc_en << CFG_VPN_HACT_WC_EN_SHIFT |
+                       adv_setting->timing_check_dis << CFG_VPN_TIMING_CHECK_DIS_SHIFT |
+                       adv_setting->auto_dly_dis << CFG_VPN_AUTO_DLY_DIS_SHIFT |
+                       adv_setting->hlp_pkt_en << CFG_VPN_HLP_PKT_EN_SHIFT |
+                       adv_setting->hex_pkt_en << CFG_VPN_HEX_PKT_EN_SHIFT |
+                       adv_setting->hfp_pkt_en << CFG_VPN_HFP_PKT_EN_SHIFT |
+                       adv_setting->hbp_pkt_en << CFG_VPN_HBP_PKT_EN_SHIFT |
+                       adv_setting->hse_pkt_en << CFG_VPN_HSE_PKT_EN_SHIFT |
+                       adv_setting->hsa_pkt_en << CFG_VPN_HSA_PKT_EN_SHIFT |
+                       adv_setting->hex_slot_en<< CFG_VPN_HEX_SLOT_EN_SHIFT |
+                       adv_setting->last_line_turn << CFG_VPN_LAST_LINE_TURN_SHIFT |
+                       adv_setting->lpm_frame_en << CFG_VPN_LPM_FRAME_EN_SHIFT |
+                       mipi_info->burst_mode << CFG_VPN_BURST_MODE_SHIFT |
+                       mipi_info->rgb_mode << CFG_VPN_RGB_TYPE_SHIFT;
+       dsi_write(base_addr, DSI_VPN_CTRL_1,reg);
+
+       dsi_write_bits(base_addr, DSI_LCD_BDG_CTRL0, CFG_VPN_FIFO_AFULL_CNT_MASK,
+               0 << CFG_VPN_FIFO_AFULL_CNT_SHIT);
+//#ifdef CONFIG_ESD_SUPPORT
+       dsi_set_bits(base_addr, DSI_LCD_BDG_CTRL0, CFG_VPN_FIFO_AFULL_BYPASS);
+//#else
+       //dsi_clear_bits(base_addr, DSI_LCD_BDG_CTRL0, CFG_VPN_FIFO_AFULL_BYPASS);
+//#endif
+       dsi_set_bits(base_addr, DSI_LCD_BDG_CTRL0, CFG_PIXEL_SWAP);
+
+       dsi_enable_cmd_mode(base_addr, false);
+       dsi_enable_video_mode(base_addr, true);
+}
+
+static void dsi_config_cmd_mode(struct spacemit_dsi_device *dsi_ctx, struct spacemit_mipi_info *mipi_info)
+{
+       int reg;
+       int rgb_mode, bpp;
+
+       switch(mipi_info -> rgb_mode){
+       case DSI_INPUT_DATA_RGB_MODE_565:
+               bpp = 16;
+               rgb_mode = 2;
+               break;
+       case DSI_INPUT_DATA_RGB_MODE_666UNPACKET:
+               bpp = 18;
+               rgb_mode = 1;
+               break;
+       case DSI_INPUT_DATA_RGB_MODE_888:
+               bpp = 24;
+               rgb_mode = 0;
+               break;
+       default:
+               pr_err("%s: unsupported rgb format!\n", __func__);
+               bpp = 24;
+               rgb_mode = 0;
+       }
+
+       reg = mipi_info->te_enable << CFG_CPN_TE_EN_SHIFT |
+                       rgb_mode << CFG_CPN_RGB_TYPE_SHIFT |
+                       1 << CFG_CPN_BURST_MODE_SHIFT |
+                       0 << CFG_CPN_DMA_DIS_SHIFT |
+                       0 << CFG_CPN_ADDR0_EN_SHIFT;
+       dsi_write(dsi_ctx->base_addr, DSI_CPN_CMD, reg);
+
+       reg = mipi_info->width * bpp / 8 << CFG_CPN_PKT_CNT_SHIFT |
+               SPACEMIT_DSI_MAX_CMD_FIFO_BYTES << CFG_CPN_FIFO_FULL_LEVEL_SHIFT;
+       dsi_write(dsi_ctx->base_addr, DSI_CPN_CTRL_1,reg);
+
+       dsi_write_bits(dsi_ctx->base_addr, DSI_LCD_BDG_CTRL0, CFG_CPN_TE_EDGE_MASK,
+               mipi_info->te_pol << CFG_CPN_TE_EDGE_SHIFT);
+       dsi_write_bits(dsi_ctx->base_addr, DSI_LCD_BDG_CTRL0, CFG_CPN_VSYNC_EDGE_MASK,
+               mipi_info->vsync_pol << CFG_CPN_VSYNC_EDGE_SHIFT);
+       dsi_write_bits(dsi_ctx->base_addr, DSI_LCD_BDG_CTRL0, CFG_CPN_TE_MODE_MASK,
+               mipi_info->te_mode << CFG_CPN_TE_MODE_SHIFT);
+
+       reg = 0x80 << CFG_CPN_TE_DLY_CNT_SHIFT |
+                       0 << CFG_CPN_TE_LINE_CNT_SHIFT;
+       dsi_write(dsi_ctx->base_addr, DSI_LCD_BDG_CTRL1, reg);
+
+       dsi_enable_video_mode(dsi_ctx->base_addr, false);
+       dsi_enable_cmd_mode(dsi_ctx->base_addr, true);
+}
+
+static int dsi_write_cmd_array(struct spacemit_dsi_device *dsi_ctx,
+                                                                       struct spacemit_dsi_cmd_desc *cmds,int count)
+{
+       struct spacemit_dsi_cmd_desc cmd_line;
+       uint8_t type, parameter[SPACEMIT_DSI_MAX_TX_FIFO_BYTES], len;
+       uint32_t crc, loop;
+       int ret = 0;
+
+       pr_debug("%s: %d cmd will be write\n", __func__, count);
+
+       if(NULL == dsi_ctx) {
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       for (loop = 0; loop < count; loop++) {
+               cmd_line = cmds[loop];
+               type = cmd_line.cmd_type;
+               len = cmd_line.length;
+               memset(parameter, 0x00, len + 6);
+               parameter[0] = type & 0xff;
+               switch (type) {
+               case SPACEMIT_DSI_DCS_SWRITE:
+               case SPACEMIT_DSI_DCS_SWRITE1:
+               case SPACEMIT_DSI_DCS_READ:
+               case SPACEMIT_DSI_GENERIC_READ1:
+               case SPACEMIT_DSI_SET_MAX_PKT_SIZE:
+                       memcpy(&parameter[1], cmd_line.data, len);
+                       len = 4;
+                       break;
+               case SPACEMIT_DSI_GENERIC_LWRITE:
+               case SPACEMIT_DSI_DCS_LWRITE:
+                       parameter[1] = len & 0xff;
+                       parameter[2] = 0;
+                       memcpy(&parameter[4], cmd_line.data, len);
+                       crc = calculate_crc16(&parameter[4], len);
+                       parameter[len + 4] = crc & 0xff;
+                       parameter[len + 5] = (crc >> 8) & 0xff;
+                       len += 6;
+                       break;
+               default:
+                       pr_err("%s: data type not supported 0x%8x\n",__func__, type);
+                       break;
+               }
+
+               parameter[3] = calculate_ecc(parameter);
+
+               /* send dsi commands */
+               ret = dsi_write_cmd(dsi_ctx->base_addr, parameter, len, cmd_line.lp);
+               if(ret)
+                       return -1;
+
+       //      if (0 != cmd_line.delay)
+       //              msleep(cmd_line.delay);
+       }
+       return 0;
+}
+
+static int dsi_read_cmd_array(struct spacemit_dsi_device *dsi_ctx, struct spacemit_dsi_rx_buf *dbuf,
+                                       struct spacemit_dsi_cmd_desc *cmds, int count)
+{
+       uint8_t parameter[SPACEMIT_DSI_MAX_RX_FIFO_BYTES];
+       uint32_t i, rx_reg, timeout, tmp, packet,
+           data_pointer, byte_count;
+
+       pr_debug("%s: %d cmds will be write\n", __func__, count);
+
+       if(NULL == dsi_ctx) {
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       memset(dbuf, 0x0, sizeof(struct spacemit_dsi_rx_buf));
+       dsi_write_cmd_array(dsi_ctx, cmds, count);
+
+       timeout = 1000;
+       do {
+               timeout--;
+               tmp = dsi_read(dsi_ctx->base_addr, DSI_IRQ_ST);
+       } while (((tmp & IRQ_RX_PKT) == 0) && timeout);
+       if (0 == timeout) {
+               pr_err("%s: dsi didn't receive packet, irq status 0x%x\n", __func__, tmp);
+               return -1;
+       }
+
+       if (tmp & IRQ_RX_TRG3)
+               pr_err("%s: not defined package is received\n", __func__);
+       if (tmp & IRQ_RX_TRG2)
+               pr_err("%s: ACK package is received\n", __func__);
+       if (tmp & IRQ_RX_TRG1)
+               pr_err("%s: TE trigger is received\n", __func__);
+       if (tmp & IRQ_RX_ERR) {
+               tmp = dsi_read(dsi_ctx->base_addr, DSI_RX_PKT_HDR_0);
+               pr_err("%s: error: ACK with error report (0x%x)\n", __func__, tmp);
+       }
+
+       packet = dsi_read(dsi_ctx->base_addr, DSI_RX_PKT_ST_0);
+
+       data_pointer = (packet & CFG_RX_PKT0_PTR_MASK) >> CFG_RX_PKT0_PTR_SHIFT;
+       tmp = dsi_read(dsi_ctx->base_addr, DSI_RX_PKT_CTRL_1);
+       byte_count = tmp & CFG_RX_PKT_BCNT_MASK;
+
+       memset(parameter, 0x00, byte_count);
+       for (i = data_pointer; i < data_pointer + byte_count; i++) {
+               rx_reg = dsi_read(dsi_ctx->base_addr, DSI_RX_PKT_CTRL);
+               rx_reg &= ~CFG_RX_PKT_RD_PTR_MASK;
+               rx_reg |= CFG_RX_PKT_RD_REQ | (i << CFG_RX_PKT_RD_PTR_SHIFT);
+               dsi_write(dsi_ctx->base_addr, DSI_RX_PKT_CTRL, rx_reg);
+               count = 10000;
+               do {
+                       count--;
+                       rx_reg = dsi_read(dsi_ctx->base_addr, DSI_RX_PKT_CTRL);
+               } while (rx_reg & CFG_RX_PKT_RD_REQ && count);
+               if ( 0 == count)
+                       pr_err("%s: error: read Rx packet FIFO error\n", __func__);
+               parameter[i - data_pointer] = rx_reg & 0xff;
+       }
+       switch (parameter[0]) {
+       case SPACEMIT_DSI_ACK_ERR_RESP:
+               pr_err("%s: error: Acknowledge with error report\n", __func__);
+               break;
+       case SPACEMIT_DSI_EOTP:
+               pr_err("%s: error: End of Transmission packet\n", __func__);
+               break;
+       case SPACEMIT_DSI_GEN_READ1_RESP:
+       case SPACEMIT_DSI_DCS_READ1_RESP:
+               dbuf->data_type = parameter[0];
+               dbuf->length = 1;
+               memcpy(dbuf->data, &parameter[1], dbuf->length);
+               break;
+       case SPACEMIT_DSI_GEN_READ2_RESP:
+       case SPACEMIT_DSI_DCS_READ2_RESP:
+               dbuf->data_type = parameter[0];
+               dbuf->length = 2;
+               memcpy(dbuf->data, &parameter[1], dbuf->length);
+               break;
+       case SPACEMIT_DSI_GEN_LREAD_RESP:
+       case SPACEMIT_DSI_DCS_LREAD_RESP:
+               dbuf->data_type = parameter[0];
+               dbuf->length = (parameter[2] << 8) | parameter[1];
+               memcpy(dbuf->data, &parameter[4], dbuf->length);
+               pr_debug("%s: read %d data: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", __func__, dbuf->length,
+                       parameter[4], parameter[5], parameter[6], parameter[7], parameter[8]);
+               break;
+       }
+       return 0;
+}
+
+static void dsi_open_dphy(struct spacemit_dsi_device* device_ctx, bool ready)
+{
+       struct spacemit_dphy *spacemit_dphy = device_ctx->phy;
+       struct spacemit_dphy_ctx *dphy_config = &spacemit_dphy->ctx;
+       struct spacemit_mipi_info *mipi_info = &device_ctx->mipi_info;
+
+       dphy_config->base_addr = device_ctx->base_addr;
+       //dphy_config->phy_freq = device_ctx->bit_clk_rate / 1000;
+       //dphy_config->esc_clk = device_ctx->esc_clk_rate / 1000;
+       dphy_config->phy_freq = mipi_info->phy_bit_clock / 1000;
+       dphy_config->esc_clk = mipi_info->phy_esc_clock / 1000;
+
+       if(mipi_info->split_enable)
+               dphy_config->lane_num = mipi_info->lane_number >> 1;
+       else
+               dphy_config->lane_num = mipi_info->lane_number;
+       dphy_config->status = DPHY_STATUS_UNINIT;
+
+       if(ready){
+               dphy_config->status = DPHY_STATUS_INIT;
+               return;
+       }
+
+       spacemit_dphy_resume(spacemit_dphy);
+}
+
+static void dsi_close_dphy(struct spacemit_dphy* spacemit_dphy)
+{
+       spacemit_dphy_suspend(spacemit_dphy);
+}
+
+static void dsi_ready_dphy(struct spacemit_dsi_device* device_ctx)
+{
+}
+
+int spacemit_dsi_open(struct spacemit_dsi_device* device_ctx, bool ready)
+{
+       int lane_number;
+       struct spacemit_mipi_info *mipi_info = &device_ctx->mipi_info;
+
+#ifdef LCD_IS_READY
+       return 0;
+#endif
+
+       DRM_DEBUG("%s() \n", __func__);
+
+       if((NULL == device_ctx) || (NULL == mipi_info)){
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+#ifdef DPTC_DPHY_TEST
+       if(!ready) {
+               dptc_board_init();
+       }
+#endif
+
+       pr_debug("%s: dsi(%d) Enter, ready = %d\n", __func__, device_ctx->id, ready);
+
+       if(mipi_info->split_enable)
+               lane_number = mipi_info->lane_number >> 1;
+       else
+               lane_number = mipi_info->lane_number;
+
+       if(!ready)
+               dsi_reset(device_ctx->base_addr);
+
+       dsi_open_dphy(device_ctx, ready);
+       if(!ready) {
+               dsi_enable_split_mode(device_ctx->base_addr, mipi_info->split_enable);
+               dsi_enable_lptx_lanes(device_ctx->base_addr, spacemit_dsi_lane[lane_number]);
+               dsi_enable_eotp(device_ctx->base_addr, mipi_info->eotp_enable);
+       }
+
+       dsi_enable_irq(device_ctx->base_addr, true);
+
+       device_ctx->status = DSI_STATUS_OPENED;
+       return 0;
+}
+
+int spacemit_dsi_close(struct spacemit_dsi_device* device_ctx)
+{
+#ifdef LCD_IS_READY
+       return 0;
+#endif
+
+       if(NULL == device_ctx){
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       pr_debug("%s: dsi(%d) Enter\n", __func__, device_ctx->id);
+
+       dsi_enable_irq(device_ctx->base_addr, false);
+
+       dsi_close_dphy(device_ctx->phy);
+
+       device_ctx->status = DSI_STATUS_UNINIT;
+
+       pr_debug("%s: dsi(%d) leave\n", __func__, device_ctx->id);
+       return 0;
+}
+
+int spacemit_dsi_ready_for_datatx(struct spacemit_dsi_device* device_ctx)
+{
+       struct spacemit_mipi_info *mipi_info = &device_ctx->mipi_info;
+
+#ifdef LCD_IS_READY
+       return 0;
+#endif
+
+       if((NULL == device_ctx) || (NULL == mipi_info)){
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       pr_debug("%s: dsi(%d) Enter\n", __func__, device_ctx->id);
+
+       if(mipi_info->work_mode == SPACEMIT_DSI_MODE_CMD){
+               dsi_config_cmd_mode(device_ctx, mipi_info);
+       } else {
+               dsi_config_video_mode(device_ctx, mipi_info);
+       }
+
+       dsi_ready_dphy(device_ctx);
+       return 0;
+}
+
+int spacemit_dsi_close_datatx(struct spacemit_dsi_device* device_ctx)
+{
+#ifdef LCD_IS_READY
+       return 0;
+#endif
+
+       if(NULL == device_ctx){
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       pr_debug("%s: dsi(%d) Enter\n", __func__, device_ctx->id);
+
+       dsi_enable_cmd_mode(device_ctx->base_addr, false);
+       dsi_enable_video_mode(device_ctx->base_addr, false);
+
+       return 0;
+}
+
+int spacemit_dsi_write_cmds(struct spacemit_dsi_device* device_ctx,
+                                                                       struct spacemit_dsi_cmd_desc *cmds, int count)
+{
+#ifdef LCD_IS_READY
+       return 0;
+#endif
+
+       if((NULL == device_ctx) || (NULL == cmds)){
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       pr_debug("%s: dsi(%d) Enter\n", __func__, device_ctx->id);
+
+       return dsi_write_cmd_array(device_ctx, cmds, count);
+}
+
+int spacemit_dsi_read_cmds(struct spacemit_dsi_device* device_ctx, struct spacemit_dsi_rx_buf *dbuf,
+                                                               struct spacemit_dsi_cmd_desc *cmds, int count)
+{
+#ifdef LCD_IS_READY
+       return 0;
+#endif
+
+       if((NULL == device_ctx) || (NULL == cmds)){
+               pr_err("%s: Invalid param\n", __func__);
+               return -1;
+       }
+
+       pr_debug("%s: dsi(%d) Enter\n", __func__, device_ctx->id);
+       return dsi_read_cmd_array(device_ctx, dbuf, cmds, count);
+}
+
+int spacemit_dsi_parse_dt(struct spacemit_dsi_device* device_ctx, struct device_node *np)
+{
+       return 0;
+}
+
+int spacemit_dsi_isr(struct spacemit_dsi_device* device_ctx)
+{
+       uint32_t irq_st;
+
+       irq_st = dsi_read(device_ctx->base_addr, DSI_IRQ_ST);
+
+       /*clear interrupt*/
+       dsi_write(device_ctx->base_addr, DSI_IRQ_ST, irq_st);
+
+       // if (irq_st & DSI_IRQ_PHY_FIFO_UNDERRUN)
+       //      pr_err("DSI: DSI_IRQ_PHY_FIFO_UNDERRUN %d\n", a);
+
+       if (irq_st & DSI_IRQ_VPN_BF_UNDERRUN_ERR)
+               pr_err("DSI: DSI_IRQ_VPN_BF_UNDERRUN_ERR\n");
+
+       if (irq_st & DSI_IRQ_VPN_BF_OVERRUN_ERR)
+               pr_err("DSI: DSI_IRQ_VPN_BF_OVERRUN_ERR\n");
+
+       return 0;
+}
+
+static struct dsi_core_ops dsi_core_ops = {
+       .parse_dt = spacemit_dsi_parse_dt,
+       .isr = spacemit_dsi_isr,
+       .dsi_open = spacemit_dsi_open,
+       .dsi_close = spacemit_dsi_close,
+       .dsi_write_cmds = spacemit_dsi_write_cmds,
+       .dsi_read_cmds = spacemit_dsi_read_cmds,
+       .dsi_ready_for_datatx = spacemit_dsi_ready_for_datatx,
+       .dsi_close_datatx = spacemit_dsi_close_datatx,
+};
+
+static struct ops_entry entry = {
+       .ver = "synopsys-dhost",
+       .ops = &dsi_core_ops,
+};
+
+static int __init dsi_core_register(void)
+{
+       return dsi_core_ops_register(&entry);
+}
+
+subsys_initcall(dsi_core_register);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/dsi/spacemit_dsi_hw.h b/drivers/gpu/drm/spacemit/dsi/spacemit_dsi_hw.h
new file mode 100644 (file)
index 0000000..55510dc
--- /dev/null
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DSI_HW_H_
+#define _SPACEMIT_DSI_HW_H_
+
+#include <linux/io.h>
+#include <drm/drm_print.h>
+
+#define DSI_CTRL_0 0x0
+#define DSI_CTRL_1 0x4
+#define DSI_IRQ_ST1  0x8
+#define DSI_IRQ_MASK1 0xC
+#define DSI_IRQ_ST 0x10
+#define DSI_IRQ_MASK 0x14
+#define DSI_IRQ_PHY_FIFO_UNDERRUN      BIT(23)
+#define DSI_IRQ_VPN_BF_UNDERRUN_ERR    BIT(19)
+#define DSI_IRQ_VPN_BF_OVERRUN_ERR     BIT(17)
+#define DSI_IRQ_MASK_BITS              ( DSI_IRQ_PHY_FIFO_UNDERRUN | \
+                                       DSI_IRQ_VPN_BF_UNDERRUN_ERR | \
+                                       DSI_IRQ_VPN_BF_OVERRUN_ERR )
+
+#ifdef CONFIG_SPACEMIT_FPGA
+#define DSI_FPGA_PHY_CTRL_0 0x18
+#define DSI_FPGA_PHY_CTRL_1 0x1C
+#endif
+
+#define DSI_CPU_CMD_0 0x20
+#define DSI_CPU_CMD_1 0x24
+#define DSI_CPU_CMD_3 0x2C
+#define DSI_CPU_WDAT 0x30
+#define DSI_CPU_STATUS_0 0x34
+#define DSI_CPU_STATUS_1 0x38
+#define DSI_CPU_STATUS_2 0x3C
+#define DSI_CPU_STATUS_3 0x40
+#define DSI_CPU_STATUS_4 0x44
+
+#define DSI_CPN_STATUS_1 0x4C
+#define DSI_CPN_CMD 0x50
+#define DSI_CPN_CTRL_0 0x54
+#define DSI_CPN_CTRL_1 0x58
+#define DSI_CPN_STATUS_0 0x5C
+
+#define DSI_RX_PKT_ST_0 0x60
+#define DSI_RX_PKT_HDR_0 0x64
+#define DSI_RX_PKT_ST_1 0x68
+#define DSI_RX_PKT_HDR_1 0x6C
+#define DSI_RX_PKT_CTRL 0x70
+#define DSI_RX_PKT_CTRL_1 0x74
+#define DSI_RX_PKT_ST_2 0x78
+#define DSI_RX_PKT_HDR_2 0x7C
+
+#define DSI_LCD_BDG_CTRL0 0x84
+#define DSI_LCD_BDG_CTRL1 0x88
+
+#define DSI_TX_TIMER 0xE4
+#define DSI_RX_TIMER 0xE8
+#define DSI_TURN_TIMER 0xEC
+
+#define DSI_VPN_CTRL_0 0x100
+#define DSI_VPN_CTRL_1 0x104
+#define DSI_VPN_TIMING_0 0x110
+#define DSI_VPN_TIMING_1 0x114
+#define DSI_VPN_TIMING_2 0x118
+#define DSI_VPN_TIMING_3 0x11C
+#define DSI_VPN_WC_0 0x120
+#define DSI_VPN_WC_1 0x124
+#define DSI_VPN_WC_2 0x128
+#define DSI_VPN_SLOT_CNT_0 0x130
+#define DSI_VPN_SLOT_CNT_1 0x134
+#define DSI_VPN_SYNC_CODE 0x138
+#define DSI_VPN_STATUS_0 0x140
+#define DSI_VPN_STATUS_1 0x144
+#define DSI_VPN_STATUS_2 0x148
+#define DSI_VPN_STATUS_3 0x14C
+#define DSI_VPN_STATUS_4 0x150
+
+#define DSI_PHY_CTRL_0 0x180
+#define DSI_PHY_CTRL_1 0x184
+#define DSI_PHY_CTRL_2 0x188
+#define DSI_PHY_CTRL_3 0x18C
+#define DSI_PHY_STATUS_0 0x190
+#define DSI_PHY_STATUS_1 0x194
+#define DSI_PHY_LPRX_0 0x198
+#define DSI_PHY_LPRX_1 0x19C
+#define DSI_PHY_LPTX_0 0x1A0
+#define DSI_PHY_LPTX_1 0x1A4
+#define DSI_PHY_LPTX_2 0x1A8
+#define DSI_PHY_STATUS_2 0x1AC
+#define DSI_PHY_TIME_0 0x1C0
+#define DSI_PHY_TIME_1 0x1C4
+#define DSI_PHY_TIME_2 0x1C8
+#define DSI_PHY_TIME_3 0x1CC
+#define DSI_PHY_CODE_0 0x1D0
+#define DSI_PHY_CODE_1 0x1D4
+#define DSI_PHY_ANA_PWR_CTRL 0x1E0
+#define DSI_PHY_ANA_CTRL0 0x1E4
+#define DSI_PHY_ANA_CTRL1 0x1E8
+
+//DSI_CTRL_0 0x0
+#define CFG_SOFT_RST BIT(31)
+#define CFG_SOFT_RST_REG BIT(30)
+#define CFG_CLR_PHY_FIFO BIT(29)
+#define CFG_RST_TXLP BIT(28)
+#define CFG_RST_CPU BIT(27)
+#define CFG_RST_CPN BIT(26)
+#define CFG_RST_VPN BIT(24)
+#define CFG_DSI_PHY_RST BIT(23)
+#define CFG_VPN_TX_EN BIT(8)
+#define CFG_VPN_SLV BIT(4)
+#define CFG_CPN_EN BIT(2)
+#define CFG_VPN_EN BIT(0)
+
+//DSI_CTRL_1 0x4
+#define CFG_EOTP_EN BIT(8)
+
+//DSI_IRQ_ST 0x10
+#define IRQ_RX_ERR BIT(25)
+#define IRQ_RX_TRG3 BIT(7)
+#define IRQ_RX_TRG2 BIT(6)
+#define IRQ_RX_TRG1 BIT(5)
+#define IRQ_RX_TRG0 BIT(4)
+#define IRQ_RX_PKT BIT(2)
+
+#ifdef CONFIG_SPACEMIT_FPGA
+//DSI_FPGA_PHY_CTRL_0 0x18
+#define CFG_DPHY_RSETZ 0
+#define CFG_DPHY_SHUTDOWN 1
+#define CFG_DPHY_RSTZCAL 2
+#define CFG_DPHY_TXRXZ 3
+#define CFG_DPHY_MASSLVZ 4
+#define CFG_DPHY_ENABLE0 5
+#define CFG_DPHY_ENABLE1 6
+#define CFG_DPHY_ENABLECLK 7
+#define CFG_DPHY_HSREQ_LANECLK 8
+#define CFG_DPHY_HSREQ_LANE0 9
+#define CFG_DPHY_HSREQ_LANE1 10
+#define CFG_DPHY_HSREQ_LANE2 11
+#define CFG_DPHY_HSREQ_LANE3 12
+#define CFG_DPHY_TXRX_BYTECLK_REV 13
+#define CFG_DPHY_FCLK_REV 14
+
+//DSI_FPGA_PHY_CTRL_1 0x1C
+#define CFG_DPHY_TESTCLK 0
+#define CFG_DPHY_TESTCLR 1
+#define CFG_DPHY_TESTEN 2
+#define CFG_DPHY_TXRXZ 3
+#define CFG_DPHY_TESTDIN 8
+#define CFG_DPHY_TESTDOUT 16
+#define CFG_DPHY_LOCK 24
+#endif
+
+//DSI_CPU_CMD_0 0x20
+#define CFG_CPU_CMD_REQ BIT(31)
+#define CFG_CPU_SP BIT(30)
+#define CFG_CPU_TURN BIT(29)
+#define CFG_CPU_TXLP BIT(27)
+#define CFG_CPU_WC_SHIFT 0
+
+//DSI_CPU_CMD_1 0x24
+#define CFG_TXLP_LPDT_SHIFT 20
+
+#define CFG_TXLP_LPDT_MASK (0xF << CFG_TXLP_LPDT_SHIFT)
+
+//DSI_CPU_CMD_3 0x2C
+#define CFG_CPU_DAT_REQ BIT(31)
+#define CFG_CPU_DAT_RW BIT(30)
+#define CFG_CPU_DAT_ADDR_SHIFT 16
+
+//DSI_CPN_CMD 0x50
+#define CFG_CPN_TE_EN_SHIFT 28
+#define CFG_CPN_RGB_TYPE_SHIFT 24
+#define CFG_CPN_BURST_MODE_SHIFT 3
+#define CFG_CPN_FIRSTP_SEL_SHIFT 2
+#define CFG_CPN_DMA_DIS_SHIFT 1
+#define CFG_CPN_ADDR0_EN_SHIFT 0
+
+//DSI_CPN_CTRL_1 0X58
+#define CFG_CPN_PKT_CNT_SHIFT  16
+#define CFG_CPN_FIFO_FULL_LEVEL_SHIFT 0
+
+//DSI_RX_PKT_ST_0 0x60
+#define CFG_RX_PKT0_PTR_SHIFT 16
+#define CFG_RX_PKT0_PTR_MASK (0x3F << CFG_RX_PKT0_PTR_SHIFT)
+
+//DSI_RX_PKT_CTRL 0x70
+#define CFG_RX_PKT_RD_REQ BIT(31)
+#define CFG_RX_PKT_RD_PTR_SHIFT 16
+#define CFG_RX_PKT_RD_PTR_MASK (0x3F << CFG_RX_PKT_RD_PTR_SHIFT)
+#define CFG_RX_PKT_RD_DATA_SHIFT 0
+#define CFG_RX_PKT_RD_DATA_MASK (0x3F << CFG_RX_PKT_RD_DATA_SHIFT)
+
+//DSI_RX_PKT_CTRL_1 0x74
+#define CFG_RX_PKT_BCNT_SHIFT 0
+#define CFG_RX_PKT_BCNT_MASK (0xff << CFG_RX_PKT_BCNT_SHIFT)
+
+//DSI_LCD_BDG_CTRL0 0x84
+#define CFG_VPN_FIFO_AFULL_CNT_SHIT 16
+#define CFG_VPN_FIFO_AFULL_CNT_MASK (0xfff << CFG_VPN_FIFO_AFULL_CNT_SHIT)
+#define CFG_VPN_FIFO_AFULL_BYPASS BIT(6)
+#define CFG_CPN_VSYNC_EDGE_SHIFT 5
+#define CFG_CPN_VSYNC_EDGE_MASK (1 << CFG_CPN_VSYNC_EDGE_SHIFT)
+#define CFG_CPN_TE_EDGE_SHIFT 4
+#define CFG_CPN_TE_EDGE_MASK (1 << CFG_CPN_TE_EDGE_SHIFT)
+#define CFG_CPN_TE_MODE_SHIFT 2
+#define CFG_CPN_TE_MODE_MASK (3 << CFG_CPN_TE_MODE_SHIFT)
+#define CFG_PIXEL_SWAP BIT(1)
+#define CFG_SPLIT_EN BIT(0)
+
+//DSI_LCD_BDG_CTRL1 0x88
+#define CFG_CPN_TE_DLY_CNT_SHIFT 16
+#define CFG_CPN_TE_LINE_CNT_SHIFT 0
+
+//DSI_VPN_CTRL_1 0x104
+#define CFG_VPN_VSYNC_RST_EN_SHIFT 31
+#define CFG_VPN_AUTO_WC_DIS_SHIFT 27
+#define CFG_VPN_HACT_WC_EN_SHIFT 26
+#define CFG_VPN_TIMING_CHECK_DIS_SHIFT 25
+#define CFG_VPN_AUTO_DLY_DIS_SHIFT 24
+#define CFG_VPN_HLP_PKT_EN_SHIFT 22
+#define CFG_VPN_HEX_PKT_EN_SHIFT 21
+#define CFG_VPN_HFP_PKT_EN_SHIFT 20
+#define CFG_VPN_HBP_PKT_EN_SHIFT 18
+#define CFG_VPN_HSE_PKT_EN_SHIFT 17
+#define CFG_VPN_HSA_PKT_EN_SHIFT 16
+#define CFG_VPN_HEX_SLOT_EN_SHIFT 14
+#define CFG_VPN_LAST_LINE_TURN_SHIFT 10
+#define CFG_VPN_LPM_FRAME_EN_SHIFT 9
+#define CFG_VPN_BURST_MODE_SHIFT 2
+#define CFG_VPN_BURST_MODE_MASK (0x3 << CFG_VPN_BURST_MODE_SHIFT)
+#define CFG_VPN_RGB_TYPE_SHIFT 0
+#define CFG_VPN_RGB_TYPE_MASK (0x3 << CFG_VPN_RGB_TYPE_SHIFT)
+
+//DSI_PHY_CTRL_1 0x184
+#define CFG_DPHY_ADD_VALID BIT(17)
+#define CFG_DPHY_VDD_VALID BIT(16)
+#define CFG_DPHY_ULPS_DATA BIT(2)
+#define CFG_DPHY_ULPS_CLK BIT(1)
+#define CFG_DPHY_CONT_CLK BIT(0)
+
+//DSI_PHY_CTRL_2 0x188
+#define CFG_DPHY_HSTX_RX BIT(14)
+#define CFG_DPHY_LANE_MAP_SHIFT 12
+#define CFG_DPHY_LANE_EN_SHIFT 4
+#define CFG_DPHY_FORCE_BTA BIT(0)
+
+#define CFG_DPHY_LANE_MAP_MASK (0x3 << CFG_DPHY_LANE_MAP_SHIFT)
+#define CFG_DPHY_LANE_EN_MASK (0xF << CFG_DPHY_LANE_EN_SHIFT)
+
+//DSI_PHY_TIME_0 0x1C0
+#define CFG_DPHY_TIME_HS_EXIT_SHIFT 24
+#define CFG_DPHY_TIME_HS_TRAIL_SHIFT 16
+#define CFG_DPHY_TIME_HS_ZERO_SHIFT 8
+#define CFG_DPHY_TIME_HS_PREP_SHIFT 0
+
+#define CFG_DPHY_TIME_HS_EXIT_MASK (0xFF << CFG_DPHY_TIME_HS_EXIT_SHIFT)
+#define CFG_DPHY_TIME_HS_TRAIL_MASK (0xFF << CFG_DPHY_TIME_HS_TRAIL_SHIFT)
+#define CFG_DPHY_TIME_HS_ZERO_MASK (0xFF << CFG_DPHY_TIME_HS_ZERO_SHIFT)
+#define CFG_DPHY_TIME_HS_PREP_MASK (0xFF << CFG_DPHY_TIME_HS_PREP_SHIFT)
+
+//DSI_PHY_TIME_1 0x1C4
+#define CFG_DPHY_TIME_TA_GET_SHIFT 24
+#define CFG_DPHY_TIME_TA_GO_SHIFT 16
+#define CFG_DPHY_TIME_WAKEUP_SHIFT 0
+
+#define CFG_DPHY_TIME_TA_GET_MASK (0xFF << CFG_DPHY_TIME_TA_GET_SHIFT)
+#define CFG_DPHY_TIME_TA_GO_MASK (0xFF << CFG_DPHY_TIME_TA_GO_SHIFT)
+#define CFG_DPHY_TIME_WAKEUP_MASK (0xFFFF << CFG_DPHY_TIME_WAKEUP_SHIFT)
+
+//DSI_PHY_TIME_2 0x1C8
+#define CFG_DPHY_TIME_CLK_EXIT_SHIFT 24
+#define CFG_DPHY_TIME_CLK_TRAIL_SHIFT 16
+#define CFG_DPHY_TIME_CLK_ZERO_SHIFT 8
+#define CFG_DPHY_TIME_CLK_LPX_SHIFT 0
+
+#define CFG_DPHY_TIME_CLK_EXIT_MASK (0xFF << CFG_DPHY_TIME_CLK_EXIT_SHIFT)
+#define CFG_DPHY_TIME_CLK_TRAIL_MASK (0xFF << CFG_DPHY_TIME_CLK_TRAIL_SHIFT)
+#define CFG_DPHY_TIME_CLK_ZERO_MASK (0xFF << CFG_DPHY_TIME_CLK_ZERO_SHIFT)
+#define CFG_DPHY_TIME_CLK_LPX_MASK (0xFF << CFG_DPHY_TIME_CLK_LPX_SHIFT)
+
+//DSI_PHY_TIME_3 0x1CC
+#define CFG_DPHY_TIME_LPX_SHIFT 8
+#define CFG_DPHY_TIME_REQRDY_SHIFT 0
+
+#define CFG_DPHY_TIME_LPX_MASK (0xFF << CFG_DPHY_TIME_LPX_SHIFT)
+#define CFG_DPHY_TIME_REQRDY_MASK (0xFF << CFG_DPHY_TIME_REQRDY_SHIFT)
+
+//DSI_PHY_ANA_PWR_CTRL 0x1E0
+#define CFG_DPHY_ANA_RESET BIT(8)
+#define CFG_DPHY_ANA_PU BIT(0)
+
+//DSI_PHY_ANA_CTRL1 0x1E8
+#ifdef CONFIG_SPACEMIT_FPGA
+#define CFG_CLK_SEL BIT(23)
+#else
+#define CFG_CLK_SEL BIT(21)
+#endif
+#define CFG_CLK_DIV2 BIT(11)
+
+
+/*dphy timming*/
+#define HS_PREP_CONSTANT_DEFAULT 40
+#define HS_PREP_UI_DEFAULT 4
+#define HS_ZERO_CONSTANT_DEFAULT 145
+#define HS_ZERO_UI_DEFAULT 10
+#define HS_TRAIL_CONSTANT_DEFAULT 60
+#define HS_TRAIL_UI_DEFAULT 4
+#define HS_EXIT_CONSTANT_DEFAULT 100
+#define HS_EXIT_UI_DEFAULT 0
+#define CK_ZERO_CONSTANT_DEFAULT 300
+#define CK_ZERO_UI_DEFAULT 0
+#define CK_TRAIL_CONSTANT_DEFAULT 60
+#define CK_TRAIL_UI_DEFAULT 0
+#define REQ_READY_DEFAULT 0x3C
+#define WAKEUP_CONSTANT_DEFAULT 1000000
+#define WAKEUP_UI_DEFAULT 0
+#define LPX_CONSTANT_DEFAULT 60
+#define LPX_UI_DEFAULT 0
+
+
+static inline uint32_t dsi_read(void __iomem *addr, uint32_t offset)
+{
+       DRM_DEBUG("DSI_READ [0x%x] = 0x%x\n", offset, __raw_readl(addr + offset));
+       return __raw_readl(addr + offset);
+}
+
+static inline void dsi_write(void __iomem *addr, uint32_t offset, uint32_t data)
+{
+       DRM_DEBUG("DSI_WRITE [0x%x] = 0x%x\n", 0xd421a800 + offset, data);
+       __raw_writel(data, (addr + offset));
+}
+
+static inline void dsi_set_bits(void __iomem *addr, uint32_t offset, uint32_t bits)
+{
+       dsi_write(addr, offset, (dsi_read(addr, offset) | bits));
+}
+
+static inline void dsi_clear_bits(void __iomem *addr, uint32_t offset, uint32_t bits)
+{
+       dsi_write(addr, offset, (dsi_read(addr, offset) & ~bits));
+}
+
+static inline void dsi_write_bits(void __iomem *addr, uint32_t offset, uint32_t mask, uint32_t value)
+{
+       uint32_t tmp = 0;
+
+       tmp = dsi_read(addr, offset);
+       tmp &= ~mask;
+       tmp |= value;
+       dsi_write(addr, offset, tmp);
+}
+
+#endif /*_SPACEMIT_DSI_HW_H_*/
diff --git a/drivers/gpu/drm/spacemit/lt8911exb.c b/drivers/gpu/drm/spacemit/lt8911exb.c
new file mode 100644 (file)
index 0000000..6065698
--- /dev/null
@@ -0,0 +1,1516 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/atomic.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_bridge.h>
+#include <linux/delay.h>
+#include <drm/display/drm_dp_aux_bus.h>
+#include <drm/display/drm_dp_helper.h>
+
+#define IT8911_DSI_DRIVER_NAME "spacemit-edp-drv"
+#define EDID_SEG_SIZE  256
+#define EDID_LEN       16
+
+#define MIPI_DSI_1920x1080  1
+#define MIPI_DSI_1920x1200  0
+
+// #define _test_pattern_
+// #define _read_edid_
+// #define _uart_debug_
+#define _eDP_2G7_
+#define _link_train_enable_
+
+#define _MIPI_Lane_ 4  // 4 3 2 1
+#define _MIPI_data_PN_Swap_En  0xF0
+#define _MIPI_data_PN_Swap_Dis 0x00
+#define _MIPI_data_PN_ _MIPI_data_PN_Swap_Dis
+#define _Nvid 0        // default 0: 0x0080
+static int Nvid_Val[] = {0x0080, 0x0800};
+
+#define _No_swap_              0x00    // default
+#define _MIPI_data_3210_       0       // default
+#define _MIPI_data_0123_       21
+#define _MIPI_data_2103_       20
+#define _MIPI_data_sequence_ _No_swap_
+
+#define eDP_lane       2
+#define PCR_PLL_PREDIV 0x40
+
+#define LT8911_LOW 1
+#define LT8911_HIGH 0
+
+static int MIPI_Timing[] =
+// hfp,        hs,     hbp,    hact,   htotal, vfp,    vs,     vbp,    vact,   vtotal, pixel_CLK/10000
+
+// 1920x1080
+#if MIPI_DSI_1920x1080
+// {48, 32, 200, 1920, 2200, 3, 6, 31, 1080, 1120, 14784};     // boe config for linux
+{48, 32, 200, 1920, 2200, 3, 6, 31, 1080, 1120, 14285};     // boe config for linux
+#endif
+
+// 1920x1200
+#if MIPI_DSI_1920x1200
+// {16, 16, 298, 1920, 2250, 3, 14, 19, 1200, 1236, 16684};     // boe config for linux
+{16, 16, 298, 1920, 2250, 3, 14, 19, 1200, 1236, 15360};     // boe config for linux
+// {16, 16, 298, 1920, 2250, 3, 14, 19, 1200, 1236, 14285};     // boe config for linux
+#endif
+
+#define _8bit_
+
+enum {
+       hfp = 0,
+       hs,
+       hbp,
+       hact,
+       htotal,
+       vfp,
+       vs,
+       vbp,
+       vact,
+       vtotal,
+       pclk_10khz
+};
+
+u32 EDID_DATA[128] = { 0 };
+u32 EDID_Timing[11] = { 0 };
+bool EDID_Reply = 0;
+
+bool   ScrambleMode = 0;
+
+static const struct drm_display_mode lt8911exb_panel_modes[] = {
+// 1920x1080
+#if MIPI_DSI_1920x1080
+       {
+               .clock = 142857143 / 1000,
+               .hdisplay = 1920,
+               .hsync_start = 1920 + 48,
+               .hsync_end = 1920 + 48 + 200,
+               .htotal = 1920 + 48 + 200 + 32,
+               .vdisplay = 1080,
+               .vsync_start = 1080 + 3,
+               .vsync_end = 1080 + 3 + 31,
+               .vtotal = 1080 + 3 + 31 + 6,
+       },
+#endif
+
+// 1920x1200
+#if MIPI_DSI_1920x1200
+
+       {
+               .clock = 142857143 / 1000,
+               .hdisplay = 1920,
+               .hsync_start = 1920 + 16,
+               .hsync_end = 1920 + 16 + 298,
+               .htotal = 1920 + 16 + 298 + 16,
+               .vdisplay = 1200,
+               .vsync_start = 1200 + 3,
+               .vsync_end = 1200 + 3 + 19,
+               .vtotal = 1200 + 3 + 19 + 14,
+       },
+#endif
+
+};
+
+enum
+{
+       _Level0_ = 0,   // 27.8 mA      0x83/0x00
+       _Level1_,       // 26.2 mA      0x82/0xe0
+       _Level2_,       // 24.6 mA      0x82/0xc0
+       _Level3_,       // 23 mA        0x82/0xa0
+       _Level4_,       // 21.4 mA      0x82/0x80
+       _Level5_,       // 18.2 mA      0x82/0x40
+       _Level6_,       // 16.6 mA      0x82/0x20
+       _Level7_,       // 15 mA        0x82/0x00       // level 1
+       _Level8_,       // 12.8mA       0x81/0x00       // level 2
+       _Level9_,       // 11.2mA       0x80/0xe0       // level 3
+       _Level10_,      // 9.6mA        0x80/0xc0       // level 4
+       _Level11_,      // 8mA          0x80/0xa0       // level 5
+       _Level12_,      // 6mA          0x80/0x80       // level 6
+};
+
+u8     Swing_Setting1[] = {0x83, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80};
+u8     Swing_Setting2[] = {0x00, 0xe0, 0xc0, 0xa0, 0x80, 0x40, 0x20, 0x00, 0x00, 0xe0, 0xc0, 0xa0, 0x80};
+
+u8     Level = _Level7_;       // normal
+
+struct lt8911exb {
+       struct device *dev;
+       struct drm_bridge bridge;
+       struct drm_connector connector;
+
+       struct regmap *regmap;
+
+       struct device_node *dsi0_node;
+       struct device_node *dsi1_node;
+       struct mipi_dsi_device *dsi0;
+       struct mipi_dsi_device *dsi1;
+
+       struct gpio_desc *reset_gpio;   //reset
+       struct gpio_desc *enable_gpio;  //power
+       struct gpio_desc *standby_gpio; //standby
+       struct gpio_desc *bl_gpio;      //backlight
+
+       bool power_on;
+       bool sleep;
+
+       struct regulator_bulk_data supplies[2];
+
+       struct i2c_client *client;
+       struct drm_panel base;
+       struct mipi_dsi_device *dsi;
+
+       enum drm_connector_status status;
+
+       u8 edid_buf[EDID_SEG_SIZE];
+       u32 vic;
+
+       struct delayed_work init_work;
+       bool init_work_pending;
+};
+
+static const struct regmap_config lt8911exb_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0xff,
+};
+
+static struct lt8911exb *panel_to_lt8911exb(struct drm_panel *panel)
+{
+       return container_of(panel, struct lt8911exb, base);
+}
+
+void lt8911exb_mipi_video_timing(struct lt8911exb *lt8911exb)
+{
+       __maybe_unused unsigned int tmp;
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xd0);
+       regmap_write(lt8911exb->regmap, 0x0d, (MIPI_Timing[vtotal] / 256));
+       regmap_write(lt8911exb->regmap, 0x0e, (MIPI_Timing[vtotal] % 256));     //vtotal
+       regmap_write(lt8911exb->regmap, 0x0f, (MIPI_Timing[vact] / 256));
+       regmap_write(lt8911exb->regmap, 0x10, (MIPI_Timing[vact] % 256));       //vactive
+       regmap_write(lt8911exb->regmap, 0x11, (MIPI_Timing[htotal] / 256));
+       regmap_write(lt8911exb->regmap, 0x12, (MIPI_Timing[htotal] % 256));     //htotal
+       regmap_write(lt8911exb->regmap, 0x13, (MIPI_Timing[hact] / 256));
+       regmap_write(lt8911exb->regmap, 0x14, (MIPI_Timing[hact] % 256));       //hactive
+       regmap_write(lt8911exb->regmap, 0x15, (MIPI_Timing[vs] % 256));         //vsa
+       regmap_write(lt8911exb->regmap, 0x16, (MIPI_Timing[hs] % 256));         //hsa
+       regmap_write(lt8911exb->regmap, 0x17, (MIPI_Timing[vfp] / 256));
+       regmap_write(lt8911exb->regmap, 0x18, (MIPI_Timing[vfp] % 256));        //vfp
+       regmap_write(lt8911exb->regmap, 0x19, (MIPI_Timing[hfp] / 256));
+       regmap_write(lt8911exb->regmap, 0x1a, (MIPI_Timing[hfp] % 256));        //hfp
+
+#ifdef _uart_debug_
+       DRM_INFO("------\n");
+       DRM_INFO("MIPI_Timing[vtotal] / 256 = %d\n", MIPI_Timing[vtotal] / 256);
+       DRM_INFO("MIPI_Timing[vtotal]  256 = %d\n", MIPI_Timing[vtotal] % 256);
+       DRM_INFO("MIPI_Timing[vact] / 256 = %d\n", MIPI_Timing[vact] / 256);
+       DRM_INFO("MIPI_Timing[vact]  256 = %d\n", MIPI_Timing[vact] % 256);
+       DRM_INFO("MIPI_Timing[htotal] / 256 = %d\n", MIPI_Timing[htotal] / 256);
+       DRM_INFO("MIPI_Timing[htotal]  256 = %d\n", MIPI_Timing[htotal] % 256);
+       DRM_INFO("MIPI_Timing[hact] / 256 = %d\n", MIPI_Timing[hact] / 256);
+       DRM_INFO("MIPI_Timing[hact]  256 = %d\n", MIPI_Timing[hact] % 256);
+
+       DRM_INFO("MIPI_Timing[vs]  256 = %d\n", MIPI_Timing[vs] % 256);
+       DRM_INFO("MIPI_Timing[hs]  256 = %d\n", MIPI_Timing[hs] % 256);
+
+       DRM_INFO("MIPI_Timing[vfp] / 256 = %d\n", MIPI_Timing[vfp] / 256);
+       DRM_INFO("MIPI_Timing[vfp]  256 = %d\n", MIPI_Timing[vfp] % 256);
+       DRM_INFO("MIPI_Timing[hfp] / 256 = %d\n", MIPI_Timing[hfp] / 256);
+       DRM_INFO("MIPI_Timing[hfp]  256 = %d\n", MIPI_Timing[hfp] % 256);
+       DRM_INFO("------\n");
+
+       regmap_read(lt8911exb->regmap, 0x0d, &tmp);
+       DRM_DEBUG_ATOMIC("0x0d = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x0e, &tmp);
+       DRM_DEBUG_ATOMIC("0x0e = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x0f, &tmp);
+       DRM_DEBUG_ATOMIC("0x0f = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x10, &tmp);
+       DRM_DEBUG_ATOMIC("0x10 = %d\n",tmp);
+
+       regmap_read(lt8911exb->regmap, 0x11, &tmp);
+       DRM_DEBUG_ATOMIC("0x11 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x12, &tmp);
+       DRM_DEBUG_ATOMIC("0x12 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x13, &tmp);
+       DRM_DEBUG_ATOMIC("0x13 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x14, &tmp);
+       DRM_DEBUG_ATOMIC("0x14 = %d\n",tmp);
+
+
+       regmap_read(lt8911exb->regmap, 0x15, &tmp);
+       DRM_DEBUG_ATOMIC("0x15 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x16, &tmp);
+       DRM_DEBUG_ATOMIC("0x16 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x17, &tmp);
+       DRM_DEBUG_ATOMIC("0x17 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x18, &tmp);
+       DRM_DEBUG_ATOMIC("0x18 = %d\n",tmp);
+
+       regmap_read(lt8911exb->regmap, 0x19, &tmp);
+       DRM_DEBUG_ATOMIC("0x19 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x1a, &tmp);
+       DRM_DEBUG_ATOMIC("0x1a = %d\n",tmp);
+#endif
+}
+
+void lt8911exb_edp_video_cfg(struct lt8911exb *lt8911exb)
+{
+       __maybe_unused unsigned int tmp;
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xa8);
+       regmap_write(lt8911exb->regmap, 0x2d, 0x88);    // MSA from register
+       regmap_write(lt8911exb->regmap, 0x05, (MIPI_Timing[htotal] / 256));
+       regmap_write(lt8911exb->regmap, 0x06, (MIPI_Timing[htotal] % 256));
+       regmap_write(lt8911exb->regmap, 0x07, ((MIPI_Timing[hs] + MIPI_Timing[hbp]) / 256 ));
+       regmap_write(lt8911exb->regmap, 0x08, ((MIPI_Timing[hs] + MIPI_Timing[hbp]) % 256));
+       regmap_write(lt8911exb->regmap, 0x09, (MIPI_Timing[hs] / 256));
+       regmap_write(lt8911exb->regmap, 0x0a, (MIPI_Timing[hs] % 256));
+       regmap_write(lt8911exb->regmap, 0x0b, (MIPI_Timing[hact] / 256));
+       regmap_write(lt8911exb->regmap, 0x0c, (MIPI_Timing[hact] % 256));
+       regmap_write(lt8911exb->regmap, 0x0d, (MIPI_Timing[vtotal] / 256));
+       regmap_write(lt8911exb->regmap, 0x0e, (MIPI_Timing[vtotal] % 256));
+       regmap_write(lt8911exb->regmap, 0x11, ((MIPI_Timing[vs] + MIPI_Timing[vbp]) / 256));
+       regmap_write(lt8911exb->regmap, 0x12, ((MIPI_Timing[vs] + MIPI_Timing[vbp]) % 256));
+       regmap_write(lt8911exb->regmap, 0x14, (MIPI_Timing[vs] % 256));
+       regmap_write(lt8911exb->regmap, 0x15, (MIPI_Timing[vact] / 256));
+       regmap_write(lt8911exb->regmap, 0x16, (MIPI_Timing[vact] % 256));
+
+#ifdef _uart_debug_
+       DRM_INFO("------\n");
+       DRM_INFO("(u8)( MIPI_Timing[htotal] / 256 ) = %d\n", (MIPI_Timing[htotal] / 256));
+       DRM_INFO("(u8)( MIPI_Timing[htotal]  256 ) = %d\n", (MIPI_Timing[htotal] % 256));
+       DRM_INFO("(u8)( ( MIPI_Timing[hs] + MIPI_Timing[hbp] ) / 256 )  = %d\n", ((MIPI_Timing[hs] + MIPI_Timing[hbp]) / 256));
+       DRM_INFO("(u8)( ( MIPI_Timing[hs] + MIPI_Timing[hbp] )  256 ) = %d\n", ((MIPI_Timing[hs] + MIPI_Timing[hbp]) % 256));
+       DRM_INFO("(u8)( MIPI_Timing[hs] / 256 ) = %d\n", (MIPI_Timing[hs] / 256));
+       DRM_INFO(" (u8)( MIPI_Timing[hs]  256 ) = %d\n", (MIPI_Timing[hs] % 256));
+       DRM_INFO(" (u8)( MIPI_Timing[hact] / 256 )  = %d\n", (MIPI_Timing[hact] / 256));
+       DRM_INFO("(u8)( MIPI_Timing[hact]  256 ) = %d\n", (MIPI_Timing[hact] % 256));
+
+       DRM_INFO("(u8)( ( MIPI_Timing[vs] + MIPI_Timing[vbp] ) / 256 ) = %d\n", ((MIPI_Timing[vs] + MIPI_Timing[vbp]) / 256));
+       DRM_INFO(" (u8)( ( MIPI_Timing[vs] + MIPI_Timing[vbp] )  256 ) = %d\n", ((MIPI_Timing[vs] + MIPI_Timing[vbp]) % 256));
+
+       DRM_INFO(" (u8)( MIPI_Timing[vs]  256 ) = %d\n", (MIPI_Timing[vs] % 256));
+       DRM_INFO(" (u8)( MIPI_Timing[vact] / 256 )  = %d\n", (MIPI_Timing[vact] / 256));
+       DRM_INFO("(u8)( MIPI_Timing[vact]  256 ) = %d\n", (MIPI_Timing[vact] % 256));
+       DRM_INFO("------\n");
+
+       regmap_read(lt8911exb->regmap, 0x05, &tmp);
+       DRM_DEBUG_ATOMIC("0x05 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x06, &tmp);
+       DRM_DEBUG_ATOMIC("0x06 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x07, &tmp);
+       DRM_DEBUG_ATOMIC("0x07 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x08, &tmp);
+       DRM_DEBUG_ATOMIC("0x08 = %d\n",tmp);
+
+       regmap_read(lt8911exb->regmap, 0x09, &tmp);
+       DRM_DEBUG_ATOMIC("0x09 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x0a, &tmp);
+       DRM_DEBUG_ATOMIC("0x0a = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x0b, &tmp);
+       DRM_DEBUG_ATOMIC("0x0b = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x0c, &tmp);
+       DRM_DEBUG_ATOMIC("0x0c = %d\n",tmp);
+
+
+       regmap_read(lt8911exb->regmap, 0x0d, &tmp);
+       DRM_DEBUG_ATOMIC("0x0d = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x0e, &tmp);
+       DRM_DEBUG_ATOMIC("0x0e = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x11, &tmp);
+       DRM_DEBUG_ATOMIC("0x11 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x12, &tmp);
+       DRM_DEBUG_ATOMIC("0x12 = %d\n",tmp);
+
+       regmap_read(lt8911exb->regmap, 0x14, &tmp);
+       DRM_DEBUG_ATOMIC("0x14 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x15, &tmp);
+       DRM_DEBUG_ATOMIC("0x15 = %d\n",tmp);
+       regmap_read(lt8911exb->regmap, 0x16, &tmp);
+       DRM_DEBUG_ATOMIC("0x16 = %d\n",tmp);
+#endif
+}
+
+void lt8911exb_read_edid(struct lt8911exb *lt8911exb)
+{
+#ifdef _read_edid_
+       u8 i, j;
+       unsigned int reg;
+
+       DRM_INFO("%s()\n", __func__);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xac );
+       regmap_write(lt8911exb->regmap, 0x00, 0x20 ); //Soft Link train
+       regmap_write(lt8911exb->regmap, 0xff, 0xa6 );
+       regmap_write(lt8911exb->regmap, 0x2a, 0x01 );
+
+       /*set edid offset addr*/
+       regmap_write(lt8911exb->regmap, 0x2b, 0x40 ); //CMD
+       regmap_write(lt8911exb->regmap, 0x2b, 0x00 ); //addr[15:8]
+       regmap_write(lt8911exb->regmap, 0x2b, 0x50 ); //addr[7:0]
+       regmap_write(lt8911exb->regmap, 0x2b, 0x00 ); //data lenth
+       regmap_write(lt8911exb->regmap, 0x2b, 0x00 ); //data lenth
+       regmap_write(lt8911exb->regmap, 0x2c, 0x00 ); //start Aux read edid
+
+       mdelay(20);                         //more than 10ms
+       regmap_read(lt8911exb->regmap, 0x25, &reg);
+       DRM_INFO("lt8911exb_read_edid 0x25 0x%x\n", reg);
+
+       if( ( reg & 0x0f ) == 0x0c )
+       {
+               for( j = 0; j < 8; j++ )
+               {
+                       if( j == 7 )
+                       {
+                               regmap_write(lt8911exb->regmap, 0x2b, 0x10 ); //MOT
+                       }else
+                       {
+                               regmap_write(lt8911exb->regmap, 0x2b, 0x50 );
+                       }
+
+                       regmap_write(lt8911exb->regmap, 0x2b, 0x00 );
+                       regmap_write(lt8911exb->regmap, 0x2b, 0x50 );
+                       regmap_write(lt8911exb->regmap, 0x2b, 0x0f );
+                       regmap_write(lt8911exb->regmap, 0x2c, 0x00 ); //start Aux read edid
+                       mdelay(50);                                   //more than 50ms
+
+                       regmap_read(lt8911exb->regmap, 0x39, &reg);
+                       DRM_INFO("lt8911exb_read_edid 0x39 0x%x\n", reg);
+                       if (reg == 0x31)
+                       {
+                               regmap_read(lt8911exb->regmap, 0x2b, &reg);
+                               DRM_INFO("lt8911exb_read_edid 0x2b 0x%x\n", reg);
+                               for( i = 0; i < 16; i++ )
+                               {
+                                       regmap_read(lt8911exb->regmap, 0x2b, &reg);
+                                       EDID_DATA[j * 16 + i] = reg;
+                               }
+
+                               EDID_Reply = 1;
+                       }else
+                       {
+                               EDID_Reply = 0;
+                               return;
+                       }
+               }
+
+               // for( i = 0; i < 128; i++ ) //print edid data
+               // {
+               //      if( ( i % 16 ) == 0 )
+               //      {
+               //              DRM_INFO( "\n" );
+               //      }
+               //      DRM_INFO( "%x", EDID_DATA[i] );
+               // }
+
+
+               EDID_Timing[hfp] = (EDID_DATA[0x41] & 0xC0) * 4 + EDID_DATA[0x3e];
+               EDID_Timing[hs] = (EDID_DATA[0x41] & 0x30) * 16 + EDID_DATA[0x3f];
+               EDID_Timing[hbp] = ((EDID_DATA[0x3a] & 0x0f) * 0x100 + EDID_DATA[0x39]) - ((EDID_DATA[0x41] & 0x30) * 16 + EDID_DATA[0x3f]) - ((EDID_DATA[0x41] & 0xC0 ) * 4 + EDID_DATA[0x3e]);
+               EDID_Timing[hact] = (EDID_DATA[0x3a] & 0xf0) * 16 + EDID_DATA[0x38];
+               EDID_Timing[htotal] = (EDID_DATA[0x3a] & 0xf0) * 16 + EDID_DATA[0x38] + ((EDID_DATA[0x3a] & 0x0f) * 0x100 + EDID_DATA[0x39]);
+               EDID_Timing[vfp] = (EDID_DATA[0x41] & 0x0c) * 4 + (EDID_DATA[0x40] & 0xf0 ) / 16;
+               EDID_Timing[vs] = (EDID_DATA[0x41] & 0x03) * 16 + (EDID_DATA[0x40] & 0x0f );
+               EDID_Timing[vbp] = ((EDID_DATA[0x3d] & 0x03) * 0x100 + EDID_DATA[0x3c]) - ((EDID_DATA[0x41] & 0x03) * 16 + (EDID_DATA[0x40] & 0x0f)) - ((EDID_DATA[0x41] & 0x0c) * 4 + (EDID_DATA[0x40] & 0xf0 ) / 16);
+               EDID_Timing[vact] = (EDID_DATA[0x3d] & 0xf0) * 16 + EDID_DATA[0x3b];
+               EDID_Timing[vtotal] = (EDID_DATA[0x3d] & 0xf0 ) * 16 + EDID_DATA[0x3b] + ((EDID_DATA[0x3d] & 0x03) * 0x100 + EDID_DATA[0x3c]);
+               EDID_Timing[pclk_10khz] = EDID_DATA[0x37] * 0x100 + EDID_DATA[0x36];
+               DRM_INFO( "eDP Timing = { H_FP / H_pluse / H_BP / H_act / H_tol / V_FP / V_pluse / V_BP / V_act / V_tol / D_CLK };" );
+               DRM_INFO( "eDP Timing = { %d       %d       %d     %d     %d     %d      %d       %d     %d    %d    %d };\n",
+                               (u32)EDID_Timing[hfp],(u32)EDID_Timing[hs],(u32)EDID_Timing[hbp],(u32)EDID_Timing[hact],(u32)EDID_Timing[htotal],
+                               (u32)EDID_Timing[vfp],(u32)EDID_Timing[vs],(u32)EDID_Timing[vbp],(u32)EDID_Timing[vact],(u32)EDID_Timing[vtotal],(u32)EDID_Timing[pclk_10khz]);
+       }
+
+       return;
+
+#endif
+}
+
+void lt8911exb_setup(struct lt8911exb *lt8911exb)
+{
+       u8      i;
+       u8      pcr_pll_postdiv;
+       u8      pcr_m;
+       u16 Temp16;
+       u32 chip_read = 0x00;
+
+       DRM_DEBUG("\r\n lt8911exb_setup");
+
+       /* init */
+       regmap_write(lt8911exb->regmap, 0xff, 0x81); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x08, 0x7f); // i2c over aux issue
+       regmap_write(lt8911exb->regmap, 0x49, 0xff); // enable 0x87xx
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x82); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x5a, 0x0e); // GPIO test output
+
+       //for power consumption//
+       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+       regmap_write(lt8911exb->regmap, 0x05, 0x06);
+       regmap_write(lt8911exb->regmap, 0x43, 0x00);
+       regmap_write(lt8911exb->regmap, 0x44, 0x1f);
+       regmap_write(lt8911exb->regmap, 0x45, 0xf7);
+       regmap_write(lt8911exb->regmap, 0x46, 0xf6);
+       regmap_write(lt8911exb->regmap, 0x49, 0x7f);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x82);
+#if (eDP_lane == 2)
+       {
+               regmap_write(lt8911exb->regmap, 0x12, 0x33);
+       }
+#elif (eDP_lane == 1)
+       {
+               regmap_write(lt8911exb->regmap, 0x12, 0x11);
+       }
+#endif
+
+       /* mipi Rx analog */
+       regmap_write(lt8911exb->regmap, 0xff, 0x82); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x32, 0x51);
+       regmap_write(lt8911exb->regmap, 0x35, 0x22); //EQ current 0x22/0x42/0x62/0x82/0xA2/0xC2/0xe2
+       regmap_write(lt8911exb->regmap, 0x3a, 0x77); //EQ 12.5db
+       regmap_write(lt8911exb->regmap, 0x3b, 0x77); //EQ 12.5db
+
+       regmap_write(lt8911exb->regmap, 0x4c, 0x0c);
+       regmap_write(lt8911exb->regmap, 0x4d, 0x00);
+
+       /* dessc_pcr  pll analog */
+       regmap_write(lt8911exb->regmap, 0xff, 0x82); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x6a, 0x40);
+       regmap_write(lt8911exb->regmap, 0x6b, PCR_PLL_PREDIV);
+
+       Temp16 = MIPI_Timing[pclk_10khz];
+
+       if (MIPI_Timing[pclk_10khz] < 8800) {
+               regmap_write(lt8911exb->regmap, 0x6e, 0x82); //0x44:pre-div = 2 ,pixel_clk=44~ 88MHz
+               pcr_pll_postdiv = 0x08;
+       } else if (MIPI_Timing[pclk_10khz] < 17600){
+               regmap_write(lt8911exb->regmap, 0x6e, 0x81); //0x40:pre-div = 1, pixel_clk =88~176MHz
+               pcr_pll_postdiv = 0x04;
+       } else {
+               regmap_write(lt8911exb->regmap, 0x6e, 0x80); //0x40:pre-div = 0, pixel_clk =176~200MHz
+               pcr_pll_postdiv = 0x02;
+       }
+
+       pcr_m = (u8)(Temp16 * pcr_pll_postdiv / 25 / 100);
+
+       /* dessc pll digital */
+       regmap_write(lt8911exb->regmap, 0xff, 0x85);    // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0xa9, 0x31);
+       regmap_write(lt8911exb->regmap, 0xaa, 0x17);
+       regmap_write(lt8911exb->regmap, 0xab, 0xba);
+       regmap_write(lt8911exb->regmap, 0xac, 0xe1);
+       regmap_write(lt8911exb->regmap, 0xad, 0x47);
+       regmap_write(lt8911exb->regmap, 0xae, 0x01);
+       regmap_write(lt8911exb->regmap, 0xae, 0x11);
+
+       /* Digital Top */
+       regmap_write(lt8911exb->regmap, 0xff, 0x85);    // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0xc0, 0x01);    //select mipi Rx
+#ifdef _6bit_
+       regmap_write(lt8911exb->regmap, 0xb0, 0xd0);    //enable dither
+#else
+       regmap_write(lt8911exb->regmap, 0xb0, 0x00);    // disable dither
+#endif
+
+       /* mipi Rx Digital */
+       regmap_write(lt8911exb->regmap, 0xff, 0xd0);    // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x00, _MIPI_data_PN_ + _MIPI_Lane_ % 4); // 0: 4 Lane / 1: 1 Lane / 2 : 2 Lane / 3: 3 Lane
+       regmap_write(lt8911exb->regmap, 0x02, 0x08);    //settle
+       regmap_write(lt8911exb->regmap, 0x03, _MIPI_data_sequence_);    // default is 0x00
+       regmap_write(lt8911exb->regmap, 0x08, 0x00);
+//     regmap_write(lt8911exb->regmap, 0x0a, 0x12);    //pcr mode
+
+       regmap_write(lt8911exb->regmap, 0x0c, 0x80);    //fifo position
+       regmap_write(lt8911exb->regmap, 0x1c, 0x80);    //fifo position
+
+       //      hs mode:MIPI行采样;vs mode:MIPI帧采样
+       regmap_write(lt8911exb->regmap, 0x24, 0x70);    // 0x30  [3:0]  line limit        //pcr mode( de hs vs)
+
+       regmap_write(lt8911exb->regmap, 0x31, 0x0a);
+
+       /*stage1 hs mode*/
+       regmap_write(lt8911exb->regmap, 0x25, 0x90);    // 0x80 // line limit
+       regmap_write(lt8911exb->regmap, 0x2a, 0x3a);    // 0x04 // step in limit
+       regmap_write(lt8911exb->regmap, 0x21, 0x4f);    // hs_step
+       regmap_write(lt8911exb->regmap, 0x22, 0xff);
+
+       /*stage2 de mode*/
+       regmap_write(lt8911exb->regmap, 0x0a, 0x02);    //de adjust pre line
+       regmap_write(lt8911exb->regmap, 0x38, 0x02);    //de_threshold 1
+       regmap_write(lt8911exb->regmap, 0x39, 0x04);    //de_threshold 2
+       regmap_write(lt8911exb->regmap, 0x3a, 0x08);    //de_threshold 3
+       regmap_write(lt8911exb->regmap, 0x3b, 0x10);    //de_threshold 4
+
+       regmap_write(lt8911exb->regmap, 0x3f, 0x04);    //de_step 1
+       regmap_write(lt8911exb->regmap, 0x40, 0x08);    //de_step 2
+       regmap_write(lt8911exb->regmap, 0x41, 0x10);    //de_step 3
+       regmap_write(lt8911exb->regmap, 0x42, 0x60);    //de_step 4
+
+       /*stage2 hs mode*/
+       // regmap_write(lt8911exb->regmap, 0x1e, 0x0A); //regmap_write(lt8911exb->regmap, 0x1e, 0x01 );                             // 0x11
+       DRM_DEBUG("\r\n lt8911exb_setup 0X1e 0x0a");
+       regmap_write(lt8911exb->regmap, 0x1e, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x23, 0xf0);    // 0x80
+
+       regmap_write(lt8911exb->regmap, 0x2b, 0x80);    // 0xa0
+
+#ifdef _test_pattern_
+       regmap_write(lt8911exb->regmap, 0x26, (pcr_m | 0x80));
+#else
+
+       regmap_write(lt8911exb->regmap, 0x26, pcr_m);
+
+//     regmap_write(lt8911exb->regmap, 0x27, Read_0xD095);
+//     regmap_write(lt8911exb->regmap, 0x28, Read_0xD096);
+#endif
+
+       lt8911exb_mipi_video_timing(lt8911exb); //defualt setting is 1080P
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x81); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x03, 0x7b); //PCR reset
+       regmap_write(lt8911exb->regmap, 0x03, 0xff);
+
+#ifdef _eDP_2G7_
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x19, 0x31);
+       regmap_write(lt8911exb->regmap, 0x1a, 0x36); // sync m
+       regmap_write(lt8911exb->regmap, 0x1b, 0x00); // sync_k [7:0]
+       regmap_write(lt8911exb->regmap, 0x1c, 0x00); // sync_k [13:8]
+
+       // txpll Analog
+       regmap_write(lt8911exb->regmap, 0xff, 0x82);
+       regmap_write(lt8911exb->regmap, 0x09, 0x00); // div hardware mode, for ssc.
+
+//     regmap_write(lt8911exb->regmap, 0x01, 0x18);// default : 0x18
+       regmap_write(lt8911exb->regmap, 0x02, 0x42);
+       regmap_write(lt8911exb->regmap, 0x03, 0x00); // txpll en = 0
+       regmap_write(lt8911exb->regmap, 0x03, 0x01); // txpll en = 1
+//     regmap_write(lt8911exb->regmap, 0x04, 0x3a);// default : 0x3A
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x0c, 0x10); // cal en = 0
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+       regmap_write(lt8911exb->regmap, 0x09, 0xfc);
+       regmap_write(lt8911exb->regmap, 0x09, 0xfd);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x0c, 0x11); // cal en = 1
+
+       // ssc
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x13, 0x83);
+       regmap_write(lt8911exb->regmap, 0x14, 0x41);
+       regmap_write(lt8911exb->regmap, 0x16, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x18, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x19, 0x33);
+#endif
+
+#ifdef _eDP_1G62_
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x19, 0x31);
+       regmap_write(lt8911exb->regmap, 0x1a, 0x20); // sync m
+       regmap_write(lt8911exb->regmap, 0x1b, 0x19); // sync_k [7:0]
+       regmap_write(lt8911exb->regmap, 0x1c, 0x99); // sync_k [13:8]
+
+       // txpll Analog
+       regmap_write(lt8911exb->regmap, 0xff, 0x82);
+       regmap_write(lt8911exb->regmap, 0x09, 0x00); // div hardware mode, for ssc.
+       //      regmap_write(lt8911exb->regmap, 0x01, 0x18);// default : 0x18
+       regmap_write(lt8911exb->regmap, 0x02, 0x42);
+       regmap_write(lt8911exb->regmap, 0x03, 0x00); // txpll en = 0
+       regmap_write(lt8911exb->regmap, 0x03, 0x01); // txpll en = 1
+       //      regmap_write(lt8911exb->regmap, 0x04, 0x3a);// default : 0x3A
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x0c, 0x10); // cal en = 0
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+       regmap_write(lt8911exb->regmap, 0x09, 0xfc);
+       regmap_write(lt8911exb->regmap, 0x09, 0xfd);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x0c, 0x11); // cal en = 1
+
+       //ssc
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+       regmap_write(lt8911exb->regmap, 0x13, 0x83);
+       regmap_write(lt8911exb->regmap, 0x14, 0x41);
+       regmap_write(lt8911exb->regmap, 0x16, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x18, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x19, 0x33);
+#endif
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+
+       for (i = 0; i < 5; i++) {//Check Tx PLL
+               mdelay(5);
+               regmap_read(lt8911exb->regmap, 0x37, &chip_read);
+               if (chip_read & 0x02) {
+                       DRM_DEBUG("\r\n LT8911 tx pll locked");
+                       break;
+               } else {
+                       DRM_DEBUG("\r\n LT8911 tx pll unlocked");
+                       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+                       regmap_write(lt8911exb->regmap, 0x09, 0xfc);
+                       regmap_write(lt8911exb->regmap, 0x09, 0xfd);
+
+                       regmap_write(lt8911exb->regmap, 0xff, 0x87);
+                       regmap_write(lt8911exb->regmap, 0x0c, 0x10);
+                       regmap_write(lt8911exb->regmap, 0x0c, 0x11);
+               }
+       }
+
+       // AUX reset
+       regmap_write(lt8911exb->regmap, 0xff, 0x81); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x07, 0xfe);
+       regmap_write(lt8911exb->regmap, 0x07, 0xff);
+       regmap_write(lt8911exb->regmap, 0x0a, 0xfc);
+       regmap_write(lt8911exb->regmap, 0x0a, 0xfe);
+
+       /* tx phy */
+       regmap_write(lt8911exb->regmap, 0xff, 0x82); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x11, 0x00);
+       regmap_write(lt8911exb->regmap, 0x13, 0x10);
+       regmap_write(lt8911exb->regmap, 0x14, 0x0c);
+       regmap_write(lt8911exb->regmap, 0x14, 0x08);
+       regmap_write(lt8911exb->regmap, 0x13, 0x20);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x82); // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x0e, 0x35);
+//     regmap_write(lt8911exb->regmap, 0x12, 0xff);
+//     regmap_write(lt8911exb->regmap, 0xff, 0x80);
+//     regmap_write(lt8911exb->regmap, 0x40, 0x22);
+
+       /*eDP Tx Digital */
+       regmap_write(lt8911exb->regmap, 0xff, 0xa8); // Change Reg bank
+
+#ifdef _test_pattern_
+
+       regmap_write(lt8911exb->regmap, 0x24, 0x50); // bit2 ~ bit 0 : test panttern image mode
+       regmap_write(lt8911exb->regmap, 0x25, 0x70); // bit6 ~ bit 4 : test Pattern color
+       regmap_write(lt8911exb->regmap, 0x27, 0x50); //0x50:Pattern; 0x10:mipi video
+
+//     regmap_write(lt8911exb->regmap, 0x2d, 0x00); //  pure color setting
+//     regmap_write(lt8911exb->regmap, 0x2d, 0x84); // black color
+       regmap_write(lt8911exb->regmap, 0x2d, 0x88); //  block
+
+#else
+       regmap_write(lt8911exb->regmap, 0x27, 0x10); //0x50:Pattern; 0x10:mipi video
+#endif
+
+#ifdef _6bit_
+       regmap_write(lt8911exb->regmap, 0x17, 0x00);
+       regmap_write(lt8911exb->regmap, 0x18, 0x00);
+#else
+       // _8bit_
+       regmap_write(lt8911exb->regmap, 0x17, 0x10);
+       regmap_write(lt8911exb->regmap, 0x18, 0x20);
+#endif
+
+       /* nvid */
+       regmap_write(lt8911exb->regmap, 0xff, 0xa0);    // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x00, (u8)(Nvid_Val[_Nvid] / 256));     // 0x08
+       regmap_write(lt8911exb->regmap, 0x01, (u8)(Nvid_Val[_Nvid] % 256));     // 0x00
+}
+
+void lt8911exb_video_check(struct lt8911exb *lt8911exb)
+{
+       unsigned int reg;
+       unsigned int temp2;
+
+       /* mipi byte clk check*/
+       regmap_write(lt8911exb->regmap, 0xff, 0x85);    // Change Reg bank
+       regmap_write(lt8911exb->regmap, 0x1d, 0x00);    //FM select byte clk
+       regmap_write(lt8911exb->regmap, 0x40, 0xf7);
+       regmap_write(lt8911exb->regmap, 0x41, 0x30);
+
+       if (ScrambleMode) {
+               regmap_write(lt8911exb->regmap, 0xa1, 0x82 ); //eDP scramble mode;
+       } else {
+               regmap_write(lt8911exb->regmap, 0xa1, 0x02 ); // DP scramble mode;
+       }
+
+       //regmap_write(lt8911exb->regmap, 0x17, 0xf0 ); // 0xf0:Close scramble; 0xD0 : Open scramble
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+       regmap_write(lt8911exb->regmap, 0x09, 0x7d);
+       regmap_write(lt8911exb->regmap, 0x09, 0xfd);
+       regmap_write(lt8911exb->regmap, 0xff, 0x85);
+       mdelay(200);
+
+       regmap_read(lt8911exb->regmap, 0x50, &temp2);
+       if (temp2 == 0x03) {
+               //reg      = LT8911EXB_IIC_Read_byte( 0x4d );
+               //reg      = reg * 256 + LT8911EXB_IIC_Read_byte( 0x4e );
+               //reg      = reg * 256 + LT8911EXB_IIC_Read_byte( 0x4f );
+
+               regmap_read(lt8911exb->regmap, 0x4d, &reg);
+               DRM_DEBUG_ATOMIC("1 0x4d = %d\n",reg);
+               regmap_read(lt8911exb->regmap, 0x4e, &temp2);
+               DRM_DEBUG_ATOMIC("1 0x4e = %d\n",temp2);
+               reg = reg * 256 + temp2;
+               DRM_DEBUG_ATOMIC("1-1 reg = %d\n",reg);
+               regmap_read(lt8911exb->regmap, 0x4f, &temp2);
+               DRM_DEBUG_ATOMIC("1-1 0x4f = %d\n",temp2);
+               reg = reg * 256 + temp2;
+               DRM_DEBUG_ATOMIC("1-2 reg = %d\n",reg);
+
+
+               DRM_DEBUG( "\r\nvideo check: mipi byteclk = %d ", reg ); // mipi byteclk = reg * 1000
+               DRM_DEBUG( "\r\nvideo check: mipi bitrate = %d ", reg * 8); // mipi byteclk = reg * 1000
+               DRM_DEBUG( "\r\nvideo check: mipi pclk = %d ", reg /3 * 4 * 1000); // mipi byteclk = reg * 1000
+       } else {
+               DRM_DEBUG( "\r\nvideo check: mipi clk unstable" );
+       }
+
+       /* mipi vtotal check*/
+       //reg      = LT8911EXB_IIC_Read_byte( 0x76 );
+       //reg      = reg * 256 + LT8911EXB_IIC_Read_byte( 0x77 );
+       regmap_read(lt8911exb->regmap, 0x76, &reg);
+       DRM_DEBUG_ATOMIC("2 0x76 = %d\n",reg);
+       regmap_read(lt8911exb->regmap, 0x77, &temp2);
+       DRM_DEBUG_ATOMIC("2 0x77 = %d\n",temp2);
+       reg = reg * 256 + temp2;
+       DRM_DEBUG_ATOMIC("2-1 reg = %d\n",reg);
+
+       DRM_DEBUG( "\r\nvideo check: Vtotal =  %d", reg);
+
+       /* mipi word count check*/
+       regmap_write(lt8911exb->regmap, 0xff, 0xd0);
+       //reg      = LT8911EXB_IIC_Read_byte( 0x82 );
+       //reg      = reg * 256 + LT8911EXB_IIC_Read_byte( 0x83 );
+       //reg      = reg / 3;
+       regmap_read(lt8911exb->regmap, 0x82, &reg);
+       DRM_DEBUG_ATOMIC("3 0x82 = %d\n",reg);
+       regmap_read(lt8911exb->regmap, 0x83, &temp2);
+       DRM_DEBUG_ATOMIC("3 0x83 = %d\n",reg);
+       reg = reg * 256 + temp2;
+       reg        = reg / 3;
+       DRM_DEBUG_ATOMIC( "\r\n3-1 reg  = %d ", reg );
+
+
+       DRM_DEBUG( "\r\nvideo check: Hact(word counter) =  %d", reg);
+
+       /* mipi Vact check*/
+       //reg      = LT8911EXB_IIC_Read_byte( 0x85 );
+       //reg      = reg * 256 + LT8911EXB_IIC_Read_byte( 0x86 );
+       regmap_read(lt8911exb->regmap, 0x85, &reg);
+       DRM_DEBUG_ATOMIC("4 0x85 = %d\n",reg);
+       regmap_read(lt8911exb->regmap, 0x86, &temp2);
+       DRM_DEBUG_ATOMIC("4 0x86 = %d\n",temp2);
+       reg = reg * 256 + temp2;
+       DRM_DEBUG_ATOMIC( "\r\n4-1 reg  = %d ", reg );
+
+       DRM_DEBUG( "\r\nvideo check: Vact = %d", reg);
+
+}
+
+void DpcdWrite(struct lt8911exb *lt8911exb, u32 Address, u8 Data)
+{
+       /***************************
+          注意大小端的问题!
+          这里默认是大端模式
+
+          Pay attention to the Big-Endian and Little-Endian!
+          The default mode is Big-Endian here.
+
+        ****************************/
+       u8      AddressH = 0x0f & (Address >> 16);
+       u8      AddressM = 0xff & (Address >> 8);
+       u8      AddressL = 0xff & Address;
+
+       unsigned int reg;
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xa6);
+       regmap_write(lt8911exb->regmap, 0x2b, (0x80 | AddressH));       //CMD
+       regmap_write(lt8911exb->regmap, 0x2b, AddressM);        //addr[15:8]
+       regmap_write(lt8911exb->regmap, 0x2b, AddressL);        //addr[7:0]
+       regmap_write(lt8911exb->regmap, 0x2b, 0x00);    //data lenth
+       regmap_write(lt8911exb->regmap, 0x2b, Data);    //data
+       regmap_write(lt8911exb->regmap, 0x2c, 0x00);    //start Aux
+
+       mdelay(20);     //more than 10ms
+       regmap_read(lt8911exb->regmap, 0x25, &reg);
+       if ((reg & 0x0f) == 0x0c) {
+               return;
+       }
+}
+
+unsigned int DpcdRead( struct lt8911exb *lt8911exb, u32 Address )
+{
+       /***************************
+          注意大小端的问题!
+          这里默认是大端模式
+
+          Pay attention to the Big-Endian and Little-Endian!
+          The default mode is Big-Endian here.
+
+        ****************************/
+
+       unsigned int DpcdValue = 0x00;
+       unsigned int AddressH = 0x0f & (Address >> 16);
+       unsigned int AddressM = 0xff & (Address >> 8);
+       unsigned int AddressL = 0xff & Address;
+       unsigned int reg;
+       unsigned int temp;
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xac);
+       regmap_write(lt8911exb->regmap, 0x00, 0x20);    //Soft Link train
+       regmap_write(lt8911exb->regmap, 0xff, 0xa6);
+       regmap_write(lt8911exb->regmap, 0x2a, 0x01);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xa6);
+       regmap_write(lt8911exb->regmap, 0x2b, (0x90 | AddressH));       //CMD
+       regmap_write(lt8911exb->regmap, 0x2b, AddressM);        //addr[15:8]
+       regmap_write(lt8911exb->regmap, 0x2b, AddressL);        //addr[7:0]
+       regmap_write(lt8911exb->regmap, 0x2b, 0x00);    //data lenth
+       regmap_write(lt8911exb->regmap, 0x2c, 0x00);    //start Aux read edid
+
+       mdelay(50);     //more than 10ms
+       regmap_read(lt8911exb->regmap, 0x25, &reg);
+       if ((reg & 0x0f) == 0x0c) {
+               regmap_read(lt8911exb->regmap, 0x39, &temp);
+               if (temp == 0x22) {
+                       //LT8911EXB_IIC_Read_byte( 0x2b );
+                       //DpcdValue = LT8911EXB_IIC_Read_byte( 0x2b );
+                       regmap_read(lt8911exb->regmap, 0x2b, &DpcdValue);
+                       regmap_read(lt8911exb->regmap, 0x2b, &DpcdValue);
+               }
+       } else {
+               regmap_write(lt8911exb->regmap, 0xff, 0x81); // change bank
+               regmap_write(lt8911exb->regmap, 0x07, 0xfe);
+               regmap_write(lt8911exb->regmap, 0x07, 0xff);
+               regmap_write(lt8911exb->regmap, 0x0a, 0xfc);
+               regmap_write(lt8911exb->regmap, 0x0a, 0xfe);
+       }
+
+       return DpcdValue;
+}
+
+
+
+void lt8911exb_link_train(struct lt8911exb *lt8911exb)
+{
+       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+       regmap_write(lt8911exb->regmap, 0x06, 0xdf); // rset VID TX
+       regmap_write(lt8911exb->regmap, 0x06, 0xff);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x85);
+
+       //regmap_write(lt8911exb->regmap, 0x17, 0xf0 ); // turn off scramble
+
+       if (ScrambleMode) {
+               regmap_write(lt8911exb->regmap, 0xa1, 0x82); // eDP scramble mode;
+               regmap_write(lt8911exb->regmap, 0xff, 0xac);
+               regmap_write(lt8911exb->regmap, 0x00, 0x20); //Soft Link train
+               regmap_write(lt8911exb->regmap, 0xff, 0xa6);
+               regmap_write(lt8911exb->regmap, 0x2a, 0x01);
+
+               DpcdWrite(lt8911exb, 0x010a, 0x01);
+               mdelay(10);
+               DpcdWrite(lt8911exb, 0x0102, 0x00);
+               mdelay(10);
+               DpcdWrite(lt8911exb, 0x010a, 0x01);
+
+               mdelay(200);
+               //*/
+       } else {
+               regmap_write(lt8911exb->regmap, 0xa1, 0x02); // DP scramble mode;
+       }
+       /* Aux setup */
+       regmap_write(lt8911exb->regmap, 0xff, 0xac);
+       regmap_write(lt8911exb->regmap, 0x00, 0x60);     //Soft Link train
+       regmap_write(lt8911exb->regmap, 0xff, 0xa6);
+       regmap_write(lt8911exb->regmap, 0x2a, 0x00);
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x81);
+       regmap_write(lt8911exb->regmap, 0x07, 0xfe);
+       regmap_write(lt8911exb->regmap, 0x07, 0xff);
+       regmap_write(lt8911exb->regmap, 0x0a, 0xfc);
+       regmap_write(lt8911exb->regmap, 0x0a, 0xfe);
+
+       /* link train */
+
+       regmap_write(lt8911exb->regmap, 0xff, 0x85);
+       regmap_write(lt8911exb->regmap, 0x1a, eDP_lane);
+
+#ifdef _link_train_enable_
+       regmap_write(lt8911exb->regmap, 0xff, 0xac);
+       regmap_write(lt8911exb->regmap, 0x00, 0x64);
+       regmap_write(lt8911exb->regmap, 0x01, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x0c, 0x85);
+       regmap_write(lt8911exb->regmap, 0x0c, 0xc5);
+#else
+       regmap_write(lt8911exb->regmap, 0xff, 0xac);
+       regmap_write(lt8911exb->regmap, 0x00, 0x00);
+       regmap_write(lt8911exb->regmap, 0x01, 0x0a);
+       regmap_write(lt8911exb->regmap, 0x14, 0x80);
+       regmap_write(lt8911exb->regmap, 0x14, 0x81);
+       mdelay(50);
+       regmap_write(lt8911exb->regmap, 0x14, 0x84);
+       mdelay(50);
+       regmap_write(lt8911exb->regmap, 0x14, 0xc0);
+#endif
+}
+
+void lt8911exb_reset(struct lt8911exb *lt8911exb)
+{
+       if (!lt8911exb->reset_gpio) {
+               DRM_DEBUG_ATOMIC("no gpio, no reset\n");
+               return;
+       }
+
+       gpiod_direction_output(lt8911exb->reset_gpio, 1);
+       usleep_range(50*1000, 100*1000); //100ms
+       gpiod_direction_output(lt8911exb->reset_gpio, 0);
+       usleep_range(50*1000, 100*1000); //100ms
+       gpiod_direction_output(lt8911exb->reset_gpio, 1);
+       usleep_range(100*1000, 150*1000); //150ms
+}
+
+void LT8911EX_TxSwingPreSet(struct lt8911exb *lt8911exb)
+{
+       regmap_write(lt8911exb->regmap, 0xFF, 0x82);
+       regmap_write(lt8911exb->regmap, 0x22, Swing_Setting1[Level]);   //lane 0 tap0
+       regmap_write(lt8911exb->regmap, 0x23, Swing_Setting2[Level]);
+       regmap_write(lt8911exb->regmap, 0x24, 0x80);    //lane 0 tap1
+       regmap_write(lt8911exb->regmap, 0x25, 0x00);
+
+#if ( eDP_lane == 2 )
+       regmap_write(lt8911exb->regmap, 0x26, Swing_Setting1[Level]);   //lane 1 tap0
+       regmap_write(lt8911exb->regmap, 0x27, Swing_Setting2[Level]);
+       regmap_write(lt8911exb->regmap, 0x28, 0x80);    //lane 1 tap1
+       regmap_write(lt8911exb->regmap, 0x29, 0x00);
+#endif
+}
+
+void LT8911EXB_LinkTrainResultCheck( struct lt8911exb *lt8911exb)
+{
+#ifdef _link_train_enable_
+       u8      i;
+       unsigned int val;
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xac);
+       for (i = 0; i < 10; i++) {
+               regmap_read(lt8911exb->regmap, 0x82, &val);
+               DRM_DEBUG("\r\n0x82: 0x%x", val);
+               if (val & 0x20) {
+                       if ((val & 0x1f) == 0x1e) {
+#ifdef _uart_debug_
+                               DRM_INFO("\r\nedp link train successed: 0x%x", val);
+#endif
+                               return;
+                       } else {
+#ifdef _uart_debug_
+                               DRM_INFO("\r\nedp link train failed: 0x%x", val);
+#endif
+                               regmap_write(lt8911exb->regmap, 0xff, 0xac);
+                               regmap_write(lt8911exb->regmap, 0x00, 0x00);
+                               regmap_write(lt8911exb->regmap, 0x01, 0x0a);
+                               regmap_write(lt8911exb->regmap, 0x14, 0x80);
+                               regmap_write(lt8911exb->regmap, 0x14, 0x81);
+                               mdelay(50);
+                               regmap_write(lt8911exb->regmap, 0x14, 0x84);
+                               mdelay(50);
+                               regmap_write(lt8911exb->regmap, 0x14, 0xc0);
+                       }
+
+#ifdef _uart_debug_
+
+                       regmap_read(lt8911exb->regmap, 0x83, &val);
+                       //DRM_DEBUG_ATOMIC("\r\nLT8911_LinkTrainResultCheck: panel link rate: 0x%bx",val);
+                       DRM_INFO( "\r\npanel link rate: 0x%x", val );
+                       regmap_read(lt8911exb->regmap, 0x84, &val);
+                       //DRM_DEBUG_ATOMIC("\r\nLT8911_LinkTrainResultCheck: panel link count: 0x%bx",val);
+                       DRM_INFO( "\r\npanel link count:0x%x ", val);
+#endif
+                       mdelay(100); // return;
+               } else {
+                       //DRM_DEBUG_ATOMIC("\r\nLT8911_LinkTrainResultCheck: link trian on going...");
+                       mdelay(100);
+               }
+       }
+#endif
+}
+
+
+void LT8911EX_link_train_result(struct lt8911exb *lt8911exb)
+{
+       u8 i;
+       unsigned int reg;
+       unsigned int temp;
+       regmap_write(lt8911exb->regmap, 0xff, 0xac);
+       for (i = 0; i < 10; i++) {
+               regmap_read(lt8911exb->regmap, 0x82, &reg);
+               DRM_DEBUG( "\r\n0x82 = 0x%x", reg );
+               if (reg & 0x20) {
+                       if((reg & 0x1f) == 0x1e) {
+                               DRM_DEBUG("\r\nLink train success, 0x82 = 0x%x", reg);
+                       } else {
+                               DRM_DEBUG("\r\nLink train fail, 0x82 = 0x%x", reg);
+                       }
+
+                       regmap_read(lt8911exb->regmap, 0x83, &temp);
+                       DRM_DEBUG("\r\npanel link rate: 0x%x", temp);
+                       regmap_read(lt8911exb->regmap, 0x84, &temp);
+                       DRM_DEBUG("\r\npanel link count: 0x%x", temp);
+                       return;
+               } else {
+                       DRM_DEBUG("\r\nlink trian on going...");
+               }
+               mdelay(100);
+       }
+}
+
+void PCR_Status(struct lt8911exb *lt8911exb)   // for debug
+{
+#ifdef _uart_debug_
+       unsigned int reg;
+
+       regmap_write(lt8911exb->regmap, 0xff, 0xd0);
+       //reg = LT8911EXB_IIC_Read_byte( 0x87 );
+       regmap_read(lt8911exb->regmap, 0x87, &reg);
+
+       DRM_INFO("\r\nReg0xD087 =       ");
+       DRM_INFO(" 0x%x ", reg);
+       DRM_INFO("\r\n ");
+       if (reg & 0x10) {
+               DRM_INFO( "\r\nPCR Clock stable" );
+       } else {
+               DRM_INFO( "\r\nPCR Clock unstable" );
+       }
+       DRM_INFO("\r\n ");
+#endif
+}
+
+static int lt8911exb_chip_id(struct lt8911exb *lt8911exb)
+{
+    u8 retry = 0;
+    unsigned int chip_id_h = 0, chip_id_m = 0, chip_id_l = 0;
+    int ret = -EAGAIN;
+
+    while(retry++ < 3) {
+        ret = regmap_write(lt8911exb->regmap, 0xff, 0x81);
+
+        if(ret < 0) {
+            dev_err(lt8911exb->dev, "LT8911EXB i2c test write addr:0xff failed\n");
+            continue;
+        }
+
+        ret = regmap_write(lt8911exb->regmap, 0x08, 0x7f);
+
+        if(ret < 0) {
+            dev_err(lt8911exb->dev, "LT8911EXB i2c test write addr:0x08 failed\n");
+            continue;
+        }
+
+        regmap_read(lt8911exb->regmap, 0x00, &chip_id_l);
+        regmap_read(lt8911exb->regmap, 0x01, &chip_id_m);
+        regmap_read(lt8911exb->regmap, 0x02, &chip_id_h);
+        // LT8911EXB i2c test success chipid: 0xe0517
+        DRM_DEBUG("LT8911EXB i2c test success chipid: 0x%x%x%x\n", chip_id_h, chip_id_m, chip_id_l);
+
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static int lt8911exb_panel_enable(struct drm_panel *panel)
+{
+       struct lt8911exb *lt8911exb = panel_to_lt8911exb(panel);
+
+       DRM_INFO("%s()\n", __func__);
+
+       schedule_delayed_work(&lt8911exb->init_work,
+                               msecs_to_jiffies(500));
+       lt8911exb->init_work_pending = true;
+
+       return 0;
+}
+
+static int lt8911exb_panel_disable(struct drm_panel *panel)
+{
+       struct lt8911exb *lt8911exb = panel_to_lt8911exb(panel);
+
+       DRM_INFO("%s()\n", __func__);
+
+       gpiod_direction_output(lt8911exb->bl_gpio, 0);
+       if (!IS_ERR_OR_NULL(lt8911exb->enable_gpio)) {
+               gpiod_direction_output(lt8911exb->enable_gpio, 0);
+       }
+       usleep_range(50*1000, 100*1000); //100ms
+
+       gpiod_direction_output(lt8911exb->standby_gpio, 0);
+
+       if (lt8911exb->init_work_pending) {
+               cancel_delayed_work_sync(&lt8911exb->init_work);
+               lt8911exb->init_work_pending = false;
+       }
+
+       return 0;
+}
+
+static int lt8911exb_panel_get_modes(struct drm_panel *panel,
+                               struct drm_connector *connector)
+{
+       unsigned int i, num = 0;
+       static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       for (i = 0; i < ARRAY_SIZE(lt8911exb_panel_modes); i++) {
+               const struct drm_display_mode *m = &lt8911exb_panel_modes[i];
+               struct drm_display_mode *mode;
+
+               mode = drm_mode_duplicate(connector->dev, m);
+               if (!mode) {
+                       dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
+                               m->hdisplay, m->vdisplay,
+                               drm_mode_vrefresh(m));
+                       continue;
+               }
+
+               mode->type |= DRM_MODE_TYPE_DRIVER;
+
+               if (i == 0)
+                       mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+               drm_mode_set_name(mode);
+               drm_mode_probed_add(connector, mode);
+               num++;
+       }
+
+       connector->display_info.bpc = 8;
+       // 1920x1080
+       #if MIPI_DSI_1920x1080
+       connector->display_info.width_mm = 309;
+       connector->display_info.height_mm = 174;
+       #endif
+
+       // 1920x1200
+       #if MIPI_DSI_1920x1200
+       connector->display_info.width_mm = 301;
+       connector->display_info.height_mm = 188;
+       #endif
+       drm_display_info_set_bus_formats(&connector->display_info,
+                               &bus_format, 1);
+       return num;
+}
+
+
+static const struct drm_panel_funcs lt8911exb_panel_funcs = {
+       .disable = lt8911exb_panel_disable,
+       .enable = lt8911exb_panel_enable,
+       .get_modes = lt8911exb_panel_get_modes,
+};
+
+static void init_work_func(struct work_struct *work)
+{
+       struct lt8911exb *lt8911exb = container_of(work, struct lt8911exb,
+                                               init_work.work);
+
+       DRM_DEBUG(" %s() \n", __func__);
+
+       gpiod_direction_output(lt8911exb->standby_gpio, 1);
+       usleep_range(50*1000, 100*1000); //100ms
+
+       lt8911exb_reset(lt8911exb);
+       lt8911exb_chip_id(lt8911exb);
+
+       lt8911exb_edp_video_cfg(lt8911exb);
+       lt8911exb_setup(lt8911exb);
+       LT8911EX_TxSwingPreSet(lt8911exb);
+
+       lt8911exb_read_edid(lt8911exb);
+
+       ScrambleMode = 0;
+       lt8911exb_link_train(lt8911exb);
+       LT8911EXB_LinkTrainResultCheck(lt8911exb);
+       LT8911EX_link_train_result(lt8911exb);
+       lt8911exb_video_check(lt8911exb); //just for Check MIPI Input
+
+       DRM_DEBUG("\r\nDpcdRead(0x0202) = 0x%x\r\n",DpcdRead(lt8911exb, 0x0202));
+
+       PCR_Status(lt8911exb);
+
+       if (!IS_ERR_OR_NULL(lt8911exb->enable_gpio)) {
+               gpiod_direction_output(lt8911exb->enable_gpio, 1);
+       }
+       gpiod_direction_output(lt8911exb->bl_gpio, 1);
+}
+static int lt8911exb_probe(struct i2c_client *client)
+{
+       struct lt8911exb *lt8911exb;
+       struct device *dev = &client->dev;
+       int ret;
+
+       struct device_node *endpoint, *dsi_host_node;
+       struct mipi_dsi_host *host;
+
+       struct device_node *lcd_node;
+       int rc;
+       const char *str;
+       char lcd_path[60];
+       const char *lcd_name;
+
+       struct mipi_dsi_device_info info = {
+               .type = IT8911_DSI_DRIVER_NAME,
+               .channel = 0,
+               .node = NULL,
+       };
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "Failed check I2C functionality");
+               return -ENODEV;
+       }
+
+       lt8911exb = devm_kzalloc(&client->dev, sizeof(*lt8911exb), GFP_KERNEL);
+       if (!lt8911exb)
+               return -ENOMEM;
+
+       lt8911exb->dev = &client->dev;
+       lt8911exb->client = client;
+
+       //regmap i2c , maybe useless
+       lt8911exb->regmap = devm_regmap_init_i2c(client, &lt8911exb_regmap_config);
+       if (IS_ERR(lt8911exb->regmap)) {
+               dev_err(lt8911exb->dev, "regmap i2c init failed\n");
+               return PTR_ERR(lt8911exb->regmap);
+       }
+
+       lt8911exb->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+                                               GPIOD_IN);
+       if (IS_ERR_OR_NULL(lt8911exb->enable_gpio)) {
+               DRM_DEBUG("%s() failed get enable gpio\n", __func__);
+               // return PTR_ERR(lt8911exb->enable_gpio);
+       }
+
+       lt8911exb->standby_gpio = devm_gpiod_get_optional(dev, "standby",
+                                               GPIOD_IN);
+       if (IS_ERR(lt8911exb->standby_gpio)) {
+               dev_err(&client->dev, "Failed get standby gpio\n");
+               return PTR_ERR(lt8911exb->standby_gpio);
+       }
+
+       lt8911exb->bl_gpio = devm_gpiod_get_optional(dev, "bl",
+                                               GPIOD_IN);
+       if (IS_ERR(lt8911exb->bl_gpio)) {
+               dev_err(&client->dev, "Failed get bl gpio\n");
+               return PTR_ERR(lt8911exb->bl_gpio);
+       }
+       gpiod_direction_output(lt8911exb->bl_gpio, 0);
+       if (!IS_ERR_OR_NULL(lt8911exb->enable_gpio)) {
+               gpiod_direction_output(lt8911exb->enable_gpio, 0);
+       }
+       usleep_range(50*1000, 100*1000); //100ms
+
+       //disable firstly
+       gpiod_direction_output(lt8911exb->standby_gpio, 0);
+       usleep_range(50*1000, 100*1000); //100ms
+       gpiod_direction_output(lt8911exb->standby_gpio, 1);
+       usleep_range(50*1000, 100*1000); //100ms
+
+       lt8911exb->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+                                               GPIOD_IN);
+       if (IS_ERR(lt8911exb->reset_gpio)) {
+               dev_err(&client->dev, "Failed get reset gpio\n");
+               return PTR_ERR(lt8911exb->reset_gpio);
+       }
+
+       lt8911exb_reset(lt8911exb);
+
+       i2c_set_clientdata(client, lt8911exb);
+
+       //check i2c communicate
+       ret = lt8911exb_chip_id(lt8911exb);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed communicate with IC use I2C\n");
+               return ret;
+       }
+
+       endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+       if (!endpoint)
+               return -ENODEV;
+
+       dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+       if (!dsi_host_node)
+               goto error;
+
+       rc = of_property_read_string(dsi_host_node, "force-attached", &str);
+       if (!rc)
+               lcd_name = str;
+
+       sprintf(lcd_path, "/lcds/%s", lcd_name);
+       lcd_node = of_find_node_by_path(lcd_path);
+       if (!lcd_node) {
+               DRM_ERROR("%pOF: could not find %s node\n", dsi_host_node, lcd_name);
+               of_node_put(endpoint);
+               return -ENODEV;
+       }
+
+       DRM_INFO("%pOF: find %s node\n", dsi_host_node, lcd_name);
+
+       host = of_find_mipi_dsi_host_by_node(dsi_host_node);
+       of_node_put(dsi_host_node);
+       if (!host) {
+               of_node_put(endpoint);
+               return -EPROBE_DEFER;
+       }
+
+       drm_panel_init(&lt8911exb->base, dev, &lt8911exb_panel_funcs,
+                       DRM_MODE_CONNECTOR_DSI);
+
+       /* This appears last, as it's what will unblock the DSI host
+        * driver's component bind function.
+        */
+       drm_panel_add(&lt8911exb->base);
+
+       lt8911exb->base.dev->of_node = lcd_node;
+       info.node = of_node_get(of_graph_get_remote_port(endpoint));
+       if (!info.node)
+               goto error;
+       of_node_put(endpoint);
+
+       lt8911exb->dsi = mipi_dsi_device_register_full(host, &info);
+       if (IS_ERR(lt8911exb->dsi)) {
+               dev_err(dev, "DSI device registration failed: %ld\n",
+                       PTR_ERR(lt8911exb->dsi));
+               return PTR_ERR(lt8911exb->dsi);
+       }
+
+       INIT_DELAYED_WORK(&lt8911exb->init_work, init_work_func);
+
+       return 0;
+error:
+       of_node_put(endpoint);
+       return -ENODEV;
+}
+
+static void lt8911exb_remove(struct i2c_client *client)
+{
+       struct lt8911exb *lt8911exb = i2c_get_clientdata(client);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       mipi_dsi_detach(lt8911exb->dsi);
+
+       drm_panel_remove(&lt8911exb->base);
+
+       mipi_dsi_device_unregister(lt8911exb->dsi);
+}
+
+static struct i2c_device_id lt8911exb_id[] = {
+       { "lontium,lt8911exb", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, lt8911exb_id);
+
+static const struct of_device_id lt8911exb_match_table[] = {
+       { .compatible = "lontium,lt8911exb" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lt8911exb_match_table);
+
+static struct i2c_driver lt8911exb_driver = {
+       .driver = {
+               .name = "lt8911exb",
+               .of_match_table = lt8911exb_match_table,
+       },
+       .probe = lt8911exb_probe,
+       .remove = lt8911exb_remove,
+       .id_table = lt8911exb_id,
+};
+
+static int lt8911exb_dsi_probe(struct mipi_dsi_device *dsi)
+{
+       int ret;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       dsi->lanes = 4;
+       dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+                               MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+                               MIPI_DSI_MODE_LPM);
+       dsi->format = MIPI_DSI_FMT_RGB888;
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0) {
+               dev_err(&dsi->dev, "failed to attach dsi to host\n");
+               mipi_dsi_device_unregister(dsi);
+       }
+
+       return ret;
+}
+
+static const struct of_device_id spacemit_edp_of_match[] = {
+       { .compatible = "spacemit,edp-panel" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spacemit_edp_of_match);
+
+static struct mipi_dsi_driver lt8911exb_dsi_driver = {
+       .probe = lt8911exb_dsi_probe,
+       .driver = {
+               .name = IT8911_DSI_DRIVER_NAME,
+               .of_match_table = spacemit_edp_of_match,
+       },
+};
+
+
+static int __init init_lt8911exb(void)
+{
+       int err;
+
+       DRM_DEBUG("%s()\n", __func__);
+       mipi_dsi_driver_register(&lt8911exb_dsi_driver);
+       err = i2c_add_driver(&lt8911exb_driver);
+
+       return err;
+}
+
+module_init(init_lt8911exb);
+
+static void __exit exit_lt8911exb(void)
+{
+       DRM_DEBUG("%s()\n", __func__);
+       i2c_del_driver(&lt8911exb_driver);
+       mipi_dsi_driver_unregister(&lt8911exb_dsi_driver);
+}
+module_exit(exit_lt8911exb);
+
+MODULE_DESCRIPTION("LT8911EXB_MIPI to eDP Reg Setting driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/lt9711.c b/drivers/gpu/drm/spacemit/lt9711.c
new file mode 100644 (file)
index 0000000..677187c
--- /dev/null
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/atomic.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_bridge.h>
+#include <linux/delay.h>
+#include <drm/display/drm_dp_aux_bus.h>
+#include <drm/display/drm_dp_helper.h>
+
+#include "spacemit_dsi.h"
+
+#define IT9711_DSI_DRIVER_NAME "spacemit-dp-drv"
+#define MIPI_DSI_1920x1080  1
+
+static const struct drm_display_mode lt9711_panel_modes[] = {
+
+#if MIPI_DSI_1920x1080
+       {
+               .clock = 142857143 / 1000,
+               .hdisplay = 1920,
+               .hsync_start = 1920 + 88,
+               .hsync_end = 1920 + 88 + 148,
+               .htotal = 1920 + 88 + 148 + 44,
+               .vdisplay = 1080,
+               .vsync_start = 1080 + 4,
+               .vsync_end = 1080 + 4 + 36,
+               .vtotal = 1080 + 4 + 36 + 5,
+       },
+#endif
+
+};
+
+struct lt9711 {
+       struct device *dev;
+       struct drm_bridge bridge;
+       struct drm_connector *connector;
+       struct spacemit_dsi_device *spacemit_dsi;
+
+       struct regmap *regmap;
+       struct gpio_desc *reset_gpio;   //reset
+       struct gpio_desc *enable_gpio;  //power
+
+       struct i2c_client *client;
+       struct drm_panel base;
+       struct mipi_dsi_device *dsi;
+
+       enum drm_connector_status status;
+       struct delayed_work detect_work;
+       bool detect_work_pending;
+};
+
+static const struct regmap_config lt9711_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0xff,
+};
+
+static struct lt9711 *panel_to_lt9711(struct drm_panel *panel)
+{
+       return container_of(panel, struct lt9711, base);
+}
+
+static int lt9711_i2c_detect(struct lt9711 *lt9711)
+{
+       u8 retry = 0;
+       unsigned int status = 0;
+       int ret = -EAGAIN;
+
+       while(retry++ < 3) {
+
+               ret = regmap_write(lt9711->regmap, 0xff, 0x80);
+               if(ret < 0) {
+                       dev_err(lt9711->dev, "LT9711 i2c detect write addr:0xff failed\n");
+                       continue;
+               }
+
+               regmap_read(lt9711->regmap, 0xd6, &status);
+               // LT9711 i2c detect success status: 0xee
+               DRM_DEBUG("LT9711 i2c detect success status: 0x%x\n", status);
+
+               if (0xee == status)
+                       lt9711->status = connector_status_connected;
+               else
+                       lt9711->status = connector_status_disconnected;
+
+               break;
+       }
+
+       return ret;
+}
+
+static int lt9711_panel_enable(struct drm_panel *panel)
+{
+       // struct lt9711 *lt9711 = panel_to_lt9711(panel);
+       DRM_DEBUG(" %s() \n", __func__);
+
+       return 0;
+}
+
+static int lt9711_panel_disable(struct drm_panel *panel)
+{
+       // struct lt9711 *lt9711 = panel_to_lt9711(panel);
+       DRM_DEBUG(" %s() \n", __func__);
+
+       return 0;
+}
+
+static int lt9711_panel_get_modes(struct drm_panel *panel,
+                               struct drm_connector *connector)
+{
+       // struct lt9711 *lt9711 = panel_to_lt9711(panel);
+       unsigned int i, num = 0;
+       static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+       DRM_DEBUG(" %s() \n", __func__);
+
+       for (i = 0; i < ARRAY_SIZE(lt9711_panel_modes); i++) {
+               const struct drm_display_mode *m = &lt9711_panel_modes[i];
+               struct drm_display_mode *mode;
+
+               mode = drm_mode_duplicate(connector->dev, m);
+               if (!mode) {
+                       dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
+                               m->hdisplay, m->vdisplay,
+                               drm_mode_vrefresh(m));
+                       continue;
+               }
+
+               mode->type |= DRM_MODE_TYPE_DRIVER;
+
+               if (i == 0)
+                       mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+               drm_mode_set_name(mode);
+               drm_mode_probed_add(connector, mode);
+               num++;
+       }
+
+       connector->display_info.bpc = 8;
+       // 1920x1080
+       #if MIPI_DSI_1920x1080
+       connector->display_info.width_mm = 309;
+       connector->display_info.height_mm = 174;
+       #endif
+
+       drm_display_info_set_bus_formats(&connector->display_info,
+                               &bus_format, 1);
+       return num;
+}
+
+
+static const struct drm_panel_funcs lt9711_panel_funcs = {
+       .disable = lt9711_panel_disable,
+       .enable = lt9711_panel_enable,
+       .get_modes = lt9711_panel_get_modes,
+};
+
+static void detect_work_func(struct work_struct *work)
+{
+       struct lt9711 *lt9711 = container_of(work, struct lt9711,
+                                               detect_work.work);
+       int ret;
+
+       //check i2c communicate
+       ret = lt9711_i2c_detect(lt9711);
+       if (ret < 0) {
+               DRM_INFO("detect DP failed communicate with IC use I2C\n");
+       }
+
+       if (lt9711->spacemit_dsi) {
+               DRM_DEBUG(" %s() connector status %d\n", __func__, lt9711->spacemit_dsi->connector_status);
+               lt9711->spacemit_dsi->connector_status = lt9711->status;
+
+               if (lt9711->spacemit_dsi->previous_connector_status != lt9711->spacemit_dsi->connector_status) {
+                       if (lt9711->connector) {
+                               DRM_INFO(" %s() detect DP connector hpd event\n", __func__);
+                               lt9711->spacemit_dsi->previous_connector_status = lt9711->spacemit_dsi->connector_status;
+                               drm_helper_hpd_irq_event(lt9711->connector->dev);
+                       }
+               }
+       }
+
+       schedule_delayed_work(&lt9711->detect_work,
+                               msecs_to_jiffies(2000));
+}
+
+
+static int lt9711_probe(struct i2c_client *client)
+{
+       struct lt9711 *lt9711;
+       struct device *dev = &client->dev;
+       int ret;
+
+       struct device_node *endpoint, *dsi_host_node;
+       struct mipi_dsi_host *host;
+
+       struct device_node *lcd_node;
+       int rc;
+       const char *str;
+       char lcd_path[60];
+       const char *lcd_name;
+
+       struct mipi_dsi_device_info info = {
+               .type = IT9711_DSI_DRIVER_NAME,
+               .channel = 0,
+               .node = NULL,
+       };
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "Failed check I2C functionality");
+               return -ENODEV;
+       }
+
+       lt9711 = devm_kzalloc(&client->dev, sizeof(*lt9711), GFP_KERNEL);
+       if (!lt9711)
+               return -ENOMEM;
+
+       lt9711->dev = &client->dev;
+       lt9711->client = client;
+       lt9711->connector = NULL;
+       lt9711->spacemit_dsi = NULL;
+       lt9711->status = connector_status_disconnected;
+
+       //regmap i2c , maybe useless
+       lt9711->regmap = devm_regmap_init_i2c(client, &lt9711_regmap_config);
+       if (IS_ERR(lt9711->regmap)) {
+               dev_err(lt9711->dev, "regmap i2c init failed\n");
+               return PTR_ERR(lt9711->regmap);
+       }
+
+       lt9711->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+                                               GPIOD_IN);
+       if (IS_ERR(lt9711->enable_gpio)) {
+               dev_err(lt9711->dev, "Failed get enable gpio\n");
+               return PTR_ERR(lt9711->enable_gpio);
+       }
+
+       lt9711->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+                                               GPIOD_IN);
+       if (IS_ERR(lt9711->reset_gpio)) {
+               dev_err(lt9711->dev, "Failed get reset gpio\n");
+               return PTR_ERR(lt9711->reset_gpio);
+       }
+
+       //disable firstly
+       gpiod_direction_output(lt9711->enable_gpio, 0);
+       usleep_range(50*1000, 100*1000); //100ms
+       gpiod_direction_output(lt9711->enable_gpio, 1);
+       usleep_range(50*1000, 100*1000); //100ms
+
+       gpiod_direction_output(lt9711->reset_gpio, 1);
+       usleep_range(50*1000, 100*1000); //100ms
+       gpiod_direction_output(lt9711->reset_gpio, 0);
+       usleep_range(50*1000, 100*1000); //100ms
+       gpiod_direction_output(lt9711->reset_gpio, 1);
+       usleep_range(100*1000, 150*1000); //150ms
+
+       i2c_set_clientdata(client, lt9711);
+       dev_set_drvdata(dev, lt9711);
+
+       //check i2c communicate
+       ret = lt9711_i2c_detect(lt9711);
+       if (ret < 0) {
+               DRM_INFO("detect DP failed communicate with IC use I2C\n");
+               return ret;
+       }
+
+       endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+       if (!endpoint)
+               return -ENODEV;
+
+       dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+       if (!dsi_host_node)
+               goto error;
+
+       rc = of_property_read_string(dsi_host_node, "force-attached", &str);
+       if (!rc)
+               lcd_name = str;
+
+       sprintf(lcd_path, "/lcds/%s", lcd_name);
+       lcd_node = of_find_node_by_path(lcd_path);
+       if (!lcd_node) {
+               DRM_ERROR("%pOF: could not find %s node\n", dsi_host_node, lcd_name);
+               of_node_put(endpoint);
+               return -ENODEV;
+       }
+
+       DRM_INFO("%pOF: find %s node\n", dsi_host_node, lcd_name);
+
+       host = of_find_mipi_dsi_host_by_node(dsi_host_node);
+       of_node_put(dsi_host_node);
+       if (!host) {
+               of_node_put(endpoint);
+               return -EPROBE_DEFER;
+       }
+
+       drm_panel_init(&lt9711->base, dev, &lt9711_panel_funcs,
+                       DRM_MODE_CONNECTOR_DisplayPort);
+
+       /* This appears last, as it's what will unblock the DSI host
+        * driver's component bind function.
+        */
+       drm_panel_add(&lt9711->base);
+
+       lt9711->base.dev->of_node = lcd_node;
+       info.node = of_node_get(of_graph_get_remote_port(endpoint));
+       if (!info.node)
+               goto error;
+       of_node_put(endpoint);
+
+       lt9711->dsi = mipi_dsi_device_register_full(host, &info);
+       if (IS_ERR(lt9711->dsi)) {
+               dev_err(dev, "DSI device registration failed: %ld\n",
+                       PTR_ERR(lt9711->dsi));
+               return PTR_ERR(lt9711->dsi);
+       }
+
+       INIT_DELAYED_WORK(&lt9711->detect_work, detect_work_func);
+       schedule_delayed_work(&lt9711->detect_work,
+                               msecs_to_jiffies(2000));
+       lt9711->detect_work_pending = true;
+
+       return 0;
+error:
+       of_node_put(endpoint);
+       return -ENODEV;
+}
+
+static void lt9711_remove(struct i2c_client *client)
+{
+       struct lt9711 *lt9711 = i2c_get_clientdata(client);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (lt9711->detect_work_pending) {
+               cancel_delayed_work_sync(&lt9711->detect_work);
+               lt9711->detect_work_pending = false;
+       }
+
+       lt9711->connector = NULL;
+       lt9711->spacemit_dsi = NULL;
+
+       mipi_dsi_detach(lt9711->dsi);
+       drm_panel_remove(&lt9711->base);
+       mipi_dsi_device_unregister(lt9711->dsi);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int lt9711_drv_pm_suspend(struct device *dev)
+{
+       struct lt9711 *lt9711 = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (lt9711->detect_work_pending) {
+               cancel_delayed_work_sync(&lt9711->detect_work);
+               lt9711->detect_work_pending = false;
+       }
+
+       gpiod_direction_output(lt9711->enable_gpio, 0);
+       usleep_range(50*1000, 100*1000); //100ms
+
+       return 0;
+}
+
+static int lt9711_drv_pm_resume(struct device *dev)
+{
+       struct lt9711 *lt9711 = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       gpiod_direction_output(lt9711->enable_gpio, 1);
+       usleep_range(50*1000, 100*1000); //100ms
+
+       if (!lt9711->detect_work_pending) {
+               schedule_delayed_work(&lt9711->detect_work,
+                               msecs_to_jiffies(2000));
+               lt9711->detect_work_pending = true;
+       }
+
+       return 0;
+}
+
+#endif
+
+static const struct dev_pm_ops lt9711_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(lt9711_drv_pm_suspend,
+                               lt9711_drv_pm_resume)
+};
+
+static struct i2c_device_id lt9711_id[] = {
+       { "lontium,lt9711", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, lt9711_id);
+
+static const struct of_device_id lt9711_match_table[] = {
+       { .compatible = "lontium,lt9711" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lt9711_match_table);
+
+static struct i2c_driver lt9711_driver = {
+       .driver = {
+               .name = "lt9711",
+               .of_match_table = lt9711_match_table,
+               .pm = &lt9711_pm_ops,
+       },
+       .probe = lt9711_probe,
+       .remove = lt9711_remove,
+       .id_table = lt9711_id,
+};
+
+static int lt9711_dsi_probe(struct mipi_dsi_device *dsi)
+{
+       int ret;
+       struct mipi_dsi_host *host;
+       struct drm_panel *panel;
+       struct spacemit_dsi *mipi_dsi;
+       struct lt9711 *lt9711;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       dsi->lanes = 4;
+       dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+                               MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+                               MIPI_DSI_MODE_LPM);
+       dsi->format = MIPI_DSI_FMT_RGB888;
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0) {
+               dev_err(&dsi->dev, "failed to attach dsi to host\n");
+               mipi_dsi_device_unregister(dsi);
+       } else {
+               host = dsi->host;
+               mipi_dsi = host_to_dsi(host);
+
+               panel = mipi_dsi->panel;
+               lt9711 = panel_to_lt9711(panel);
+
+               mipi_dsi->ctx.dsi_subconnector = SPACEMIT_DSI_SUBCONNECTOR_DP;
+               mipi_dsi->ctx.previous_connector_status = lt9711->status;
+               mipi_dsi->ctx.connector_status = mipi_dsi->ctx.previous_connector_status;
+
+               lt9711->connector = &mipi_dsi->connector;
+               lt9711->spacemit_dsi = &mipi_dsi->ctx;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id spacemit_dp_of_match[] = {
+       { .compatible = "spacemit,dp-panel" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spacemit_edp_of_match);
+
+static struct mipi_dsi_driver lt9711_dsi_driver = {
+       .probe = lt9711_dsi_probe,
+       .driver = {
+               .name = IT9711_DSI_DRIVER_NAME,
+               .of_match_table = spacemit_dp_of_match,
+       },
+};
+
+
+static int __init init_lt9711(void)
+{
+       int err;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       mipi_dsi_driver_register(&lt9711_dsi_driver);
+       err = i2c_add_driver(&lt9711_driver);
+
+       return err;
+}
+
+module_init(init_lt9711);
+
+static void __exit exit_lt9711(void)
+{
+       DRM_DEBUG("%s()\n", __func__);
+
+       i2c_del_driver(&lt9711_driver);
+       mipi_dsi_driver_unregister(&lt9711_dsi_driver);
+}
+module_exit(exit_lt9711);
+
+MODULE_DESCRIPTION("LT9711_MIPI to DP Reg Setting driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/spacemit_bootloader.c b/drivers/gpu/drm/spacemit/spacemit_bootloader.c
new file mode 100644 (file)
index 0000000..1bd7460
--- /dev/null
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+
+static bool spacemit_dpu_free_logo = false;
+
+int spacemit_dpu_free_bootloader_mem(struct reserved_mem *rmem)
+{
+       void __iomem *rmem_vaddr_start;
+       void __iomem *rmem_vaddr_end;
+
+       if (spacemit_dpu_free_logo)
+               return 0;
+
+       pr_info("Reserved memory: detected bootloader logo memory at %pa, size %ld MB\n",
+               &rmem->base, (unsigned long)rmem->size / SZ_1M);
+
+       rmem_vaddr_start = phys_to_virt(rmem->base);
+       rmem_vaddr_end = phys_to_virt(rmem->base + rmem->size - 1);
+
+       free_reserved_area(rmem_vaddr_start, rmem_vaddr_end, -1, "framebuffer");
+
+       spacemit_dpu_free_logo = true;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/spacemit/spacemit_bootloader.h b/drivers/gpu/drm/spacemit/spacemit_bootloader.h
new file mode 100644 (file)
index 0000000..0a0a74f
--- /dev/null
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_BOOTLOADER_H_
+#define _SPACEMIT_BOOTLOADER_H_
+
+#include <linux/of_reserved_mem.h>
+
+int spacemit_dpu_free_bootloader_mem(struct reserved_mem *rmem);
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/spacemit_cmdlist.c b/drivers/gpu/drm/spacemit/spacemit_cmdlist.c
new file mode 100644 (file)
index 0000000..266fd2c
--- /dev/null
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/sort.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include "spacemit_cmdlist.h"
+#include "spacemit_dpu_reg.h"
+#include "spacemit_drm.h"
+#include "dpu/dpu_saturn.h"
+
+#define CL_HEADER_SZ sizeof(struct cmdlist_header)
+#define CL_ROW_SZ    sizeof(struct cmdlist_row)
+
+static inline struct
+spacemit_plane_state *cl_to_spacemit_pstate(const struct cmdlist *cl)
+{
+       return container_of(cl, struct spacemit_plane_state, cl);
+}
+
+static void print_row(struct cmdlist_row *row) {
+       u32 * p = (u32 *) row;
+       DRM_DEBUG("print_row: 0x%02x, 0x%02x, 0x%02x, 0x%02x", *p, *(p+1), *(p+2), *(p+3));
+}
+
+static int cmdlist_reg_cmp(const void * r1, const void * r2) {
+       const struct cmdlist_reg *reg1 = (const struct cmdlist_reg *) r1;
+       const struct cmdlist_reg *reg2 = (const struct cmdlist_reg *) r2;
+       if (reg1->offset > reg2->offset)
+               return 1;
+       else if (reg1->offset < reg2->offset)
+               return -1;
+       else
+               return 0;
+}
+
+static void cmdlist_reg_swap(void * r1, void * r2, int size) {
+       struct cmdlist_reg *reg1 = (struct cmdlist_reg *) r1;
+       struct cmdlist_reg *reg2 = (struct cmdlist_reg *) r2;
+       struct cmdlist_reg tmp = *reg1;
+       *reg1 = *reg2;
+       *reg2 = tmp;
+}
+
+void cmdlist_regs_packing(struct drm_plane *plane) {
+       struct spacemit_dpu *dpu = crtc_to_dpu(plane->state->crtc);
+       struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(plane->state);
+       struct cmdlist *cl = &spacemit_pstate->cl;
+       struct cmdlist_row *row;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct cmdlist_reg *regs = priv->cmdlist_regs;
+       int i;
+
+       cl->size = PER_CMDLIST_SIZE;
+       cl->va = dma_alloc_coherent(dpu->dev, cl->size, &cl->pa, GFP_KERNEL | __GFP_ZERO);
+       if (cl->va == NULL) {
+               DRM_ERROR("Failed to allocate %d bytes for dpu plane%d cmdlist buffer\n",
+                         PER_CMDLIST_SIZE, plane->state->zpos);
+               return;
+       }
+
+       sort(regs, priv->cmdlist_num, sizeof(struct cmdlist_reg), cmdlist_reg_cmp, cmdlist_reg_swap);
+       for (i = 0; i < priv->cmdlist_num; i++) {
+               DRM_DEBUG("cmdlist_reg, regs[%d] = {0x%02x, 0x%02x}", i, regs[i].offset, regs[i].value);
+       }
+
+       DRM_DEBUG("-----cmdlist_regs_packing----- priv->cmdlist_num = %u, rch_id = %d, ch_y = %u\n",
+                 priv->cmdlist_num, spacemit_pstate->rdma_id, spacemit_pstate->state.crtc_y);
+
+       row = (struct cmdlist_row *)((char *)cl->va + CL_HEADER_SZ);
+       for (i = 0; i < priv->cmdlist_num;) {
+               row->module_cfg_addr = regs[i].offset >> 2;
+               row->module_regs[0] = regs[i].value;
+               row->module_cfg_num = 1;
+               row->module_cfg_strobe = 0xf;
+               if (i + 1 < priv->cmdlist_num) {
+                       if (regs[i + 1].offset - regs[i].offset == sizeof(u32)) {
+                               row->module_regs[1] = regs[i + 1].value;
+                               row->module_cfg_num = 2;
+                               row->module_cfg_strobe = 0xff;
+                       }
+               }
+               if (i + 2 < priv->cmdlist_num) {
+                       if (regs[i + 2].offset - regs[i].offset == sizeof(u32) * 2) {
+                               row->module_regs[2] = regs[i + 2].value;
+                               row->module_cfg_num = 3;
+                               row->module_cfg_strobe = 0xfff;
+                       }
+               }
+
+               print_row(row);
+
+               i += row->module_cfg_num;
+               cl->nod_len++;
+               row = (struct cmdlist_row *)((char *)row + CL_ROW_SZ);
+       }
+       priv->cmdlist_num = 0; //clear cmdlist_regs buffer
+       DRM_DEBUG("-----cmdlist_regs_packing----- row_num = %d\n", row->module_cfg_num);
+}
+
+static inline void fill_top_row(struct cmdlist *cl) {
+       struct cmdlist_header *header;
+       struct cmdlist_row *row;
+       unsigned int cl_addr_h = 0;
+       struct spacemit_plane_state *spacemit_pstate = cl_to_spacemit_pstate(cl);
+       u8 rch_id = spacemit_pstate->rdma_id;
+       u8 scl_id = spacemit_pstate->scaler_id;
+       bool use_scl = spacemit_pstate->use_scl;
+       unsigned int zpos = spacemit_pstate->state.zpos;
+       u32 size = 0;
+
+       // fill header
+       header = (struct cmdlist_header *)(cl->va);
+       if (cl->next) {
+               header->next_list_addr = cl->next->pa;
+       } else
+               header->list_tag = 1;
+       // end cl->nod_len = cl->nod_len + 4, 3(row) + 1(head)
+       header->nod_len = cl->nod_len + 4;
+
+       // fill scaler row;
+       row = (struct cmdlist_row *)((char *)header + CL_HEADER_SZ + cl->nod_len * CL_ROW_SZ);
+       row->module_cfg_addr = ((DPU_CTRL_BASE_ADDR + DPU_NUM_REUSE_SCL) >> 2) + scl_id;
+       row->module_cfg_strobe = 0xff;
+       if (scl_id != SCALER_INVALID_ID) {
+               row->module_cfg_num = 1;
+               if (use_scl)
+                       row->module_regs[0] = 1;
+               else
+                       row->module_regs[0] = 0;
+       } else
+               row->module_cfg_num = 0;
+
+       // fill ch_y row
+       row = (struct cmdlist_row *)((char *)row + CL_ROW_SZ);
+       cl_addr_h = header->next_list_addr >> 32;
+       row->module_cfg_addr = (CMDLIST_BASE_ADDR + CMDLIST_CH_Y + rch_id * 4) >> 2;
+       row->module_cfg_num = cl->next ? 1 : 0;
+       row->module_cfg_strobe = 0xf;
+       row->module_regs[0] = cl->next ? cl_to_spacemit_pstate(cl->next)->state.crtc_y << 8 | \
+                             (cl_addr_h & CMDLIST_ADDRH_MASK) : 0;
+       row->module_regs[1] = 0;
+
+       // fill last_row
+       row = (struct cmdlist_row *)((char *)row + CL_ROW_SZ);
+       row->module_cfg_addr = (DPU_CTRL_BASE_ADDR + CMDLIST_CFG_READY) >> 2;
+       row->module_cfg_num = 1;
+       row->module_cfg_strobe = 0xf0f;
+       row->row_eof_tag = 3;
+       row->module_regs[0] = 1 << rch_id;
+       row->module_regs[2] = rch_id;
+
+       size = (unsigned long)((char *)row + CL_ROW_SZ) - (unsigned long)header;
+       if (size > PER_CMDLIST_SIZE)
+               DRM_ERROR("plane%d cmdlist occupies %d bytes!\n", zpos, size);
+}
+
+void cmdlist_sort_by_group(struct drm_crtc *crtc) {
+       struct cmdlist * cur_cl;
+       struct cmdlist * p;
+       struct cmdlist * prev;
+       struct drm_plane *plane;
+       struct spacemit_dpu_rdma *rdmas = to_spacemit_crtc_state(crtc->state)->rdmas;
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+
+       drm_atomic_crtc_for_each_plane(plane, crtc) {
+               struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(plane->state);
+               u32 rdma_id = spacemit_pstate->rdma_id;
+               cur_cl = (struct cmdlist *)(&(spacemit_pstate->cl));
+
+               if (!cur_cl->va)
+                       continue;
+
+               rdmas[rdma_id].in_use = true;
+               if (priv->cmdlist_groups[rdma_id]) {
+                       p = priv->cmdlist_groups[rdma_id];
+                       prev = NULL;
+                       while(p) {
+                               if (cl_to_spacemit_pstate(p)->state.crtc_y < spacemit_pstate->state.crtc_y) {
+                                       prev = p;
+                                       p = p->next;
+                               }
+                               else
+                                       break;
+                       }
+                       if (!prev) {
+                               priv->cmdlist_groups[rdma_id] = cur_cl;
+                               cur_cl->next = p;
+                       } else {
+                               prev->next = cur_cl;
+                               cur_cl->next = p;
+                       }
+               } else {
+                       priv->cmdlist_groups[rdma_id] = cur_cl;
+                       cur_cl->next = NULL;
+               }
+       }
+}
+
+void cmdlist_atomic_commit(struct drm_crtc *crtc,
+                          struct drm_crtc_state *old_state) {
+       int i;
+       struct cmdlist *cur_cl, *first_cl;
+       u32 val;
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       struct spacemit_dpu_rdma *cur_rdmas = to_spacemit_crtc_state(crtc->state)->rdmas;
+       struct spacemit_dpu_rdma *old_rdmas = to_spacemit_crtc_state(old_state)->rdmas;
+
+       for (i = 0; i < hwdev->rdma_nums; i++) {
+               /* Shut down the rdma used in previous frame first */
+               if (old_rdmas[i].in_use) {
+                       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR,
+                                     dpu_ctl_reg_69[i].ctl_nml_cmdlist_rch_en, 0);
+                       dpu_write_reg(hwdev, CMDLIST_REG, CMDLIST_BASE_ADDR, value32[i], 0);
+               }
+
+               if (cur_rdmas[i].in_use) {
+                       DRM_DEBUG("+++++cmdlist_atomic_commit+++++ cmdlist group = %d\n", i);
+                       cur_cl = priv->cmdlist_groups[i];
+                       first_cl = cur_cl;
+                       while(cur_cl) {
+                               fill_top_row(cur_cl);
+                               cur_cl = cur_cl->next;
+                       }
+                       val = cl_to_spacemit_pstate(first_cl)->state.crtc_y;
+                       dpu_write_reg(hwdev, CMDLIST_REG, CMDLIST_BASE_ADDR, cmdlist_reg_14[i].cmdlist_ch_y, val);
+                       val = ((priv->cmdlist_groups[i]->pa) & CMDLIST_ADDRL_ALIGN_MASK) >> CMDLIST_ADDRL_ALIGN_BITS;
+                       dpu_write_reg(hwdev, CMDLIST_REG, CMDLIST_BASE_ADDR, cmdlist_reg_0[i].cmdlist_ch_start_addrl, val);
+#if defined (CONFIG_ARM64) || defined (CONFIG_ARM_LPAE)
+                       val = (priv->cmdlist_groups[i]->pa) >> 32;
+                       dpu_write_reg(hwdev, CMDLIST_REG, CMDLIST_BASE_ADDR, cmdlist_reg_14[i].cmdlist_ch_start_addrh, val);
+#else
+                       dpu_write_reg(hwdev, CMDLIST_REG, CMDLIST_BASE_ADDR, cmdlist_reg_14[i].cmdlist_ch_start_addrh, 0);
+#endif
+                       dpu_write_reg(hwdev, DPU_CTL_REG, DPU_CTRL_BASE_ADDR,
+                                     dpu_ctl_reg_69[i].ctl_nml_cmdlist_rch_en, 1);
+                       priv->cmdlist_groups[i] = NULL;
+               }
+       }
+}
diff --git a/drivers/gpu/drm/spacemit/spacemit_cmdlist.h b/drivers/gpu/drm/spacemit/spacemit_cmdlist.h
new file mode 100644 (file)
index 0000000..6c2a1d9
--- /dev/null
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SATURN_FW_CMDLIST_H_
+#define _SATURN_FW_CMDLIST_H_
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_plane.h>
+
+#define PER_CMDLIST_SIZE             4096
+#define CMDLIST_CH_Y                 0x38
+#define DPU_NUM_REUSE_SCL            0xF4
+#define CMDLIST_CFG_READY            0x14C
+
+#define CMDLIST_ADDRH_MASK             (0x3)
+
+#define CMDLIST_REGS_NUM    (PAGE_SIZE / sizeof(struct cmdlist_reg))
+
+struct cmdlist {
+       u16 nod_len;        //the number of cmdlist row.
+       u32 size;
+       void *va;
+       dma_addr_t pa;
+       struct cmdlist * next;
+};
+
+/* cmdlist structure */
+struct cmdlist_header {
+       u64 next_list_addr : 34; //the next cmdlist address
+       // reserved
+       u32  :6;
+       u32 nod_len : 16;
+       // if the cmdlist is last one tag = 1, otherwish tag = 0
+       u32 list_tag : 1;
+       // reserved
+       u32   : 7;
+       u64   : 64;
+};
+
+struct cmdlist_row {
+       u32 module_cfg_addr   : 16;
+       //the last row must 1-2, otherwise 1-3.
+       u32 module_cfg_num    : 2;
+       u32 module_cfg_strobe : 12;
+       //the last row tag = 3, otherwise tag = 0.
+       u32 row_eof_tag       : 2;
+       u32 module_regs[3];
+};
+
+struct cmdlist_reg {
+    uint32_t offset;
+    uint32_t value;
+};
+
+#define dpu_offsetof(TYPE, MEMBER)  ((size_t)&((TYPE *)0)->v.MEMBER)
+#define write_to_cmdlist(priv, module_name, module_base, reg, val) \
+{ \
+       u32 offset = module_base + dpu_offsetof(module_name, reg); \
+       priv->cmdlist_regs[priv->cmdlist_num].value = val; \
+       priv->cmdlist_regs[priv->cmdlist_num++].offset = offset; \
+}
+
+void cmdlist_regs_packing(struct drm_plane *plane);
+void cmdlist_sort_by_group(struct drm_crtc *crtc);
+void cmdlist_atomic_commit(struct drm_crtc *crtc,
+                          struct drm_crtc_state *old_state);
+#endif
diff --git a/drivers/gpu/drm/spacemit/spacemit_dmmu.c b/drivers/gpu/drm/spacemit/spacemit_dmmu.c
new file mode 100644 (file)
index 0000000..e91853c
--- /dev/null
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include "linux/compiler.h"
+#include <linux/stddef.h>
+#include <linux/gfp.h>
+#include <linux/dma-mapping.h>
+#include <drm/drm_framebuffer.h>
+
+#include "spacemit_cmdlist.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_gem.h"
+#include "spacemit_drm.h"
+#include "spacemit_dpu.h"
+#include "spacemit_dpu_reg.h"
+
+static inline void spacemit_dmmu_fill_pgtable(uint32_t *ttbr, struct sg_table *sgt)
+{
+       struct sg_dma_page_iter dma_iter;
+       uint32_t i = 0, temp = 0;
+
+       for_each_sgtable_dma_page(sgt, &dma_iter, 0)
+               *ttbr++ = (sg_page_iter_dma_address(&dma_iter) >> PAGE_SHIFT) & 0x3FFFFF;
+       /*
+        * Due to slicon's alignment access requirement, fill more mmu entries
+        * to avoid ddrc filter error. We simply use the last valid ttbr value.
+        */
+        temp = *(ttbr - 1);
+        for (i = 0; i < HW_ALIGN_TTB_NUM; i++)
+               *ttbr++ = temp;
+}
+
+int spacemit_dmmu_map(struct drm_framebuffer *fb, struct dpu_mmu_tbl *mmu_tbl, u8 tbu_id, bool wb)
+{
+       struct spacemit_drm_private *priv = fb->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       const struct drm_format_info *format = NULL;
+       struct sg_table *sgt = NULL;
+       uint32_t total_size, offset1, offset2;
+       struct tbu_instance tbu = {0x0};
+       u8 fbc_mode, plane_num, rdma_id;
+       u32 val;
+
+       rdma_id = tbu_id / 2;
+
+       format = fb->format;
+       sgt = to_spacemit_obj(fb->obj[0])->sgt;
+
+       if (!wb && priv->contig_mem) {
+               phys_addr_t contig_pa = sg_dma_address(sgt->sgl);
+
+               write_to_cmdlist(priv, MMU_REG, MMU_BASE_ADDR, TBU[tbu_id].TBU_Ctrl, 0x0);
+#if defined (CONFIG_ARM64) || defined (CONFIG_ARM_LPAE)
+               CONFIG_RDMA_ADDR_REG(priv, 0, rdma_id, contig_pa);
+               CONFIG_RDMA_ADDR_REG(priv, 1, rdma_id, (contig_pa + fb->offsets[1]));
+               CONFIG_RDMA_ADDR_REG(priv, 2, rdma_id, (contig_pa + fb->offsets[2]));
+#else
+               CONFIG_RDMA_ADDR_REG_32(priv, 0, rdma_id, contig_pa);
+               CONFIG_RDMA_ADDR_REG_32(priv, 1, rdma_id, (contig_pa + fb->offsets[1]));
+               CONFIG_RDMA_ADDR_REG_32(priv, 2, rdma_id, (contig_pa + fb->offsets[2]));
+#endif
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, RDMA_BASE_ADDR(rdma_id), \
+                                LEFT_RDMA_STRIDE0, (fb->pitches[1] << 16) | fb->pitches[0]);
+
+               return 0;
+       }
+
+       plane_num = format->num_planes;
+       fbc_mode = (fb->modifier > 0);
+       total_size = roundup(fb->obj[0]->size, PAGE_SIZE);
+       offset1 = plane_num > 1 ? fb->offsets[1] : total_size;
+       offset2 = plane_num > 2 ? fb->offsets[2] : total_size;
+
+       switch (plane_num) {
+       case 3:
+               tbu.ttb_pa[2] = mmu_tbl->pa + (offset2 >> PAGE_SHIFT) * 4;
+               tbu.tbu_va[2] = TBU_BASE_VA(tbu_id) + offset2;
+               tbu.ttb_size[2] = PAGE_ALIGN(total_size - rounddown(offset2, PAGE_SIZE)) >> PAGE_SHIFT;
+               fallthrough;
+       case 2:
+               tbu.ttb_pa[1] = mmu_tbl->pa + (offset1 >> PAGE_SHIFT) * 4;
+               tbu.tbu_va[1] = TBU_BASE_VA(tbu_id) + offset1;
+               tbu.ttb_size[1] = PAGE_ALIGN(offset2 - rounddown(offset1, PAGE_SIZE)) >> PAGE_SHIFT;
+               fallthrough;
+       case 1:
+               tbu.ttb_pa[0] = mmu_tbl->pa;
+               tbu.tbu_va[0] = TBU_BASE_VA(tbu_id);
+               tbu.ttb_size[0] = PAGE_ALIGN(offset1) >> PAGE_SHIFT;
+               fallthrough;
+       default:
+               break;
+       }
+       /* Special handling for afbc */
+       if (fbc_mode) {
+               tbu.tbu_va[1] = tbu.tbu_va[0];
+               tbu.ttb_pa[1] = tbu.ttb_pa[0];
+               tbu.ttb_size[1] = tbu.ttb_size[0];
+       }
+
+       spacemit_dmmu_fill_pgtable((uint32_t *)(mmu_tbl->va), sgt);
+
+       val = 0x1 | (fbc_mode << 1) | ((fbc_mode ? 1 : plane_num - 1) << 2);
+       /* Config hw regs */
+       if (wb) {
+               val = val | (DPU_QOS_LOW << 8);
+               CONFIG_WB_ADDR_REG(hwdev, 0, tbu.tbu_va[0]);
+               CONFIG_WB_ADDR_REG(hwdev, 1, tbu.tbu_va[1]);
+               dpu_write_reg(hwdev, WB_TOP_REG, WB0_TOP_BASE_ADDR, \
+                               wb_wdma_stride, (fb->pitches[1] << 16) | fb->pitches[0]);
+               CONFIG_TBU_REGS(priv, hwdev, 0, tbu_id);
+               CONFIG_TBU_REGS(priv, hwdev, 1, tbu_id);
+               CONFIG_TBU_REGS(priv, hwdev, 2, tbu_id);
+               dpu_write_reg(hwdev, MMU_REG, MMU_BASE_ADDR, v.TBU[tbu_id].TBU_Ctrl, val);
+       } else {
+               val = val | (DPU_QOS_URGENT << 8);
+#if defined (CONFIG_ARM64) || defined (CONFIG_ARM_LPAE)
+               CONFIG_RDMA_ADDR_REG(priv, 0, rdma_id, tbu.tbu_va[0]);
+               CONFIG_RDMA_ADDR_REG(priv, 1, rdma_id, tbu.tbu_va[1]);
+               CONFIG_RDMA_ADDR_REG(priv, 2, rdma_id, tbu.tbu_va[2]);
+#else
+               CONFIG_RDMA_ADDR_REG_32(priv, 0, rdma_id, tbu.tbu_va[0]);
+               CONFIG_RDMA_ADDR_REG_32(priv, 1, rdma_id, tbu.tbu_va[1]);
+               CONFIG_RDMA_ADDR_REG_32(priv, 2, rdma_id, tbu.tbu_va[2]);
+#endif
+               write_to_cmdlist(priv, RDMA_PATH_X_REG, RDMA_BASE_ADDR(rdma_id), \
+                               LEFT_RDMA_STRIDE0, (fb->pitches[1] << 16) | fb->pitches[0]);
+               CONFIG_TBU_REGS(priv, NULL, 0, tbu_id);
+               CONFIG_TBU_REGS(priv, NULL, 1, tbu_id);
+               CONFIG_TBU_REGS(priv, NULL, 2, tbu_id);
+               write_to_cmdlist(priv, MMU_REG, MMU_BASE_ADDR, TBU[tbu_id].TBU_Ctrl, val);
+       }
+
+       return 0;
+}
+
+void spacemit_dmmu_unmap(struct drm_plane *plane)
+{
+       return;
+}
diff --git a/drivers/gpu/drm/spacemit/spacemit_dmmu.h b/drivers/gpu/drm/spacemit/spacemit_dmmu.h
new file mode 100644 (file)
index 0000000..a5998c3
--- /dev/null
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DMMU_H_
+#define _SPACEMIT_DMMU_H_
+
+#include <linux/types.h>
+
+#include <drm/drm_file.h>
+#include "dpu/dpu_saturn.h"
+
+#define DPU_QOS_URGENT 4
+#define DPU_QOS_NORMAL 3
+#define DPU_QOS_LOW    2
+/*
+ * In worst case, the tlb alignment requires src_x * 16 * pixel_bytes.
+ * The maximum src_x currently support is 4096. So we need to fill
+ * extra 60 (4096*15*4) entries into mmu page table.
+ */
+#define HW_ALIGN_TTB_NUM       60
+
+#define RD_OUTS_NUM             16
+#define RDMA_TIMELIMIT  0xFFFF
+
+#define BASE_VA         0x10000000ULL
+#define VA_STEP_PER_TBU 0x40000000ULL
+
+#define TBU_BASE_VA(tbu_id) ((uint64_t)BASE_VA + (uint64_t)VA_STEP_PER_TBU * tbu_id)
+
+#define CONFIG_TBU_REGS(priv, hwdev, reg_id, tbu_id) \
+{\
+       struct spacemit_hw_device *hwdev_p = (struct spacemit_hw_device *)hwdev; \
+       if (hwdev_p) { \
+               dpu_write_reg(hwdev_p, MMU_REG, MMU_BASE_ADDR, v.TBU[tbu_id].TBU_Base_Addr##reg_id##_Low, \
+                               tbu.ttb_pa[reg_id] & 0xFFFFFFFF); \
+               dpu_write_reg(hwdev_p, MMU_REG, MMU_BASE_ADDR, v.TBU[tbu_id].TBU_Base_Addr##reg_id##_High, \
+                               (tbu.ttb_pa[reg_id] >> 32) & 0x3); \
+               dpu_write_reg(hwdev_p, MMU_REG, MMU_BASE_ADDR, v.TBU[tbu_id].TBU_VA##reg_id, \
+                               (tbu.tbu_va[reg_id] >> 12) & 0x3FFFFF); \
+               dpu_write_reg(hwdev_p, MMU_REG, MMU_BASE_ADDR, \
+                               v.TBU[tbu_id].TBU_SIZE##reg_id, tbu.ttb_size[reg_id]); \
+       } else { \
+               write_to_cmdlist(priv, MMU_REG, MMU_BASE_ADDR, \
+                               TBU[tbu_id].TBU_Base_Addr##reg_id##_Low, \
+                               tbu.ttb_pa[reg_id] & 0xFFFFFFFF); \
+               write_to_cmdlist(priv, MMU_REG, MMU_BASE_ADDR, \
+                               TBU[tbu_id].TBU_Base_Addr##reg_id## _High, \
+                               (tbu.ttb_pa[reg_id] >> 32) & 0x3); \
+               write_to_cmdlist(priv, MMU_REG, MMU_BASE_ADDR, TBU[tbu_id].TBU_VA##reg_id, \
+                               (tbu.tbu_va[reg_id] >> 12) & 0x3FFFFF); \
+               write_to_cmdlist(priv, MMU_REG, MMU_BASE_ADDR, \
+                               TBU[tbu_id].TBU_SIZE##reg_id, tbu.ttb_size[reg_id]); \
+       } \
+}
+
+#define CONFIG_RDMA_ADDR_REG(priv, reg_id, rdma_id, addr) \
+{ \
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, RDMA_BASE_ADDR(rdma_id), \
+                        LEFT_BASE_ADDR##reg_id##_LOW, addr & 0xFFFFFFFF); \
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, RDMA_BASE_ADDR(rdma_id), \
+                        LEFT_BASE_ADDR##reg_id##_HIGH, (addr >> 32) & 0x3); \
+}
+
+#define CONFIG_RDMA_ADDR_REG_32(priv, reg_id, rdma_id, addr) \
+{ \
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, RDMA_BASE_ADDR(rdma_id), \
+                        LEFT_BASE_ADDR##reg_id##_LOW, addr & 0xFFFFFFFF); \
+       write_to_cmdlist(priv, RDMA_PATH_X_REG, RDMA_BASE_ADDR(rdma_id), \
+                        LEFT_BASE_ADDR##reg_id##_HIGH, 0); \
+}
+
+#define CONFIG_WB_ADDR_REG(hwdev, reg_id, addr) \
+{ \
+       struct spacemit_hw_device *hwdev_p = (struct spacemit_hw_device *)hwdev; \
+       dpu_write_reg(hwdev_p, WB_TOP_REG, WB0_TOP_BASE_ADDR, \
+                       wb_wdma_base_addr##reg_id##_low, addr & 0xFFFFFFFF); \
+       dpu_write_reg(hwdev_p, WB_TOP_REG, WB0_TOP_BASE_ADDR, \
+                       wb_wdma_base_addr##reg_id##_high, (addr >> 32) & 0x3); \
+}
+
+struct tbu_instance {
+       uint64_t ttb_pa[3];
+       uint64_t tbu_va[3];
+       uint32_t ttb_size[3];
+};
+
+int spacemit_dmmu_map(struct drm_framebuffer *fb, struct dpu_mmu_tbl *mmu_tbl, u8 tbu_id, bool wb);
+void spacemit_dmmu_unmap(struct drm_plane *plane);
+
+#endif /* _SPACEMIT_DMMU_H_ */
diff --git a/drivers/gpu/drm/spacemit/spacemit_dphy.c b/drivers/gpu/drm/spacemit/spacemit_dphy.c
new file mode 100644 (file)
index 0000000..8510c14
--- /dev/null
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "spacemit_dphy.h"
+#include "sysfs/sysfs_display.h"
+
+LIST_HEAD(dphy_core_head);
+
+int spacemit_dphy_resume(struct spacemit_dphy *dphy)
+{
+       if (dphy->core && dphy->core->init)
+               dphy->core->init(&dphy->ctx);
+
+       DRM_DEBUG("dphy resume OK\n");
+       return 0;
+}
+
+int spacemit_dphy_suspend(struct spacemit_dphy *dphy)
+{
+       if (dphy->core && dphy->core->uninit)
+               dphy->core->uninit(&dphy->ctx);
+
+       DRM_DEBUG("dphy suspend OK\n");
+       return 0;
+}
+
+int spacemit_dphy_reset(struct spacemit_dphy *dphy)
+{
+       if (dphy->core && dphy->core->reset)
+               dphy->core->reset(&dphy->ctx);
+
+       DRM_DEBUG("dphy reset OK\n");
+       return 0;
+}
+
+int spacemit_dphy_get_status(struct spacemit_dphy *dphy)
+{
+       if (dphy->core && dphy->core->get_status)
+               dphy->core->get_status(&dphy->ctx);
+
+       DRM_DEBUG("dphy get status OK\n");
+       return 0;
+}
+
+static int spacemit_dphy_device_create(struct spacemit_dphy *dphy,
+                                  struct device *parent)
+{
+       int ret;
+
+       dphy->dev.class = display_class;
+       dphy->dev.parent = parent;
+       dphy->dev.of_node = parent->of_node;
+       dev_set_name(&dphy->dev, "dphy%d", dphy->ctx.id);
+       dev_set_drvdata(&dphy->dev, dphy);
+
+       ret = device_register(&dphy->dev);
+       if (ret)
+               DRM_ERROR("dphy device register failed\n");
+
+       return ret;
+}
+
+static int spacemit_dphy_context_init(struct spacemit_dphy *dphy,
+                                 struct device_node *np)
+{
+       struct resource r;
+       u32 tmp;
+
+       if (dphy->core && dphy->core->parse_dt)
+               dphy->core->parse_dt(&dphy->ctx, np);
+
+       if (!of_address_to_resource(np, 0, &r)) {
+               dphy->ctx.base_addr = (void __iomem *)ioremap(r.start, resource_size(&r));
+               if (NULL == dphy->ctx.base_addr) {
+                       DRM_ERROR("dphy ctrlbase ioremap failed\n");
+                       return -EFAULT;
+               }
+       } else {
+               DRM_ERROR("parse dphy ctrl reg base failed\n");
+               return -EINVAL;
+       }
+
+       if (!of_property_read_u32(np, "dev-id", &tmp))
+               dphy->ctx.id = tmp;
+
+       return 0;
+}
+
+static int spacemit_dphy_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct spacemit_dphy *dphy;
+       const char *str;
+       int ret;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
+       if (!dphy)
+               return -ENOMEM;
+
+       if (!of_property_read_string(np, "ip", &str))
+               dphy->core = dphy_core_ops_attach(str);
+       else
+               DRM_WARN("dphy pll ops parse failed\n");
+
+       ret = spacemit_dphy_context_init(dphy, pdev->dev.of_node);
+       if (ret)
+               return ret;
+
+       spacemit_dphy_device_create(dphy, &pdev->dev);
+       spacemit_dphy_sysfs_init(&dphy->dev);
+       platform_set_drvdata(pdev, dphy);
+
+       DRM_DEBUG("dphy driver probe success\n");
+
+       return 0;
+}
+
+
+static const struct of_device_id dt_ids[] = {
+       { .compatible = "spacemit,dsi0-phy", },
+       { .compatible = "spacemit,dsi1-phy", },
+       { .compatible = "spacemit,dsi2-phy", },
+       {},
+};
+
+struct platform_driver spacemit_dphy_driver = {
+       .probe = spacemit_dphy_probe,
+       .driver = {
+               .name = "spacemit-dphy-drv",
+               .of_match_table = of_match_ptr(dt_ids),
+       }
+};
+
+//module_platform_driver(spacemit_dphy_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Spacemit MIPI DSI PHY driver");
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_dphy.h b/drivers/gpu/drm/spacemit/spacemit_dphy.h
new file mode 100644 (file)
index 0000000..80c4527
--- /dev/null
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DPHY_H_
+#define _SPACEMIT_DPHY_H_
+
+#include <asm/types.h>
+#include <drm/drm_print.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "spacemit_lib.h"
+
+#define DPHY_BITCLK_DEFAULT    614400000
+#define DPHY_ESCCLK_DEFAULT    51200000
+
+enum spacemit_dphy_lane_map {
+       DPHY_LANE_MAP_0123 = 0,
+       DPHY_LANE_MAP_0312 = 1,
+       DPHY_LANE_MAP_0231 = 2,
+       DPHY_LANE_MAP_MAX
+};
+
+enum spacemit_dphy_status {
+       DPHY_STATUS_UNINIT = 0,
+       DPHY_STATUS_INIT = 1,
+       DPHY_STATUS_MAX
+};
+
+enum spacemit_dphy_bit_clk_src {
+       DPHY_BIT_CLK_SRC_PLL5 = 1,
+       DPHY_BIT_CLK_SRC_MUX = 2,
+       DPHY_BIT_CLK_SRC_MAX
+};
+
+struct spacemit_dphy_timing {
+       uint32_t hs_prep_constant;    /* Unit: ns. */
+       uint32_t hs_prep_ui;
+       uint32_t hs_zero_constant;
+       uint32_t hs_zero_ui;
+       uint32_t hs_trail_constant;
+       uint32_t hs_trail_ui;
+       uint32_t hs_exit_constant;
+       uint32_t hs_exit_ui;
+       uint32_t ck_zero_constant;
+       uint32_t ck_zero_ui;
+       uint32_t ck_trail_constant;
+       uint32_t ck_trail_ui;
+       uint32_t req_ready;
+       uint32_t wakeup_constant;
+       uint32_t wakeup_ui;
+       uint32_t lpx_constant;
+       uint32_t lpx_ui;
+};
+
+struct spacemit_dphy_ctx {
+       u8 id;
+       void __iomem *base_addr;
+       uint32_t phy_freq; /*kHz*/
+       uint32_t lane_num;
+       uint32_t esc_clk; /*kHz*/
+       uint32_t half_pll5;
+       enum spacemit_dphy_bit_clk_src clk_src;
+       struct spacemit_dphy_timing dphy_timing;
+       uint32_t dphy_status0; /*status0 reg*/
+       uint32_t dphy_status1; /*status1 reg*/
+       uint32_t dphy_status2; /*status2 reg*/
+
+       enum spacemit_dphy_status status;
+};
+
+struct dphy_core_ops {
+       int (*parse_dt)(struct spacemit_dphy_ctx *ctx, struct device_node *np);
+       void (*init)(struct spacemit_dphy_ctx *ctx);
+       void (*uninit)(struct spacemit_dphy_ctx *ctx);
+       void (*reset)(struct spacemit_dphy_ctx *ctx);
+       void (*get_status)(struct spacemit_dphy_ctx *ctx);
+};
+
+struct spacemit_dphy {
+       struct device dev;
+       struct spacemit_dphy_ctx ctx;
+       struct dphy_core_ops *core;
+};
+
+extern struct list_head dphy_core_head;
+
+#define dphy_core_ops_register(entry) \
+       disp_ops_register(entry, &dphy_core_head)
+#define dphy_core_ops_attach(str) \
+       disp_ops_attach(str, &dphy_core_head)
+
+int spacemit_dphy_resume(struct spacemit_dphy *dphy);
+int spacemit_dphy_suspend(struct spacemit_dphy *dphy);
+int spacemit_dphy_reset(struct spacemit_dphy *dphy);
+int spacemit_dphy_get_status(struct spacemit_dphy *dphy);
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/spacemit_dpu.c b/drivers/gpu/drm/spacemit/spacemit_dpu.c
new file mode 100644 (file)
index 0000000..752e26e
--- /dev/null
@@ -0,0 +1,1135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/component.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/trace_events.h>
+#include <linux/of_reserved_mem.h>
+#include <dt-bindings/display/spacemit-dpu.h>
+#include "spacemit_cmdlist.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_drm.h"
+#include "spacemit_dpu.h"
+#include "spacemit_gem.h"
+#include "spacemit_lib.h"
+#include "spacemit_bootloader.h"
+#include "dpu/dpu_saturn.h"
+#include "dpu/dpu_debug.h"
+#include "sysfs/sysfs_display.h"
+#include "dpu/dpu_trace.h"
+
+LIST_HEAD(dpu_core_head);
+
+static int spacemit_dpu_init(struct spacemit_dpu *dpu);
+static int spacemit_dpu_uninit(struct spacemit_dpu *dpu);
+static int dpu_pm_suspend(struct device *dev);
+static int dpu_pm_resume(struct device *dev);
+
+static int spacemit_crtc_atomic_check_color_matrix(struct drm_crtc *crtc,
+                                         struct drm_crtc_state *state)
+{
+       struct spacemit_crtc_state *ac = to_spacemit_crtc_state(state);
+       struct drm_property_blob *blob = ac->color_matrix_blob_prop;
+       int *data;
+       int n;
+
+       if (blob){
+               data = (int *)blob->data;
+               for (n = 0; n < 9; n++){
+                       if ((data[n] > 8191) || (data[n] < -8192)){
+                               DRM_DEBUG("The value of color matrix coeffs is invalid: value %d, n %d\n", data[n], n);
+                               return -EINVAL;
+                       }
+                       else
+                               data[n] = data[n] & 0x3FFF;
+               }
+
+               for (n = 9; n < 12; n++){
+                       if ((data[n] > 4095) || (data[n] < -4096)){
+                               DRM_DEBUG("The value of color matrix offset is invalid: value %d, n %d\n", data[n], n);
+                               return -EINVAL;
+                       }
+                       else
+                               data[n] = data[n] & 0x3FFF;
+               }
+       }
+
+       return 0;
+}
+
+static int spacemit_crtc_atomic_check_scaling(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *crtc_state)
+{
+       struct drm_plane *plane;
+       struct spacemit_crtc_state *ac = to_spacemit_crtc_state(crtc_state);
+       const struct drm_plane_state *pstate;
+       struct spacemit_dpu_scaler *scaler = NULL;
+       struct spacemit_plane_state *spacemit_pstate;
+       u32 i;
+
+       drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+               spacemit_pstate = to_spacemit_plane_state(pstate);
+
+               if (spacemit_pstate->use_scl) {
+                       for (i = 0; i < MAX_SCALER_NUMS; i++) {
+                               scaler = &(ac->scalers[i]);
+                               if (scaler->in_use == 0x0 || scaler->rdma_id == spacemit_pstate->rdma_id) {
+                                       scaler->in_use |= (1 << plane->index);
+                                       scaler->rdma_id = spacemit_pstate->rdma_id;
+                                       break;
+                               }
+                       }
+
+                       if (i == MAX_SCALER_NUMS)
+                               return -EINVAL;
+               }
+       }
+
+       drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+               spacemit_pstate = to_spacemit_plane_state(pstate);
+
+               if (spacemit_pstate->rdma_id != RDMA_INVALID_ID) {
+                       for (i = 0; i < MAX_SCALER_NUMS; i++) {
+                               scaler = &(ac->scalers[i]);
+                               if (scaler->rdma_id == spacemit_pstate->rdma_id)
+                                       spacemit_pstate->scaler_id = i;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* crtc actual mclk depends on max(mclk, aclk) */
+static int spacemit_crtc_atomic_update_mclk(struct drm_crtc *crtc,
+               struct drm_crtc_state *old_state)
+{
+       struct spacemit_crtc_state *new_ac = to_spacemit_crtc_state(crtc->state);
+
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct dpu_clk_context *clk_ctx = NULL;
+
+       if (dpu == NULL)
+               return 0;
+
+       if (!dpu->enable_auto_fc)
+               return 0;
+
+       /* when shutdown, all planes disabled, crtc still work, need keep cur mclk */
+       if (new_ac->real_mclk == 0) {
+               trace_u64_data("new_ac->real_mclk", new_ac->real_mclk);
+               return 0;
+       }
+
+       clk_ctx = &dpu->clk_ctx;
+       cancel_work_sync(&dpu->work_update_clk); /* incase mclk fq doing */
+
+       dpu->cur_mclk = clk_get_rate(clk_ctx->mclk);
+       dpu->new_mclk = clk_round_rate(clk_ctx->mclk, new_ac->real_mclk);
+
+       trace_u64_data("crtc mclk cur", dpu->cur_mclk);
+       trace_u64_data("crtc mclk new", dpu->new_mclk);
+       if (dpu->core && dpu->core->update_clk && (dpu->new_mclk > dpu->cur_mclk)) {
+                       trace_u64_data("mclk increase to", dpu->new_mclk);
+                       dpu->core->update_clk(dpu, dpu->new_mclk);
+       }
+
+       cancel_work_sync(&dpu->work_update_bw);
+
+       dpu->new_bw = new_ac->bw;
+
+       trace_u64_data("crtc bw cur", dpu->cur_bw);
+       trace_u64_data("crtc bw new", dpu->new_bw);
+
+       if (dpu->core && dpu->core->update_bw && (dpu->new_bw > dpu->cur_bw)) {
+                       trace_u64_data("bw increase to", dpu->new_bw);
+                       dpu->core->update_bw(dpu, dpu->new_bw);
+       }
+
+       return 0;
+}
+
+/* crtc aclk depends on sum of all rdma bw */
+static int spacemit_crtc_atomic_check_aclk(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *crtc_state)
+{
+       const struct drm_plane_state *pstate;
+       struct drm_plane *plane;
+       struct spacemit_crtc_state *ac = to_spacemit_crtc_state(crtc_state); /* new state */
+       bool scl_en = false;
+       uint64_t afbc_effc = ~0ULL;
+       uint64_t tmp, tmp1;
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       if (!dpu->enable_auto_fc)
+               return 0;
+
+       drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+               uint64_t tmp_bw = to_spacemit_plane_state(pstate)->bw;
+               bool tmp_scl_en = to_spacemit_plane_state(pstate)->use_scl;
+               ac->bw += tmp_bw;
+               if (tmp_scl_en) {
+                       scl_en = true;
+                       if (afbc_effc > to_spacemit_plane_state(pstate)->afbc_effc)
+                               afbc_effc = to_spacemit_plane_state(pstate)->afbc_effc;
+               }
+       }
+
+       if (ac->bw == 0)
+               ac->bw = DPU_MIN_QOS_REQ;
+
+       if (ac->bw > (DPU_MAX_QOS_REQ * MHZ2KHZ)) {
+               DRM_INFO("crtc:%lld bandwidth too large\n", ac->bw);
+               return -EINVAL;
+       }
+
+       if (scl_en) {
+               //ac->aclk = max(ac->bw / 16 * 10 / 5, ac->mclk / afbc_effc);
+               tmp = ac->bw;
+               do_div(tmp, 16);
+               tmp = tmp * 100;
+               do_div(tmp, 50);
+               tmp1 = ac->mclk;
+               do_div(tmp1, afbc_effc);
+               ac->aclk = max(tmp, tmp1);
+       }
+       else {
+               //ac->aclk = ac->bw / 16 * 10 / 5;
+               tmp = ac->bw;
+               do_div(tmp, 16);
+               tmp = tmp * 100;
+               do_div(tmp, 50);
+               ac->aclk = tmp;
+       }
+
+       trace_u64_data("crtc calc bw", ac->bw);
+       ac->bw += DPU_MARGIN_QOS_REQ * MHZ2KHZ;
+
+       ac->real_mclk = max(ac->mclk, ac->aclk);
+
+       if (ac->real_mclk < dpu->min_mclk)
+               ac->real_mclk = dpu->min_mclk;
+
+       trace_u64_data("crtc fixed bw", ac->bw);
+       trace_u64_data("crtc calc aclk", ac->aclk);
+       trace_u64_data("crtc calc mclk", ac->mclk);
+       trace_u64_data("crtc calc real_mclk", ac->real_mclk);
+
+       return 0;
+}
+
+/* crtc mclk depends on max of all rdma mclk */
+static int spacemit_crtc_atomic_check_mclk(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *crtc_state)
+{
+       const struct drm_plane_state *pstate;
+       struct drm_plane *plane;
+       struct spacemit_crtc_state *ac = to_spacemit_crtc_state(crtc_state);
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       if (!dpu->enable_auto_fc)
+               return 0;
+
+       drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+               uint64_t tmp_mclk = to_spacemit_plane_state(pstate)->mclk;
+               if (ac->mclk < tmp_mclk)
+                       ac->mclk = tmp_mclk;
+       }
+
+       return 0;
+}
+
+static int spacemit_crtc_atomic_check_fbmem(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *crtc_state)
+{
+       struct spacemit_dpu_rdma *rdmas = to_spacemit_crtc_state(crtc_state)->rdmas;
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+
+       const struct drm_plane_state *pstate;
+       struct drm_plane *plane;
+       /* Calc each rdma required fbc mem size */
+       drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+               u32 rdma_id = to_spacemit_plane_state(pstate)->rdma_id;
+               u32 layer_fbcmem_size = to_spacemit_plane_state(pstate)->fbcmem_size;
+               if (rdma_id != RDMA_INVALID_ID) {
+                       if (rdmas[rdma_id].mode == UP_DOWN)
+                               rdmas[rdma_id].fbcmem.size = max(layer_fbcmem_size,
+                                                                rdmas[rdma_id].fbcmem.size);
+                       else
+                               rdmas[rdma_id].fbcmem.size += layer_fbcmem_size;
+               }
+       }
+
+       /* Adjust each rdma's fbcmem layout */
+       return dpu->core->adjust_rdma_fbcmem(hwdev, rdmas);
+
+}
+
+static void spacemit_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct videomode vm;
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_crtc_mode_set_nofb(dpu->dev_id);
+       drm_display_mode_to_videomode(&crtc->mode, &vm);
+}
+
+static void spacemit_crtc_atomic_enable(struct drm_crtc *crtc,
+                                  struct drm_atomic_state *old_state)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_INFO("%s(power on)\n", __func__);
+       trace_spacemit_crtc_atomic_enable(dpu->dev_id);
+
+       pm_runtime_get_sync(dpu->dev);
+       dpu_pm_resume(dpu->dev);
+
+#ifdef CONFIG_SPACEMIT_DEBUG
+       dpu->is_working = true;
+#endif
+       drm_crtc_vblank_on(&dpu->crtc);
+
+       spacemit_dpu_init(dpu);
+
+       if (!IS_ERR_OR_NULL(dpu->enable_gpio)) {
+               gpiod_direction_output(dpu->enable_gpio, 1);
+       }
+}
+
+static void spacemit_crtc_atomic_disable(struct drm_crtc *crtc,
+                                   struct drm_atomic_state *old_state)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct drm_device *drm = dpu->crtc.dev;
+
+       DRM_INFO("%s(power off)\n", __func__);
+       trace_spacemit_crtc_atomic_disable(dpu->dev_id);
+
+       if (!IS_ERR_OR_NULL(dpu->enable_gpio)) {
+               gpiod_direction_output(dpu->enable_gpio, 0);
+       }
+
+       spacemit_dpu_uninit(dpu);
+
+       drm_crtc_vblank_off(&dpu->crtc);
+#ifdef CONFIG_SPACEMIT_DEBUG
+       dpu->is_working = false;
+#endif
+
+       dpu_pm_suspend(dpu->dev);
+       pm_runtime_put_sync(dpu->dev);
+
+       spin_lock_irq(&drm->event_lock);
+       if (crtc->state->event) {
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               crtc->state->event = NULL;
+       }
+       spin_unlock_irq(&drm->event_lock);
+}
+
+static int spacemit_crtc_atomic_check(struct drm_crtc *crtc,
+                                               struct drm_atomic_state *atomic_state)
+{
+       struct drm_crtc_state *state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       int ret = 0;
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_crtc_atomic_check(dpu->dev_id);
+
+       ret = spacemit_crtc_atomic_check_scaling(crtc, state);
+
+       if (spacemit_crtc_atomic_check_color_matrix(crtc, state)){
+               DRM_DEBUG("The value of color matrix is invalid\n");
+               return -EINVAL;
+       }
+
+       if (spacemit_crtc_atomic_check_fbmem(crtc, state)) {
+               DRM_DEBUG("Failed to satisfy fbcmem size for all rdmas!\n");
+               return -EINVAL;
+       }
+
+       if (spacemit_crtc_atomic_check_mclk(crtc, state)) {
+               DRM_ERROR("Failed to satisfy mclk for all rdmas!\n");
+               return -EINVAL;
+       }
+
+       if (spacemit_crtc_atomic_check_aclk(crtc, state)) {
+               DRM_INFO("Failed to satisfy aclk for all rdmas!\n");
+               return -EINVAL;
+       }
+       return ret;
+}
+
+static void spacemit_crtc_atomic_begin(struct drm_crtc *crtc,
+                                 struct drm_atomic_state *state)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_crtc_atomic_begin(dpu->dev_id);
+}
+
+static void spacemit_crtc_atomic_flush(struct drm_crtc *crtc,
+                                 struct drm_atomic_state *state)
+
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_crtc_atomic_flush(dpu->dev_id);
+       // spacemit_dpu_wb_config(dpu);
+       saturn_conf_dpuctrl_color_matrix(dpu, old_state);
+       spacemit_crtc_atomic_update_mclk(crtc, old_state);
+       spacemit_dpu_run(crtc, old_state);
+}
+
+static struct drm_crtc_state *spacemit_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+       struct spacemit_crtc_state *state, *old_state;
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u8 n_rdma, i;
+
+       if (WARN_ON(!crtc->state))
+               return NULL;
+
+       old_state = to_spacemit_crtc_state(crtc->state);
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+       memset(&state->scalers, 0x0, sizeof(struct spacemit_dpu_scaler) * MAX_SCALER_NUMS);
+
+       n_rdma = hwdev->rdma_nums;
+       state->rdmas = kzalloc(sizeof(struct spacemit_dpu_rdma) * n_rdma, GFP_KERNEL);
+       if (!state->rdmas) {
+               kfree(state);
+               return NULL;
+       }
+       /* Rdma use UP_DOWN mode by default */
+       for (i = 0; i < n_rdma; i++) {
+               state->rdmas[i].mode = UP_DOWN;
+               state->rdmas[i].in_use = false;
+       }
+
+       if (state->color_matrix_blob_prop)
+               drm_property_blob_get(state->color_matrix_blob_prop);
+
+       return &state->base;
+}
+
+static void spacemit_crtc_destroy_state(struct drm_crtc *crtc,
+                                     struct drm_crtc_state *state)
+{
+       struct spacemit_crtc_state *spacemit_state = NULL;
+
+       if (state) {
+               spacemit_state = to_spacemit_crtc_state(state);
+               __drm_atomic_helper_crtc_destroy_state(state);
+
+               if (spacemit_state->color_matrix_blob_prop)
+                       drm_property_blob_put(spacemit_state->color_matrix_blob_prop);
+
+               kfree(spacemit_state->rdmas);
+               kfree(spacemit_state);
+       }
+}
+
+static void spacemit_crtc_reset(struct drm_crtc *crtc)
+{
+       struct spacemit_crtc_state *state =
+               kzalloc(sizeof(*state), GFP_KERNEL);
+       struct spacemit_drm_private *priv = crtc->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u8 n_rdma;
+
+       if (crtc->state)
+               spacemit_crtc_destroy_state(crtc, crtc->state);
+
+       __drm_atomic_helper_crtc_reset(crtc, &state->base);
+
+       n_rdma = hwdev->rdma_nums;
+       state->rdmas = kzalloc(sizeof(struct spacemit_dpu_rdma) * n_rdma, GFP_KERNEL);
+       if (!state->rdmas) {
+               DRM_ERROR("Failed to allocate memory of struct spacemit_dpu_rdma!\n");
+               return;
+       }
+
+}
+
+static int spacemit_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_crtc_enable_vblank(dpu->dev_id);
+
+       if (dpu->core && dpu->core->enable_vsync)
+               dpu->core->enable_vsync(dpu);
+
+       return 0;
+}
+
+static void spacemit_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_crtc_disable_vblank(dpu->dev_id);
+
+       if (dpu->core && dpu->core->disable_vsync)
+               dpu->core->disable_vsync(dpu);
+}
+
+static int spacemit_crtc_atomic_set_property(struct drm_crtc *crtc,
+                                  struct drm_crtc_state *state,
+                                  struct drm_property *property,
+                                  uint64_t val)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct spacemit_crtc_state *s = to_spacemit_crtc_state(state);
+       bool replaced = false;
+       int ret = 0;
+
+       DRM_DEBUG("%s() name = %s, val = %llu\n",
+                 __func__, property->name, val);
+
+       if (property == dpu->color_matrix_property){
+               ret = spacemit_atomic_replace_property_blob_from_id(crtc->dev,
+                                       &s->color_matrix_blob_prop,
+                                       val,
+                                       -1,
+                                       sizeof(int),
+                                       &replaced);
+               return ret;
+       } else {
+               DRM_ERROR("property %s is invalid\n", property->name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int spacemit_crtc_atomic_get_property(struct drm_crtc *crtc,
+                                         const struct drm_crtc_state *state,
+                                         struct drm_property *property,
+                                         u64 *val)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+       struct spacemit_crtc_state *s = to_spacemit_crtc_state(state);
+
+       DRM_DEBUG("%s() name = %s\n", __func__, property->name);
+
+       if (property == dpu->color_matrix_property){
+               if (s->color_matrix_blob_prop)
+                       *val = (s->color_matrix_blob_prop) ? s->color_matrix_blob_prop->base.id : 0;
+       }
+       else {
+               DRM_ERROR("property %s is invalid\n", property->name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct drm_crtc_helper_funcs spacemit_crtc_helper_funcs = {
+       .mode_set_nofb = spacemit_crtc_mode_set_nofb,
+       .atomic_check = spacemit_crtc_atomic_check,
+       .atomic_begin = spacemit_crtc_atomic_begin,
+       .atomic_flush = spacemit_crtc_atomic_flush,
+       .atomic_enable = spacemit_crtc_atomic_enable,
+       .atomic_disable = spacemit_crtc_atomic_disable,
+};
+
+static const struct drm_crtc_funcs spacemit_crtc_funcs = {
+       .atomic_get_property = spacemit_crtc_atomic_get_property,
+       .atomic_set_property = spacemit_crtc_atomic_set_property,
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
+       .reset = spacemit_crtc_reset,
+       .atomic_duplicate_state = spacemit_crtc_duplicate_state,
+       .atomic_destroy_state = spacemit_crtc_destroy_state,
+       .enable_vblank = spacemit_crtc_enable_vblank,
+       .disable_vblank = spacemit_crtc_disable_vblank,
+};
+
+static int spacemit_crtc_create_properties(struct drm_crtc *crtc)
+{
+       struct drm_property *prop;
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_DEBUG("%s()\n", __func__);
+       /* create rotation property */
+
+       prop = drm_property_create(crtc->dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
+                       "color_matrix_coef", 0);
+       if (!prop)
+               return -ENOMEM;
+       drm_object_attach_property(&crtc->base, prop, 0);
+       dpu->color_matrix_property = prop;
+
+       return 0;
+}
+
+static int spacemit_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
+                        struct drm_plane *primary, struct device_node *port)
+{
+       int err;
+
+       /*
+        * set crtc port so that drm_of_find_possible_crtcs call works
+        */
+       of_node_put(port);
+       crtc->port = port;
+
+       err = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
+                                       &spacemit_crtc_funcs, NULL);
+       if (err) {
+               DRM_ERROR("failed to init crtc.\n");
+               return err;
+       }
+
+       drm_mode_crtc_set_gamma_size(crtc, 256);
+
+       drm_crtc_helper_add(crtc, &spacemit_crtc_helper_funcs);
+
+       spacemit_crtc_create_properties(crtc);
+
+       DRM_DEBUG("%s() ok\n", __func__);
+       return 0;
+}
+
+int spacemit_dpu_wb_config(struct spacemit_dpu *dpu)
+{
+       if (dpu->core && dpu->core->wb_config)
+               dpu->core->wb_config(dpu);
+
+       return 0;
+}
+
+int spacemit_dpu_run(struct drm_crtc *crtc,
+               struct drm_crtc_state *old_state)
+{
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_DEBUG("%s() type %d \n", __func__, dpu->type);
+       trace_spacemit_dpu_run(dpu->dev_id);
+
+       if (dpu->core && dpu->core->run)
+               dpu->core->run(crtc, old_state);
+
+       return 0;
+}
+
+int spacemit_dpu_stop(struct spacemit_dpu *dpu)
+{
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+       trace_spacemit_dpu_stop(dpu->dev_id);
+
+       if (dpu->core && dpu->core->stop)
+               dpu->core->stop(dpu);
+
+       drm_crtc_handle_vblank(&dpu->crtc);
+
+       return 0;
+}
+
+static int spacemit_dpu_init(struct spacemit_dpu *dpu)
+{
+       trace_spacemit_dpu_init(dpu->dev_id);
+
+       if (dpu->core && dpu->core->init)
+               dpu->core->init(dpu);
+
+       dpu->is_1st_f = true;
+
+       return 0;
+}
+
+static int spacemit_dpu_uninit(struct spacemit_dpu *dpu)
+{
+       trace_spacemit_dpu_uninit(dpu->dev_id);
+
+       if (dpu->core && dpu->core->uninit)
+               dpu->core->uninit(dpu);
+
+       return 0;
+}
+
+static irqreturn_t spacemit_dpu_isr(int irq, void *data)
+{
+       struct spacemit_dpu *dpu = data;
+       u32 int_mask = 0;
+
+       if (dpu->core && dpu->core->isr)
+               int_mask = dpu->core->isr(dpu);
+
+       if (int_mask & DPU_INT_UNDERRUN)
+               DRM_DEBUG("Warning: dpu underrun!\n");
+
+       return IRQ_HANDLED;
+}
+
+static int spacemit_dpu_irqs_init(struct spacemit_dpu *dpu,
+                               struct device_node *np, struct platform_device *pdev)
+{
+       int err;
+       int irq;
+
+       /*request irq*/
+       irq = platform_get_irq_byname(pdev, "ONLINE_IRQ");
+       if (irq < 0) {
+               DRM_ERROR("%s: failed to get ONLINE irq number\n", __func__);
+               return -EINVAL;
+       }
+       DRM_DEBUG("dpu online_irq = %d\n", irq);
+       err = request_irq(irq, spacemit_dpu_isr, 0, "DPU_ONLINE", dpu);
+       if (err) {
+               DRM_ERROR("error: dpu request online irq failed\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq_byname(pdev, "OFFLINE_IRQ");
+       if (irq < 0) {
+               DRM_ERROR("%s: failed to get OFFLINE irq number\n", __func__);
+               return -EINVAL;
+       }
+       DRM_DEBUG("dpu offline_irq = %d\n", irq);
+       err = request_irq(irq, spacemit_dpu_isr, 0, "DPU_OFFLINE", dpu);
+       if (err) {
+               DRM_ERROR("error: dpu request offline irq failed\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void dpu_wq_update_bw(struct work_struct *work)
+{
+       struct spacemit_dpu *dpu =
+               container_of(work, struct spacemit_dpu, work_update_bw);
+
+       if (dpu->core && dpu->core->update_bw) {
+               trace_u64_data("bw decrease to", dpu->new_bw);
+               dpu->core->update_bw(dpu, dpu->new_bw);
+       }
+}
+
+static void dpu_wq_update_clk(struct work_struct *work)
+{
+       struct spacemit_dpu *dpu =
+               container_of(work, struct spacemit_dpu, work_update_clk);
+
+       if (dpu->core && dpu->core->update_clk) {
+               trace_u64_data("mclk decrease to", dpu->new_mclk);
+               dpu->core->update_clk(dpu, dpu->new_mclk);
+       }
+}
+
+#ifdef CONFIG_SPACEMIT_DEBUG
+static bool check_dpu_running_status(struct spacemit_dpu* dpu)
+{
+       return dpu->is_working;
+}
+
+#define to_dpuinfo(_nb) container_of(_nb, struct spacemit_dpu, nb)
+static int dpu_clkoffdet_notifier_handler(struct notifier_block *nb,
+               unsigned long msg, void *data)
+{
+       struct clk_notifier_data *cnd = data;
+       struct spacemit_dpu *dpu = to_dpuinfo(nb);
+
+       if ((__clk_is_enabled(cnd->clk)) && (msg & PRE_RATE_CHANGE) && (cnd->new_rate == 0) && (cnd->old_rate != 0)) {
+               if (dpu->is_dpu_running(dpu))
+               return NOTIFY_BAD;
+       }
+
+       return NOTIFY_OK;
+}
+#endif
+
+static int spacemit_dpu_bind(struct device *dev, struct device *master, void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm_dev = data;
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+       struct device_node *np = dev->of_node;
+       struct drm_plane *plane;
+       int ret;
+#ifdef CONFIG_SPACEMIT_DEBUG
+       struct dpu_clk_context *clk_ctx = NULL;
+#endif
+       DRM_INFO("%s()\n", __func__);
+
+       ret = spacemit_dpu_irqs_init(dpu, np, pdev);
+       if (ret)
+               return ret;
+
+       ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+       if (ret) {
+               DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
+               return ret;
+       }
+
+       ret = of_reserved_mem_device_init(dpu->dev);
+       if (ret) {
+               DRM_ERROR("Failed to reserve dpu memory, ret:%d\n", ret);
+               return ret;
+       }
+
+       dpu->dpu_underrun_wq = create_singlethread_workqueue("dpu_underrun_wq");
+       if (!dpu->dpu_underrun_wq) {
+               DRM_ERROR("%s: failed to create wq.\n", __func__);
+               ret = -ESRCH;
+               goto alloc_fail;
+       }
+
+       INIT_WORK(&dpu->work_stop_trace, dpu_underrun_wq_stop_trace);
+       INIT_WORK(&dpu->work_update_clk, dpu_wq_update_clk);
+       INIT_WORK(&dpu->work_update_bw, dpu_wq_update_bw);
+
+       plane = spacemit_plane_init(drm_dev, dpu);
+       if (IS_ERR_OR_NULL(plane)) {
+               ret = PTR_ERR(plane);
+               goto err_destroy_workqueue;
+       }
+
+       ret = spacemit_crtc_init(drm_dev, &dpu->crtc, plane, np);
+       if (ret)
+               goto err_destroy_workqueue;
+
+       // spacemit_wb_init(drm_dev, &dpu->crtc);
+
+       spacemit_dpu_sysfs_init(dev);
+
+#ifdef CONFIG_SPACEMIT_DEBUG
+       clk_ctx = &dpu->clk_ctx;
+       dpu->is_dpu_running = check_dpu_running_status;
+       dpu->nb.notifier_call = dpu_clkoffdet_notifier_handler;
+       clk_notifier_register(clk_ctx->mclk, &dpu->nb);
+#endif
+       DRM_DEBUG("dpu driver probe success\n");
+
+       return 0;
+
+err_destroy_workqueue:
+       destroy_workqueue(dpu->dpu_underrun_wq);
+alloc_fail:
+       of_reserved_mem_device_release(dpu->dev);
+       return ret;
+}
+
+static void spacemit_dpu_unbind(struct device *dev, struct device *master,
+       void *data)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+#ifdef CONFIG_SPACEMIT_DEBUG
+       struct dpu_clk_context *clk_ctx = &dpu->clk_ctx;
+#endif
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       pm_runtime_disable(dev);
+       of_reserved_mem_device_release(dpu->dev);
+       drm_crtc_cleanup(&dpu->crtc);
+
+#ifdef CONFIG_SPACEMIT_DEBUG
+       clk_notifier_unregister(clk_ctx->mclk, &dpu->nb);
+#endif
+}
+
+static const struct component_ops dpu_component_ops = {
+       .bind = spacemit_dpu_bind,
+       .unbind = spacemit_dpu_unbind,
+};
+
+static int spacemit_dpu_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct spacemit_dpu *dpu;
+       struct device_node *np = dev->of_node;
+       struct device_node *rmem_np;
+       struct device_node *fb_np;
+       struct reserved_mem rmem;
+       struct resource rsrv_mem;
+       int ret;
+
+       const char *str;
+       u32 dpu_id;
+       u32 dpu_type;
+       static int dpu_num = 0;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (!dev->of_node) {
+               DRM_DEV_ERROR(dev, "can't find dpu devices\n");
+               return -ENODEV;
+       }
+
+       dpu = devm_kzalloc(dev, sizeof(*dpu), GFP_KERNEL);
+       if (!dpu)
+               return -ENOMEM;
+       dpu->dev = dev;
+       dpu->logo_booton = true;
+       dev_set_drvdata(dev, dpu);
+
+       if (of_property_read_u32(np, "pipeline-id", &dpu_id))
+               return -EINVAL;
+       dpu->dev_id = dpu_id;
+
+       if (of_property_read_u32(np, "type", &dpu_type))
+               return -EINVAL;
+       dpu->type = dpu_type;
+
+       if (!of_property_read_string(np, "ip", &str)) {
+               dpu->core = dpu_core_ops_attach(str);
+       } else
+               DRM_WARN("ip was not found\n");
+
+       /* Clk dts nodes must be parsed in head of pm_runtime_xxx */
+       if (dpu->core && dpu->core->parse_dt)
+               dpu->core->parse_dt(dpu, np);
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       dpu->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+                                               GPIOD_IN);
+       if (!IS_ERR_OR_NULL(dpu->enable_gpio)) {
+               gpiod_direction_output(dpu->enable_gpio, 0);
+               msleep(50);
+       } else {
+               DRM_DEV_DEBUG(dev, "not found enable gpio\n");
+       }
+
+       if (dpu->type == DSI) {
+               dpu->dsi_reset = devm_reset_control_get_optional_shared(&pdev->dev, "dsi_reset");
+               if (IS_ERR_OR_NULL(dpu->dsi_reset)) {
+                       DRM_DEV_DEBUG(dev, "not found dsi_reset\n");
+               }
+               dpu->mclk_reset = devm_reset_control_get_optional_shared(&pdev->dev, "mclk_reset");
+               if (IS_ERR_OR_NULL(dpu->mclk_reset)) {
+                       DRM_DEV_DEBUG(dev, "not found mclk_reset\n");
+               }
+               dpu->esc_reset = devm_reset_control_get_optional_shared(&pdev->dev, "esc_reset");
+               if (IS_ERR_OR_NULL(dpu->esc_reset)) {
+                       DRM_DEV_DEBUG(dev, "not found esc_reset\n");
+               }
+               dpu->lcd_reset = devm_reset_control_get_optional_shared(&pdev->dev, "lcd_reset");
+               if (IS_ERR_OR_NULL(dpu->lcd_reset)) {
+                       DRM_DEV_DEBUG(dev, "not found lcd_reset\n");
+               }
+       } else if (dpu->type == HDMI) {
+               dpu->hdmi_reset = devm_reset_control_get_optional_shared(&pdev->dev, "hdmi_reset");
+               if (IS_ERR_OR_NULL(dpu->hdmi_reset)) {
+                       DRM_DEV_DEBUG(dev, "not found hdmi_reset\n");
+               }
+       } else {
+               DRM_DEV_ERROR(dev, "can't find dpu type %d\n", dpu->type);
+               return -ENODEV;
+       }
+
+       dpu_num++;
+       pm_runtime_enable(&pdev->dev);
+       /*
+        * To keep bootloader logo on, below operations must be
+        * done in probe func as power domain framework will turn
+        * on/off lcd power domain before/after probe func.
+        */
+       if (dpu->logo_booton) {
+               pm_runtime_get_sync(&pdev->dev);
+               dpu_pm_resume(&pdev->dev);
+               dpu_pm_suspend(&pdev->dev);
+               pm_runtime_put_sync(&pdev->dev);
+               dpu->logo_booton = false;
+               msleep(10);
+       }
+
+       rmem_np = of_find_node_by_name(NULL, "reserved-memory");
+       if (rmem_np && (dpu_num >= 2)) {
+               fb_np = of_find_node_by_name(rmem_np, "framebuffer");
+               if (fb_np) {
+                       ret = of_address_to_resource(fb_np, 0, &rsrv_mem);
+                       if (ret < 0) {
+                               DRM_DEV_ERROR(dev, "no reserved memory resource find in reserved framebuffer node\n");
+                       } else {
+                               rmem.base = rsrv_mem.start;
+                               rmem.size = resource_size(&rsrv_mem);
+
+                               spacemit_dpu_free_bootloader_mem(&rmem);
+                       }
+               }
+       }
+
+       return component_add(dev, &dpu_component_ops);
+}
+
+static int spacemit_dpu_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dpu_component_ops);
+       return 0;
+}
+
+static int dpu_pm_suspend(struct device *dev)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+       int result;
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       if (dpu->core && dpu->core->disable_clk)
+               dpu->core->disable_clk(dpu);
+
+       if (dpu->type == HDMI) {
+               if (!IS_ERR_OR_NULL(dpu->hdmi_reset)) {
+                       result = reset_control_assert(dpu->hdmi_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to assert hdmi_reset: %d\n", result);
+                       }
+               }
+       } else if (dpu->type == DSI) {
+
+               if (!IS_ERR_OR_NULL(dpu->lcd_reset)) {
+                       result = reset_control_assert(dpu->lcd_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to assert lcd_reset: %d\n", result);
+                       }
+               }
+               if (!IS_ERR_OR_NULL(dpu->esc_reset)) {
+                       result = reset_control_assert(dpu->esc_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to assert esc_reset: %d\n", result);
+                       }
+               }
+               if (!IS_ERR_OR_NULL(dpu->mclk_reset)) {
+                       result = reset_control_assert(dpu->mclk_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to assert mclk_reset: %d\n", result);
+                       }
+               }
+               if (!IS_ERR_OR_NULL(dpu->dsi_reset)) {
+                       result = reset_control_assert(dpu->dsi_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to assert dsi_reset: %d\n", result);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int dpu_pm_resume(struct device *dev)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+       int result;
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       if (dpu->type == HDMI) {
+               if (!IS_ERR_OR_NULL(dpu->hdmi_reset)) {
+                       result = reset_control_deassert(dpu->hdmi_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to deassert hdmi_reset: %d\n", result);
+                       }
+               }
+       } else if (dpu->type == DSI){
+               if (!IS_ERR_OR_NULL(dpu->dsi_reset)) {
+                       result = reset_control_deassert(dpu->dsi_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to deassert dsi_reset: %d\n", result);
+                       }
+               }
+               if (!IS_ERR_OR_NULL(dpu->mclk_reset)) {
+                       result = reset_control_deassert(dpu->mclk_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to deassert mclk_reset: %d\n", result);
+                       }
+               }
+               if (!IS_ERR_OR_NULL(dpu->esc_reset)) {
+                       result = reset_control_deassert(dpu->esc_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to deassert esc_reset: %d\n", result);
+                       }
+               }
+               if (!IS_ERR_OR_NULL(dpu->lcd_reset)) {
+                       result = reset_control_deassert(dpu->lcd_reset);
+                       if (result < 0) {
+                               DRM_INFO("Failed to deassert lcd_reset: %d\n", result);
+                       }
+               }
+       }
+
+       if (dpu->core && dpu->core->enable_clk)
+               dpu->core->enable_clk(dpu);
+
+       return 0;
+}
+
+static int dpu_rt_pm_suspend(struct device *dev)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       return 0;
+}
+
+static int dpu_rt_pm_resume(struct device *dev)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s() type %d\n", __func__, dpu->type);
+
+       return 0;
+}
+
+static const struct dev_pm_ops dpu_pm_ops = {
+       SET_RUNTIME_PM_OPS(dpu_rt_pm_suspend,
+                       dpu_rt_pm_resume,
+                       NULL)
+};
+
+static const struct of_device_id dpu_match_table[] = {
+       { .compatible = "spacemit,dpu-online0" },
+       { .compatible = "spacemit,dpu-online1" },
+       { .compatible = "spacemit,dpu-online2" },
+       { .compatible = "spacemit,dpu-offline0" },
+       { .compatible = "spacemit,dpu-offline1" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dpu_match_table);
+
+struct platform_driver spacemit_dpu_driver = {
+       .probe = spacemit_dpu_probe,
+       .remove = spacemit_dpu_remove,
+       .driver = {
+               .name = "spacemit-dpu-drv",
+               .of_match_table = dpu_match_table,
+               .pm = &dpu_pm_ops,
+       },
+};
+
+MODULE_DESCRIPTION("Spacemit Display Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/spacemit_dpu.h b/drivers/gpu/drm/spacemit/spacemit_dpu.h
new file mode 100644 (file)
index 0000000..5f2a499
--- /dev/null
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DPU_H_
+#define _SPACEMIT_DPU_H_
+
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <video/videomode.h>
+#include <linux/workqueue.h>
+#include <linux/reset.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_print.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_writeback.h>
+#include <dt-bindings/display/spacemit-dpu.h>
+#include "dpu/saturn_regs/reg_map.h"
+#include "spacemit_drm.h"
+#include "spacemit_lib.h"
+
+#define DPU_INT_UPDATE_DONE    BIT(0)
+#define DPU_INT_UNDERRUN       BIT(2)
+#define DPU_INT_OVERRUN                BIT(3)
+#define DPU_INT_WBDONE         BIT(4)
+
+#define N_SCALER_MAX                   5
+#define N_DMA_CHANNEL_MAX              12
+#define N_DMA_LAYER_MAX                        16
+#define N_COMPOSER_MAX                 4
+#define N_COMPOSER_LAYER_MAX           16
+#define N_PANEL_MAX                    3
+#define N_OUTCTRL_MAX                  3
+#define N_WRITEBACK_MAX                        2
+#define N_DISPLAY_MAX                  5
+#define N_LUT3D_MAX                    3
+#define N_CMDLIST_MAX                  14
+
+#define MHZ2HZ 1000000ULL
+#define MHZ2KHZ        1000ULL
+#define DPU_QOS_REQ                    1100000
+#define DPU_MIN_QOS_REQ                        331070976
+#define DPU_MARGIN_QOS_REQ             900000ULL
+#define DPU_MAX_QOS_REQ                        (3900000ULL - DPU_MARGIN_QOS_REQ)
+#define RDMA_INVALID_ID                        (~0)
+#define SCALER_INVALID_ID              (u8)(~0)
+
+#define DPU_STOP_TIMEOUT               (2000)
+#define DPU_CTRL_MAX_TIMING_INTER1     (0xf)
+
+/*
+#define DPU_PXCLK_DEFAULT      98000000
+#define DPU_MCLK_DEFAULT       307000000
+#define DPU_MCLK_MAX           614400000
+#define DPU_MCLK_MIN           40960000
+#define DPU_AXICLK_DEFAULT     409000000
+#define DPU_ESCCLK_DEFAULT     52000000
+#define DPU_BITCLK_DEFAULT     624000000
+*/
+
+#define DPU_PXCLK_DEFAULT      88000000
+// #define DPU_MCLK_DEFAULT    307200000
+#define DPU_MCLK_DEFAULT       491520000
+#define DPU_MCLK_MAX           614400000
+#define DPU_MCLK_MIN           40960000
+#define DPU_AXICLK_DEFAULT     409000000
+#define DPU_ESCCLK_DEFAULT     51200000
+#define DPU_BITCLK_DEFAULT     614400000
+
+#define MAX_SCALER_NUMS                1
+
+struct spacemit_dpu_scaler {
+       u32 rdma_id;
+       u32 in_use;
+};
+
+enum rdma_mode {
+       UP_DOWN = BIT(0),
+       LEFT_RIGHT = BIT(1),
+};
+
+struct spacemit_dpu_fbcmem {
+       u32 start;
+       u32 size;
+       bool map;
+};
+
+struct spacemit_dpu_rdma {
+       enum rdma_mode mode;
+       struct spacemit_dpu_fbcmem fbcmem;
+       bool in_use;
+};
+
+struct dpu_mmu_tbl {
+       u32 size;
+       void *va;
+       dma_addr_t pa;
+};
+
+struct dpu_clk_context {
+       struct clk *pxclk;
+       struct clk *mclk;
+       struct clk *hclk;
+       struct clk *escclk;
+       struct clk *bitclk;
+       struct clk *hmclk;
+};
+
+struct spacemit_dpu {
+       struct device *dev;
+       struct drm_crtc crtc;
+       struct drm_writeback_connector wb_connector;
+       struct dpu_core_ops *core;
+       struct workqueue_struct *dpu_underrun_wq;
+       struct work_struct work_stop_trace;
+       struct work_struct work_update_clk;
+       struct work_struct work_update_bw;
+       struct dpu_mmu_tbl mmu_tbl;
+       int dev_id;
+       int type;
+       bool enable_dump_reg;
+       bool enable_dump_fps;
+       bool enable_auto_fc;
+       struct timespec64 last_tm;
+
+       bool is_1st_f;
+       bool logo_booton;
+       struct dpu_clk_context clk_ctx;
+       uint64_t new_mclk;              /* new frame mclk */
+       uint64_t cur_mclk;              /* current frame mclk */
+       unsigned int min_mclk;          /* min_mclk of board panel resolution */
+       uint64_t new_bw;
+       uint64_t cur_bw;
+       struct drm_property *color_matrix_property;
+       uint32_t bitclk;
+       uint32_t escclk;
+       struct reset_control *dsi_reset;
+       struct reset_control *mclk_reset;
+       struct reset_control *lcd_reset;
+       struct reset_control *esc_reset;
+       struct reset_control *hdmi_reset;
+       struct gpio_desc *enable_gpio;
+
+#ifdef CONFIG_SPACEMIT_DEBUG
+       bool (*is_dpu_running)(struct spacemit_dpu* dpu);
+       struct notifier_block nb;
+       bool is_working;
+#endif
+};
+
+extern struct list_head dpu_core_head;
+
+static inline struct spacemit_dpu *crtc_to_dpu(struct drm_crtc *crtc)
+{
+       return crtc ? container_of(crtc, struct spacemit_dpu, crtc) : NULL;
+}
+
+struct dpu_core_ops {
+       int (*parse_dt)(struct spacemit_dpu *dpu, struct device_node *np);
+       u32 (*version)(struct spacemit_dpu *dpu);
+       int (*init)(struct spacemit_dpu *dpu);
+       void (*uninit)(struct spacemit_dpu *dpu);
+       void (*run)(struct drm_crtc *crtc,
+                   struct drm_crtc_state *old_state);
+       void (*stop)(struct spacemit_dpu *dpu);
+       void (*flip)(struct spacemit_dpu *dpu);
+       void (*disable_vsync)(struct spacemit_dpu *dpu);
+       void (*enable_vsync)(struct spacemit_dpu *dpu);
+       u32 (*isr)(struct spacemit_dpu *dpu);
+       int (*modeset)(struct spacemit_dpu *dpu, struct drm_mode_modeinfo *mode);
+       int (*enable_clk)(struct spacemit_dpu *dpu);
+       int (*disable_clk)(struct spacemit_dpu *dpu);
+       int (*cal_layer_fbcmem_size)(struct drm_plane *plane, \
+                                    struct drm_plane_state *state);
+       int (*adjust_rdma_fbcmem)(struct spacemit_hw_device *hwdev, \
+                                struct spacemit_dpu_rdma *rdmas);
+       int (*calc_plane_mclk_bw)(struct drm_plane *plane, \
+                       struct drm_plane_state *state);
+       void (*wb_config)(struct spacemit_dpu *dpu);
+       int (*update_clk)(struct spacemit_dpu *dpu, uint64_t mclk);
+       int (*update_bw)(struct spacemit_dpu *dpu, uint64_t bw);
+};
+
+#define dpu_core_ops_register(entry) \
+       disp_ops_register(entry, &dpu_core_head)
+#define dpu_core_ops_attach(str) \
+       disp_ops_attach(str, &dpu_core_head)
+
+int spacemit_dpu_run(struct drm_crtc *crtc,
+               struct drm_crtc_state *old_state);
+int spacemit_dpu_stop(struct spacemit_dpu *dpu);
+int spacemit_dpu_wb_config(struct spacemit_dpu *dpu);
+
+struct spacemit_plane {
+       struct drm_plane plane;
+       struct spacemit_hw_device *hwdev;
+       struct drm_property *rdma_id_property;
+       struct drm_property *solid_color_property;
+       struct drm_property *hdr_coef_property;
+       struct drm_property *scale_coef_property;
+       u32 hw_pid;
+};
+
+struct spacemit_plane_state {
+       struct drm_plane_state state;
+       u32 rdma_id;
+       u32 solid_color;
+       /*
+        * scaler id. As long as its rdma channel is identical with any
+        * one uses the scaler, it's set to the scaler's id, even if it
+        * doesn't really use the scaler.
+        */
+       u8 scaler_id;
+       /* hw format */
+       u8 format;
+       bool use_scl;
+       bool is_offline; //to indicate rdma is offline
+       bool right_image;
+       u32 fbcmem_size;
+       uint64_t mclk;  //DPU MCLK = MAX(Mclk, Aclk) of all planes
+       uint64_t bw;    //BandWidth = SUM(BW_single) * 1.08
+       struct dpu_mmu_tbl mmu_tbl;
+       struct cmdlist cl;
+       uint64_t afbc_effc;
+       struct drm_property_blob *hdr_coefs_blob_prop;
+       struct drm_property_blob *scale_coefs_blob_prop;
+};
+
+struct spacemit_plane *to_spacemit_plane(struct drm_plane *plane);
+
+static inline struct
+spacemit_plane_state *to_spacemit_plane_state(const struct drm_plane_state *state)
+{
+       return container_of(state, struct spacemit_plane_state, state);
+}
+
+struct spacemit_crtc_state {
+       struct drm_crtc_state base;
+       struct spacemit_dpu_scaler scalers[MAX_SCALER_NUMS];
+       struct spacemit_dpu_rdma *rdmas;
+       uint64_t mclk;  /* max of all rdma mclk */
+       uint64_t bw;    /* sum of all rdma bw*/
+       uint64_t aclk;  /* based on bw */
+       uint64_t real_mclk; /* real_mclk = max(mclk, aclk) */
+       struct drm_property_blob *color_matrix_blob_prop;
+};
+
+#define to_spacemit_crtc_state(x) container_of(x, struct spacemit_crtc_state, base)
+
+struct drm_plane *spacemit_plane_init(struct drm_device *drm,
+                                       struct spacemit_dpu *dpu);
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_dpu_reg.h b/drivers/gpu/drm/spacemit/spacemit_dpu_reg.h
new file mode 100644 (file)
index 0000000..bd9ca39
--- /dev/null
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DPU_REG_H_
+#define _SPACEMIT_DPU_REG_H_
+
+enum REG_FILED_TYPE {
+       FIELD_DEF,  //reg field is rw
+       FIELD_W1C,  //reg field is write 1 cleared
+       FIELD_W1S,  //reg field is write 1 set
+       FIELD_W0C,  //reg field is write 0 cleared
+};
+
+#define dpu_read_reg(hwdev, module_name, module_base, field) \
+       ({ u32 __v = (((volatile module_name *)(module_base + hwdev->base))->field); \
+       __v; }) \
+
+#define dpu_write_reg_common(hwdev, module_name, module_base, field, data, type) \
+{ \
+       volatile module_name *module = (module_name *)(module_base + hwdev->base); \
+       /* Ensure register value is as expected */ \
+       module->field = data; \
+}
+
+#define dpu_write_reg(hwdev, module_name, module_base, field, data) \
+       dpu_write_reg_common(hwdev, module_name, module_base, field, data, FIELD_DEF)
+
+#define dpu_write_reg_w1c(hwdev, module_name, module_base, field, data) \
+       dpu_write_reg_common(hwdev, module_name, module_base, field, data, FIELD_W1C)
+
+#define dpu_write_reg_w1s(hwdev, module_name, module_base, field, data) \
+       dpu_write_reg_common(hwdev, module_name, module_base, field, data, FIELD_W1S)
+
+#define dpu_write_reg_w0c(hwdev, module_name, module_base, field, data) \
+       dpu_write_reg_common(hwdev, module_name, module_base, field, data, FIELD_W0C)
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/spacemit_drm.c b/drivers/gpu/drm/spacemit/spacemit_drm.c
new file mode 100644 (file)
index 0000000..da25ec8
--- /dev/null
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_aperture.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_of.h>
+#include <linux/component.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/kernel.h>
+
+#include "spacemit_drm.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_gem.h"
+
+#define DRIVER_NAME    "spacemit"
+#define DRIVER_DESC    "Spacemit SoCs' DRM Driver"
+#define DRIVER_DATE    "20231115"
+#define DRIVER_MAJOR   1
+#define DRIVER_MINOR   0
+
+void spacemit_drm_atomic_commit_tail(struct drm_atomic_state *old_state)
+{
+       struct drm_device *dev = old_state->dev;
+
+       drm_atomic_helper_commit_modeset_disables(dev, old_state);
+
+       drm_atomic_helper_commit_modeset_enables(dev, old_state);
+
+       drm_atomic_helper_commit_planes(dev, old_state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
+
+       // spacemit_wb_atomic_commit(dev, old_state);
+
+       drm_atomic_helper_wait_for_flip_done(dev, old_state);
+
+       drm_atomic_helper_commit_hw_done(old_state);
+
+       drm_atomic_helper_cleanup_planes(dev, old_state);
+}
+
+static const struct drm_mode_config_helper_funcs spacemit_drm_mode_config_helper = {
+       .atomic_commit_tail = spacemit_drm_atomic_commit_tail,
+};
+
+static const struct drm_mode_config_funcs spacemit_drm_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void spacemit_drm_mode_config_init(struct drm_device *drm)
+{
+       drm_mode_config_init(drm);
+
+       /*HW has no limitation of min width and min height,
+         even for YUV format, which is limitated in plane check*/
+       drm->mode_config.min_width = 1;
+       drm->mode_config.min_height = 1;
+       drm->mode_config.max_width = 4096;
+       drm->mode_config.max_height = 4096;
+       //drm->mode_config.allow_fb_modifiers = true;
+
+       drm->mode_config.funcs = &spacemit_drm_mode_config_funcs;
+       drm->mode_config.helper_private = &spacemit_drm_mode_config_helper;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define FRAMEBUFFER_DUMP_PATH "/tmp"
+static int spacemit_framebuffer_dump(struct drm_plane *plane) {
+       unsigned int buffer_size = 0;
+       int i, j;
+       void *mmu_tbl_vaddr = NULL;
+       phys_addr_t dpu_buffer_paddr = 0;
+       void __iomem *dpu_buffer_vaddr = NULL;
+       loff_t pos = 0;
+       struct file *filep = NULL;
+       struct drm_framebuffer *fb;
+       char file_name[128];
+       struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(plane->state);
+
+       fb = plane->state->fb;
+       if (!fb) {
+               return -EINVAL;
+       }
+
+       for (i = 0; i < fb->format->num_planes; i++) {
+               if (fb->obj[i]) {
+                       char format[5];
+                       format[0] = (fb->format->format & 0xFF);
+                       format[1] = ((fb->format->format >> 8) & 0xFF);
+                       format[2] = ((fb->format->format >> 16) & 0xFF);
+                       format[3] = ((fb->format->format >> 24) & 0xFF);
+                       format[4] = '\0';
+
+                       if (fb->format->is_yuv) {
+                               snprintf(file_name, 100, "%s/plane%d_fb%d_%s_planes%d_%dx%d.%s", FRAMEBUFFER_DUMP_PATH, plane->base.id, fb->base.id,
+                               format, i, fb->width, fb->height, "yuv");
+                       } else {
+                               snprintf(file_name, 100, "%s/plane%d_fb%d_%s_planes%d_%dx%d.%s", FRAMEBUFFER_DUMP_PATH, plane->base.id, fb->base.id,
+                               format, i, fb->width, fb->height, "rgb");
+                       }
+
+                       mmu_tbl_vaddr = spacemit_pstate->mmu_tbl.va;
+                       buffer_size = plane->state->fb->obj[i]->size >> PAGE_SHIFT;
+                       filep = filp_open(file_name, O_RDWR | O_APPEND | O_CREAT, 0644);
+
+                       if (IS_ERR(filep)) {
+                               DRM_ERROR("Open file %s failed!\n", file_name);
+                               return -EINVAL;
+                       }
+                       for (j = 0; j < buffer_size; j++) {
+                               dpu_buffer_paddr = *(volatile u32 __force *)mmu_tbl_vaddr;
+                               dpu_buffer_paddr = dpu_buffer_paddr << PAGE_SHIFT;
+                               if (dpu_buffer_paddr >= 0x80000000UL) {
+                                       dpu_buffer_paddr += 0x80000000UL;
+                               }
+                               dpu_buffer_vaddr = phys_to_virt((unsigned long)dpu_buffer_paddr);
+                               mmu_tbl_vaddr += 4;
+                               kernel_write(filep, (void *)dpu_buffer_vaddr, PAGE_SIZE, &pos);
+                       }
+                       filp_close(filep, NULL);
+                       filep = NULL;
+                       DRM_INFO("dump framebuffer: %s\n", file_name);
+               }
+
+       }
+
+       return 0;
+}
+
+static int spacemit_drm_dump_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 drm_plane *plane;
+       struct drm_framebuffer *fb;
+
+       DRM_INFO("%s()\n", __func__);
+       mutex_lock(&drm_dev->mode_config.fb_lock);
+       drm_for_each_fb(fb, drm_dev) {
+               DRM_INFO("framebuffer[%u] \n", fb->base.id);
+               drm_for_each_plane(plane, drm_dev) {
+                       if (plane->state->fb != fb)
+                               continue;
+
+                       spacemit_framebuffer_dump(plane);
+               }
+       }
+       mutex_unlock(&drm_dev->mode_config.fb_lock);
+
+       return 0;
+}
+
+static const struct drm_info_list spacemit_debugfs_files[] = {
+       { "dump", spacemit_drm_dump_show, 0 },
+};
+
+static void spacemit_drm_debugfs_init(struct drm_minor *minor)
+{
+       DRM_DEBUG("%s()\n", __func__);
+       drm_debugfs_create_files(spacemit_debugfs_files,
+                                               ARRAY_SIZE(spacemit_debugfs_files),
+                                               minor->debugfs_root,
+                                               minor);
+}
+#endif
+
+static const struct file_operations spacemit_drm_fops = {
+       .owner          = THIS_MODULE,
+       .open           = drm_open,
+       .release                = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+       .compat_ioctl   = drm_compat_ioctl,
+       .poll                   = drm_poll,
+       .read                   = drm_read,
+       .llseek         = no_llseek,
+       .mmap           = spacemit_gem_mmap,
+};
+
+const struct vm_operations_struct spacemit_gem_vm_ops = {
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
+static struct drm_driver spacemit_drm_drv = {
+       .driver_features = DRIVER_GEM | DRIVER_MODESET |
+                                       DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
+       .fops = &spacemit_drm_fops,
+       .dumb_create = spacemit_gem_dumb_create,
+       .gem_prime_import_sg_table = spacemit_gem_prime_import_sg_table,
+#ifdef CONFIG_DEBUG_FS
+       .debugfs_init           = spacemit_drm_debugfs_init,
+#endif
+
+       .name           = DRIVER_NAME,
+       .desc                   = DRIVER_DESC,
+       .date                   = DRIVER_DATE,
+       .major          = DRIVER_MAJOR,
+       .minor          = DRIVER_MINOR,
+};
+
+static int spacemit_drm_bind(struct device *dev)
+{
+       struct drm_device *drm;
+       struct spacemit_drm_private *priv;
+       int err;
+
+       DRM_DEBUG("%s()\n", __func__);
+       /* Remove existing drivers that may own the framebuffer memory. */
+       err = drm_aperture_remove_framebuffers(&spacemit_drm_drv);
+       if (err) {
+               DRM_ERROR("Failed to remove existing framebuffers - %d.\n", err);
+               return err;
+       }
+
+       drm = drm_dev_alloc(&spacemit_drm_drv, dev);
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
+
+       priv = dev_get_drvdata(dev);
+       priv->ddev = drm;
+       drm->dev_private = priv;
+       priv->dev = dev;
+
+       spacemit_drm_mode_config_init(drm);
+
+       /* bind and init sub drivers */
+       err = component_bind_all(drm->dev, drm);
+       if (err) {
+               DRM_ERROR("failed to bind all component.\n");
+               goto err_dc_cleanup;
+       }
+
+       /* vblank init */
+       err = drm_vblank_init(drm, drm->mode_config.num_crtc);
+       if (err) {
+               DRM_ERROR("failed to initialize vblank.\n");
+               goto err_unbind_all;
+       }
+       // /* with irq_enabled = true, we can use the vblank feature. */
+       // drm->irq_enabled = true;
+
+       /* reset all the states of crtc/plane/encoder/connector */
+       drm_mode_config_reset(drm);
+
+       /* init kms poll for handling hpd */
+       drm_kms_helper_poll_init(drm);
+
+       /* force detection after connectors init */
+       drm_helper_hpd_irq_event(drm);
+
+       err = drm_dev_register(drm, 0);
+       if (err < 0)
+               goto err_kms_helper_poll_fini;
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
+
+err_kms_helper_poll_fini:
+       drm_kms_helper_poll_fini(drm);
+err_unbind_all:
+       component_unbind_all(drm->dev, drm);
+err_dc_cleanup:
+       drm_mode_config_cleanup(drm);
+       drm_dev_put(drm);
+       return err;
+}
+
+static void spacemit_drm_put_dev(struct drm_device *dev)
+{
+       DRM_DEBUG("\n");
+
+       if (!dev) {
+               DRM_ERROR("cleanup called no dev\n");
+               return;
+       }
+
+       drm_dev_unregister(dev);
+       drm_dev_put(dev);
+}
+
+static void spacemit_drm_unbind(struct device *dev)
+{
+       DRM_DEBUG("%s()\n", __func__);
+       spacemit_drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops drm_component_ops = {
+       .bind = spacemit_drm_bind,
+       .unbind = spacemit_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+       struct device_node *np = data;
+
+       DRM_DEBUG("compare %s\n", np->full_name);
+
+       return dev->of_node == np;
+}
+
+int spacemit_drm_of_component_probe(struct device *dev,
+                          int (*compare_of)(struct device *, void *),
+                          const struct component_master_ops *m_ops)
+{
+       struct device_node *ep, *port, *remote;
+       struct component_match *match = NULL;
+       int i;
+
+       if (!dev->of_node)
+               return -EINVAL;
+
+       /*
+        * Bind the crtc's ports first, so that drm_of_find_possible_crtcs()
+        * called from encoder's .bind callbacks works as expected
+        */
+       for (i = 0; ; i++) {
+               port = of_parse_phandle(dev->of_node, "ports", i);
+               if (!port)
+                       break;
+
+               if (of_device_is_available(port->parent))
+                       drm_of_component_match_add(dev, &match, compare_of,
+                                                  port);
+
+               of_node_put(port);
+       }
+
+       if (i == 0) {
+               dev_err(dev, "missing 'ports' property\n");
+               return -ENODEV;
+       }
+
+       if (!match) {
+               dev_err(dev, "no available port\n");
+               return -ENODEV;
+       }
+
+       /*
+        * For bound crtcs, bind the encoders attached to their remote endpoint
+        */
+       for (i = 0; ; i++) {
+               port = of_parse_phandle(dev->of_node, "ports", i);
+               if (!port)
+                       break;
+
+               if (!of_device_is_available(port->parent)) {
+                       of_node_put(port);
+                       continue;
+               }
+
+               for_each_child_of_node(port, ep) {
+                       remote = of_graph_get_remote_port_parent(ep);
+                       if (!remote || !of_device_is_available(remote)) {
+                               of_node_put(remote);
+                               continue;
+                       } else if (!of_device_is_available(remote->parent)) {
+                               dev_warn(dev, "parent device of %pOF is not available\n",
+                                        remote);
+                               of_node_put(remote);
+                               continue;
+                       }
+
+                       drm_of_component_match_add(dev, &match, compare_of,
+                                                  remote);
+                       of_node_put(remote);
+               }
+               of_node_put(port);
+       }
+
+       return component_master_add_with_match(dev, m_ops, match);
+}
+
+static int spacemit_drm_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct spacemit_drm_private *priv;
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *r;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+       if (ret)
+               DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
+
+       /* Allocate and initialize the driver private structure. */
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       dev_set_drvdata(&pdev->dev, priv);
+
+       // if (of_property_read_u32(np, "hw_ver", &priv->hw_ver))
+       //      return -EINVAL;
+
+       priv->contig_mem = false;
+
+       priv->num_pipes = of_count_phandle_with_args(np, "ports", NULL);
+       if (priv->num_pipes <= 0) {
+               DRM_ERROR("no ports are defined\n");
+               return -EINVAL;
+       }
+
+       priv->hwdev = (struct spacemit_hw_device *)of_device_get_match_data(&pdev->dev);
+
+       priv->cmdlist_num = 0;
+       priv->cmdlist_regs = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!priv->cmdlist_regs)
+               return -ENOMEM;
+
+       priv->cmdlist_groups = kzalloc(sizeof(struct cmdlist *) * \
+                              priv->hwdev->rdma_nums, GFP_KERNEL);
+       if (!priv->cmdlist_groups)
+               return -ENOMEM;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->hwdev->phy_addr = r->start;
+       priv->hwdev->base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(priv->hwdev->base))
+               return PTR_ERR(priv->hwdev->base);
+
+       return spacemit_drm_of_component_probe(&pdev->dev, compare_of,  &drm_component_ops);
+}
+
+static int spacemit_drm_remove(struct platform_device *pdev)
+{
+       component_master_del(&pdev->dev, &drm_component_ops);
+       return 0;
+}
+
+static void spacemit_drm_shutdown(struct platform_device *pdev)
+{
+       struct spacemit_drm_private *priv = dev_get_drvdata(&pdev->dev);
+       struct drm_device *drm = priv->ddev;
+
+       if (!drm) {
+               DRM_WARN("drm device is not available, no shutdown\n");
+               return;
+       }
+
+       drm_atomic_helper_shutdown(drm);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int spacemit_drm_suspend(struct device *dev)
+{
+       struct spacemit_drm_private *priv = dev_get_drvdata(dev);
+       struct drm_device *drm = priv->ddev;
+
+       return drm_mode_config_helper_suspend(drm);
+}
+
+static int spacemit_drm_resume(struct device *dev)
+{
+       struct spacemit_drm_private *priv = dev_get_drvdata(dev);
+       struct drm_device *drm = priv->ddev;
+
+       return drm_mode_config_helper_resume(drm);
+}
+#endif
+
+static const struct dev_pm_ops spacemit_drm_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(spacemit_drm_suspend,
+                               spacemit_drm_resume)
+};
+
+static const struct of_device_id drm_match_table[] = {
+       {
+               .compatible = "spacemit,saturn-hdmi",
+               .data = &spacemit_dp_devices[SATURN_HDMI],
+       },
+       {
+               .compatible = "spacemit,saturn-le",
+               .data = &spacemit_dp_devices[SATURN_LE],
+       },
+       {},
+
+};
+MODULE_DEVICE_TABLE(of, drm_match_table);
+
+static struct platform_driver spacemit_drm_driver = {
+       .probe = spacemit_drm_probe,
+       .remove = spacemit_drm_remove,
+       .shutdown = spacemit_drm_shutdown,
+       .driver = {
+               .name = "spacemit-drm-drv",
+               .of_match_table = drm_match_table,
+               .pm = &spacemit_drm_pm_ops,
+       },
+};
+
+static struct platform_driver * const spacemit_drm_drivers[] = {
+       &spacemit_drm_driver,
+       &spacemit_dpu_driver,
+       &spacemit_dsi_driver,
+       &spacemit_dphy_driver,
+};
+
+#ifdef MODULE
+extern int dsi_core_register(void);
+extern int dpu_core_register(void);
+extern int dphy_core_register(void);
+extern int display_class_init(void);
+void drm_core_register(void)
+{
+       display_class_init();
+       dsi_core_register();
+       dpu_core_register();
+       dphy_core_register();
+}
+#endif
+static int __init spacemit_drm_drivers_init(void)
+{
+#ifdef MODULE
+       drm_core_register();
+#endif
+       return platform_register_drivers(spacemit_drm_drivers,
+                                        ARRAY_SIZE(spacemit_drm_drivers));
+}
+
+static void __exit spacemit_drm_drivers_exit(void)
+{
+       platform_unregister_drivers(spacemit_drm_drivers,
+                                   ARRAY_SIZE(spacemit_drm_drivers));
+}
+
+module_init(spacemit_drm_drivers_init);
+module_exit(spacemit_drm_drivers_exit);
+
+MODULE_DESCRIPTION("Spacemit DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/spacemit_drm.h b/drivers/gpu/drm/spacemit/spacemit_drm.h
new file mode 100644 (file)
index 0000000..9e73160
--- /dev/null
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DRM_H_
+#define _SPACEMIT_DRM_H_
+
+#include <drm/drm_print.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/dma-mapping.h>
+#include "spacemit_cmdlist.h"
+
+struct spacemit_hw_device {
+       void __iomem *base;
+       phys_addr_t phy_addr;
+       u8 plane_nums;
+       u8 rdma_nums;
+       const struct spacemit_hw_rdma *rdmas;
+       u8 n_formats;
+       const struct dpu_format_id *formats;
+       u8 n_fbcmems;
+       const u32 *fbcmem_sizes;
+       const u32 *rdma_fixed_fbcmem_sizes;
+       u32 solid_color_shift;
+       int hdr_coef_size;
+       int scale_coef_size;
+       bool is_hdmi;
+};
+
+struct spacemit_drm_private {
+       struct drm_device *ddev;
+       struct device *dev;
+       struct spacemit_hw_device *hwdev;
+       int hw_ver;
+       bool contig_mem;
+       int num_pipes;
+       struct cmdlist **cmdlist_groups;
+       struct cmdlist_reg *cmdlist_regs;
+       int cmdlist_num;
+};
+
+extern struct platform_driver spacemit_dpu_driver;
+extern struct platform_driver spacemit_dphy_driver;
+extern struct platform_driver spacemit_dsi_driver;
+
+int spacemit_wb_init(struct drm_device *drm, struct drm_crtc *crtc);
+void spacemit_wb_atomic_commit(struct drm_device *drm, struct drm_atomic_state *old_state);
+
+#endif /* _SPACEMIT_DRM_H_ */
diff --git a/drivers/gpu/drm/spacemit/spacemit_dsi.c b/drivers/gpu/drm/spacemit/spacemit_dsi.c
new file mode 100644 (file)
index 0000000..03e9b1e
--- /dev/null
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_of.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <video/mipi_display.h>
+#include "spacemit_lib.h"
+#include "spacemit_dpu.h"
+#include "spacemit_dsi.h"
+#include "sysfs/sysfs_display.h"
+
+LIST_HEAD(dsi_core_head);
+
+static void spacemit_dsi_encoder_enable(struct drm_encoder *encoder)
+{
+       struct spacemit_dsi *dsi = encoder_to_dsi(encoder);
+       //struct spacemit_dpu *dpu = crtc_to_dpu(encoder->crtc);
+       void __iomem *addr = (void __iomem *)ioremap(0xd421a1a8, 100);
+
+       DRM_INFO("%s()\n", __func__);
+
+       if (!dsi->core || !dsi->core->dsi_open) {
+               DRM_ERROR("%s(), dsi->core is null!\n", __func__);
+               return;
+       }
+
+       /* Dsi online setup */
+       writel(0x40000001, addr);
+       writel(0x1, addr + 0x10);
+
+       dsi->core->dsi_open(&dsi->ctx, false);
+
+       if (dsi->panel) {
+               drm_panel_prepare(dsi->panel);
+               drm_panel_enable(dsi->panel);
+       }
+
+       if (dsi->core && dsi->core->dsi_ready_for_datatx)
+               dsi->core->dsi_ready_for_datatx(&dsi->ctx);
+}
+
+static void spacemit_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+       struct spacemit_dsi *dsi = encoder_to_dsi(encoder);
+       struct spacemit_dpu *dpu = crtc_to_dpu(encoder->crtc);
+
+       DRM_INFO("%s()\n", __func__);
+
+       spacemit_dpu_stop(dpu);
+
+       if (dsi->core && dsi->core->dsi_close_datatx)
+               dsi->core->dsi_close_datatx(&dsi->ctx);
+
+       if (dsi->panel) {
+               drm_panel_disable(dsi->panel);
+               drm_panel_unprepare(dsi->panel);
+       }
+
+       if (dsi->core && dsi->core->dsi_close)
+               dsi->core->dsi_close(&dsi->ctx);
+}
+
+static void spacemit_dsi_encoder_mode_set(struct drm_encoder *encoder,
+                                struct drm_display_mode *mode,
+                                struct drm_display_mode *adj_mode)
+{
+       struct spacemit_dsi *dsi = encoder_to_dsi(encoder);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       drm_display_mode_to_videomode(mode, &dsi->ctx.vm);
+}
+
+static int spacemit_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+                                   struct drm_crtc_state *crtc_state,
+                                   struct drm_connector_state *conn_state)
+{
+       DRM_DEBUG("%s()\n", __func__);
+
+       return 0;
+}
+
+static const struct drm_encoder_helper_funcs spacemit_encoder_helper_funcs = {
+       .atomic_check = spacemit_dsi_encoder_atomic_check,
+       .mode_set = spacemit_dsi_encoder_mode_set,
+       .enable = spacemit_dsi_encoder_enable,
+       .disable = spacemit_dsi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs spacemit_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static int spacemit_dsi_encoder_init(struct drm_device *drm, struct spacemit_dsi *dsi)
+{
+       struct drm_encoder *encoder = &dsi->encoder;
+       struct device *dev = dsi->host.dev;
+       u32 crtc_mask;
+       int ret;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       crtc_mask = drm_of_find_possible_crtcs(drm, dev->of_node);
+       if (!crtc_mask) {
+               DRM_ERROR("failed to find crtc mask\n");
+               return -EINVAL;
+       }
+       DRM_INFO("find possible crtcs: 0x%08x\n", crtc_mask);
+
+       encoder->possible_crtcs = crtc_mask;
+       ret = drm_encoder_init(drm, encoder, &spacemit_encoder_funcs,
+                              DRM_MODE_ENCODER_DSI, NULL);
+       if (ret) {
+               DRM_ERROR("failed to init dsi encoder\n");
+               return ret;
+       }
+
+       drm_encoder_helper_add(encoder, &spacemit_encoder_helper_funcs);
+
+       return 0;
+}
+
+static int spacemit_dsi_find_panel(struct spacemit_dsi *dsi)
+{
+       struct device *dev = dsi->host.dev;
+       struct device_node *child, *lcds_node;
+       struct drm_panel *panel;
+
+       /* search /lcds child node first */
+       lcds_node = of_find_node_by_path("/lcds");
+       for_each_child_of_node(lcds_node, child) {
+               panel = of_drm_find_panel(child);
+               if (!IS_ERR(panel)) {
+                       dsi->panel = panel;
+                       of_node_put(child);
+                       return 0;
+               }
+       }
+
+       /*
+        * If /lcds child node search failed, we search
+        * the child of dsi host node.
+        */
+       for_each_child_of_node(dev->of_node, child) {
+               panel = of_drm_find_panel(child);
+               if (panel) {
+                       dsi->panel = panel;
+                       of_node_put(child);
+                       return 0;
+               }
+       }
+
+       DRM_ERROR("of_drm_find_panel() failed\n");
+       return -ENODEV;
+}
+
+static int spacemit_dsi_phy_attach(struct spacemit_dsi *dsi)
+{
+       struct device *dev;
+
+       dev = spacemit_disp_pipe_get_output(&dsi->dev);
+       if (!dev)
+               return -ENODEV;
+
+       dsi->ctx.phy = dev_get_drvdata(dev);
+       if (!dsi->ctx.phy) {
+               DRM_ERROR("dsi attach phy failed\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void spacemit_dsi_get_mipi_info(struct device_node *lcd_node, struct spacemit_mipi_info *mipi_info)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(lcd_node, "height", &val);
+       if (ret)
+               DRM_ERROR("of get mipi height failed\n");
+       else
+               mipi_info->height = val;
+
+       ret = of_property_read_u32(lcd_node, "width", &val);
+       if (ret)
+               DRM_ERROR("of get mipi width failed\n");
+       else
+               mipi_info->width = val;
+
+       ret = of_property_read_u32(lcd_node, "hfp", &val);
+       if (ret)
+               DRM_ERROR("of get mipi hfp failed\n");
+       else
+               mipi_info->hfp = val;
+
+       ret = of_property_read_u32(lcd_node, "hbp", &val);
+       if (ret)
+               DRM_ERROR("of get mipi hbp failed\n");
+       else
+               mipi_info->hbp = val;
+
+       ret = of_property_read_u32(lcd_node, "hsync", &val);
+       if (ret)
+               DRM_ERROR("of get mipi hsync failed\n");
+       else
+               mipi_info->hsync = val;
+
+       ret = of_property_read_u32(lcd_node, "vfp", &val);
+       if (ret)
+               DRM_ERROR("of get mipi vfp failed\n");
+       else
+               mipi_info->vfp = val;
+
+       ret = of_property_read_u32(lcd_node, "vbp", &val);
+       if (ret)
+               DRM_ERROR("of get mipi vbp failed\n");
+       else
+               mipi_info->vbp = val;
+
+       ret = of_property_read_u32(lcd_node, "vsync", &val);
+       if (ret)
+               DRM_ERROR("of get mipi vsync failed\n");
+       else
+               mipi_info->vsync = val;
+
+       ret = of_property_read_u32(lcd_node, "fps", &val);
+       if (ret)
+               DRM_ERROR("of get mipi fps failed\n");
+       else
+               mipi_info->fps = val;
+
+       ret = of_property_read_u32(lcd_node, "work-mode", &val);
+       if (ret)
+               DRM_ERROR("of get mipi work-mode failed\n");
+       else
+               mipi_info->work_mode = val;
+
+       ret = of_property_read_u32(lcd_node, "rgb-mode", &val);
+       if (ret)
+               DRM_ERROR("of get mipi rgb-mode failed\n");
+       else
+               mipi_info->rgb_mode = val;
+
+       ret = of_property_read_u32(lcd_node, "lane-number", &val);
+       if (ret)
+               DRM_ERROR("of get mipi lane-number failed\n");
+       else
+               mipi_info->lane_number = val;
+
+       ret = of_property_read_u32(lcd_node, "phy-bit-clock", &val);
+       if (ret)
+               mipi_info->phy_bit_clock = DPHY_BITCLK_DEFAULT;
+       else
+               mipi_info->phy_bit_clock = val;
+
+
+       ret = of_property_read_u32(lcd_node, "phy-esc-clock", &val);
+       if (ret)
+               mipi_info->phy_esc_clock = DPHY_ESCCLK_DEFAULT;
+       else
+               mipi_info->phy_esc_clock = val;
+
+       ret = of_property_read_u32(lcd_node, "split-enable", &val);
+       if (ret)
+               DRM_ERROR("of get mipi split-enable failed\n");
+       else
+               mipi_info->split_enable = val;
+
+       ret = of_property_read_u32(lcd_node, "eotp-enable", &val);
+       if (ret)
+               DRM_ERROR("of get mipi eotp-enable failed\n");
+       else
+               mipi_info->eotp_enable = val;
+
+       ret = of_property_read_u32(lcd_node, "burst-mode", &val);
+       if (ret)
+               DRM_ERROR("of get mipi burst-mode failed\n");
+       else
+               mipi_info->burst_mode = val;
+}
+
+static void spacemit_dsi_get_advanced_info(struct spacemit_dsi *dsi, struct spacemit_mipi_info *mipi_info)
+{
+       struct spacemit_dsi_device *ctx = &dsi->ctx;
+       struct spacemit_dsi_advanced_setting *adv_setting = &ctx->adv_setting;
+       struct device_node *np = dsi->dev.of_node;
+       int ret;
+
+       /*advanced_setting*/
+       ret = of_property_read_u32(np, "lpm_frame_en", &adv_setting->lpm_frame_en);
+       if(ret)
+               adv_setting->lpm_frame_en = LPM_FRAME_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "last_line_turn", &adv_setting->last_line_turn);
+       if(ret)
+               adv_setting->last_line_turn = LAST_LINE_TURN_DEFAULT;
+
+       ret = of_property_read_u32(np, "hex_slot_en", &adv_setting->hex_slot_en);
+       if(ret)
+               adv_setting->hex_slot_en = HEX_SLOT_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "hsa_pkt_en", &adv_setting->hsa_pkt_en);
+       if(ret){
+               if(mipi_info->burst_mode == DSI_BURST_MODE_NON_BURST_SYNC_PULSE)
+                       adv_setting->hsa_pkt_en = HSA_PKT_EN_DEFAULT_SYNC_PULSE;
+               else
+                       adv_setting->hsa_pkt_en = HSA_PKT_EN_DEFAULT_OTHER;
+       }
+
+       ret = of_property_read_u32(np, "hse_pkt_en", &adv_setting->hse_pkt_en);
+       if(ret){
+               if(mipi_info->burst_mode == DSI_BURST_MODE_NON_BURST_SYNC_PULSE)
+                       adv_setting->hse_pkt_en = HSE_PKT_EN_DEFAULT_SYNC_PULSE;
+               else
+                       adv_setting->hse_pkt_en = HSE_PKT_EN_DEFAULT_OTHER;
+       }
+
+       ret = of_property_read_u32(np, "hbp_pkt_en", &adv_setting->hbp_pkt_en);
+       if(ret)
+               adv_setting->hbp_pkt_en = HBP_PKT_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "hfp_pkt_en", &adv_setting->hfp_pkt_en);
+       if(ret)
+               adv_setting->hfp_pkt_en = HFP_PKT_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "hex_pkt_en", &adv_setting->hex_pkt_en);
+       if(ret)
+               adv_setting->hex_pkt_en = HEX_PKT_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "hlp_pkt_en", &adv_setting->hlp_pkt_en);
+       if(ret)
+               adv_setting->hlp_pkt_en = HLP_PKT_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "auto_dly_dis", &adv_setting->auto_dly_dis);
+       if(ret)
+               adv_setting->auto_dly_dis = AUTO_DLY_DIS_DEFAULT;
+
+       ret = of_property_read_u32(np, "timing_check_dis", &adv_setting->timing_check_dis);
+       if(ret)
+               adv_setting->timing_check_dis = TIMING_CHECK_DIS_DEFAULT;
+
+       ret = of_property_read_u32(np, "hact_wc_en", &adv_setting->hact_wc_en);
+       if(ret)
+               adv_setting->hact_wc_en = HACT_WC_EN_DEFAULT;
+
+       ret = of_property_read_u32(np, "auto_wc_dis", &adv_setting->auto_wc_dis);
+       if(ret)
+               adv_setting->auto_wc_dis = AUTO_WC_DIS_DEFAULT;
+
+       ret = of_property_read_u32(np, "vsync_rst_en", &adv_setting->vsync_rst_en);
+       if(ret)
+               adv_setting->vsync_rst_en = VSYNC_RST_EN_DEFAULT;
+}
+
+static int spacemit_dsi_host_attach(struct mipi_dsi_host *host,
+                          struct mipi_dsi_device *slave)
+{
+       struct spacemit_dsi *dsi = host_to_dsi(host);
+       struct spacemit_dsi_device *ctx = &dsi->ctx;
+       struct spacemit_mipi_info *mipi_info = &ctx->mipi_info;
+       struct device_node *lcd_node;
+       int ret;
+
+       DRM_INFO("%s()\n", __func__);
+
+       dsi->slave = slave;
+
+       ret = spacemit_dsi_phy_attach(dsi);
+       if (ret)
+               return ret;
+
+       ret = spacemit_dsi_find_panel(dsi);
+       if (ret)
+               return ret;
+
+       lcd_node = dsi->panel->dev->of_node;
+
+       spacemit_dsi_get_mipi_info(lcd_node, mipi_info);
+       spacemit_dsi_get_advanced_info(dsi, mipi_info);
+
+       return 0;
+}
+
+static int spacemit_dsi_host_detach(struct mipi_dsi_host *host,
+                          struct mipi_dsi_device *slave)
+{
+       DRM_INFO("%s()\n", __func__);
+       /* do nothing */
+       return 0;
+}
+
+static ssize_t spacemit_dsi_host_transfer(struct mipi_dsi_host *host,
+                               const struct mipi_dsi_msg *msg)
+{
+       struct spacemit_dsi *dsi = host_to_dsi(host);
+       struct spacemit_dsi_cmd_desc cmd;
+       struct spacemit_dsi_rx_buf dbuf = {0x0};
+
+       cmd.cmd_type = msg->type;
+       cmd.length = msg->tx_len;
+       memcpy(cmd.data, msg->tx_buf, cmd.length);
+
+       if(msg->flags & MIPI_DSI_MSG_USE_LPM)
+               cmd.lp = 1;
+       else
+               cmd.lp = 0;
+
+       if (msg->rx_buf && msg->rx_len) {
+               if (dsi->core && dsi->core->dsi_read_cmds)
+                       dsi->core->dsi_read_cmds(&dsi->ctx, &dbuf, &cmd, 1);
+               memcpy(msg->rx_buf, dbuf.data, 1);
+               return 0;
+       }
+
+       if (msg->tx_buf && msg->tx_len) {
+               if (dsi->core && dsi->core->dsi_write_cmds)
+                       dsi->core->dsi_write_cmds(&dsi->ctx, &cmd, 1);
+       }
+
+       return 0;
+}
+
+static const struct mipi_dsi_host_ops spacemit_dsi_host_ops = {
+       .attach = spacemit_dsi_host_attach,
+       .detach = spacemit_dsi_host_detach,
+       .transfer = spacemit_dsi_host_transfer,
+};
+
+static int spacemit_dsi_host_init(struct device *dev, struct spacemit_dsi *dsi)
+{
+       int ret;
+
+       dsi->host.dev = dev;
+       dsi->host.ops = &spacemit_dsi_host_ops;
+
+       ret = mipi_dsi_host_register(&dsi->host);
+       if (ret)
+               DRM_ERROR("failed to register dsi host\n");
+
+       return ret;
+}
+
+static int spacemit_dsi_connector_get_modes(struct drm_connector *connector)
+{
+       struct spacemit_dsi *dsi = connector_to_dsi(connector);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       return drm_panel_get_modes(dsi->panel, connector);
+}
+
+static enum drm_mode_status
+spacemit_dsi_connector_mode_valid(struct drm_connector *connector,
+                        struct drm_display_mode *mode)
+{
+       enum drm_mode_status mode_status = MODE_OK;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       return mode_status;
+}
+
+static struct drm_encoder *
+spacemit_dsi_connector_best_encoder(struct drm_connector *connector)
+{
+       struct spacemit_dsi *dsi = connector_to_dsi(connector);
+
+       DRM_DEBUG("%s()\n", __func__);
+       return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs spacemit_dsi_connector_helper_funcs = {
+       .get_modes = spacemit_dsi_connector_get_modes,
+       .mode_valid = spacemit_dsi_connector_mode_valid,
+       .best_encoder = spacemit_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+spacemit_dsi_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct spacemit_dsi *dsi = connector_to_dsi(connector);
+
+       DRM_DEBUG("%s() dsi subconnector type %d status %d\n", __func__, dsi->ctx.dsi_subconnector, dsi->ctx.connector_status);
+
+       if (SPACEMIT_DSI_SUBCONNECTOR_DP == dsi->ctx.dsi_subconnector)
+               return dsi->ctx.connector_status;
+       else
+               return connector_status_connected;
+}
+
+static void spacemit_dsi_connector_destroy(struct drm_connector *connector)
+{
+       DRM_INFO("%s()\n", __func__);
+
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs spacemit_dsi_atomic_connector_funcs = {
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = spacemit_dsi_connector_detect,
+       .destroy = spacemit_dsi_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int spacemit_dsi_connector_init(struct drm_device *drm, struct spacemit_dsi *dsi)
+{
+       struct drm_encoder *encoder = &dsi->encoder;
+       struct drm_connector *connector = &dsi->connector;
+       int ret;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+       ret = drm_connector_init(drm, connector,
+                               &spacemit_dsi_atomic_connector_funcs,
+                               DRM_MODE_CONNECTOR_DSI);
+       if (ret) {
+               DRM_ERROR("drm_connector_init() failed\n");
+               return ret;
+       }
+
+       drm_connector_helper_add(connector,
+                                &spacemit_dsi_connector_helper_funcs);
+
+       //drm_mode_connector_attach_encoder(connector, encoder);
+       drm_connector_attach_encoder(connector, encoder);
+
+       return 0;
+}
+
+static int __maybe_unused spacemit_dsi_bridge_attach(struct spacemit_dsi *dsi)
+{
+       struct drm_encoder *encoder = &dsi->encoder;
+       struct drm_bridge *bridge = dsi->bridge;
+       struct device *dev = dsi->host.dev;
+       struct device_node *bridge_node;
+       int ret;
+
+       bridge_node = of_graph_get_remote_node(dev->of_node, 2, 0);
+       if (!bridge_node)
+               return 0;
+
+       bridge = of_drm_find_bridge(bridge_node);
+       if (!bridge) {
+               DRM_ERROR("of_drm_find_bridge() failed\n");
+               return -ENODEV;
+       }
+       dsi->bridge = bridge;
+
+       ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+       if (ret) {
+               DRM_ERROR("failed to attach external bridge\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static irqreturn_t spacemit_dsi_isr(int irq, void *data)
+{
+       struct spacemit_dsi *dsi = data;
+       DRM_DEBUG("%s: dsi Enter\n", __func__);
+
+       if (dsi->core && dsi->core->isr)
+               dsi->core->isr(&dsi->ctx);
+
+       return IRQ_HANDLED;
+}
+
+static int spacemit_dsi_irq_request(struct spacemit_dsi *dsi)
+{
+       int ret;
+       int irq;
+
+       irq = irq_of_parse_and_map(dsi->host.dev->of_node, 0);
+       if (irq) {
+               DRM_DEBUG("dsi irq num = %d\n", irq);
+               ret = request_irq(irq, spacemit_dsi_isr, 0, "DSI", dsi);
+               if (ret) {
+                       DRM_ERROR("dsi failed to request irq int0!\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int spacemit_dsi_bind(struct device *dev, struct device *master, void *data)
+{
+       struct drm_device *drm = data;
+       struct spacemit_dsi *dsi = dev_get_drvdata(dev);
+       int ret;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       ret = spacemit_dsi_encoder_init(drm, dsi);
+       if (ret)
+               goto cleanup_host;
+
+       ret = spacemit_dsi_connector_init(drm, dsi);
+       if (ret)
+               goto cleanup_encoder;
+
+       ret = spacemit_dsi_irq_request(dsi);
+       if (ret)
+               goto cleanup_connector;
+
+       return 0;
+
+cleanup_connector:
+       drm_connector_cleanup(&dsi->connector);
+cleanup_encoder:
+       drm_encoder_cleanup(&dsi->encoder);
+cleanup_host:
+       mipi_dsi_host_unregister(&dsi->host);
+       return ret;
+}
+
+static void spacemit_dsi_unbind(struct device *dev,
+                       struct device *master, void *data)
+{
+       /* do nothing */
+       DRM_DEBUG("%s()\n", __func__);
+
+}
+
+static const struct component_ops dsi_component_ops = {
+       .bind   = spacemit_dsi_bind,
+       .unbind = spacemit_dsi_unbind,
+};
+
+static int spacemit_dsi_device_create(struct spacemit_dsi *dsi, struct device *parent)
+{
+       int ret;
+
+       dsi->dev.class = display_class;
+       dsi->dev.parent = parent;
+       dsi->dev.of_node = parent->of_node;
+       dev_set_name(&dsi->dev, "dsi%d", dsi->ctx.id);
+       dev_set_drvdata(&dsi->dev, dsi);
+
+       ret = device_register(&dsi->dev);
+       if (ret)
+               DRM_ERROR("dsi device register failed\n");
+
+       return ret;
+}
+
+static int spacemit_dsi_context_init(struct spacemit_dsi *dsi, struct device_node *np)
+{
+       struct spacemit_dsi_device *ctx = &dsi->ctx;
+       struct resource r;
+       u32 tmp;
+
+       if (dsi->core && dsi->core->parse_dt)
+               dsi->core->parse_dt(&dsi->ctx, np);
+
+       if (of_address_to_resource(np, 0, &r)) {
+               DRM_ERROR("parse dsi ctrl reg base failed\n");
+               return -ENODEV;
+       }
+       ctx->base_addr = (void __iomem *)ioremap(r.start, resource_size(&r));
+       if (NULL == ctx->base_addr) {
+               DRM_ERROR("dsi ctrl reg base ioremap failed\n");
+               return -ENODEV;
+       }
+
+       ctx->dsi_subconnector = SPACEMIT_DSI_SUBCONNECTOR_MIPI_DSI;
+       ctx->previous_connector_status = connector_status_connected;
+       ctx->connector_status = connector_status_connected;
+
+       if (!of_property_read_u32(np, "dev-id", &tmp))
+               ctx->id = tmp;
+
+       return 0;
+}
+
+static int spacemit_dsi_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct spacemit_dsi *dsi;
+       const char *str;
+       int ret;
+
+       DRM_INFO("%s()\n", __func__);
+
+       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+       if (!dsi) {
+               DRM_ERROR("failed to allocate dsi data.\n");
+               return -ENOMEM;
+       }
+
+       if (!of_property_read_string(np, "ip", &str))
+               dsi->core = dsi_core_ops_attach(str);
+       else
+               DRM_WARN("error: 'ip' was not found\n");
+
+       ret = spacemit_dsi_context_init(dsi, np);
+       if (ret)
+               return -EINVAL;
+
+       spacemit_dsi_device_create(dsi, &pdev->dev);
+       spacemit_dsi_sysfs_init(&dsi->dev);
+       platform_set_drvdata(pdev, dsi);
+
+       ret = spacemit_dsi_host_init(&pdev->dev, dsi);
+       if (ret)
+               return ret;
+
+       return component_add(&pdev->dev, &dsi_component_ops);
+}
+
+static int spacemit_dsi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dsi_component_ops);
+
+       return 0;
+}
+
+static const struct of_device_id spacemit_dsi_of_match[] = {
+       { .compatible = "spacemit,dsi0-host" },
+       { .compatible = "spacemit,dsi1-host" },
+       { .compatible = "spacemit,dsi2-host" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, spacemit_dsi_of_match);
+
+struct platform_driver spacemit_dsi_driver = {
+       .probe = spacemit_dsi_probe,
+       .remove = spacemit_dsi_remove,
+       .driver = {
+               .name = "spacemit-dsi-drv",
+               .of_match_table = spacemit_dsi_of_match,
+       },
+};
+
+MODULE_DESCRIPTION("Spacemit MIPI DSI HOST Controller Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_dsi.h b/drivers/gpu/drm/spacemit/spacemit_dsi.h
new file mode 100644 (file)
index 0000000..988184c
--- /dev/null
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_DSI_H_
+#define _SPACEMIT_DSI_H_
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <video/videomode.h>
+
+#include <drm/drm_print.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_panel.h>
+
+#include "spacemit_lib.h"
+#include "spacemit_dphy.h"
+
+/* define LCD_IS_READY to bypass kernel dsi driver */
+/* #define LCD_IS_READY */
+
+/*advanced setting*/
+#define LPM_FRAME_EN_DEFAULT 0
+#define LAST_LINE_TURN_DEFAULT 0
+#define HEX_SLOT_EN_DEFAULT 0
+#define HSA_PKT_EN_DEFAULT_SYNC_PULSE 1
+#define HSA_PKT_EN_DEFAULT_OTHER 0
+#define HSE_PKT_EN_DEFAULT_SYNC_PULSE 1
+#define HSE_PKT_EN_DEFAULT_OTHER 0
+#define HBP_PKT_EN_DEFAULT 1
+#define HFP_PKT_EN_DEFAULT 0
+#define HEX_PKT_EN_DEFAULT 0
+#define HLP_PKT_EN_DEFAULT 0
+#define AUTO_DLY_DIS_DEFAULT 0
+#define TIMING_CHECK_DIS_DEFAULT 0
+#define HACT_WC_EN_DEFAULT 1
+#define AUTO_WC_DIS_DEFAULT 0
+#define VSYNC_RST_EN_DEFAULT 1
+
+#define MAX_TX_CMD_COUNT 256
+#define MAX_RX_DATA_COUNT 64
+
+enum spacemit_mipi_burst_mode{
+       DSI_BURST_MODE_NON_BURST_SYNC_PULSE = 0,
+       DSI_BURST_MODE_NON_BURST_SYNC_EVENT = 1,
+       DSI_BURST_MODE_BURST = 2,
+       DSI_BURST_MODE_MAX
+};
+
+enum spacemit_mipi_input_data_mode{
+       DSI_INPUT_DATA_RGB_MODE_565 = 0,
+       DSI_INPUT_DATA_RGB_MODE_666PACKET = 1,
+       DSI_INPUT_DATA_RGB_MODE_666UNPACKET = 2,
+       DSI_INPUT_DATA_RGB_MODE_888 = 3,
+       DSI_INPUT_DATA_RGB_MODE_MAX
+};
+
+enum spacemit_dsi_work_mode {
+       SPACEMIT_DSI_MODE_VIDEO,
+       SPACEMIT_DSI_MODE_CMD,
+       SPACEMIT_DSI_MODE_MAX
+};
+
+enum spacemit_dsi_cmd_type {
+       SPACEMIT_DSI_DCS_SWRITE = 0x5,
+       SPACEMIT_DSI_DCS_SWRITE1 = 0x15,
+       SPACEMIT_DSI_DCS_LWRITE = 0x39,
+       SPACEMIT_DSI_DCS_READ = 0x6,
+       SPACEMIT_DSI_GENERIC_LWRITE = 0x29,
+       SPACEMIT_DSI_GENERIC_READ1 = 0x14,
+       SPACEMIT_DSI_SET_MAX_PKT_SIZE = 0x37,
+};
+
+enum spacemit_dsi_tx_mode {
+       SPACEMIT_DSI_HS_MODE = 0,
+       SPACEMIT_DSI_LP_MODE = 1,
+};
+
+enum spacemit_dsi_rx_data_type {
+       SPACEMIT_DSI_ACK_ERR_RESP = 0x2,
+       SPACEMIT_DSI_EOTP = 0x8,
+       SPACEMIT_DSI_GEN_READ1_RESP = 0x11,
+       SPACEMIT_DSI_GEN_READ2_RESP = 0x12,
+       SPACEMIT_DSI_GEN_LREAD_RESP = 0x1A,
+       SPACEMIT_DSI_DCS_READ1_RESP = 0x21,
+       SPACEMIT_DSI_DCS_READ2_RESP = 0x22,
+       SPACEMIT_DSI_DCS_LREAD_RESP = 0x1C,
+};
+
+enum spacemit_dsi_polarity {
+       SPACEMIT_DSI_POLARITY_POS = 0,
+       SPACEMIT_DSI_POLARITY_NEG,
+       SPACEMIT_DSI_POLARITY_MAX
+};
+
+enum spacemit_dsi_te_mode {
+       SPACEMIT_DSI_TE_MODE_NO = 0,
+       SPACEMIT_DSI_TE_MODE_A,
+       SPACEMIT_DSI_TE_MODE_B,
+       SPACEMIT_DSI_TE_MODE_C,
+       SPACEMIT_DSI_TE_MODE_MAX,
+};
+
+enum spacemit_dsi_event_id {
+       SPACEMIT_DSI_EVENT_ERROR,
+       SPACEMIT_DSI_EVENT_MAX,
+};
+
+enum spacemit_dsi_status {
+       DSI_STATUS_UNINIT = 0,
+       DSI_STATUS_OPENED = 1,
+       DSI_STATUS_INIT = 2,
+       DSI_STATUS_MAX
+};
+
+struct spacemit_dsi_advanced_setting {
+       uint32_t lpm_frame_en; /*return to LP mode every frame*/
+       uint32_t last_line_turn;
+       uint32_t hex_slot_en;
+       uint32_t hsa_pkt_en;
+       uint32_t hse_pkt_en;
+       uint32_t hbp_pkt_en; /*bit:18*/
+       uint32_t hfp_pkt_en; /*bit:20*/
+       uint32_t hex_pkt_en;
+       uint32_t hlp_pkt_en; /*bit:22*/
+       uint32_t auto_dly_dis;
+       uint32_t timing_check_dis;
+       uint32_t hact_wc_en;
+       uint32_t auto_wc_dis;
+       uint32_t vsync_rst_en;
+};
+
+struct spacemit_mipi_info {
+       unsigned int height;
+       unsigned int width;
+       unsigned int hfp; /*pixel*/
+       unsigned int hbp;
+       unsigned int hsync;
+       unsigned int vfp; /*line*/
+       unsigned int vbp;
+       unsigned int vsync;
+       unsigned int fps;
+
+       unsigned int work_mode; /*command_mode, video_mode*/
+       unsigned int rgb_mode;
+       unsigned int lane_number;
+       unsigned int phy_bit_clock;
+       unsigned int phy_esc_clock;
+       unsigned int split_enable;
+       unsigned int eotp_enable;
+
+       /*for video mode*/
+       unsigned int burst_mode;
+
+       /*for cmd mode*/
+       unsigned int te_enable;
+       unsigned int vsync_pol;
+       unsigned int te_pol;
+       unsigned int te_mode;
+
+       /*The following fields need not be set by panel*/
+       unsigned int real_fps;
+};
+
+struct spacemit_dsi_cmd_desc {
+       enum spacemit_dsi_cmd_type cmd_type;
+       uint8_t lp;
+       uint32_t delay; /* time to delay */
+       uint32_t length;        /* cmds length */
+       uint8_t data[MAX_TX_CMD_COUNT];
+};
+
+struct spacemit_dsi_rx_buf {
+       enum spacemit_dsi_rx_data_type data_type;
+       uint32_t length; /* cmds length */
+       uint8_t data[MAX_RX_DATA_COUNT];
+};
+
+enum spacemit_dsi_subconnector {
+       SPACEMIT_DSI_SUBCONNECTOR_MIPI_DSI   = 0,
+       SPACEMIT_DSI_SUBCONNECTOR_HDMI       = 1,
+       SPACEMIT_DSI_SUBCONNECTOR_DP         = 2,
+       SPACEMIT_DSI_SUBCONNECTOR_eDP        = 3,
+};
+
+struct spacemit_dsi_device {
+       uint16_t id;
+       void __iomem *base_addr;
+       struct spacemit_dsi_advanced_setting adv_setting;
+       struct spacemit_mipi_info mipi_info;
+       struct videomode vm;
+       struct spacemit_dphy *phy;
+       enum spacemit_dsi_status status;
+       enum drm_connector_status previous_connector_status;
+       enum drm_connector_status connector_status;
+       enum spacemit_dsi_subconnector dsi_subconnector;
+};
+
+struct dsi_core_ops {
+       int (*parse_dt)(struct spacemit_dsi_device *ctx, struct device_node *np);
+       int (*isr)(struct spacemit_dsi_device *ctx);
+       int (*dsi_open)(struct spacemit_dsi_device *ctx, bool ready);
+       int (*dsi_close)(struct spacemit_dsi_device *ctx);
+       int (*dsi_write_cmds)(struct spacemit_dsi_device *ctx, struct spacemit_dsi_cmd_desc *cmds, int count);
+       int (*dsi_read_cmds)(struct spacemit_dsi_device *ctx, struct spacemit_dsi_rx_buf *dbuf,
+                                                               struct spacemit_dsi_cmd_desc *cmds, int count);
+       int (*dsi_ready_for_datatx)(struct spacemit_dsi_device *ctx);
+       int (*dsi_close_datatx)(struct spacemit_dsi_device *ctx);
+};
+
+struct spacemit_dsi {
+       struct device dev;
+       struct mipi_dsi_host host;
+       struct mipi_dsi_device *slave;
+       struct drm_encoder encoder;
+       struct drm_connector connector;
+       struct drm_bridge *bridge;
+       struct drm_panel *panel;
+       struct dsi_core_ops *core;
+       struct spacemit_dsi_device ctx;
+};
+
+extern struct list_head dsi_core_head;
+
+#define dsi_core_ops_register(entry) \
+       disp_ops_register(entry, &dsi_core_head)
+#define dsi_core_ops_attach(str) \
+       disp_ops_attach(str, &dsi_core_head)
+
+#define encoder_to_dsi(encoder) \
+       container_of(encoder, struct spacemit_dsi, encoder)
+#define host_to_dsi(host) \
+       container_of(host, struct spacemit_dsi, host)
+#define connector_to_dsi(connector) \
+       container_of(connector, struct spacemit_dsi, connector)
+
+#endif /* _SPACEMIT_DSI_H_ */
diff --git a/drivers/gpu/drm/spacemit/spacemit_gem.c b/drivers/gpu/drm/spacemit/spacemit_gem.c
new file mode 100644 (file)
index 0000000..9c5733d
--- /dev/null
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-map-ops.h>
+#include <drm/drm_prime.h>
+#include <drm/drm_gem_dma_helper.h>
+
+#include "spacemit_gem.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_drm.h"
+
+static const unsigned int orders[] = {8, 4, 0};
+static const int num_orders = ARRAY_SIZE(orders);
+
+static struct page *__alloc_largest_available(size_t size,
+                               unsigned int max_order)
+{
+       struct page *page;
+       unsigned int order;
+       gfp_t gfp_flags;
+       int i;
+
+       for (i = 0; i < num_orders; i++) {
+               order = orders[i];
+
+               if (size < (PAGE_SIZE << order))
+                       continue;
+               if (max_order < order)
+                       continue;
+
+               gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
+                               __GFP_COMP | __GFP_RECLAIM);
+
+               page = alloc_pages(gfp_flags, order);
+               if (!page)
+                       continue;
+
+               return page;
+       }
+
+       return NULL;
+}
+
+static int spacemit_gem_sysmem_alloc(struct drm_device *drm,
+                               struct spacemit_gem_object *spacemit_obj,
+                               size_t size)
+{
+       struct sg_table *sgt;
+       struct scatterlist *sg;
+       struct list_head pages;
+       struct page *page, *tmp_page;
+       size_t size_remaining = size;
+       unsigned int max_order = orders[0];
+       int i = 0;
+       u32 page_nums = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       struct spacemit_drm_private *priv = drm->dev_private;
+
+
+       sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+       if (!sgt)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&pages);
+       if (priv->contig_mem) {
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+               return -ENOMEM;
+#else
+               page = dma_alloc_from_contiguous(drm->dev, page_nums, 0, 0);
+               if (!page) {
+                       kfree(sgt);
+                       DRM_ERROR("Failed to alloc %zd bytes continous mem\n", size);
+                       return -ENOMEM;
+               }
+               list_add_tail(&page->lru, &pages);
+               i++;
+#endif
+       } else {
+               while (size_remaining > 0) {
+                       if (fatal_signal_pending(current))
+                               goto free_pages;
+
+                       page = __alloc_largest_available(size_remaining, max_order);
+                       if (!page)
+                               goto free_pages;
+
+                       list_add_tail(&page->lru, &pages);
+                       size_remaining -= page_size(page);
+                       max_order = compound_order(page);
+                       i++;
+               }
+       }
+
+       if (sg_alloc_table(sgt, i, GFP_KERNEL))
+               goto free_pages;
+
+       sg = sgt->sgl;
+       list_for_each_entry_safe(page, tmp_page, &pages, lru) {
+               if (priv->contig_mem)
+                       sg_set_page(sg, page, page_nums << PAGE_SHIFT, 0);
+               else
+                       sg_set_page(sg, page, page_size(page), 0);
+               sg = sg_next(sg);
+               list_del(&page->lru);
+       }
+
+       dma_map_sgtable(drm->dev, sgt, DMA_BIDIRECTIONAL, 0);
+       dma_unmap_sgtable(drm->dev, sgt, DMA_BIDIRECTIONAL, 0);
+
+       spacemit_obj->sgt = sgt;
+
+       return 0;
+
+free_pages:
+       list_for_each_entry_safe(page, tmp_page, &pages, lru) {
+               if (priv->contig_mem)
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+                       nop();
+#else
+                       dma_release_from_contiguous(drm->dev, page, page_nums);
+#endif
+               else
+                       __free_pages(page, compound_order(page));
+       }
+       kfree(sgt);
+
+       return -ENOMEM;
+}
+
+static void spacemit_gem_sysmem_free(struct spacemit_gem_object *spacemit_obj)
+{
+       struct sg_table *sgt = spacemit_obj->sgt;
+       struct scatterlist *sg;
+       int i;
+       struct drm_device *drm = spacemit_obj->base.dev;
+       struct spacemit_drm_private *priv = drm->dev_private;
+
+       if (!sgt) {
+               return;
+       }
+
+       for_each_sgtable_sg(sgt, sg, i) {
+               struct page *page = sg_page(sg);
+               if (priv->contig_mem)
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+                       nop();
+#else
+                       dma_release_from_contiguous(drm->dev, page, sg->length >> PAGE_SHIFT);
+#endif
+               else
+                       __free_pages(page, compound_order(page));
+       }
+
+       sg_free_table(sgt);
+       kfree(sgt);
+}
+
+static int spacemit_gem_alloc_buf(struct drm_device *drm,
+                               struct spacemit_gem_object *spacemit_obj,
+                               size_t size)
+{
+       int ret;
+
+       ret = spacemit_gem_sysmem_alloc(drm, spacemit_obj, size);
+       if (ret) {
+               DRM_ERROR("SPACEMIT_GEM: failed to allocate %zu byte buffer", size);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void spacemit_gem_free_buf(struct spacemit_gem_object *spacemit_obj)
+{
+       spacemit_gem_sysmem_free(spacemit_obj);
+}
+
+static void spacemit_gem_free_object(struct drm_gem_object *gem_obj)
+{
+       struct spacemit_gem_object *spacemit_obj = to_spacemit_obj(gem_obj);
+
+       if (gem_obj->import_attach) {
+               drm_prime_gem_destroy(gem_obj, spacemit_obj->sgt);
+       } else {
+               spacemit_gem_free_buf(spacemit_obj);
+       }
+
+       drm_gem_object_release(gem_obj);
+
+       kfree(spacemit_obj);
+}
+
+static struct sg_table *__dup_sg_table(struct sg_table *sgt)
+{
+       struct sg_table *new_sgt;
+       struct scatterlist *sg, *new_sg;
+       int ret, i;
+
+       new_sgt = kzalloc(sizeof(*new_sgt), GFP_KERNEL);
+       if (!new_sgt)
+               return ERR_PTR(-ENOMEM);
+
+       ret = sg_alloc_table(new_sgt, sgt->orig_nents, GFP_KERNEL);
+       if (ret) {
+               kfree(new_sgt);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       new_sg = new_sgt->sgl;
+       for_each_sgtable_sg(sgt, sg, i) {
+               sg_set_page(new_sg, sg_page(sg), sg->length, sg->offset);
+               new_sg = sg_next(new_sg);
+       }
+
+       return new_sgt;
+}
+
+static struct sg_table *spacemit_gem_prime_get_sg_table(struct drm_gem_object *gem_obj)
+{
+       struct spacemit_gem_object *spacemit_obj = to_spacemit_obj(gem_obj);
+
+       return __dup_sg_table(spacemit_obj->sgt);
+}
+
+int spacemit_gem_prime_vmap(struct drm_gem_object *gem_obj, struct iosys_map *map)
+{
+       struct spacemit_gem_object *spacemit_obj = to_spacemit_obj(gem_obj);
+       struct sg_page_iter piter;
+       int ret = 0;
+       void *vaddr;
+       int npages = PAGE_ALIGN(gem_obj->size) >> PAGE_SHIFT;
+       struct page **pages, **tmp;
+       pgprot_t pgprot = pgprot_writecombine(PAGE_KERNEL);
+
+       mutex_lock(&spacemit_obj->vmap_lock);
+
+
+       if (spacemit_obj->vmap_cnt)
+               goto vmap_success;
+
+       pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
+       tmp = pages;
+       if (!pages)
+               goto vmap_fail;
+       for_each_sgtable_page(spacemit_obj->sgt, &piter, 0) {
+               WARN_ON(tmp - pages >= npages);
+               *(tmp++) = sg_page_iter_page(&piter);
+       }
+       vaddr = vmap(pages, npages, VM_MAP, pgprot);
+       kvfree(pages);
+
+       if (!vaddr)
+               goto vmap_fail;
+
+       spacemit_obj->vaddr = vaddr;
+
+vmap_success:
+       spacemit_obj->vmap_cnt++;
+       iosys_map_set_vaddr(map, vaddr);
+       mutex_unlock(&spacemit_obj->vmap_lock);
+       return ret;
+
+vmap_fail:
+       mutex_unlock(&spacemit_obj->vmap_lock);
+       return -ENOMEM;
+}
+
+void spacemit_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+       struct spacemit_gem_object *spacemit_obj = to_spacemit_obj(obj);
+
+       mutex_lock(&spacemit_obj->vmap_lock);
+       spacemit_obj->vmap_cnt--;
+       if (!spacemit_obj->vmap_cnt) {
+               vunmap(map->vaddr);
+               spacemit_obj->vaddr = NULL;
+       }
+       mutex_unlock(&spacemit_obj->vmap_lock);
+}
+
+const struct vm_operations_struct vm_ops = {
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
+static const struct drm_gem_object_funcs spacemit_gem_funcs = {
+       .free = spacemit_gem_free_object,
+       .get_sg_table = spacemit_gem_prime_get_sg_table,
+       .vmap = spacemit_gem_prime_vmap,
+       .vunmap = spacemit_gem_prime_vunmap,
+       .vm_ops = &vm_ops
+};
+
+static struct drm_gem_object *
+__spacemit_gem_create_object(struct drm_device *drm, size_t size)
+{
+       struct spacemit_gem_object *spacemit_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       spacemit_obj = kzalloc(sizeof(*spacemit_obj), GFP_KERNEL);
+       if (!spacemit_obj)
+               return ERR_PTR(-ENOMEM);
+
+       gem_obj = &spacemit_obj->base;
+
+       gem_obj->funcs = &spacemit_gem_funcs;
+
+       drm_gem_private_object_init(drm, gem_obj, size);
+
+       ret = drm_gem_create_mmap_offset(gem_obj);
+       if (ret) {
+               drm_gem_object_release(gem_obj);
+               kfree(spacemit_obj);
+               return ERR_PTR(ret);
+       }
+
+       spacemit_obj->sgt = NULL;
+       spacemit_obj->vaddr = NULL;
+       spacemit_obj->vmap_cnt = 0;
+       mutex_init(&spacemit_obj->vmap_lock);
+
+       return gem_obj;
+}
+
+static struct drm_gem_object *
+spacemit_gem_create_object(struct drm_device *drm, size_t size)
+{
+       struct spacemit_gem_object *spacemit_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       size = PAGE_ALIGN(size);
+
+       gem_obj = __spacemit_gem_create_object(drm, size);
+       if (IS_ERR(gem_obj))
+               return gem_obj;
+
+       spacemit_obj = to_spacemit_obj(gem_obj);
+       ret = spacemit_gem_alloc_buf(drm, spacemit_obj, size);
+       if (ret) {
+               drm_gem_object_put(gem_obj);
+               return ERR_PTR(ret);
+       }
+
+       return gem_obj;
+}
+
+static struct drm_gem_object *
+spacemit_gem_create_with_handle(struct drm_file *file_priv, struct drm_device *drm,
+                                               size_t size, unsigned int *handle)
+{
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       gem_obj = spacemit_gem_create_object(drm, size);
+       if (IS_ERR(gem_obj))
+               return gem_obj;
+
+       ret = drm_gem_handle_create(file_priv, gem_obj, handle);
+       drm_gem_object_put(gem_obj);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return gem_obj;
+}
+
+int spacemit_gem_dumb_create(struct drm_file *file_priv,
+                               struct drm_device *drm,
+                               struct drm_mode_create_dumb *args)
+{
+       struct drm_gem_object *gem_obj;
+
+       args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+       args->size = args->pitch * args->height;
+
+       gem_obj = spacemit_gem_create_with_handle(file_priv, drm, args->size, &args->handle);
+
+       return PTR_ERR_OR_ZERO(gem_obj);
+}
+
+static int spacemit_gem_object_mmap(struct drm_gem_object *gem_obj,
+                               struct vm_area_struct *vma)
+{
+       struct spacemit_gem_object *spacemit_obj = to_spacemit_obj(gem_obj);
+       unsigned long addr = vma->vm_start;
+       struct sg_page_iter piter;
+       int ret;
+
+       for_each_sgtable_page(spacemit_obj->sgt, &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;
+}
+
+int spacemit_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret)
+               return ret;
+
+       gem_obj = vma->vm_private_data;
+
+       vma->vm_pgoff -= drm_vma_node_start(&gem_obj->vma_node);
+
+       if (gem_obj->import_attach) {
+               drm_gem_object_put(gem_obj);
+               vma->vm_private_data = NULL;
+
+               return dma_buf_mmap(gem_obj->dma_buf, vma, vma->vm_pgoff);
+       }
+
+       return spacemit_gem_object_mmap(gem_obj, vma);
+}
+
+struct drm_gem_object *
+spacemit_gem_prime_import_sg_table(struct drm_device *drm,
+                               struct dma_buf_attachment *attach,
+                               struct sg_table *sgt)
+{
+       struct spacemit_gem_object *spacemit_obj;
+       struct drm_gem_object *gem_obj;
+       size_t size = PAGE_ALIGN(attach->dmabuf->size);
+
+       gem_obj = __spacemit_gem_create_object(drm, size);
+       if (IS_ERR(gem_obj))
+               return gem_obj;
+
+       spacemit_obj = to_spacemit_obj(gem_obj);
+       spacemit_obj->sgt = sgt;
+
+       return gem_obj;
+}
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_gem.h b/drivers/gpu/drm/spacemit/spacemit_gem.h
new file mode 100644 (file)
index 0000000..c0dc199
--- /dev/null
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_GEM_H_
+#define _SPACEMIT_GEM_H_
+
+#include <linux/scatterlist.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+#include "spacemit_dmmu.h"
+
+struct spacemit_gem_object {
+       struct drm_gem_object base;
+       struct sg_table *sgt;
+       void *vaddr;
+       int vmap_cnt;
+       struct mutex vmap_lock;
+       struct list_head ttb_entry;
+};
+
+static inline struct spacemit_gem_object *
+to_spacemit_obj(struct drm_gem_object *gem_obj)
+{
+       return container_of(gem_obj, struct spacemit_gem_object, base);
+}
+
+struct drm_gem_object *
+spacemit_gem_prime_import_sg_table(struct drm_device *drm, struct dma_buf_attachment *attch,
+                                 struct sg_table *sgt);
+int spacemit_gem_dumb_create(struct drm_file *file_priv, struct drm_device *drm,
+                       struct drm_mode_create_dumb *args);
+int spacemit_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+#endif /* _SPACEMIT_GEM_H_ */
diff --git a/drivers/gpu/drm/spacemit/spacemit_hdmi.c b/drivers/gpu/drm/spacemit/spacemit_hdmi.c
new file mode 100644 (file)
index 0000000..cde07b9
--- /dev/null
@@ -0,0 +1,1087 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_atomic_helper.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 "spacemit_hdmi.h"
+#include "spacemit_lib.h"
+#include "spacemit_dpu.h"
+
+#define SPACEMIT_HDMI_DDC_OTHER_MASK        BIT(31)
+#define SPACEMIT_HDMI_DDC_DONE_MASK         BIT(30)
+#define SPACEMIT_HDMI_HPD_IQR_MASK          BIT(29)
+
+#define SPACEMIT_HDMI_DDC_NACK              BIT(15)
+#define SPACEMIT_HDMI_DDC_DONE              BIT(14)
+#define SPACEMIT_HDMI_HPD_IQR               BIT(13)
+
+#define SPACEMIT_HDMI_HPD_STATUS            BIT(12)
+
+#define SPACEMIT_HDMI_PHY_STATUS            0xC
+
+
+struct hdmi_data_info {
+       uint8_t edid[256];
+       int vic;
+       bool sink_has_audio;
+       unsigned int enc_in_format;
+       unsigned int enc_out_format;
+       unsigned int colorimetry;
+};
+
+struct spacemit_hdmi_i2c {
+       u8 segment_addr;
+
+       struct mutex lock;
+       struct completion cmp;
+};
+
+struct spacemit_hdmi {
+       struct device *dev;
+       struct drm_device *drm_dev;
+
+       int irq;
+       struct clk *pclk;
+       void __iomem *regs;
+
+       struct drm_connector    connector;
+       struct drm_encoder encoder;
+
+       struct reset_control *hdmi_reset;
+       struct clk *hdmi_mclk;
+
+       unsigned int tmds_rate;
+
+       bool edid_done;
+       bool use_no_edid;
+       struct hdmi_data_info *hdmi_data;
+       struct drm_display_mode previous_mode;
+};
+
+#define encoder_to_spacemit_hdmi(encoder) \
+       container_of(encoder, struct spacemit_hdmi, encoder)
+
+#define connector_to_spacemit_hdmi(connector) \
+       container_of(connector, struct spacemit_hdmi, connector)
+
+static inline u32 hdmi_readb(struct spacemit_hdmi *hdmi, u16 offset)
+{
+       return readl_relaxed(hdmi->regs + (offset));
+}
+
+static inline void hdmi_writeb(struct spacemit_hdmi *hdmi, u16 offset, u32 val)
+{
+       writel_relaxed(val, hdmi->regs + (offset));
+}
+
+static int hdmi_get_plug_in_status(struct spacemit_hdmi *hdmi)
+{
+       u32 value;
+       value = readl_relaxed(hdmi->regs + SPACEMIT_HDMI_PHY_STATUS) & SPACEMIT_HDMI_HPD_STATUS;
+
+       return !!value;
+}
+
+static void spacemit_hdmi_set_pwr_mode(struct spacemit_hdmi *hdmi, int mode)
+{
+       //normal/ low power
+}
+
+static void spacemit_hdmi_reset(struct spacemit_hdmi *hdmi)
+{
+}
+
+static int spacemit_hdmi_config_video_vsi(struct spacemit_hdmi *hdmi,
+                                     struct drm_display_mode *mode)
+{
+       union hdmi_infoframe frame;
+       int rc;
+
+       rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+                                                        &hdmi->connector,
+                                                        mode);
+
+       return 0;
+}
+
+static int spacemit_hdmi_upload_frame(struct spacemit_hdmi *hdmi, int setup_rc,
+                                 union hdmi_infoframe *frame, u32 frame_index,
+                                 u32 mask, u32 disable, u32 enable)
+{
+       if (setup_rc >= 0) {
+               u8 packed_frame[0x11];
+               ssize_t rc;
+
+               rc = hdmi_infoframe_pack(frame, packed_frame,
+                                        sizeof(packed_frame));
+               if (rc < 0)
+                       return rc;
+       }
+
+       return setup_rc;
+}
+
+static int spacemit_hdmi_config_video_avi(struct spacemit_hdmi *hdmi,
+                                     struct drm_display_mode *mode)
+{
+       union hdmi_infoframe frame;
+       int rc;
+
+       rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
+                                                     &hdmi->connector,
+                                                     mode);
+
+       if (hdmi->hdmi_data->enc_out_format == HDMI_COLORSPACE_YUV444)
+               frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+       else if (hdmi->hdmi_data->enc_out_format == HDMI_COLORSPACE_YUV422)
+               frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+       else
+               frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+       return spacemit_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int spacemit_hdmi_config_video_timing(struct spacemit_hdmi *hdmi,
+                                        struct drm_display_mode *mode)
+{
+       return 0;
+}
+
+enum bit_depth{
+       EIGHT_BPP = 0,
+       TEN_BPP = 1,
+       TWELVE_BPP =2,
+};
+
+int power_of_two(int n) {
+       int result = 1;
+       for (int i = 0; i < n; ++i) {
+               result <<= 1;
+       }
+
+       return result;
+}
+
+int pll8_bit_5_6 (int bit_clock, int n){
+       int ret = 0;
+       bit_clock = bit_clock / n;
+
+       if (bit_clock < 425)
+               ret = 3;
+       else if (bit_clock < 850)
+               ret = 2;
+       else if (bit_clock < 1700)
+               ret = 1;
+       else
+               ret = 0;
+
+       return ret;
+}
+
+int pll6_bit_4_5 (int bit_clock, int n){
+       int ret = 0;
+       bit_clock = bit_clock / n;
+
+       if (bit_clock <= 337)
+               ret = 0;
+       else if (bit_clock < 425)
+               ret = 1;
+       else if (bit_clock < 675)
+               ret = 0;
+       else if (bit_clock < 850)
+               ret = 1;
+       else if (bit_clock < 1350)
+               ret = 0;
+       else if (bit_clock < 1700)
+               ret = 1;
+       else
+               ret = 0;
+
+       return ret;
+}
+
+int pll5_bit_0_2 (int bit_clock, int n){
+       int value =  bit_clock * power_of_two(pll8_bit_5_6(bit_clock, n)) / n;
+       int ret;
+
+       if (value < 1830)
+               ret = 0;
+       else if (value < 2030)
+               ret = 1;
+       else if (value < 2275)
+               ret = 2;
+       else if (value < 2520)
+               ret = 3;
+       else if (value < 2765)
+               ret = 4;
+       else if (value < 3015)
+               ret = 5;
+       else if (value < 3260)
+               ret = 6;
+       else
+               ret = 7;
+
+       return ret;
+}
+
+int PLL9_BIT0_1[3] = {0x0, 0x1, 0x2};
+
+void pll_reg_cal(int bit_clock, int ref_clock, int n, int *integer_part, u32 *hmdi_e8_reg) {
+       long long int_para = 1000000000;
+       long long value = (power_of_two(pll8_bit_5_6(bit_clock, n))) * bit_clock * int_para / (n * (pll6_bit_4_5(bit_clock, n) + 1) * ref_clock);
+       long long integer = (power_of_two(pll8_bit_5_6(bit_clock, n)))* bit_clock / (n * (pll6_bit_4_5(bit_clock, n) + 1) * ref_clock) * int_para;
+       long long fraction = value - integer;
+       bool negative = false;
+       int bit = 0;
+       int frac_20bit = 0;
+       int pll2_reg = 0;
+       int pll1_reg = 0;
+       int pll0_reg = 0;
+
+       negative =  fraction > 500000000 ? true : false;
+       fraction = negative ? 1000000000 - fraction : fraction;
+       *integer_part = negative ? integer/int_para + 1 : integer/int_para;
+
+
+       for (int i = 0; i < 20; i++){
+               if (bit >= int_para) {
+                       frac_20bit |= 1 << (19 - i);
+                       fraction -= int_para;
+               }
+               fraction *= 2;
+               bit = fraction;
+       }
+
+       if (!negative){
+               pll2_reg = ((frac_20bit & 0xF0000) >> 16) | (1 << 5);
+       } else {
+               frac_20bit = (~frac_20bit + 1) & 0xfffff;
+               pll2_reg = 0x10 | ((frac_20bit & 0xF0000) >> 16) | (1 << 5);
+       }
+
+       pll1_reg = (frac_20bit & 0xFF00) >> 8;
+       pll0_reg = frac_20bit & 0xFF;
+       *hmdi_e8_reg = (0x20 << 24) | (pll2_reg << 16) | (pll1_reg << 8) | pll0_reg;
+}
+
+int pll_reg (struct spacemit_hdmi *hdmi, int pixel_clock, int bit_depth) {
+       int pll9_reg = 0, pll8_reg = 0, pll7_reg = 0, pll6_reg = 0, pll5_reg = 0, pll4_reg = 0;
+       int n = 100;
+       int ref_clock = 24;
+       int hdmi_ec_reg = 0;
+       int hdmi_f0_reg = 0;
+       int hdmi_e8_reg = 0;
+       int pow = 0;
+       int bit_clock = bit_depth == EIGHT_BPP ? pixel_clock : pixel_clock * 125 / 100;
+
+       int integer_part = 0;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       pll_reg_cal(bit_clock, ref_clock, n, &integer_part, &hdmi_e8_reg);
+
+       pll9_reg = 2 << 2 | PLL9_BIT0_1[bit_depth];
+       pll8_reg = (0 << 7) | (pll8_bit_5_6(bit_clock, n) << 5) | 1;
+       pll7_reg = 0x50;
+       pll6_reg = 0xD | (pll6_bit_4_5(bit_clock, n) << 4) | (2 << 6);
+       pll5_reg = 0x40 | pll5_bit_0_2(bit_clock, n);
+
+       pow = (pll8_bit_5_6(bit_clock, n));
+
+       pll4_reg = integer_part;
+
+       hdmi_ec_reg = (pll7_reg << 24) | (pll6_reg << 16) | (pll5_reg << 8) | pll4_reg;
+       hdmi_f0_reg = (pll9_reg << 8) | pll8_reg;
+
+       writel(hdmi_e8_reg, hdmi->regs + 0xe8);
+       DRM_DEBUG("%s() hdmi 0xe8 0x%x\n", __func__, hdmi_e8_reg);
+
+       writel(hdmi_ec_reg, hdmi->regs + 0xec);
+       DRM_DEBUG("%s() hdmi 0xec 0x%x\n", __func__, hdmi_ec_reg);
+
+       writel(hdmi_f0_reg, hdmi->regs + 0xf0);
+       DRM_DEBUG("%s() hdmi 0xf0 0x%x\n", __func__, hdmi_f0_reg);
+
+       return 0;
+}
+
+static void hdmi_i2c_timing(struct spacemit_hdmi *hdmi)
+{
+       uint32_t reg = 0;
+       uint32_t apb_clk = 153500*1000;/*Hz*/
+       uint32_t apb_time = 1000*1000*1000 / apb_clk; /*ns*/
+       uint32_t i2c_clk = 100*1000; /*Hz*/
+       uint32_t i2c_time = 1000*1000*1000 / i2c_clk; /*ns*/
+       uint32_t scl_high_time, scl_low_time; /*ns*/
+       uint32_t scl_high_count = 0, scl_low_count = 0;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       reg = hdmi_readb(hdmi, 0x18);
+       DRM_DEBUG("%s() hdmi 0x18 #1 0x%x\n", __func__, reg);
+
+       scl_high_time = i2c_time / 2;
+       scl_low_time = i2c_time / 2;
+       scl_high_count = scl_high_time / apb_time;
+       scl_low_count = scl_low_time / apb_time;
+
+       scl_high_count = (scl_high_count - 3) >> 2;
+       scl_low_count = (scl_low_count - 3) >> 2;
+
+       reg &= ~0x7FFFF;
+       reg |= (5 << 16); /*glitch*/
+       reg |= scl_high_count << 8; /*high. time = value *4 + 3*/
+       reg |= scl_low_count; /*low. time = value *4 +3*/
+       DRM_DEBUG("%s() scl_high_count %d, scl_low_count %d\n", __func__, scl_high_count, scl_low_count);
+
+       hdmi_writeb(hdmi, 0x18, reg);
+       msleep(2);
+       reg = hdmi_readb(hdmi, 0x18);
+       DRM_DEBUG("%s() hdmi 0x18 #2 0x%x\n", __func__, reg);
+}
+
+static void hdmi_i2c_read(struct spacemit_hdmi *hdmi, uint8_t addr, uint8_t* message, uint32_t length)
+{
+       int i, count = 0, left = length;
+       uint8_t *pvalue = message;
+       uint32_t value;
+       uint32_t reg, num;
+       int timeout = 1000;
+
+       DRM_DEBUG("hdmi_i2c_read ++%u\r\n", length);
+
+       do{
+               if(left <= 16)
+                       count = left;
+               else
+                       count = 16;
+               left -= count;
+
+               value = ((count-1) << 8) + (addr << 1) + 1;
+               hdmi_writeb(hdmi, 0x8, value & 0xFFFF);
+
+               reg = hdmi_readb(hdmi, 0xC);
+               num = (reg & 0x1f0) >> 4;
+
+               while(num < count){
+                       reg = hdmi_readb(hdmi, 0xC);
+                       num = (reg & 0x1f0) >> 4;
+               }
+
+               for(i = 0; i < count; i++){
+                       value = hdmi_readb(hdmi, 0x4);
+                       *pvalue++ = value;
+               }
+       } while(left > 0);
+
+       while(timeout) {
+               if ((hdmi_readb(hdmi, 0xc) & BIT(14)) != 0)
+                       break;
+
+               udelay(100);
+               timeout--;
+       };
+
+       if (timeout == 0)
+               DRM_INFO("%s wait hdmi ddc command done timeout\n", __func__);
+
+       value = hdmi_readb(hdmi, 0xc);
+       DRM_DEBUG("%s hdmi status 0x%x\n", __func__, value);
+       value |= SPACEMIT_HDMI_DDC_DONE;
+
+       hdmi_writeb(hdmi, 0xc, value);
+       udelay(100);
+
+       DRM_DEBUG("hdmi_i2c_read --%u\r\n", length);
+
+       return;
+}
+
+static int hdmi_i2c_write(struct spacemit_hdmi *hdmi, uint8_t addr, uint8_t* message, uint32_t length)
+{
+       int i, count = 0, left = length;
+       uint8_t *pvalue = message;
+       uint32_t value, reg;
+       int timeout = 1000;
+
+       DRM_DEBUG("hdmi_i2c_write ++ %u\r\n", length);
+
+       do{
+               if(left <= 16)
+                       count = left;
+               else
+                       count = 16;
+               left -= count;
+
+               for(i = 0; i < count; i++){
+                       value = *pvalue++;
+                       hdmi_writeb(hdmi, 0x0, value & 0xFF);
+               }
+               value = ((count-1) << 8) + (addr << 1);
+
+               hdmi_writeb(hdmi, 0x8, value & 0xFFFF);
+               reg = hdmi_readb(hdmi, 0x0C);
+
+               if (reg & BIT(16)) {
+                       DRM_INFO("hdmi_i2c_write  i2c ARB FAIL!!");
+                       return -1;
+               }
+       } while(left > 0);
+
+       while(timeout) {
+               if ((hdmi_readb(hdmi, 0x0C) & BIT(14)) != 0)
+                       break;
+
+               udelay(100);
+               timeout--;
+       };
+
+       if (timeout == 0) {
+               DRM_INFO("%s wait hdmi ddc command done timeout\n", __func__);
+               return -1;
+       }
+       udelay(100);
+
+       DRM_DEBUG("hdmi_i2c_write --%u\r\n", length);
+
+       return 0;
+
+}
+
+int edid_read (struct spacemit_hdmi *hdmi){
+       int i;
+       struct hdmi_data_info *hdmi_data = hdmi->hdmi_data;
+       uint8_t offset;
+       int result;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       for(i = 0; i < 8; i++) {
+               offset = i * 16;
+               result = hdmi_i2c_write(hdmi, 0x50, &offset, 1);
+               if (result < 0)
+                       break;
+               hdmi_i2c_read(hdmi, 0x50, hdmi_data->edid + offset, 16);
+       }
+
+       if (result < 0) {
+               // memset(hdmi_data->edid, 0x00, EDID_LENGTH);
+               memset(hdmi_data->edid, 0x00, 256);
+               return result;
+       }
+
+       if (hdmi_data->edid[0x7e] == 0x01) {
+               // extend edid
+               for(i = 8; i < 16; i++) {
+                       offset = i * 16;
+                       result = hdmi_i2c_write(hdmi, 0x50, &offset, 1);
+                       if (result < 0)
+                               break;
+                       hdmi_i2c_read(hdmi, 0x50, hdmi_data->edid + offset, 16);
+               }
+       }
+
+       for(i = 0; i < 256; i += 8){
+               DRM_DEBUG("EDID 0x%x: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\r\n", i,
+                       hdmi_data->edid[i], hdmi_data->edid[i+1], hdmi_data->edid[i+2], hdmi_data->edid[i+3],
+                       hdmi_data->edid[i+4], hdmi_data->edid[i+5], hdmi_data->edid[i+6], hdmi_data->edid[i+7]);
+       }
+
+       if ((hdmi_data->edid[0] == 0x00) && (hdmi_data->edid[1] == 0xff) && (hdmi_data->edid[2] == 0xff) &&
+               (hdmi_data->edid[3] == 0xff) && (hdmi_data->edid[4] == 0xff) && (hdmi_data->edid[5] == 0xff) &&
+               (hdmi_data->edid[6] == 0xff) && (hdmi_data->edid[7] == 0x00)) {
+               hdmi->edid_done = true;
+       } else {
+               hdmi->edid_done = false;
+       }
+
+       return 0;
+}
+
+static int spacemit_hdmi_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+       struct spacemit_hdmi *hdmi = data;
+       struct hdmi_data_info *hdmi_data = hdmi->hdmi_data;
+       uint32_t value;
+       int ret;
+
+       DRM_INFO("%s() len %zd\n", __func__, len);
+
+       if (len > 128)
+               return -EINVAL;
+
+       if (!hdmi->edid_done) {
+               hdmi_i2c_timing(hdmi);
+               ret = edid_read(hdmi);
+               if (ret < 0) {
+                       DRM_INFO("%s() failed to read edid\n", __func__);
+                       return -EINVAL;
+               }
+
+               memcpy(buf, hdmi_data->edid, len);
+
+               if (!hdmi->edid_done) {
+                       value = hdmi_readb(hdmi, SPACEMIT_HDMI_PHY_STATUS);
+                       DRM_INFO("%s() get edid failed, hdmi status 0x%x\n", __func__, value);
+                       value |= (SPACEMIT_HDMI_DDC_DONE | SPACEMIT_HDMI_DDC_NACK);
+                       hdmi_writeb(hdmi, SPACEMIT_HDMI_PHY_STATUS, value);
+                       udelay(5);
+               }
+
+       } else {
+               memcpy(buf, hdmi_data->edid + EDID_LENGTH, len);
+       }
+
+       return 0;
+}
+
+void hdmi_write_bits(struct spacemit_hdmi *hdmi, u16 offset, u32 value, u32 mask, u32 shifts)
+{
+       u32 reg_val;
+
+       reg_val = readl_relaxed(hdmi->regs + (offset));
+       reg_val &= ~(mask << shifts);
+       reg_val |= (value << shifts);
+       writel_relaxed(reg_val, hdmi->regs + (offset));
+}
+
+void hdmi_init (struct spacemit_hdmi *hdmi, int pixel_clock, int bit_depth){
+       u32 value = 0;
+       int color_depth = bit_depth == EIGHT_BPP ? 4 : 5;
+
+       u32 good_phase = 0x00;
+       u32 bias_current = 0x01;
+       u32 bias_risistor = 0x07;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       writel(0xAE5C410F, hdmi->regs + 0xe0);
+       hdmi_write_bits(hdmi, 0xe0, bias_current, 0x03, 29);
+       hdmi_write_bits(hdmi, 0xe0, bias_risistor, 0x0F, 18);
+       hdmi_write_bits(hdmi, 0xe0, good_phase, 0x03, 14);
+       // writel(0xEE40410F, hdmi->regs + 0xe0);
+       // value = readl_relaxed(hdmi->regs + 0xe0);
+       // DRM_DEBUG("%s() hdmi 0xe0 0x%x\n", __func__, value);
+
+       value = 0x0000000d | (color_depth << 4);
+       writel(value, hdmi->regs + 0x34);
+       DRM_DEBUG("%s() hdmi 0x34 0x%x\n", __func__, value);
+
+       pll_reg(hdmi, pixel_clock, bit_depth);
+       writel(0x03, hdmi->regs + 0xe4);
+       value = readl_relaxed(hdmi->regs + 0xe4);
+       DRM_DEBUG("%s() hdmi pll lock status 0x%x\n", __func__, value);
+       // while ( (value & 0x10000) != 0) {
+       //      value = readl_relaxed(hdmi->regs + 0xe4);
+       // }
+       udelay(100);
+
+       // value = 0x3018C000 | bit_depth;
+       value = 0x1C208000 | bit_depth;
+       writel(value, hdmi->regs + 0x28);
+       DRM_DEBUG("%s() hdmi 0x28 0x%x\n", __func__, value);
+}
+
+static int spacemit_hdmi_setup(struct spacemit_hdmi *hdmi,
+                          struct drm_display_mode *mode)
+{
+       void __iomem *ciu = (void __iomem *)ioremap(0xD4282C00, 0x200);
+       struct hdmi_data_info *hdmi_data = hdmi->hdmi_data;
+       int bit_depth = EIGHT_BPP;
+       u32 value;
+
+       DRM_DEBUG("%s() \n", __func__);
+
+       // ciu chip id
+       value = readl_relaxed(ciu);
+       if (value == 0xa08501) {
+               // default 10bpc
+               bit_depth = TEN_BPP;
+
+               // 08H, 09H: ID Manufacturer Nanme
+               // 0AH, 0BH: ID Product Code
+               if ((hdmi_data->edid[8] == 0x30) && (hdmi_data->edid[9] == 0xa3) &&
+                       ((hdmi_data->edid[10] == 0x88) || (hdmi_data->edid[10] == 0x89)) && (hdmi_data->edid[11] == 0x23)) {
+                       // Lecoo HU20238FB0
+                       bit_depth = EIGHT_BPP;
+               } else if ((hdmi_data->edid[8] == 0x26) && (hdmi_data->edid[9] == 0x01) &&
+                       (hdmi_data->edid[10] == 0x12) && (hdmi_data->edid[11] == 0x24)) {
+                       // IPASON XC242-J
+                       bit_depth = EIGHT_BPP;
+               } else if ((hdmi_data->edid[8] == 0x05) && (hdmi_data->edid[9] == 0xe3) &&
+                               (hdmi_data->edid[10] == 0x90) && (hdmi_data->edid[11] == 0x24)) {
+                       // AOC Q2490W1
+                       bit_depth = EIGHT_BPP;
+               }
+       }
+
+       if (bit_depth == EIGHT_BPP) {
+               DRM_INFO("%s() id 0x%x, hdmi 8bpc \n", __func__, value);
+       } else if (bit_depth == TEN_BPP) {
+               DRM_INFO("%s() id 0x%x, hdmi 10bpc \n", __func__, value);
+       }
+
+       hdmi_init(hdmi, hdmi->previous_mode.clock, bit_depth);
+
+       spacemit_hdmi_config_video_timing(hdmi, mode);
+       spacemit_hdmi_config_video_avi(hdmi, mode);
+       spacemit_hdmi_config_video_vsi(hdmi, mode);
+
+       iounmap(ciu);
+
+       return 0;
+}
+
+static void spacemit_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+                                      struct drm_display_mode *mode,
+                                      struct drm_display_mode *adj_mode)
+{
+       struct spacemit_hdmi *hdmi = encoder_to_spacemit_hdmi(encoder);
+       DRM_DEBUG("%s() \n", __func__);
+
+       /* Store the display mode for plugin/DPMS poweron events */
+       drm_mode_copy(&hdmi->previous_mode, adj_mode);
+}
+
+static void spacemit_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+       struct spacemit_hdmi *hdmi = encoder_to_spacemit_hdmi(encoder);
+       DRM_INFO("%s()\n", __func__);
+
+       spacemit_hdmi_set_pwr_mode(hdmi, NORMAL);
+       spacemit_hdmi_setup(hdmi, &hdmi->previous_mode);
+}
+
+static void spacemit_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+       struct spacemit_hdmi *hdmi = encoder_to_spacemit_hdmi(encoder);
+       struct spacemit_dpu *dpu = crtc_to_dpu(encoder->crtc);
+       DRM_INFO("%s()\n", __func__);
+
+       spacemit_dpu_stop(dpu);
+       writel(0x00, hdmi->regs + 0xe4);
+       udelay(100);
+       spacemit_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool spacemit_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+                                        const struct drm_display_mode *mode,
+                                        struct drm_display_mode *adj_mode)
+{
+       return true;
+}
+
+static int
+spacemit_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+                              struct drm_crtc_state *crtc_state,
+                              struct drm_connector_state *conn_state)
+{
+       DRM_DEBUG("%s()\n", __func__);
+       return 0;
+}
+
+static struct drm_encoder_helper_funcs spacemit_hdmi_encoder_helper_funcs = {
+       .enable     = spacemit_hdmi_encoder_enable,
+       .disable    = spacemit_hdmi_encoder_disable,
+       .mode_fixup = spacemit_hdmi_encoder_mode_fixup,
+       .mode_set   = spacemit_hdmi_encoder_mode_set,
+       .atomic_check = spacemit_hdmi_encoder_atomic_check,
+};
+
+static enum drm_connector_status
+spacemit_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct spacemit_hdmi *hdmi = connector_to_spacemit_hdmi(connector);
+       int ret;
+       enum drm_connector_status status;
+
+       DRM_DEBUG("%s() \n", __func__);
+
+       ret = pm_runtime_get_sync(hdmi->dev);
+       if (ret < 0) {
+               DRM_INFO("%s() pm_runtime_get_sync failed\n", __func__);
+               return connector_status_connected;
+       }
+
+       if (hdmi_get_plug_in_status(hdmi)) {
+               DRM_INFO("%s() hdmi status connected\n", __func__);
+               status = connector_status_connected;
+
+       } else {
+               DRM_INFO("%s() hdmi status disconnected\n", __func__);
+               status = connector_status_disconnected;
+       }
+
+       pm_runtime_put(hdmi->dev);
+
+       return status;
+}
+
+static int spacemit_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+       struct spacemit_hdmi *hdmi = connector_to_spacemit_hdmi(connector);
+       int ret;
+       struct edid *edid;
+       uint32_t value;
+
+       DRM_DEBUG("%s() \n", __func__);
+
+       if (hdmi->use_no_edid)
+               return drm_add_modes_noedid(connector, 1920, 1080);
+
+       value = hdmi_readb(hdmi, SPACEMIT_HDMI_PHY_STATUS);
+       DRM_DEBUG("%s() hdmi status 0x%x\n", __func__, value);
+       value &= ~(SPACEMIT_HDMI_DDC_OTHER_MASK | SPACEMIT_HDMI_DDC_DONE_MASK);
+       value |= (SPACEMIT_HDMI_HPD_IQR | SPACEMIT_HDMI_DDC_DONE | SPACEMIT_HDMI_DDC_NACK);
+       hdmi_writeb(hdmi, SPACEMIT_HDMI_PHY_STATUS, value);
+       udelay(5);
+
+       hdmi->edid_done = false;
+
+       edid = drm_do_get_edid(connector, spacemit_hdmi_get_edid_block, hdmi);
+       if (edid) {
+               if (hdmi->edid_done) {
+                       drm_connector_update_edid_property(connector, edid);
+                       ret = drm_add_edid_modes(connector, edid);
+               } else {
+                       ret = drm_add_modes_noedid(connector, 1920, 1080);
+               }
+               kfree(edid);
+       } else {
+               DRM_INFO("%s() get edid failed\n", __func__);
+               ret = drm_add_modes_noedid(connector, 1920, 1080);
+       }
+
+       return ret;
+}
+
+static enum drm_mode_status
+spacemit_hdmi_connector_mode_valid(struct drm_connector *connector,
+                              struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static int
+spacemit_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+                                      uint32_t maxX, uint32_t maxY)
+{
+       return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void spacemit_hdmi_connector_destroy(struct drm_connector *connector)
+{
+       struct spacemit_hdmi *hdmi = connector_to_spacemit_hdmi(connector);
+       kfree(hdmi->hdmi_data);
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs spacemit_hdmi_connector_funcs = {
+       .fill_modes = spacemit_hdmi_probe_single_connector_modes,
+       .detect = spacemit_hdmi_connector_detect,
+       .destroy = spacemit_hdmi_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs spacemit_hdmi_connector_helper_funcs = {
+       .get_modes = spacemit_hdmi_connector_get_modes,
+       .mode_valid = spacemit_hdmi_connector_mode_valid,
+};
+
+static int spacemit_hdmi_register(struct drm_device *drm, struct spacemit_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);
+
+       /*
+        * If we failed to find the CRTC(s) which this encoder is
+        * supposed to be connected to, it's because the CRTC has
+        * not been registered yet.  Defer probing, and hope that
+        * the required CRTC is added later.
+        */
+       if (encoder->possible_crtcs == 0)
+               return -EPROBE_DEFER;
+
+       drm_encoder_helper_add(encoder, &spacemit_hdmi_encoder_helper_funcs);
+       drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+       drm_connector_helper_add(&hdmi->connector,
+                                &spacemit_hdmi_connector_helper_funcs);
+
+       drm_connector_init(drm, &hdmi->connector,
+                                &spacemit_hdmi_connector_funcs,
+                                DRM_MODE_CONNECTOR_HDMIA);
+
+       drm_connector_attach_encoder(&hdmi->connector, encoder);
+
+       return 0;
+}
+
+static irqreturn_t spacemit_hdmi_hardirq(int irq, void *dev_id)
+{
+       struct spacemit_hdmi *hdmi = dev_id;
+       irqreturn_t ret = IRQ_NONE;
+       uint32_t value;
+
+       value = hdmi_readb(hdmi, SPACEMIT_HDMI_PHY_STATUS);
+       if (value & SPACEMIT_HDMI_HPD_IQR) {
+               value |= SPACEMIT_HDMI_HPD_IQR;
+               hdmi_writeb(hdmi, 0xc, value);
+               ret = IRQ_WAKE_THREAD;
+       }
+
+       return ret;
+}
+
+static irqreturn_t spacemit_hdmi_irq(int irq, void *dev_id)
+{
+       struct spacemit_hdmi *hdmi = dev_id;
+       hdmi->edid_done = false;
+
+       drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+       return IRQ_HANDLED;
+}
+
+static int spacemit_hdmi_bind(struct device *dev, struct device *master,
+                                void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm = data;
+       struct spacemit_hdmi *hdmi;
+       int irq;
+       int ret;
+
+       DRM_DEBUG("%s() \n", __func__);
+
+       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+       if (!hdmi)
+               return -ENOMEM;
+
+       hdmi->hdmi_data = devm_kzalloc(dev, sizeof(*(hdmi->hdmi_data)), GFP_KERNEL);
+       if (!hdmi->hdmi_data)
+               return -ENOMEM;
+
+       hdmi->dev = dev;
+       hdmi->drm_dev = drm;
+
+       hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(hdmi->regs))
+               return PTR_ERR(hdmi->regs);
+
+       if (of_property_read_bool(dev->of_node, "use-no-edid"))
+               hdmi->use_no_edid = true;
+       else
+               hdmi->use_no_edid = false;
+
+       hdmi->hdmi_reset = devm_reset_control_get_optional_shared(&pdev->dev, "hdmi_reset");
+       if (IS_ERR_OR_NULL(hdmi->hdmi_reset)) {
+               DRM_INFO("Failed to found hdmi_reset\n");
+       }
+
+       hdmi->hdmi_mclk = of_clk_get_by_name(dev->of_node, "hmclk");
+       if (IS_ERR(hdmi->hdmi_mclk)) {
+               DRM_INFO("Failed to found hdmi mclk\n");
+       }
+
+       dev_set_drvdata(dev, hdmi);
+
+       pm_runtime_enable(&pdev->dev);
+
+       if (!IS_ERR_OR_NULL(hdmi->hdmi_reset)) {
+               ret = reset_control_deassert(hdmi->hdmi_reset);
+               if (ret < 0) {
+                       DRM_INFO("Failed to deassert hdmi_reset\n");
+               }
+       }
+
+       pm_runtime_get_sync(&pdev->dev);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               DRM_ERROR("%s() get hdmi phd irq failed\n", __func__);
+               return -ENOENT;
+       }
+       DRM_DEBUG("%s() hdmi hpd irq %d\n", __func__, irq);
+
+       spacemit_hdmi_reset(hdmi);
+       hdmi->edid_done = false;
+
+       ret = spacemit_hdmi_register(drm, hdmi);
+
+       ret = devm_request_threaded_irq(dev, irq, spacemit_hdmi_hardirq,
+                                       spacemit_hdmi_irq, IRQF_SHARED,
+                                       dev_name(dev), hdmi);
+       if (ret < 0)
+               goto irq_err;
+
+       return 0;
+
+irq_err:
+       hdmi->connector.funcs->destroy(&hdmi->connector);
+       hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+       return ret;
+}
+
+static void spacemit_hdmi_unbind(struct device *dev, struct device *master,
+                            void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct spacemit_hdmi *hdmi = dev_get_drvdata(dev);
+       int ret;
+
+       DRM_DEBUG("%s() \n", __func__);
+
+       hdmi->connector.funcs->destroy(&hdmi->connector);
+       hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+       pm_runtime_put_sync(&pdev->dev);
+       if (!IS_ERR_OR_NULL(hdmi->hdmi_reset)) {
+               ret = reset_control_assert(hdmi->hdmi_reset);
+               if (ret < 0) {
+                       DRM_INFO("Failed to assert hdmi_reset\n");
+               }
+       }
+       pm_runtime_disable(dev);
+}
+
+static const struct component_ops spacemit_hdmi_ops = {
+       .bind   = spacemit_hdmi_bind,
+       .unbind = spacemit_hdmi_unbind,
+};
+
+static int spacemit_hdmi_probe(struct platform_device *pdev)
+{
+       DRM_DEBUG("%s() \n", __func__);
+
+       return component_add(&pdev->dev, &spacemit_hdmi_ops);
+}
+
+static int spacemit_hdmi_remove(struct platform_device *pdev)
+{
+       DRM_DEBUG("%s() \n", __func__);
+
+       component_del(&pdev->dev, &spacemit_hdmi_ops);
+
+       return 0;
+}
+
+static int hdmi_rt_pm_resume(struct device *dev)
+{
+       struct spacemit_hdmi *hdmi = dev_get_drvdata(dev);
+       uint64_t clk_val;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       clk_prepare_enable(hdmi->hdmi_mclk);
+
+       clk_val = clk_get_rate(hdmi->hdmi_mclk);
+       DRM_DEBUG("get hdmi mclk=%lld\n", clk_val);
+       if(clk_val != DPU_MCLK_DEFAULT){
+               clk_val = clk_round_rate(hdmi->hdmi_mclk, DPU_MCLK_DEFAULT);
+               clk_set_rate(hdmi->hdmi_mclk, clk_val);
+               DRM_INFO("set hdmi mclk=%lld\n", clk_val);
+       }
+
+       return 0;
+}
+
+static int hdmi_rt_pm_suspend(struct device *dev)
+{
+       struct spacemit_hdmi *hdmi = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       clk_disable_unprepare(hdmi->hdmi_mclk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int hdmi_drv_pm_suspend(struct device *dev)
+{
+       struct spacemit_hdmi *hdmi = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       clk_disable_unprepare(hdmi->hdmi_mclk);
+
+       return 0;
+}
+
+static int hdmi_drv_pm_resume(struct device *dev)
+{
+       struct spacemit_hdmi *hdmi = dev_get_drvdata(dev);
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       clk_prepare_enable(hdmi->hdmi_mclk);
+
+       return 0;
+}
+
+#endif
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+       SET_RUNTIME_PM_OPS(hdmi_rt_pm_suspend,
+                       hdmi_rt_pm_resume,
+                       NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(hdmi_drv_pm_suspend,
+                               hdmi_drv_pm_resume)
+};
+
+static const struct of_device_id spacemit_hdmi_dt_ids[] = {
+       { .compatible = "spacemit,hdmi",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, spacemit_hdmi_dt_ids);
+
+struct platform_driver spacemit_hdmi_driver = {
+       .probe  = spacemit_hdmi_probe,
+       .remove = spacemit_hdmi_remove,
+       .driver = {
+               .name = "spacemit-hdmi-drv",
+               .of_match_table = spacemit_hdmi_dt_ids,
+               .pm = &hdmi_pm_ops,
+       },
+};
+
+// module_platform_driver(spacemit_hdmi_driver);
+
+static int spacemit_hdmi_driver_init(void)
+{
+       return platform_driver_register(&spacemit_hdmi_driver);
+}
+late_initcall_sync(spacemit_hdmi_driver_init);
+
+MODULE_DESCRIPTION("Spacemit HDMI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/spacemit_hdmi.h b/drivers/gpu/drm/spacemit/spacemit_hdmi.h
new file mode 100644 (file)
index 0000000..21009e4
--- /dev/null
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+enum {
+       INFOFRAME_VSI = 0x05,
+       INFOFRAME_AVI = 0x06,
+       INFOFRAME_AAI = 0x08,
+};
+
+enum PWR_MODE {
+       NORMAL,
+       LOWER_PWR,
+};
+
+enum {
+       HDMI_TIMING_640_480P_60 = 0,
+       HDMI_TIMING_720_480P_60 = 1,
+       HDMI_TIMING_720_576P_50 = 2,
+       HDMI_TIMING_1280_720P_30 = 3,
+       HDMI_TIMING_1280_720P_50 = 4,
+       HDMI_TIMING_1920_1080P_24 = 5,
+       HDMI_TIMING_1920_1080P_60 = 6,
+       HDMI_TIMING_MAX
+};
+
+enum {
+       HDMI_SYNC_POL_POS,
+       HDMI_SYNC_POL_NEG,
+};
+
+struct hdmi_timing
+{
+       unsigned short hfp;
+       unsigned short hbp;
+       unsigned short hsync;
+       unsigned short hact;
+
+       unsigned short vfp;
+       unsigned short vbp;
+       unsigned short vsync;
+       unsigned short vact;
+
+       unsigned short vic;
+       unsigned short hpol;
+       unsigned short vpol;
+       unsigned short reserved;
+};
diff --git a/drivers/gpu/drm/spacemit/spacemit_lib.c b/drivers/gpu/drm/spacemit/spacemit_lib.c
new file mode 100644 (file)
index 0000000..60aa57b
--- /dev/null
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include "spacemit_lib.h"
+
+struct bmp_header {
+       u16 magic;
+       u32 size;
+       u32 unused;
+       u32 start;
+} __attribute__((__packed__));
+
+struct dib_header {
+       u32 size;
+       u32 width;
+       u32 height;
+       u16 planes;
+       u16 bpp;
+       u32 compression;
+       u32 data_size;
+       u32 h_res;
+       u32 v_res;
+       u32 colours;
+       u32 important_colours;
+       u32 red_mask;
+       u32 green_mask;
+       u32 blue_mask;
+       u32 alpha_mask;
+       u32 colour_space;
+       u32 unused[12];
+} __attribute__((__packed__));
+
+int str_to_u32_array(const char *p, u32 base, u32 array[])
+{
+       const char *start = p;
+       char str[12];
+       int length = 0;
+       int i, ret;
+
+       pr_info("input: %s", p);
+
+       for (i = 0 ; i < 255; i++) {
+               while (*p == ' ')
+                       p++;
+               if (*p == '\0')
+                       break;
+               start = p;
+
+               while ((*p != ' ') && (*p != '\0'))
+                       p++;
+
+               if ((p - start) >= sizeof(str))
+                       break;
+
+               memset(str, 0, sizeof(str));
+               memcpy(str, start, p - start);
+
+               ret = kstrtou32(str, base, &array[i]);
+               if (ret) {
+                       DRM_ERROR("input format error\n");
+                       break;
+               }
+
+               length++;
+       }
+
+       return length;
+}
+EXPORT_SYMBOL_GPL(str_to_u32_array);
+
+int str_to_u8_array(const char *p, u32 base, u8 array[])
+{
+       const char *start = p;
+       char str[12];
+       int length = 0;
+       int i, ret;
+
+       pr_info("input: %s", p);
+
+       for (i = 0 ; i < 255; i++) {
+               while (*p == ' ')
+                       p++;
+               if (*p == '\0')
+                       break;
+               start = p;
+
+               while ((*p != ' ') && (*p != '\0'))
+                       p++;
+
+               if ((p - start) >= sizeof(str))
+                       break;
+
+               memset(str, 0, sizeof(str));
+               memcpy(str, start, p - start);
+
+               ret = kstrtou8(str, base, &array[i]);
+               if (ret) {
+                       DRM_ERROR("input format error\n");
+                       break;
+               }
+
+               length++;
+       }
+
+       return length;
+}
+EXPORT_SYMBOL_GPL(str_to_u8_array);
+
+#if IS_ENABLED(CONFIG_GKI_FIX_WORKAROUND)
+static struct file *gki_filp_open(const char *filename, int flags, umode_t mode)
+{
+       return 0;
+}
+static ssize_t gki_vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
+{
+       return 0;
+}
+#endif
+
+void *disp_ops_attach(const char *str, struct list_head *head)
+{
+       struct ops_list *list;
+       const char *ver;
+
+       list_for_each_entry(list, head, head) {
+               ver = list->entry->ver;
+               if (!strcmp(str, ver))
+                       return list->entry->ops;
+       }
+
+       DRM_ERROR("attach disp ops %s failed\n", str);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(disp_ops_attach);
+
+int disp_ops_register(struct ops_entry *entry, struct list_head *head)
+{
+       struct ops_list *list;
+
+       list = kzalloc(sizeof(struct ops_list), GFP_KERNEL);
+       if (!list)
+               return -ENOMEM;
+
+       list->entry = entry;
+       list_add(&list->head, head);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(disp_ops_register);
+
+struct device *spacemit_disp_pipe_get_by_port(struct device *dev, int port)
+{
+       struct device_node *np = dev->of_node;
+       struct device_node *endpoint;
+       struct device_node *remote_node;
+       struct platform_device *remote_pdev;
+
+       endpoint = of_graph_get_endpoint_by_regs(np, port, 0);
+       if (!endpoint) {
+               DRM_ERROR("%s/port%d/endpoint0 was not found\n",
+                         np->full_name, port);
+               return NULL;
+       }
+
+       remote_node = of_graph_get_remote_port_parent(endpoint);
+       if (!remote_node) {
+               DRM_ERROR("device node was not found by endpoint0\n");
+               return NULL;
+       }
+
+       remote_pdev = of_find_device_by_node(remote_node);
+       if (remote_pdev == NULL) {
+               DRM_ERROR("find %s platform device failed\n",
+                         remote_node->full_name);
+               return NULL;
+       }
+
+       return &remote_pdev->dev;
+}
+EXPORT_SYMBOL_GPL(spacemit_disp_pipe_get_by_port);
+
+struct device *spacemit_disp_pipe_get_input(struct device *dev)
+{
+       return spacemit_disp_pipe_get_by_port(dev, 1);
+}
+EXPORT_SYMBOL_GPL(spacemit_disp_pipe_get_input);
+
+struct device *spacemit_disp_pipe_get_output(struct device *dev)
+{
+       return spacemit_disp_pipe_get_by_port(dev, 0);
+}
+EXPORT_SYMBOL_GPL(spacemit_disp_pipe_get_output);
+
+/*
+ * copy from drm opensource, change name from drm_atomic_replace_property_blob_from_id
+ * to spacemit_atomic_replace_property_blob_from_id
+ */
+int spacemit_atomic_replace_property_blob_from_id(struct drm_device *dev,
+                                        struct drm_property_blob **blob,
+                                        uint64_t blob_id,
+                                        ssize_t expected_size,
+                                        ssize_t expected_elem_size,
+                                        bool *replaced)
+{
+       struct drm_property_blob *new_blob = NULL;
+
+       if (blob_id != 0) {
+               new_blob = drm_property_lookup_blob(dev, blob_id);
+               if (new_blob == NULL)
+                       return -EINVAL;
+
+               if (expected_size > 0 &&
+                   new_blob->length != expected_size) {
+                       drm_property_blob_put(new_blob);
+                       return -EINVAL;
+               }
+               if (expected_elem_size > 0 &&
+                   new_blob->length % expected_elem_size != 0) {
+                       drm_property_blob_put(new_blob);
+                       return -EINVAL;
+               }
+       }
+
+       *replaced |= drm_property_replace_blob(blob, new_blob);
+       drm_property_blob_put(new_blob);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(spacemit_atomic_replace_property_blob_from_id);
+
+MODULE_DESCRIPTION("display common API library");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_lib.h b/drivers/gpu/drm/spacemit/spacemit_lib.h
new file mode 100644 (file)
index 0000000..33a3b17
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_LIB_H_
+#define _SPACEMIT_LIB_H_
+
+#include <drm/drm_print.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_mode_object.h>
+#include <linux/list.h>
+
+struct ops_entry {
+       const char *ver;
+       void *ops;
+};
+
+struct ops_list {
+       struct list_head head;
+       struct ops_entry *entry;
+};
+
+int str_to_u32_array(const char *p, u32 base, u32 array[]);
+int str_to_u8_array(const char *p, u32 base, u8 array[]);
+int dump_bmp32(const char *p, u32 width, u32 height,
+               bool bgra, const char *filename);
+
+void *disp_ops_attach(const char *str, struct list_head *head);
+int disp_ops_register(struct ops_entry *entry, struct list_head *head);
+
+struct device *spacemit_disp_pipe_get_by_port(struct device *dev, int port);
+struct device *spacemit_disp_pipe_get_input(struct device *dev);
+struct device *spacemit_disp_pipe_get_output(struct device *dev);
+int spacemit_atomic_replace_property_blob_from_id(struct drm_device *dev,
+                                        struct drm_property_blob **blob,
+                                        uint64_t blob_id,
+                                        ssize_t expected_size,
+                                        ssize_t expected_elem_size,
+                                        bool *replaced);
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_mipi_panel.c b/drivers/gpu/drm/spacemit/spacemit_mipi_panel.c
new file mode 100644 (file)
index 0000000..680f391
--- /dev/null
@@ -0,0 +1,745 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <linux/atomic.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <video/mipi_display.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+#include "spacemit_mipi_panel.h"
+#include "spacemit_dsi.h"
+#include "sysfs/sysfs_display.h"
+
+const char *lcd_name;
+
+static inline struct spacemit_panel *to_spacemit_panel(struct drm_panel *panel)
+{
+       return container_of(panel, struct spacemit_panel, base);
+}
+
+static int __maybe_unused  spacemit_panel_send_cmds(struct mipi_dsi_device *dsi,
+                               const void *data, int size)
+{
+       struct spacemit_panel *panel;
+       struct spacemit_dsi_cmd_desc *cmds = NULL;
+       u16 len;
+       int data_off = 0;
+       int i = 0;
+       unsigned char *tmp = NULL;
+
+       if (dsi == NULL)
+               return -EINVAL;
+
+       panel = mipi_dsi_get_drvdata(dsi);
+       cmds  = devm_kzalloc(&dsi->dev, sizeof(struct spacemit_dsi_cmd_desc), GFP_KERNEL);
+       if (!cmds)
+               return -ENOMEM;
+
+       while (size > 0) {
+               cmds->cmd_type = *(unsigned char *)(data + data_off++);
+               cmds->lp = *(unsigned char *)(data + data_off++);
+               cmds->delay = *(unsigned char *)(data + data_off++);
+               cmds->length = *(unsigned char *)(data + data_off++);
+               for (i = 0; i < cmds->length; i++) {
+                       tmp = (unsigned char *)data + data_off++;
+                       cmds->data[i] = *(unsigned char *)tmp;
+               }
+
+               len = cmds->length;
+
+               if(cmds->lp)
+                       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+               else
+                       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+               if (panel->info.use_dcs)
+                       mipi_dsi_dcs_write_buffer(dsi, cmds->data, len);
+               else
+                       mipi_dsi_generic_write(dsi, cmds->data, len);
+
+               if (cmds->delay)
+                       msleep(cmds->delay);
+               size -= (len + 4);
+       }
+
+       devm_kfree(&dsi->dev, cmds);
+
+       return 0;
+}
+
+/* drm_register_client - register a client notifier */
+int spacemit_drm_register_client(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&drm_notifier_list, nb);
+}
+EXPORT_SYMBOL(spacemit_drm_register_client);
+
+/* drm_unregister_client - unregister a client notifier */
+int spacemit_drm_unregister_client(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&drm_notifier_list, nb);
+}
+EXPORT_SYMBOL(spacemit_drm_unregister_client);
+
+/* drm_notifier_call_chain - notify clients of drm_events */
+int spacemit_drm_notifier_call_chain(unsigned long val, void *v)
+{
+       return blocking_notifier_call_chain(&drm_notifier_list, val, v);
+}
+EXPORT_SYMBOL_GPL(spacemit_drm_notifier_call_chain);
+
+
+static int spacemit_panel_unprepare(struct drm_panel *p)
+{
+       struct spacemit_panel *panel = to_spacemit_panel(p);
+
+       /* do nothing before spacemit_panel_prepare been called */
+       int blank = DRM_PANEL_BLANK_POWERDOWN;
+       struct spacemit_drm_notifier_mipi noti_blank;
+       noti_blank.blank = & blank;
+       spacemit_drm_notifier_call_chain(DRM_PANEL_EARLY_EVENT_BLANK, &noti_blank);
+       pr_info("mipi: POWERDOWN!!\n");
+
+       if (!atomic_read(&panel->prepare_refcnt))
+               return 0;
+
+       DRM_INFO("%s()\n", __func__);
+
+       gpio_direction_output(panel->gpio_reset, 1);
+
+       if(INVALID_GPIO != panel->gpio_bl) {
+               gpio_direction_output(panel->gpio_bl, 0);
+       }
+       msleep(150);
+
+       gpio_direction_output(panel->gpio_dc[0], 0);
+       gpio_direction_output(panel->gpio_dc[1], 0);
+
+       if(INVALID_GPIO != panel->gpio_avdd[0]) {
+               gpio_direction_output(panel->gpio_avdd[0], 0);
+       }
+       if(INVALID_GPIO != panel->gpio_avdd[1]) {
+               gpio_direction_output(panel->gpio_avdd[1], 0);
+       }
+
+       if (panel->vdd_1v2) {
+               regulator_disable(panel->vdd_1v2);
+       }
+       if (panel->vdd_1v8) {
+               regulator_disable(panel->vdd_1v8);
+       }
+       if (panel->vdd_2v8) {
+               regulator_disable(panel->vdd_2v8);
+       }
+
+       atomic_set(&panel->prepare_refcnt, 0);
+       return 0;
+}
+
+void spacemit_prepare_regulator (struct spacemit_panel *panel){
+       int ret = 0;
+
+       if (panel->vdd_2v8 != NULL) {
+               ret = regulator_enable(panel->vdd_2v8);
+               if (ret)
+                       DRM_ERROR("enable lcd regulator vdd_2v8 failed\n");
+       }
+
+       if (panel->vdd_1v8 != NULL) {
+               ret = regulator_enable(panel->vdd_1v8);
+               if (ret)
+                       DRM_ERROR("enable lcd regulator vdd_1v8 failed\n");
+       }
+
+       if (panel->vdd_1v2 != NULL) {
+               ret = regulator_enable(panel->vdd_1v2);
+               if (ret)
+                       DRM_ERROR("enable lcd regulator vdd_1v2 failed\n");
+       }
+}
+
+static int spacemit_panel_prepare(struct drm_panel *p)
+{
+       struct spacemit_panel *panel = to_spacemit_panel(p);
+       int i = 0;
+
+       int blank_ = DRM_PANEL_BLANK_UNBLANK;
+       struct spacemit_drm_notifier_mipi noti_blank;
+
+       /* prevent this function been called twice */
+       if (atomic_read(&panel->prepare_refcnt))
+               return 0;
+
+       DRM_INFO("%s()\n", __func__);
+
+       // spacemit_prepare_regulator(panel);
+
+       gpio_direction_output(panel->gpio_dc[0], 1);
+       gpio_direction_output(panel->gpio_dc[1], 1);
+
+       if(panel->gpio_avdd[0] != INVALID_GPIO) {
+               gpio_direction_output(panel->gpio_avdd[0], 1);
+       }
+       if(panel->gpio_avdd[1] != INVALID_GPIO) {
+               gpio_direction_output(panel->gpio_avdd[1], 1);
+       }
+
+       if(panel->gpio_bl != INVALID_GPIO) {
+               gpio_direction_output(panel->gpio_bl, 1);
+       }
+
+       gpio_direction_output(panel->gpio_reset, 1);
+       for (; i < panel->reset_toggle_cnt; i++) {
+               msleep(10);
+               gpio_direction_output(panel->gpio_reset, 0);
+               msleep(10);
+               gpio_direction_output(panel->gpio_reset, 1);
+       }
+       msleep(panel->delay_after_reset);
+
+       noti_blank.blank = & blank_;
+       spacemit_drm_notifier_call_chain(DRM_PANEL_EVENT_BLANK, &noti_blank);
+       pr_info("mipi: UNBLANK!!\n");
+
+       /* update refcnt */
+       atomic_set(&panel->prepare_refcnt, 1);
+       return 0;
+}
+
+static int spacemit_panel_disable(struct drm_panel *p)
+{
+       struct spacemit_panel *panel = to_spacemit_panel(p);
+
+       if (!atomic_read(&panel->enable_refcnt))
+               return 0;
+
+       DRM_INFO("%s()\n", __func__);
+
+       if (panel->esd_work_pending) {
+               cancel_delayed_work_sync(&panel->esd_work);
+               panel->esd_work_pending = false;
+       }
+
+       spacemit_panel_send_cmds(panel->slave,
+                            panel->info.cmds[CMD_CODE_SLEEP_IN],
+                            panel->info.cmds_len[CMD_CODE_SLEEP_IN]);
+
+       atomic_set(&panel->enable_refcnt, 0);
+       return 0;
+}
+
+static int spacemit_panel_enable(struct drm_panel *p)
+{
+       struct spacemit_panel *panel = to_spacemit_panel(p);
+
+       if (atomic_read(&panel->enable_refcnt))
+               return 0;
+
+       DRM_INFO("%s()\n", __func__);
+
+       spacemit_panel_send_cmds(panel->slave,
+                            panel->info.cmds[CMD_CODE_INIT],
+                            panel->info.cmds_len[CMD_CODE_INIT]);
+
+       if (panel->info.esd_check_en) {
+               schedule_delayed_work(&panel->esd_work,
+                                     msecs_to_jiffies(1000));
+               panel->esd_work_pending = true;
+       }
+
+       atomic_set(&panel->enable_refcnt, 1);
+       return 0;
+}
+
+static int spacemit_panel_get_modes(struct drm_panel *p, struct drm_connector *connector)
+{
+       struct drm_display_mode *mode;
+       struct spacemit_panel *panel = to_spacemit_panel(p);
+
+       DRM_INFO("%s()\n", __func__);
+
+       mode = drm_mode_duplicate(connector->dev, &panel->info.mode);
+       if (!mode) {
+               DRM_ERROR("failed to add mode %ux%ux@%u\n",
+                         panel->info.mode.hdisplay,
+                         panel->info.mode.vdisplay,
+                         60);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       drm_mode_probed_add(connector, mode);
+
+       connector->display_info.width_mm = panel->info.mode.width_mm;
+       connector->display_info.height_mm = panel->info.mode.height_mm;
+
+       return 1;
+}
+
+static const struct drm_panel_funcs spacemit_panel_funcs = {
+       .get_modes = spacemit_panel_get_modes,
+       .enable = spacemit_panel_enable,
+       .disable = spacemit_panel_disable,
+       .prepare = spacemit_panel_prepare,
+       .unprepare = spacemit_panel_unprepare,
+};
+
+static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
+                                       struct mipi_dsi_msg *msg)
+{
+       const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+
+       if (!ops || !ops->transfer)
+               return -ENOSYS;
+
+       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);
+}
+
+static int spacemit_mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
+                                           u16 value)
+{
+       u8 tx[2] = { value & 0xff, value >> 8 };
+       int ret;
+       struct mipi_dsi_msg msg = {0x0};
+
+       msg.channel = dsi->channel;
+       msg.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
+       msg.tx_len = sizeof(tx);
+       msg.tx_buf = tx;
+
+       ret = mipi_dsi_device_transfer(dsi, &msg);
+
+       return (ret < 0) ? ret : 0;
+}
+
+static int spacemit_panel_esd_check(struct spacemit_panel *panel)
+{
+       struct panel_info *info = &panel->info;
+       u8 read_val = 0;
+
+       spacemit_mipi_dsi_set_maximum_return_packet_size(panel->slave, 1);
+       mipi_dsi_dcs_read(panel->slave, info->esd_check_reg,
+                         &read_val, 1);
+
+       if (read_val != info->esd_check_val) {
+               DRM_ERROR("esd check failed, read value = 0x%02x\n",
+                         read_val);
+               return -EINVAL;
+       } else
+               DRM_INFO("esd check, read value = 0x%02x\n", read_val);
+
+       return 0;
+}
+
+static void spacemit_panel_esd_work_func(struct work_struct *work)
+{
+       struct spacemit_panel *panel = container_of(work, struct spacemit_panel,
+                                               esd_work.work);
+       struct panel_info *info = &panel->info;
+       int ret;
+
+       ret = spacemit_panel_esd_check(panel);
+       if (ret) {
+               /*
+               const struct drm_encoder_helper_funcs *funcs;
+               struct drm_encoder *encoder;
+
+               encoder = panel->base.connector->encoder;
+               funcs = encoder->helper_private;
+               panel->esd_work_pending = false;
+
+               DRM_INFO("====== esd recovery start ========\n");
+               funcs->disable(encoder);
+               funcs->enable(encoder);
+               DRM_INFO("======= esd recovery end =========\n");
+               */
+       } else
+               schedule_delayed_work(&panel->esd_work,
+                       msecs_to_jiffies(info->esd_check_period));
+}
+
+static int spacemit_panel_parse_dt(struct device_node *np, struct spacemit_panel *panel)
+{
+       u32 val;
+       struct device_node *lcd_node;
+       struct panel_info *info = &panel->info;
+       int bytes, rc;
+       const void *p;
+       const char *str;
+       char lcd_path[60];
+
+       rc = of_property_read_string(np, "force-attached", &str);
+       if (!rc)
+               lcd_name = str;
+
+       sprintf(lcd_path, "/lcds/%s", lcd_name);
+       lcd_node = of_find_node_by_path(lcd_path);
+       if (!lcd_node) {
+               DRM_ERROR("%pOF: could not find %s node\n", np, lcd_name);
+               return -ENODEV;
+       }
+       info->of_node = lcd_node;
+
+       rc = of_property_read_u32(lcd_node, "dsi-work-mode", &val);
+       if (!rc) {
+               if (val == DSI_MODE_CMD)
+                       info->mode_flags = 0;
+               else if (val == DSI_MODE_VIDEO_BURST)
+                       info->mode_flags = MIPI_DSI_MODE_VIDEO |
+                                          MIPI_DSI_MODE_VIDEO_BURST;
+               else if (val == DSI_MODE_VIDEO_SYNC_PULSE)
+                       info->mode_flags = MIPI_DSI_MODE_VIDEO |
+                                          MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+               else if (val == DSI_MODE_VIDEO_SYNC_EVENT)
+                       info->mode_flags = MIPI_DSI_MODE_VIDEO;
+       } else {
+               DRM_ERROR("dsi work mode is not found! use video mode\n");
+               info->mode_flags = MIPI_DSI_MODE_VIDEO |
+                                  MIPI_DSI_MODE_VIDEO_BURST;
+       }
+
+       if (of_property_read_bool(lcd_node, "dsi-non-continuous-clock"))
+               info->mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+       rc = of_property_read_u32(lcd_node, "dsi-lane-number", &val);
+       if (!rc)
+               info->lanes = val;
+       else
+               info->lanes = 4;
+
+       rc = of_property_read_string(lcd_node, "dsi-color-format", &str);
+       if (rc)
+               info->format = MIPI_DSI_FMT_RGB888;
+       else if (!strcmp(str, "rgb888"))
+               info->format = MIPI_DSI_FMT_RGB888;
+       else if (!strcmp(str, "rgb666"))
+               info->format = MIPI_DSI_FMT_RGB666;
+       else if (!strcmp(str, "rgb666_packed"))
+               info->format = MIPI_DSI_FMT_RGB666_PACKED;
+       else if (!strcmp(str, "rgb565"))
+               info->format = MIPI_DSI_FMT_RGB565;
+       else
+               DRM_ERROR("dsi-color-format (%s) is not supported\n", str);
+
+       rc = of_property_read_u32(lcd_node, "width-mm", &val);
+       if (!rc)
+               info->mode.width_mm = val;
+       else
+               info->mode.width_mm = 68;
+
+       rc = of_property_read_u32(lcd_node, "height-mm", &val);
+       if (!rc)
+               info->mode.height_mm = val;
+       else
+               info->mode.height_mm = 121;
+
+       rc = of_property_read_u32(lcd_node, "esd-check-enable", &val);
+       if (!rc)
+               info->esd_check_en = val;
+
+       rc = of_property_read_u32(lcd_node, "esd-check-mode", &val);
+       if (!rc)
+               info->esd_check_mode = val;
+       else
+               info->esd_check_mode = 1;
+
+       rc = of_property_read_u32(lcd_node, "esd-check-period", &val);
+       if (!rc)
+               info->esd_check_period = val;
+       else
+               info->esd_check_period = 1000;
+
+       rc = of_property_read_u32(lcd_node, "esd-check-register", &val);
+       if (!rc)
+               info->esd_check_reg = val;
+       else
+               info->esd_check_reg = 0x0A;
+
+       rc = of_property_read_u32(lcd_node, "esd-check-value", &val);
+       if (!rc)
+               info->esd_check_val = val;
+       else
+               info->esd_check_val = 0x9C;
+
+       if (of_property_read_bool(lcd_node, "use-dcs-write"))
+               info->use_dcs = true;
+       else
+               info->use_dcs = false;
+
+       p = of_get_property(lcd_node, "read-id-command", &bytes);
+       if (p) {
+               info->cmds[CMD_CODE_READ_ID] = p;
+               info->cmds_len[CMD_CODE_READ_ID] = bytes;
+       } else
+               DRM_ERROR("can't find read-id property\n");
+
+       p = of_get_property(lcd_node, "initial-command", &bytes);
+       if (p) {
+               info->cmds[CMD_CODE_INIT] = p;
+               info->cmds_len[CMD_CODE_INIT] = bytes;
+       } else
+               DRM_ERROR("can't find initial-command property\n");
+
+       p = of_get_property(lcd_node, "sleep-in-command", &bytes);
+       if (p) {
+               info->cmds[CMD_CODE_SLEEP_IN] = p;
+               info->cmds_len[CMD_CODE_SLEEP_IN] = bytes;
+       } else
+               DRM_ERROR("can't find sleep-in-command property\n");
+
+       p = of_get_property(lcd_node, "sleep-out-command", &bytes);
+       if (p) {
+               info->cmds[CMD_CODE_SLEEP_OUT] = p;
+               info->cmds_len[CMD_CODE_SLEEP_OUT] = bytes;
+       } else
+               DRM_ERROR("can't find sleep-out-command property\n");
+
+       rc = of_get_drm_display_mode(lcd_node, &info->mode, 0,
+                                    OF_USE_NATIVE_MODE);
+       if (rc) {
+               DRM_ERROR("get display timing failed\n");
+               return rc;
+       }
+
+       //info->mode.vrefresh = drm_mode_vrefresh(&info->mode);
+
+       return 0;
+}
+
+static int spacemit_panel_device_create(struct device *parent,
+                                   struct spacemit_panel *panel)
+{
+       panel->dev.class = display_class;
+       panel->dev.parent = parent;
+       panel->dev.of_node = panel->info.of_node;
+       dev_set_name(&panel->dev, "panel%d", panel->id);
+       dev_set_drvdata(&panel->dev, panel);
+
+       return device_register(&panel->dev);
+}
+
+static int spacemit_panel_probe(struct mipi_dsi_device *slave)
+{
+       int ret;
+       struct spacemit_panel *panel;
+       struct device *dev = &slave->dev;
+       int count;
+       const char *strings[3];
+       u32 tmp;
+
+       DRM_INFO("%s()\n", __func__);
+
+       panel = devm_kzalloc(&slave->dev, sizeof(*panel), GFP_KERNEL);
+       if (!panel)
+               return -ENOMEM;
+
+       if (!of_property_read_u32(dev->of_node, "id", &tmp))
+               panel->id = tmp;
+
+       count = of_property_count_strings(dev->of_node, "vin-supply-names");
+       if (count <= 0 || count != 3) {
+               panel->vdd_2v8 = NULL;
+               panel->vdd_1v8 = NULL;
+               panel->vdd_1v2 = NULL;
+       } else {
+               of_property_read_string_array(dev->of_node, "vin-supply-names", strings, 3);
+
+               panel->vdd_2v8 = devm_regulator_get(&slave->dev, strings[0]);
+               if (IS_ERR_OR_NULL(panel->vdd_2v8)) {
+                       DRM_DEBUG("get lcd regulator vdd_2v8 failed\n");
+                       panel->vdd_2v8 = NULL;
+               } else {
+                       regulator_set_voltage(panel->vdd_2v8, 2800000, 2800000);
+                       ret = regulator_enable(panel->vdd_2v8);
+                       if (ret)
+                               DRM_ERROR("enable lcd regulator vdd_2v8 failed\n");
+               }
+
+               panel->vdd_1v8 = devm_regulator_get(&slave->dev, strings[1]);
+               if (IS_ERR_OR_NULL(panel->vdd_1v8)) {
+                       DRM_DEBUG("get lcd regulator vdd_1v8 failed\n");
+                       panel->vdd_1v8 = NULL;
+               } else {
+                       regulator_set_voltage(panel->vdd_1v8, 1800000, 1800000);
+                       ret = regulator_enable(panel->vdd_1v8);
+                       if (ret)
+                               DRM_ERROR("enable lcd regulator vdd_1v8 failed\n");
+               }
+
+               panel->vdd_1v2 = devm_regulator_get(&slave->dev, strings[2]);
+               if (IS_ERR_OR_NULL(panel->vdd_1v2)) {
+                       DRM_DEBUG("get regulator vdd_1v2 failed\n");
+                       panel->vdd_1v2 = NULL;
+               } else {
+                       regulator_set_voltage(panel->vdd_1v2, 1200000, 1200000);
+                       ret = regulator_enable(panel->vdd_1v2);
+                       if (ret)
+                               DRM_ERROR("enable lcd regulator vdd_1v2 failed\n");
+               }
+       }
+
+       ret = of_property_read_u32(dev->of_node, "gpios-reset", &panel->gpio_reset);
+       if (ret || !gpio_is_valid(panel->gpio_reset)) {
+               dev_err(dev, "Missing dt property: gpios-reset\n");
+       } else {
+               ret = gpio_request(panel->gpio_reset, NULL);
+               if (ret) {
+                       pr_err("gpio_reset request fail\n");
+               }
+       }
+
+       ret = of_property_read_u32(dev->of_node, "gpios-bl", &panel->gpio_bl);
+       if (ret || !gpio_is_valid(panel->gpio_bl)) {
+               dev_dbg(dev, "Missing dt property: gpios-bl\n");
+               panel->gpio_bl = INVALID_GPIO;
+       } else {
+               ret = gpio_request(panel->gpio_bl, NULL);
+               if (ret) {
+                       pr_err("gpio_bl request fail\n");
+               }
+       }
+
+       ret = of_property_read_u32_array(dev->of_node, "gpios-dc", panel->gpio_dc, 2);
+       if (ret || !gpio_is_valid(panel->gpio_dc[0]) || !gpio_is_valid(panel->gpio_dc[1])) {
+               dev_err(dev, "Missing dt property: gpios-dc\n");
+               return -EINVAL;
+       } else {
+               ret = gpio_request(panel->gpio_dc[0], NULL);
+               ret |= gpio_request(panel->gpio_dc[1], NULL);
+               if (ret) {
+                       pr_err("gpio_dc request fail\n");
+                       return ret;
+               }
+       }
+
+       ret = of_property_read_u32_array(dev->of_node, "gpios-avdd", panel->gpio_avdd, 2);
+       if (ret || !gpio_is_valid(panel->gpio_avdd[0]) || !gpio_is_valid(panel->gpio_avdd[1])) {
+               dev_dbg(dev, "Missing avdd property: gpios-avdd\n");
+               panel->gpio_avdd[0] = INVALID_GPIO;
+               panel->gpio_avdd[1] = INVALID_GPIO;
+       } else {
+               ret = gpio_request(panel->gpio_avdd[0], NULL);
+               ret |= gpio_request(panel->gpio_avdd[1], NULL);
+               if (ret) {
+                       pr_err("gpio_avdd request fail\n");
+                       return ret;
+               }
+       }
+
+       if (of_property_read_u32(dev->of_node, "reset-toggle-cnt", &panel->reset_toggle_cnt))
+               panel->reset_toggle_cnt = LCD_PANEL_RESET_CNT;
+
+       if (of_property_read_u32(dev->of_node, "delay-after-reset", &panel->delay_after_reset))
+               panel->delay_after_reset = LCD_DELAY_AFTER_RESET;
+
+       ret = spacemit_panel_parse_dt(slave->dev.of_node, panel);
+       if (ret) {
+               DRM_ERROR("parse panel info failed\n");
+               return ret;
+       }
+
+       ret = spacemit_panel_device_create(&slave->dev, panel);
+       if (ret) {
+               DRM_ERROR("panel device create failed\n");
+               return ret;
+       }
+
+       panel->base.dev = &panel->dev;
+       panel->base.funcs = &spacemit_panel_funcs;
+       drm_panel_init(&panel->base, &panel->dev, &spacemit_panel_funcs, DRM_MODE_CONNECTOR_DSI);
+
+       ret = drm_panel_of_backlight(&panel->base);
+       if (ret) {
+               DRM_ERROR("panel device get backlight failed\n");
+               return ret;
+       }
+
+       drm_panel_add(&panel->base);
+       backlight_disable(panel->base.backlight);
+
+       slave->lanes = panel->info.lanes;
+       slave->format = panel->info.format;
+       slave->mode_flags = panel->info.mode_flags;
+
+       ret = mipi_dsi_attach(slave);
+       if (ret) {
+               DRM_ERROR("failed to attach dsi panel to host\n");
+               drm_panel_remove(&panel->base);
+               return ret;
+       }
+       panel->slave = slave;
+
+       spacemit_mipi_panel_sysfs_init(&panel->dev);
+       mipi_dsi_set_drvdata(slave, panel);
+
+       /*do esd init work*/
+       if (panel->info.esd_check_en) {
+               INIT_DELAYED_WORK(&panel->esd_work, spacemit_panel_esd_work_func);
+               /*
+               schedule_delayed_work(&panel->esd_work,
+                                     msecs_to_jiffies(2000));
+               panel->esd_work_pending = true;
+               */
+       }
+
+       atomic_set(&panel->enable_refcnt, 0);
+       atomic_set(&panel->prepare_refcnt, 0);
+
+       DRM_INFO("panel driver probe success\n");
+
+       return 0;
+}
+
+static void spacemit_panel_remove(struct mipi_dsi_device *slave)
+{
+       struct spacemit_panel *panel = mipi_dsi_get_drvdata(slave);
+       int ret;
+
+       DRM_INFO("%s()\n", __func__);
+
+       spacemit_panel_disable(&panel->base);
+       spacemit_panel_unprepare(&panel->base);
+
+       ret = mipi_dsi_detach(slave);
+       if (ret < 0)
+               DRM_ERROR("failed to detach from DSI host: %d\n", ret);
+
+       drm_panel_remove(&panel->base);
+
+       return;
+}
+
+static const struct of_device_id panel_of_match[] = {
+       { .compatible = "spacemit,mipi-panel0", },
+       { .compatible = "spacemit,mipi-panel1", },
+       { .compatible = "spacemit,mipi-panel2", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, panel_of_match);
+
+static struct mipi_dsi_driver spacemit_panel_driver = {
+       .driver = {
+               .name = "spacemit-mipi-panel-drv",
+               .of_match_table = panel_of_match,
+       },
+       .probe = spacemit_panel_probe,
+       .shutdown = spacemit_panel_remove,
+};
+module_mipi_dsi_driver(spacemit_panel_driver);
+
+MODULE_DESCRIPTION("Spacemit MIPI Panel Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_mipi_panel.h b/drivers/gpu/drm/spacemit/spacemit_mipi_panel.h
new file mode 100644 (file)
index 0000000..2de013c
--- /dev/null
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_MIPI_PANEL_H_
+#define _SPACEMIT_MIPI_PANEL_H_
+
+#include <drm/drm_print.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <soc/spacemit/spacemit_panel.h>
+
+#define INVALID_GPIO 0xFFFFFFFF
+
+#define LCD_PANEL_RESET_CNT 2
+#define LCD_DELAY_AFTER_RESET 100
+
+enum {
+       CMD_CODE_INIT = 0,
+       CMD_CODE_SLEEP_IN,
+       CMD_CODE_SLEEP_OUT,
+       CMD_CODE_READ_ID,
+       CMD_CODE_READ_POWER,
+       CMD_CODE_MAX,
+};
+
+enum {
+       DSI_MODE_CMD = 0,
+       DSI_MODE_VIDEO_BURST,
+       DSI_MODE_VIDEO_SYNC_PULSE,
+       DSI_MODE_VIDEO_SYNC_EVENT,
+};
+
+struct panel_info {
+       /* common parameters */
+       struct device_node *of_node;
+       struct drm_display_mode mode;
+       const void *cmds[CMD_CODE_MAX];
+       int cmds_len[CMD_CODE_MAX];
+
+       /* esd check parameters*/
+       bool esd_check_en;
+       u8 esd_check_mode;
+       u16 esd_check_period;
+       u32 esd_check_reg;
+       u32 esd_check_val;
+
+       /* MIPI DSI specific parameters */
+       u32 format;
+       u32 lanes;
+       u32 mode_flags;
+       bool use_dcs;
+};
+
+struct spacemit_panel {
+       int id;
+       struct device dev;
+       struct drm_panel base;
+       struct mipi_dsi_device *slave;
+       struct panel_info info;
+
+       struct delayed_work esd_work;
+       bool esd_work_pending;
+
+       struct regulator *vdd_1v2;
+       struct regulator *vdd_1v8;
+       struct regulator *vdd_2v8;
+       u32 gpio_reset;
+       u32 gpio_bl;
+       u32 gpio_dc[2];
+       u32 gpio_avdd[2];
+       atomic_t enable_refcnt;
+       atomic_t prepare_refcnt;
+       u32 reset_toggle_cnt;
+       u32 delay_after_reset;
+};
+
+struct spacemit_drm_notifier_mipi
+{
+       void *blank;
+};
+
+static BLOCKING_NOTIFIER_HEAD(drm_notifier_list);
+
+int spacemit_drm_notifier_call_chain(unsigned long val, void *v);
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_planes.c b/drivers/gpu/drm/spacemit/spacemit_planes.c
new file mode 100644 (file)
index 0000000..f47bbcc
--- /dev/null
@@ -0,0 +1,702 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_color_mgmt.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include "spacemit_cmdlist.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_dpu.h"
+#include "spacemit_drm.h"
+#include "spacemit_gem.h"
+#include "spacemit_lib.h"
+#include "dpu/dpu_saturn.h"
+#include "dpu/dpu_trace.h"
+
+//as per weston requirement, suppoted modifiers need to be reported to user space,
+//which is different from sf
+static const uint64_t supported_format_modifiers[] = {
+       DRM_FORMAT_MOD_LINEAR,
+       DRM_FORMAT_MOD_INVALID
+};
+
+struct spacemit_plane *to_spacemit_plane(struct drm_plane *plane)
+{
+       return container_of(plane, struct spacemit_plane, plane);
+}
+
+static int spacemit_plane_check_rdma(const struct spacemit_hw_rdma *rdma, u32 rdma_id, struct drm_plane_state *state)
+{
+       unsigned int rot = state->rotation;
+       unsigned int zpos = state->zpos;
+       u32 format = state->fb->format->format;
+       u16 hw_formats = rdma[rdma_id].formats;
+       u16 hw_rots = rdma[rdma_id].rots;
+       bool afbc = (state->fb->modifier > 0);
+       const struct drm_format_info *info = drm_format_info(format);
+       struct spacemit_dpu *dpu = crtc_to_dpu(state->crtc);
+
+       trace_spacemit_plane_check_rdma(dpu->dev_id);
+       if ((info->is_yuv) && !afbc && ((hw_formats & FORMAT_RAW_YUV) == 0)) {
+               DRM_DEBUG("rdma%d doesn't support RAW YUV format with zpos%d!\n", rdma_id, zpos);
+               return -EINVAL;
+       }
+
+       if (afbc && ((hw_formats & FORMAT_AFBC) == 0)) {
+               DRM_DEBUG("rdma%d doesn't support AFBC format with zpos%d!\n", rdma_id, zpos);
+               return -EINVAL;
+       }
+
+       if (rot == DRM_MODE_ROTATE_90 || rot == DRM_MODE_ROTATE_270) {
+               if (afbc) {
+                       if ((hw_rots & ROTATE_AFBC_90_270) == 0) {
+                               DRM_DEBUG("rdma%d doesn't support AFBC 90/270 rotation with zpos%d\n", rdma_id, zpos);
+                               return -EINVAL;
+                       }
+               } else {
+                       if ((hw_rots & ROTATE_RAW_90_270) == 0) {
+                               DRM_DEBUG("rdma%d doesn't support RAW 90/270 rotation with zpos%d!\n", rdma_id, zpos);
+                               return -EINVAL;
+                       }
+               }
+       } else {
+               if ((hw_rots & ROTATE_COMMON) == 0) {
+                       DRM_DEBUG("rdma%d doesn't support common rotation with zpos%d!\n", rdma_id, zpos);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int spacemit_plane_atomic_check_hdr_coefs (struct drm_plane *plane,
+                                         struct drm_plane_state *state)
+{
+       struct spacemit_plane_state *apstate = to_spacemit_plane_state(state);
+       struct drm_property_blob *blob;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       int size = hwdev->hdr_coef_size;
+       int *coef_data;
+       int n = 0;
+
+       if ((apstate->hdr_coefs_blob_prop)){
+               blob = apstate->hdr_coefs_blob_prop;
+               coef_data = (int *)blob->data;
+
+               for (n = 0; n < size; n++){
+                       if ((coef_data[n] > 65535) || (coef_data[n] < 0)){
+                               DRM_ERROR("HDR coef is invalid %d\n", coef_data[n]);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int spacemit_plane_atomic_check_scale_coefs (struct drm_plane *plane,
+                                         struct drm_plane_state *state)
+{
+       struct spacemit_plane_state *apstate = to_spacemit_plane_state(state);
+       struct drm_property_blob *blob;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       int size = hwdev->scale_coef_size;
+       int *coef_data;
+       int n = 0;
+
+       if ((apstate->scale_coefs_blob_prop)){
+               blob = apstate->scale_coefs_blob_prop;
+               coef_data = (int *)blob->data;
+
+               for (n = 0; n < size; n++){
+                       if ((coef_data[n] > 32766) || (coef_data[n] < -32767)){
+                               DRM_ERROR("scale coef is invalid %d\n", coef_data[n]);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int spacemit_plane_atomic_check(struct drm_plane *plane,
+                                 struct drm_atomic_state *atomic_state)
+{
+       struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, plane);
+       struct drm_framebuffer *fb = state->fb;
+       u16 pixel_alpha = state->pixel_blend_mode;
+       u32 src_w, src_h, src_x, src_y;
+       u32 crtc_w, crtc_h;
+       struct spacemit_plane_state *cur_state = to_spacemit_plane_state(state);
+       u32 cur_rdma_id = cur_state->rdma_id;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       const struct spacemit_hw_rdma *rdmas = hwdev->rdmas;
+       struct spacemit_dpu *dpu = NULL;
+
+       if (!state->crtc || WARN_ON(!state->fb)) {
+               return 0;
+       }
+
+       dpu = crtc_to_dpu(state->crtc);
+       trace_spacemit_plane_atomic_check(dpu->dev_id);
+
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+       crtc_w = state->crtc_w;
+       crtc_h = state->crtc_h;
+
+       /* For multi planes, only support planes with its offsets is set */
+       if (fb->format->num_planes > 3 ||
+          (fb->format->num_planes > 2 && fb->offsets[2] == 0) ||
+          (fb->format->num_planes > 1 && fb->offsets[1] == 0)) {
+               DRM_ERROR("%s, Unsupported plane format: plane_num:%d offsets[1]:%d offsets[2]:%d\n", \
+                         __func__, fb->format->num_planes, fb->offsets[1], fb->offsets[2]);
+               return -EINVAL;
+       }
+
+       if (fb->format->num_planes > 1 && fb->modifier) {
+               DRM_ERROR("%s, Unsupported afbc with plane_num:%d\n", __func__, fb->format->num_planes);
+               return -EINVAL;
+       }
+
+       if (fb->format->format == DRM_FORMAT_NV12 || fb->format->format == DRM_FORMAT_YUV420_8BIT) {
+               if (src_x % 2 || src_y % 2 || src_w % 2 || src_h % 2) {
+                       DRM_ERROR("YUV420 coordinations must be even! src_x:%d src_y:%d \
+                                 src_w:%d src_h:%d\n", src_x, src_y, src_w, src_h);
+                       return -EINVAL;
+               }
+       }
+
+       /* adjust rdma id */
+       if (src_w == 0 && src_h == 0)
+               cur_rdma_id = RDMA_INVALID_ID;
+       else {
+               /* In case the userspace hasn't set rdma id */
+               if (cur_rdma_id == RDMA_INVALID_ID)
+                       cur_rdma_id = state->zpos;
+       }
+       cur_state->rdma_id = cur_rdma_id;
+
+       /* Skip solid color */
+       if (cur_rdma_id != RDMA_INVALID_ID) {
+               if (src_w != crtc_w || src_h != crtc_h)
+                       cur_state->use_scl = true;
+               else
+                       cur_state->use_scl = false;
+               if (cur_rdma_id < hwdev->rdma_nums) {
+                       if (spacemit_plane_check_rdma(rdmas, cur_rdma_id, state))
+                               return -EINVAL;
+               } else {
+                       DRM_ERROR("Invalid rdma id:%d\n", cur_rdma_id);
+                       return -EINVAL;
+               }
+
+               if (dpu->core->cal_layer_fbcmem_size(plane, state)) {
+                       DRM_ERROR("plane:%d Invalid fbcmem size\n", state->zpos);
+                       return -EINVAL;
+               }
+
+               if(dpu->core->calc_plane_mclk_bw(plane, state)) {
+                       DRM_INFO("plane:%d unsupported mclk or bandwidth\n", state->zpos);
+                       return -EINVAL;
+               }
+       }
+
+       if (spacemit_plane_atomic_check_hdr_coefs(plane, state)){
+               DRM_ERROR("The value of hdr coef is invalid\n");
+               return -EINVAL;
+       }
+
+       if (spacemit_plane_atomic_check_scale_coefs(plane, state)){
+               DRM_ERROR("The value of scale coef is invalid\n");
+               return -EINVAL;
+       }
+
+       /* HW can't support plane + pixel blending */
+       if ((state->alpha != DRM_BLEND_ALPHA_OPAQUE) &&
+           (pixel_alpha != DRM_MODE_BLEND_PIXEL_NONE) &&
+           fb->format->has_alpha) {
+               DRM_ERROR("Can't support mixed blend mode!\n");
+               return -EINVAL;
+       }
+
+       cur_state->format = spacemit_plane_hw_get_format_id(fb->format->format);
+       if (cur_state->format == SPACEMIT_DPU_INVALID_FORMAT_ID) {
+               DRM_ERROR("Can't support format:0x%x\n", fb->format->format);
+               return -EINVAL;
+       }
+
+       /* Use default values as they are not actually used now  */
+       cur_state->right_image = 0;
+       cur_state->is_offline = 1;
+       return 0;
+}
+
+static void spacemit_plane_atomic_update(struct drm_plane *plane,
+                                   struct drm_atomic_state *state)
+{
+       int ret = 0;
+       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+       struct spacemit_dpu *dpu = crtc_to_dpu(plane->state->crtc);
+       struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(plane->state);
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u32 rdma_id = spacemit_pstate->rdma_id;
+
+       DRM_DEBUG("%s()\n", __func__);
+       trace_spacemit_plane_atomic_update(dpu->dev_id);
+
+       spacemit_plane_update_hw_channel(plane);
+
+       spacemit_update_hdr_matrix(plane, spacemit_pstate);
+       spacemit_update_csc_matrix(plane, old_state);
+
+       /* No need for solid color layer */
+       if (rdma_id < hwdev->rdma_nums) {
+               u8 tbu_id;
+
+               spacemit_pstate->mmu_tbl.size = ((PAGE_ALIGN(plane->state->fb->obj[0]->size) >> PAGE_SHIFT) +
+                                          HW_ALIGN_TTB_NUM) * 4;
+               spacemit_pstate->mmu_tbl.va = dma_alloc_coherent(dpu->dev, spacemit_pstate->mmu_tbl.size, \
+                                                          &spacemit_pstate->mmu_tbl.pa, GFP_KERNEL | __GFP_ZERO);
+               if (spacemit_pstate->mmu_tbl.va == NULL) {
+                       DRM_ERROR("Failed to allocate %d bytes for dpu plane%d mmu table\n",
+                                  spacemit_pstate->mmu_tbl.size, plane->state->zpos);
+                       return;
+               }
+               tbu_id = !spacemit_pstate->right_image ? (rdma_id * 2) : (rdma_id * 2 + 1);
+               ret = spacemit_dmmu_map(plane->state->fb, &spacemit_pstate->mmu_tbl, tbu_id, false);
+               if (!ret)
+                       cmdlist_regs_packing(plane);
+               else
+                       DRM_ERROR("%s failed to map plane with ret = %d\n", __func__, ret);
+       }
+}
+
+static void spacemit_plane_atomic_disable(struct drm_plane *plane,
+                                    struct drm_atomic_state *state)
+{
+       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+       DRM_DEBUG("%s()\n", __func__);
+
+       spacemit_dmmu_unmap(plane);
+       spacemit_plane_disable_hw_channel(plane, old_state);
+}
+
+// static void spacemit_plane_atomic_cleanup_fb(struct drm_plane *plane,
+//                                  struct drm_plane_state *old_state)
+// {
+
+// }
+
+static void spacemit_plane_reset(struct drm_plane *plane)
+{
+       struct spacemit_plane *p = to_spacemit_plane(plane);
+       struct spacemit_plane_state *s;
+       struct spacemit_drm_private *priv = plane->dev->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       struct spacemit_dpu *dpu = NULL;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (plane->state) {
+               s = to_spacemit_plane_state(plane->state);
+               dpu = crtc_to_dpu(plane->crtc);
+               trace_spacemit_plane_reset(dpu->dev_id);
+               __drm_atomic_helper_plane_destroy_state(plane->state);
+               kfree(s);
+               plane->state = NULL;
+       }
+
+       s = kzalloc(sizeof(*s), GFP_KERNEL);
+       if (s) {
+               __drm_atomic_helper_plane_reset(plane, &s->state);
+               s->state.zpos = hwdev->plane_nums - p->hw_pid - 1;
+               s->rdma_id = RDMA_INVALID_ID;
+               s->is_offline = 1;
+               s->scaler_id = SCALER_INVALID_ID;
+       }
+}
+
+static struct drm_plane_state *
+spacemit_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+       struct spacemit_plane_state *s;
+       struct spacemit_plane_state *old_state = to_spacemit_plane_state(plane->state);
+       struct spacemit_dpu *dpu = NULL;
+
+       if (plane->crtc) {
+               dpu = crtc_to_dpu(plane->crtc);
+               trace_spacemit_plane_atomic_duplicate_state(dpu->dev_id);
+       }
+       DRM_DEBUG("%s()\n", __func__);
+
+       s = kzalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return NULL;
+
+       __drm_atomic_helper_plane_duplicate_state(plane, &s->state);
+
+       WARN_ON(s->state.plane != plane);
+
+       s->is_offline = old_state->is_offline;
+       s->rdma_id = old_state->rdma_id;
+       s->format = old_state->format;
+       s->right_image = old_state->right_image;
+       s->scaler_id = SCALER_INVALID_ID;
+       s->use_scl = false;
+       s->fbcmem_size = 0;
+       if (s->hdr_coefs_blob_prop)
+               drm_property_blob_get(s->hdr_coefs_blob_prop);
+       if (s->scale_coefs_blob_prop)
+               drm_property_blob_get(s->scale_coefs_blob_prop);
+       return &s->state;
+}
+
+static void spacemit_plane_atomic_destroy_state(struct drm_plane *plane,
+                                           struct drm_plane_state *state)
+{
+       struct spacemit_plane_state *spacemit_pstate = to_spacemit_plane_state(state);
+       struct spacemit_dpu *dpu = NULL;
+       DRM_DEBUG("%s()\n", __func__);
+
+       if (state->crtc) {
+               dpu = crtc_to_dpu(state->crtc);
+
+               if (spacemit_pstate->mmu_tbl.va)
+                       dma_free_coherent(dpu->dev, spacemit_pstate->mmu_tbl.size, \
+                                         spacemit_pstate->mmu_tbl.va, spacemit_pstate->mmu_tbl.pa);
+
+               if (spacemit_pstate->cl.va)
+                       dma_free_coherent(dpu->dev, spacemit_pstate->cl.size, \
+                                         spacemit_pstate->cl.va, spacemit_pstate->cl.pa);
+               trace_spacemit_plane_atomic_destroy_state(dpu->dev_id);
+       }
+       __drm_atomic_helper_plane_destroy_state(state);
+
+       if (spacemit_pstate->hdr_coefs_blob_prop)
+               drm_property_blob_put(spacemit_pstate->hdr_coefs_blob_prop);
+
+       if (spacemit_pstate->scale_coefs_blob_prop)
+               drm_property_blob_put(spacemit_pstate->scale_coefs_blob_prop);
+
+       kfree(to_spacemit_plane_state(state));
+}
+
+static int spacemit_plane_atomic_set_property(struct drm_plane *plane,
+                                         struct drm_plane_state *state,
+                                         struct drm_property *property,
+                                         u64 val)
+{
+       struct spacemit_plane *p = to_spacemit_plane(plane);
+       struct spacemit_plane_state *s = to_spacemit_plane_state(state);
+       bool replaced = false;
+       int ret = 0;
+
+       DRM_DEBUG("%s() name = %s, val = %llu\n",
+                 __func__, property->name, val);
+
+       if (property == p->rdma_id_property)
+               s->rdma_id = val;
+       else if (property == p->solid_color_property)
+               s->solid_color = val;
+       else if (property == p->hdr_coef_property) {
+               ret = spacemit_atomic_replace_property_blob_from_id(plane->dev,
+                                       &s->hdr_coefs_blob_prop,
+                                       val,
+                                       -1,
+                                       sizeof(int),
+                                       &replaced);
+               return ret;
+       } else if (property == p->scale_coef_property) {
+               ret = spacemit_atomic_replace_property_blob_from_id(plane->dev,
+                                       &s->scale_coefs_blob_prop,
+                                       val,
+                                       -1,
+                                       sizeof(int),
+                                       &replaced);
+               return ret;
+       } else {
+               DRM_ERROR("property %s is invalid\n", property->name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int spacemit_plane_atomic_get_property(struct drm_plane *plane,
+                                         const struct drm_plane_state *state,
+                                         struct drm_property *property,
+                                         u64 *val)
+{
+       struct spacemit_plane *p = to_spacemit_plane(plane);
+       const struct spacemit_plane_state *s = to_spacemit_plane_state(state);
+
+       DRM_DEBUG("%s() name = %s\n", __func__, property->name);
+
+       if (property == p->rdma_id_property)
+               *val = s->rdma_id;
+       else if (property == p->solid_color_property)
+               *val = s->solid_color;
+       else if (property == p->hdr_coef_property){
+               if (s->hdr_coefs_blob_prop)
+                       *val = (s->hdr_coefs_blob_prop) ? s->hdr_coefs_blob_prop->base.id : 0;
+       }
+       else if (property == p->scale_coef_property){
+               if (s->scale_coefs_blob_prop)
+                       *val = (s->scale_coefs_blob_prop) ? s->scale_coefs_blob_prop->base.id : 0;
+       }
+       else {
+               DRM_ERROR("property %s is invalid\n", property->name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const char * const color_encoding_name[] = {
+       [DRM_COLOR_YCBCR_BT601] = "ITU-R BT.601 YCbCr",
+       [DRM_COLOR_YCBCR_BT709] = "ITU-R BT.709 YCbCr",
+       [DRM_COLOR_YCBCR_BT2020] = "ITU-R BT.2020 YCbCr",
+};
+
+static const char * const color_range_name[] = {
+       [DRM_COLOR_YCBCR_FULL_RANGE] = "YCbCr full range",
+       [DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
+};
+
+int spacemit_drm_plane_create_color_properties(struct drm_plane *plane,
+                                     u32 supported_encodings,
+                                     u32 supported_ranges,
+                                     enum drm_color_encoding default_encoding,
+                                     enum drm_color_range default_range)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_property *prop;
+       struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
+                                                      DRM_COLOR_RANGE_MAX)];
+       int i, len;
+
+       if (WARN_ON(supported_encodings == 0 ||
+                   (supported_encodings & -BIT(DRM_COLOR_ENCODING_MAX)) != 0 ||
+                   (supported_encodings & BIT(default_encoding)) == 0))
+               return -EINVAL;
+
+       if (WARN_ON(supported_ranges == 0 ||
+                   (supported_ranges & -BIT(DRM_COLOR_RANGE_MAX)) != 0 ||
+                   (supported_ranges & BIT(default_range)) == 0))
+               return -EINVAL;
+
+       len = 0;
+       for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
+               if ((supported_encodings & BIT(i)) == 0)
+                       continue;
+
+               enum_list[len].type = i;
+               enum_list[len].name = color_encoding_name[i];
+               len++;
+       }
+
+       prop = drm_property_create_enum(dev, 0, "COLOR_ENCODING",
+                                       enum_list, len);
+       if (!prop)
+               return -ENOMEM;
+       plane->color_encoding_property = prop;
+       drm_object_attach_property(&plane->base, prop, default_encoding);
+       if (plane->state)
+               plane->state->color_encoding = default_encoding;
+
+       len = 0;
+       for (i = 0; i < DRM_COLOR_RANGE_MAX; i++) {
+               if ((supported_ranges & BIT(i)) == 0)
+                       continue;
+
+               enum_list[len].type = i;
+               enum_list[len].name = color_range_name[i];
+               len++;
+       }
+
+       prop = drm_property_create_enum(dev, 0, "COLOR_RANGE",
+                                       enum_list, len);
+       if (!prop)
+               return -ENOMEM;
+       plane->color_range_property = prop;
+       drm_object_attach_property(&plane->base, prop, default_range);
+       if (plane->state)
+               plane->state->color_range = default_range;
+
+       return 0;
+}
+
+static int spacemit_plane_create_properties(struct spacemit_plane *p, int index)
+{
+       struct drm_property *prop;
+       unsigned int support_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+               BIT(DRM_MODE_BLEND_PREMULTI) |
+               BIT(DRM_MODE_BLEND_COVERAGE);
+       int ret = 0;
+
+       DRM_DEBUG("%s()\n", __func__);
+       /* create rotation property */
+       drm_plane_create_rotation_property(&p->plane,
+                                          DRM_MODE_ROTATE_0,
+                                          DRM_MODE_ROTATE_MASK |
+                                          DRM_MODE_REFLECT_MASK);
+
+       /* create zpos property */
+       drm_plane_create_zpos_immutable_property(&p->plane, index);
+
+       /* create layer alpha property */
+       drm_plane_create_alpha_property(&p->plane);
+
+       /* create blend mode property */
+       drm_plane_create_blend_mode_property(&p->plane, support_modes);
+
+       prop = drm_property_create_range(p->plane.dev, 0,
+                       "RDMA_ID", 0, ULLONG_MAX);
+       if (!prop)
+               return -ENOMEM;
+       drm_object_attach_property(&p->plane.base, prop, 0);
+       p->rdma_id_property = prop;
+
+       prop = drm_property_create_range(p->plane.dev, 0,
+                       "SOLID_COLOR", 0, ULLONG_MAX);
+       if (!prop)
+               return -ENOMEM;
+       drm_object_attach_property(&p->plane.base, prop, 0);
+       p->solid_color_property = prop;
+
+       prop = drm_property_create(p->plane.dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
+                       "hdr_coefs", 0);
+       if (!prop)
+               return -ENOMEM;
+       drm_object_attach_property(&p->plane.base, prop, 0);
+       p->hdr_coef_property = prop;
+
+       prop = drm_property_create(p->plane.dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
+                       "scale_coefs", 0);
+       if (!prop)
+               return -ENOMEM;
+       drm_object_attach_property(&p->plane.base, prop, 0);
+       p->scale_coef_property = prop;
+
+       ret = spacemit_drm_plane_create_color_properties(&p->plane,
+                                       BIT(DRM_COLOR_YCBCR_BT601) | \
+                                       BIT(DRM_COLOR_YCBCR_BT709) | \
+                                       BIT(DRM_COLOR_YCBCR_BT2020),
+                                       BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | \
+                                       BIT(DRM_COLOR_YCBCR_FULL_RANGE),
+                                       DRM_COLOR_YCBCR_BT601, DRM_COLOR_YCBCR_LIMITED_RANGE);
+       if(ret)
+               DRM_ERROR("Failed to create color properties %d\n", ret);
+
+       return 0;
+}
+
+static const struct drm_plane_helper_funcs spacemit_plane_helper_funcs = {
+       .atomic_check = spacemit_plane_atomic_check,
+       .atomic_update = spacemit_plane_atomic_update,
+       .atomic_disable = spacemit_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs spacemit_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane  = drm_atomic_helper_disable_plane,
+       .destroy = drm_plane_cleanup,
+       .reset = spacemit_plane_reset,
+       .atomic_duplicate_state = spacemit_plane_atomic_duplicate_state,
+       .atomic_destroy_state = spacemit_plane_atomic_destroy_state,
+       .atomic_set_property = spacemit_plane_atomic_set_property,
+       .atomic_get_property = spacemit_plane_atomic_get_property,
+};
+
+struct drm_plane *spacemit_plane_init(struct drm_device *drm,
+                                       struct spacemit_dpu *dpu)
+{
+       struct drm_plane *primary = NULL;
+       struct spacemit_plane *p = NULL;
+       enum drm_plane_type plane_type;
+       int err, i, j;
+       u32 *formats;
+       struct spacemit_drm_private *priv = drm->dev_private;
+       struct spacemit_hw_device *hwdev = priv->hwdev;
+       u8 n_planes = hwdev->plane_nums;
+       u8 n_formats = hwdev->n_formats;
+       u8 n_fbcmems = hwdev->n_fbcmems;
+       u8 n_rdmas = hwdev->rdma_nums;
+       u32 plane_crtc_mask;
+
+       trace_spacemit_plane_init(dpu->dev_id);
+       if (n_fbcmems * 2 != n_rdmas) {
+               DRM_ERROR("Unmatched rdma and fbcmem numbers, \
+                          n_rdmas:%d n_fbcmems:%d!\n", n_rdmas, n_fbcmems);
+               err = -EINVAL;
+               return ERR_PTR(err);
+       }
+
+       formats = kcalloc(n_formats, sizeof(*formats), GFP_KERNEL);
+       if (!formats) {
+               err = -ENOMEM;
+               return ERR_PTR(err);
+       }
+
+       /* Create all planes first. They can all be put to any CRTC. */
+       plane_crtc_mask = (1 << priv->num_pipes) - 1;
+
+       for (i = 0; i < n_planes; i++) {
+               p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
+               if (!p) {
+                       kfree(formats);
+                       return ERR_PTR(-ENOMEM);
+               }
+
+               /* build the list of DRM supported formats based on the map */
+               for (j = 0; j < n_formats; j++)
+                       formats[j] = hwdev->formats[j].format;
+
+               plane_type = (i < priv->num_pipes)
+                          ? DRM_PLANE_TYPE_PRIMARY
+                          : DRM_PLANE_TYPE_OVERLAY;
+
+               err = drm_universal_plane_init(drm, &p->plane, plane_crtc_mask,
+                                              &spacemit_plane_funcs, formats,
+                                              n_formats, supported_format_modifiers,
+                                              plane_type, NULL);
+               if (err) {
+                       DRM_ERROR("fail to init %s plane%d\n", (i < priv->num_pipes) ? "primary" : "overlay", i);
+                       return ERR_PTR(err);
+               }
+
+               drm_plane_helper_add(&p->plane, &spacemit_plane_helper_funcs);
+
+               spacemit_plane_create_properties(p, i);
+
+               p->hwdev = hwdev;
+               p->hw_pid = n_planes - i - 1;
+               if (i == 0)
+                       primary = &p->plane;
+       }
+
+       kfree(formats);
+
+       if (p)
+               DRM_INFO("dpu plane init ok\n");
+
+       return primary;
+}
diff --git a/drivers/gpu/drm/spacemit/spacemit_wb.c b/drivers/gpu/drm/spacemit/spacemit_wb.c
new file mode 100644 (file)
index 0000000..3bcadfd
--- /dev/null
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+//#include <drm/drm_gem_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_writeback.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_of.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_graph.h>
+#include "spacemit_lib.h"
+#include "spacemit_dpu.h"
+#include "spacemit_wb.h"
+#include "sysfs/sysfs_display.h"
+
+static const u32 spacemit_wb_formats[] = {
+       DRM_FORMAT_XRGB8888,
+};
+
+void spacemit_wb_atomic_commit(struct drm_device *drm, struct drm_atomic_state *old_state)
+{
+       struct drm_crtc *crtc;
+       struct spacemit_dpu *dpu;
+       struct drm_crtc_state *new_crtc_state;
+       struct drm_writeback_connector *wb_conn;
+       struct drm_connector_state *conn_state;
+       int i;
+
+       for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
+               if (!new_crtc_state->active)
+                       continue;
+
+               dpu = crtc_to_dpu(crtc);
+               wb_conn = &dpu->wb_connector;
+               conn_state = wb_conn->base.state;
+
+               if (!conn_state)
+                       return;
+
+               if (conn_state->writeback_job)
+                       drm_writeback_queue_job(wb_conn, conn_state);
+       }
+}
+
+static int spacemit_wb_encoder_atomic_check(struct drm_encoder *encoder,
+                                   struct drm_crtc_state *crtc_state,
+                                   struct drm_connector_state *conn_state)
+{
+       DRM_DEBUG("%s()\n", __func__);
+
+       return 0;
+}
+
+static const struct drm_encoder_helper_funcs spacemit_wb_encoder_helper_funcs = {
+       .atomic_check = spacemit_wb_encoder_atomic_check,
+};
+
+static const struct drm_encoder_funcs spacemit_wb_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static int spacemit_wb_connector_get_modes(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       return drm_add_modes_noedid(connector, dev->mode_config.max_width,
+                                   dev->mode_config.max_height);
+}
+
+static enum drm_mode_status
+spacemit_wb_connector_mode_valid(struct drm_connector *connector,
+                        struct drm_display_mode *mode)
+{
+       enum drm_mode_status mode_status = MODE_OK;
+
+       DRM_DEBUG("%s(%s)\n", __func__, mode->name);
+
+       return mode_status;
+}
+
+static enum drm_connector_status
+spacemit_wb_connector_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+static void spacemit_wb_connector_destroy(struct drm_connector *connector)
+{
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_helper_funcs spacemit_wb_connector_helper_funcs = {
+       .get_modes = spacemit_wb_connector_get_modes,
+       .mode_valid = spacemit_wb_connector_mode_valid,
+};
+
+void spacemit_wb_drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
+                                         struct drm_connector_state *state)
+{
+       struct drm_crtc *crtc = NULL;
+       struct spacemit_dpu *dpu = NULL;
+
+       crtc = state->crtc;
+       dpu = crtc_to_dpu(crtc);
+
+       if (dpu->mmu_tbl.va)
+               dma_free_coherent(dpu->dev, dpu->mmu_tbl.size, \
+                       dpu->mmu_tbl.va, dpu->mmu_tbl.pa);
+}
+
+static const struct drm_connector_funcs spacemit_wb_connector_funcs = {
+       .reset = drm_atomic_helper_connector_reset,
+       .detect = spacemit_wb_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = spacemit_wb_connector_destroy,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = spacemit_wb_drm_atomic_helper_connector_destroy_state,
+};
+
+static int spacemit_wb_connector_init(struct drm_device *drm, struct drm_crtc *crtc)
+{
+       int ret;
+       struct spacemit_dpu *dpu = crtc_to_dpu(crtc);
+
+       dpu->wb_connector.encoder.possible_crtcs = 1 << drm_crtc_index(crtc);
+
+       drm_connector_helper_add(&dpu->wb_connector.base,
+                                &spacemit_wb_connector_helper_funcs);
+
+       ret = drm_writeback_connector_init(drm, &dpu->wb_connector,
+                                          &spacemit_wb_connector_funcs,
+                                          &spacemit_wb_encoder_helper_funcs,
+                                          spacemit_wb_formats,
+                                          ARRAY_SIZE(spacemit_wb_formats),
+                                          1 << drm_crtc_index(crtc));
+       if (ret) {
+               DRM_ERROR("drm_connector_init() failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int spacemit_wb_init(struct drm_device *drm, struct drm_crtc *dpu_crtc)
+{
+       struct drm_crtc *crtc = dpu_crtc;
+       int ret;
+
+       ret = spacemit_wb_connector_init(drm, crtc);
+       if (ret)
+               return -1;
+
+       return ret;
+}
+
+static int spacemit_wb_device_create(struct spacemit_wb *wb, struct device *parent)
+{
+       int ret;
+
+       wb->dev.class = display_class;
+       wb->dev.parent = parent;
+       wb->dev.of_node = parent->of_node;
+       dev_set_name(&wb->dev, "wb%d", wb->ctx.id);
+       dev_set_drvdata(&wb->dev, wb);
+
+       ret = device_register(&wb->dev);
+       if (ret)
+               DRM_ERROR("wb device register failed\n");
+
+       return ret;
+}
+
+static int spacemit_wb_context_init(struct spacemit_wb *wb, struct device_node *np)
+{
+       struct spacemit_wb_device *ctx = &wb->ctx;
+       u32 tmp;
+
+       if (!of_property_read_u32(np, "dev-id", &tmp))
+               ctx->id = tmp;
+
+       return 0;
+}
+
+static int spacemit_wb_bind(struct device *dev, struct device *master, void *data)
+{
+
+       return 0;
+}
+
+static void spacemit_wb_unbind(struct device *dev,
+                               struct device *master, void *data)
+{
+       /* do nothing */
+       DRM_DEBUG("%s()\n", __func__);
+}
+
+static const struct component_ops spacemit_wb_component_ops = {
+       .bind = spacemit_wb_bind,
+       .unbind = spacemit_wb_unbind,
+};
+
+static int spacemit_wb_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct spacemit_wb *wb;
+       int ret;
+
+       DRM_DEBUG("%s()\n", __func__);
+
+       wb = devm_kzalloc(&pdev->dev, sizeof(*wb), GFP_KERNEL);
+       if (!wb) {
+               DRM_ERROR("failed to allocate wb data.\n");
+               return -ENOMEM;
+       }
+
+       ret = spacemit_wb_context_init(wb, np);
+       if (ret) {
+               return -EINVAL;
+       }
+
+       spacemit_wb_device_create(wb, &pdev->dev);
+       //spacemit_wb_sysfs_init(&wb->dev);
+       platform_set_drvdata(pdev, wb);
+
+       return component_add(&pdev->dev, &spacemit_wb_component_ops);
+}
+
+static int spacemit_wb_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &spacemit_wb_component_ops);
+       return 0;
+}
+
+static const struct of_device_id spacemit_wb_of_match[] = {
+       {.compatible = "spacemit,wb0"},
+       {.compatible = "spacemit,wb1"},
+       { }
+};
+MODULE_DEVICE_TABLE(of, spacemit_wb_of_match);
+
+struct platform_driver spacemit_wb_driver = {
+       .probe = spacemit_wb_probe,
+       .remove = spacemit_wb_remove,
+       .driver = {
+               .name = "spacemit-wb-drv",
+               .of_match_table = spacemit_wb_of_match,
+       },
+};
+
+// module_platform_driver(spacemit_wb_driver);
+static int spacemit_wb_driver_init(void)
+{
+       return platform_driver_register(&spacemit_wb_driver);
+}
+late_initcall(spacemit_wb_driver_init);
+
+MODULE_DESCRIPTION("Spacemit WB Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpu/drm/spacemit/spacemit_wb.h b/drivers/gpu/drm/spacemit/spacemit_wb.h
new file mode 100644 (file)
index 0000000..ff1f21a
--- /dev/null
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SPACEMIT_MW_H_
+#define _SPACEMIT_MW_H_
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <video/videomode.h>
+
+#include <drm/drm_print.h>
+#include <drm/drm_writeback.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_bridge.h>
+
+#include "spacemit_lib.h"
+
+enum spacemit_wb_loc {
+       SPACEMIT_WB_COMP0 = 0,
+       SPACEMIT_WB_COMP1,
+       SPACEMIT_WB_COMP2,
+       SPACEMIT_WB_COMP3,
+       SPACEMIT_WB_COMP4,
+       SPACEMIT_WB_RCH0 = 8,
+       SPACEMIT_WB_RCH1,
+       SPACEMIT_WB_RCH2,
+       SPACEMIT_WB_RCH3,
+       SPACEMIT_WB_RCH4,
+       SPACEMIT_WB_RCH5,
+       SPACEMIT_WB_RCH6,
+       SPACEMIT_WB_RCH7,
+       SPACEMIT_WB_RCH8,
+       SPACEMIT_WB_RCH9,
+       SPACEMIT_WB_RCH10,
+       SPACEMIT_WB_RCH11,
+       SPACEMIT_WB_POST0 = 24,
+       SPACEMIT_WB_POST1,
+       SPACEMIT_WB_POST2,
+};
+
+struct spacemit_wb_device {
+       uint32_t id;
+       struct videomode vm;
+       int status;
+};
+
+struct spacemit_wb {
+       struct device dev;
+       struct drm_encoder encoder;
+       struct spacemit_wb_device ctx;
+};
+
+void saturn_wb_config(struct spacemit_dpu *dpu);
+
+#endif
diff --git a/drivers/gpu/drm/spacemit/sysfs/sysfs_class.c b/drivers/gpu/drm/spacemit/sysfs/sysfs_class.c
new file mode 100644 (file)
index 0000000..d4d991d
--- /dev/null
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "sysfs_display.h"
+
+struct class *display_class;
+EXPORT_SYMBOL_GPL(display_class);
+
+#ifndef MODULE
+static int __init display_class_init(void)
+#else
+int display_class_init(void)
+#endif
+{
+       pr_info("display class register\n");
+
+       display_class = class_create("display");
+       if (IS_ERR(display_class)) {
+               pr_err("Unable to create display class\n");
+               return PTR_ERR(display_class);
+       }
+
+       return 0;
+}
+
+#ifndef MODULE
+postcore_initcall(display_class_init);
+#endif
+
+MODULE_DESCRIPTION("Provide display class for hardware driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpu/drm/spacemit/sysfs/sysfs_display.h b/drivers/gpu/drm/spacemit/sysfs/sysfs_display.h
new file mode 100644 (file)
index 0000000..53057ec
--- /dev/null
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef _SYSFS_DISPLAY_H_
+#define _SYSFS_DISPLAY_H_
+
+#include <linux/device.h>
+
+extern struct class *display_class;
+
+int spacemit_dpu_sysfs_init(struct device *dev);
+int spacemit_dsi_sysfs_init(struct device *dev);
+int spacemit_dphy_sysfs_init(struct device *dev);
+int spacemit_mipi_panel_sysfs_init(struct device *dev);
+
+#endif
+
diff --git a/drivers/gpu/drm/spacemit/sysfs/sysfs_dphy.c b/drivers/gpu/drm/spacemit/sysfs/sysfs_dphy.c
new file mode 100644 (file)
index 0000000..33e76c5
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#include "../spacemit_lib.h"
+#include "../spacemit_dphy.h"
+#include "sysfs_display.h"
+
+
+
+int spacemit_dphy_sysfs_init(struct device *dev)
+{
+       int rc = 0;
+/*
+       rc = sysfs_create_groups(&dev->kobj, dphy_groups);
+       if (rc)
+               pr_err("create dphy attr node failed, rc=%d\n", rc);
+*/
+       return rc;
+}
+EXPORT_SYMBOL(spacemit_dphy_sysfs_init);
+
+
+MODULE_DESCRIPTION("Provide mipi dsi phy attribute nodes for userspace");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpu/drm/spacemit/sysfs/sysfs_dpu.c b/drivers/gpu/drm/spacemit/sysfs/sysfs_dpu.c
new file mode 100644 (file)
index 0000000..060826b
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sysfs.h>
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+#include "../spacemit_lib.h"
+#include "../spacemit_dpu.h"
+#include "../spacemit_mipi_panel.h"
+#include "sysfs_display.h"
+
+#ifdef CONFIG_PM
+static ssize_t spacemit_dpu_get_enable_auto_fc(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "enable_auto_fc = %d %d\n", dpu->enable_auto_fc, dpu->dev->power.runtime_status);
+}
+#endif
+
+#ifdef CONFIG_PM
+static ssize_t spacemit_dpu_set_enable_auto_fc(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf,
+                                       size_t count)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+       int enable_auto_fc = 0;
+       int ret = 0;
+
+       if (dpu->dev->power.runtime_status != RPM_SUSPENDED) {
+               pr_err("set dpu_enable_auto_fc only support when screen off!\n");
+               return -EINVAL;
+       }
+
+       ret = sscanf(buf, "%d\n", &enable_auto_fc);
+       if ((ret != 1) || (enable_auto_fc < 0) || (enable_auto_fc > 1)) {
+               pr_err("Wrong parameter! Please echo 0 or 1\n");
+               return -EINVAL;
+       }
+
+       if (enable_auto_fc == 0) {
+               dpu->new_mclk = DPU_MCLK_DEFAULT;
+               if (dpu->core && dpu->core->update_clk)
+                       dpu->core->update_clk(dpu, dpu->new_mclk);
+       }
+
+       dpu->enable_auto_fc = enable_auto_fc;
+
+       return count;
+}
+#endif
+
+static ssize_t spacemit_dpu_get_enable_dump_reg(struct device *dev,
+                                               struct device_attribute *attr,
+                                               char *buf)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "enable_dump_reg = %d\n", dpu->enable_dump_reg);
+}
+
+static ssize_t spacemit_dpu_set_enable_dump_reg(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf,
+                                               size_t count)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+       int enable_dump_reg = 0;
+       int ret = 0;
+
+       ret = sscanf(buf, "%d\n", &enable_dump_reg);
+       if ((ret != 1) || (enable_dump_reg < 0) || (enable_dump_reg > 1)) {
+               pr_err("Wrong parameter! Please echo 0 or 1\n");
+               return -EINVAL;
+       }
+
+       dpu->enable_dump_reg = enable_dump_reg;
+
+       return count;
+}
+
+static ssize_t spacemit_dpu_get_enable_dump_fps(struct device *dev,
+                                               struct device_attribute *attr,
+                                               char *buf)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "enable_dump_fps = %d\n", dpu->enable_dump_fps);
+}
+
+static ssize_t spacemit_dpu_set_enable_dump_fps(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf,
+                                               size_t count)
+{
+       struct spacemit_dpu *dpu = dev_get_drvdata(dev);
+       int enable_dump_fps = 0;
+       int ret = 0;
+
+       ret = sscanf(buf, "%d\n", &enable_dump_fps);
+       if ((ret != 1) || (enable_dump_fps < 0) || (enable_dump_fps > 1)) {
+               pr_err("Wrong parameter! Please echo 0 or 1\n");
+               return -EINVAL;
+       }
+
+       dpu->enable_dump_fps = enable_dump_fps;
+
+       return count;
+}
+
+static DEVICE_ATTR(dpu_enable_dump_fps, S_IRUGO | S_IWUSR, spacemit_dpu_get_enable_dump_fps, spacemit_dpu_set_enable_dump_fps);
+static DEVICE_ATTR(dpu_enable_dump_reg, S_IRUGO | S_IWUSR, spacemit_dpu_get_enable_dump_reg, spacemit_dpu_set_enable_dump_reg);
+#ifdef CONFIG_PM
+static DEVICE_ATTR(dpu_enable_auto_fc, S_IRUGO | S_IWUSR, spacemit_dpu_get_enable_auto_fc, spacemit_dpu_set_enable_auto_fc);
+#endif
+
+int spacemit_dpu_sysfs_init(struct device *dev)
+{
+
+       int ret = 0;
+
+       ret = device_create_file(dev, &dev_attr_dpu_enable_dump_reg);
+       if (ret)
+               DRM_ERROR("failed to create device file: enable_dump_reg\n");
+       else
+               DRM_DEBUG("create device file enable_dump_reg\n");
+
+       ret = device_create_file(dev, &dev_attr_dpu_enable_dump_fps);
+       if (ret)
+               DRM_ERROR("failed to create device file: enable_dump_fps\n");
+       else
+               DRM_DEBUG("create device file enable_dump_fps\n");
+#ifdef CONFIG_PM
+       ret = device_create_file(dev, &dev_attr_dpu_enable_auto_fc);
+       if (ret)
+               DRM_ERROR("failed to create device file: enable_auto_fc\n");
+       else
+               DRM_DEBUG("create device file enable_auto_fc\n");
+#endif
+       return 0;
+}
+EXPORT_SYMBOL(spacemit_dpu_sysfs_init);
+
+MODULE_DESCRIPTION("Provide dpu attribute nodes for userspace");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/spacemit/sysfs/sysfs_dsi.c b/drivers/gpu/drm/spacemit/sysfs/sysfs_dsi.c
new file mode 100644 (file)
index 0000000..ca7f4c1
--- /dev/null
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "../spacemit_mipi_panel.h"
+#include "../spacemit_dsi.h"
+#include "../spacemit_dpu.h"
+#include "sysfs_display.h"
+
+
+int spacemit_dsi_sysfs_init(struct device *dev)
+{
+
+       return 0;
+}
+EXPORT_SYMBOL(spacemit_dsi_sysfs_init);
+
+MODULE_DESCRIPTION("Provide mipi dsi attribute nodes for userspace");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/gpu/drm/spacemit/sysfs/sysfs_mipi_panel.c b/drivers/gpu/drm/spacemit/sysfs/sysfs_mipi_panel.c
new file mode 100644 (file)
index 0000000..99584df
--- /dev/null
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <video/videomode.h>
+
+#include "../spacemit_lib.h"
+#include "../spacemit_mipi_panel.h"
+#include "sysfs_display.h"
+
+
+int spacemit_mipi_panel_sysfs_init(struct device *dev)
+{
+       return 0;
+}
+EXPORT_SYMBOL(spacemit_mipi_panel_sysfs_init);
+
+MODULE_DESCRIPTION("Provide panel attribute nodes for userspace");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/spacemit/spacemit_panel.h b/include/soc/spacemit/spacemit_panel.h
new file mode 100644 (file)
index 0000000..8af7ef8
--- /dev/null
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Spacemit Co., Ltd.
+ *
+ */
+
+#ifndef __SPACEMIT_PANEL_H__
+#define __SPACEMIT_PANEL_H__
+
+#include <linux/notifier.h>
+
+/*  complete the definition of DRM Macros */
+enum{
+       DRM_PANEL_EARLY_EVENT_BLANK = 0,
+       DRM_PANEL_EVENT_BLANK,
+       DRM_PANEL_BLANK_UNBLANK,
+       DRM_PANEL_BLANK_POWERDOWN,
+};
+
+extern int spacemit_drm_register_client(struct notifier_block *nb);
+extern int spacemit_drm_unregister_client(struct notifier_block *nb);
+
+#endif