drm: verisilicon: Enable LicheePi4 drm drivers 85/311585/2
authorMichal Wilczynski <m.wilczynski@samsung.com>
Wed, 22 May 2024 11:52:30 +0000 (13:52 +0200)
committerMichal Wilczynski <m.wilczynski@samsung.com>
Wed, 22 May 2024 21:41:44 +0000 (23:41 +0200)
Ported from vendor kernel [1].

[1] https://gitee.com/thead-yocto/kernel.git

Change-Id: I66e5fc772a89f9bcccac678a0cd2652d3dee1357
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
34 files changed:
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/verisilicon/Kconfig [new file with mode: 0644]
drivers/gpu/drm/verisilicon/Makefile [new file with mode: 0644]
drivers/gpu/drm/verisilicon/dw_hdmi-light.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/dw_hdmi_light.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/dw_hdmi_tx_phy_gen2.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/dw_mipi_dsi.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/dw_mipi_dsi.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_crtc.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_crtc.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc_dec.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc_dec.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc_hw.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc_hw.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc_mmu.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_dc_mmu.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_drv.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_drv.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_fb.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_fb.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_gem.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_gem.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_plane.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_plane.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_simple_enc.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_simple_enc.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_type.h [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_virtual.c [new file with mode: 0644]
drivers/gpu/drm/verisilicon/vs_virtual.h [new file with mode: 0644]
include/uapi/drm/drm_fourcc.h
include/uapi/drm/vs_drm.h [new file with mode: 0644]

index 3caa020391c75283c9145d18fb3be151496620af..b8994c37ea2972e9b58394ce8882e2b3490a265a 100644 (file)
@@ -387,6 +387,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
 
 source "drivers/gpu/drm/sprd/Kconfig"
 
+source "drivers/gpu/drm/verisilicon/Kconfig"
+
 config DRM_HYPERV
        tristate "DRM Support for Hyper-V synthetic video device"
        depends on DRM && PCI && MMU && HYPERV
index 215e78e791250b8ad0d0a273e627b84296ecc4d9..a0902298f98ff9d20f3b0ea39e287e2132f526f3 100644 (file)
@@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y                  += solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
 obj-$(CONFIG_DRM_LOONGSON) += loongson/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
new file mode 100644 (file)
index 0000000..8a8b454
--- /dev/null
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_VERISILICON
+       tristate "DRM Support for VeriSilicon"
+       depends on DRM
+       select DRM_KMS_HELPER
+       select VERISILICON_DW_MIPI_DSI
+       select VERISILICON_DW_HDMI_LIGHT
+       help
+         Choose this option if you have a VeriSilicon soc chipset.
+         This driver provides VeriSilicon kernel mode
+         setting and buffer management. It does not
+         provide 2D or 3D acceleration.
+
+config VERISILICON_VIRTUAL_DISPLAY
+       bool "display content output to debugfs file"
+       depends on DRM_VERISILICON
+       select DEBUG_FS
+       help
+         This is a debug feature which capture video content output
+         from display controller. Output path is debugfs/dri/connector/.
+         The content format is ARGB which Alpha is 0 for 8bits.
+         Disabled in default.
+
+config VERISILICON_DW_MIPI_DSI
+       bool "VeriSilicon specific driver for Synopsys DW MIPI DSI"
+       depends on DRM_VERISILICON
+       select DRM_MIPI_DSI
+       select GENERIC_PHY
+       select GENERIC_PHY_MIPI_DPHY
+       select PHY_DW_DPHY
+       help
+         This driver supports Synopsys DW MIPI DSI controller.
+         MIPI DSI controller is a bridge between encoder
+         and panel. Also it is a MIPI DSI host and can work
+         in dual mode.
+
+config VERISILICON_DW_HDMI_LIGHT
+       bool "THEAD sepcific driver for Synopsys DW HDMI on Light"
+       depends on DRM_VERISILICON
+       select DRM_DW_HDMI
+       help
+         This driver supports Synopsys DW HDMI controller on Light
+         platform from THEAD Corp.
+
+config VERISILICON_MMU
+       bool "MMU support for VeriSilicon display controller"
+       depends on DRM_VERISILICON
+       help
+         This is a memory management function which translate virtual address
+         to physical address. DPU MMU only do address translate, doesn't
+         support shareable.
+
+config VERISILICON_DEC
+       bool "DEC support for VeriSilicon display controller"
+       depends on DRM_VERISILICON
+       help
+         This is a decompression function which reads compressed pixels from
+         external memory (DDR or SRAM) under DPU's control, then it decompress
+         comprssed pixels before returing these pixels to DPU.
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
new file mode 100644 (file)
index 0000000..104a491
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+vs_drm-objs := vs_dc_hw.o \
+               vs_dc.o \
+               vs_crtc.o \
+               vs_drv.o \
+               vs_fb.o \
+               vs_gem.o \
+               vs_plane.o \
+               vs_simple_enc.o
+
+vs_drm-$(CONFIG_VERISILICON_VIRTUAL_DISPLAY) += vs_virtual.o
+vs_drm-$(CONFIG_VERISILICON_DW_MIPI_DSI) += dw_mipi_dsi.o
+vs_drm-$(CONFIG_VERISILICON_DW_HDMI_LIGHT) += dw_hdmi-light.o
+vs_drm-$(CONFIG_VERISILICON_MMU) += vs_dc_mmu.o
+vs_drm-$(CONFIG_VERISILICON_DEC) += vs_dc_dec.o
+
+obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
diff --git a/drivers/gpu/drm/verisilicon/dw_hdmi-light.c b/drivers/gpu/drm/verisilicon/dw_hdmi-light.c
new file mode 100644 (file)
index 0000000..a876fee
--- /dev/null
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_of.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include "dw_hdmi_tx_phy_gen2.h"
+
+struct light_hdmi {
+       struct device *dev;
+       struct drm_encoder encoder;
+       struct dw_hdmi *dw_hdmi;
+       /* TODO: may not be used */
+       struct regmap *sysreg;
+};
+
+static void dw_hdmi_light_encoder_enable(struct drm_encoder *encoder)
+{
+}
+
+static int dw_hdmi_light_encoder_atomic_check(struct drm_encoder *encoder,
+                                             struct drm_crtc_state *crtc_state,
+                                             struct drm_connector_state *conn_state)
+{
+       return 0;
+}
+
+static struct dw_hdmi_plat_data light_hdmi_drv_data = {
+       .mode_valid     = dw_hdmi_tx_phy_gen2_mode_valid,
+       .configure_phy  = dw_hdmi_tx_phy_gen2_configure,
+};
+
+static const struct of_device_id dw_hdmi_light_dt_ids[] = {
+       {
+               .compatible = "thead,light-hdmi-tx",
+               .data = &light_hdmi_drv_data,
+       },
+       {
+               /* sentinel */
+       },
+};
+
+static const struct drm_encoder_helper_funcs dw_hdmi_light_encoder_helper_funcs = {
+       .enable         = dw_hdmi_light_encoder_enable,
+       .atomic_check   = dw_hdmi_light_encoder_atomic_check,
+};
+
+struct dw_hdmi_light_private dw_hdmi_priv_data = {
+       .max_pixclock = 594000,
+       .max_width    = 0,
+       .max_height   = 0,
+};
+
+static int dw_hdmi_light_bind(struct device *dev, struct device *master,
+                             void *data)
+{
+       int ret = 0;
+       u32 max_pixclock = 0;
+       u16 max_width    = 0;
+       u16 max_height   = 0;
+       int property_ret = 0;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct device_node *np = dev->of_node;
+       const struct of_device_id *match;
+       const struct dw_hdmi_plat_data *plat_data;
+       struct light_hdmi *hdmi;
+       struct drm_device *drm = data;
+       struct drm_encoder *encoder;
+
+       if (!np)
+               return -ENODEV;
+       hdmi = dev_get_drvdata(dev);
+
+       match = of_match_node(dw_hdmi_light_dt_ids, np);
+       if (unlikely(!match))
+               return -ENODEV;
+
+       property_ret = of_property_read_u32(np, "max_pixclock", &max_pixclock);
+       if(property_ret == 0){
+               printk(KERN_INFO "Hdmi max pixel clock = %d\n", max_pixclock);
+               if(dw_hdmi_priv_data.max_pixclock > max_pixclock){
+                       dw_hdmi_priv_data.max_pixclock = max_pixclock;
+                       printk(KERN_INFO"Hdmi max pixel clock adjust to %d\n", max_pixclock);
+               }
+               else{
+                       printk(KERN_INFO"Hdmi max pixel clock not take effect\n");
+               }
+       }
+
+       property_ret = of_property_read_u16(np, "max_width", &max_width);
+       if(property_ret == 0){
+               dw_hdmi_priv_data.max_width = max_width;
+       }
+
+       property_ret = of_property_read_u16(np, "max_height", &max_height);
+       if(property_ret == 0){
+               dw_hdmi_priv_data.max_height = max_height;
+       }
+
+       light_hdmi_drv_data.priv_data = (void*)&dw_hdmi_priv_data;
+
+       plat_data = match->data;
+       hdmi->dev = &pdev->dev;
+       encoder = &hdmi->encoder;
+
+       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, np);
+       if (!encoder->possible_crtcs)
+               return -EPROBE_DEFER;
+
+       drm_encoder_helper_add(encoder, &dw_hdmi_light_encoder_helper_funcs);
+       drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+       hdmi->dw_hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+       if (IS_ERR(hdmi->dw_hdmi)) {
+               ret = PTR_ERR(hdmi->dw_hdmi);
+               drm_encoder_cleanup(encoder);
+       }
+       pm_runtime_enable(dev);
+
+       return ret;
+}
+
+static void dw_hdmi_light_unbind(struct device *dev, struct device *master,
+                                void *data)
+{
+       struct light_hdmi *hdmi = dev_get_drvdata(dev);
+
+       pm_runtime_disable(dev);
+
+       dw_hdmi_unbind(hdmi->dw_hdmi);
+}
+
+static const struct component_ops dw_hdmi_light_ops = {
+       .bind   = dw_hdmi_light_bind,
+       .unbind = dw_hdmi_light_unbind,
+};
+
+static int dw_hdmi_light_probe(struct platform_device *pdev)
+{
+       struct light_hdmi *hdmi;
+
+       int ret_comp;
+
+       hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+       if (!hdmi)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, hdmi);
+
+
+       ret_comp  = component_add(&pdev->dev, &dw_hdmi_light_ops);
+
+       return ret_comp;
+}
+
+static int dw_hdmi_light_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dw_hdmi_light_ops);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int hdmi_runtime_suspend(struct device *dev)
+{
+       struct light_hdmi *hdmi = dev_get_drvdata(dev);
+
+       return dw_hdmi_runtime_suspend(hdmi->dw_hdmi);
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+       struct light_hdmi *hdmi = dev_get_drvdata(dev);
+
+       return dw_hdmi_runtime_resume(hdmi->dw_hdmi);
+}
+#endif
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_resume(struct device *dev)
+{
+       struct light_hdmi *hdmi = dev_get_drvdata(dev);
+        dev_info(dev,"hdmi resume\n");
+        dw_hdmi_resume(hdmi->dw_hdmi);
+       return 0;
+}
+#endif
+static const struct dev_pm_ops dw_hdmi_pm_ops = {
+    SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                pm_runtime_force_resume)
+    SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
+    #ifdef CONFIG_PM_SLEEP
+     SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL,hdmi_resume)
+    #endif
+};
+
+struct platform_driver dw_hdmi_light_platform_driver = {
+       .probe  = dw_hdmi_light_probe,
+       .remove = dw_hdmi_light_remove,
+       .driver = {
+               .name = "dwhdmi-light",
+               .of_match_table = dw_hdmi_light_dt_ids,
+               .pm = &dw_hdmi_pm_ops,
+       },
+};
+
+MODULE_AUTHOR("You Xiao <youxiao.fc@linux.alibaba.com>");
+MODULE_DESCRIPTION("Light Platforms Specific DW-HDMI Driver Extention");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwhdmi-light");
diff --git a/drivers/gpu/drm/verisilicon/dw_hdmi_light.h b/drivers/gpu/drm/verisilicon/dw_hdmi_light.h
new file mode 100644 (file)
index 0000000..15f4e18
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __DW_HDMI_LIGHT_H__
+#define __DW_HDMI_LIGHT_H__
+
+extern struct platform_driver dw_hdmi_light_platform_driver;
+#endif
diff --git a/drivers/gpu/drm/verisilicon/dw_hdmi_tx_phy_gen2.h b/drivers/gpu/drm/verisilicon/dw_hdmi_tx_phy_gen2.h
new file mode 100644 (file)
index 0000000..1d346ef
--- /dev/null
@@ -0,0 +1,709 @@
+#ifndef _DW_HDMI_PHY_GEN2_H_
+#define _DW_HDMI_PHY_GEN2_H_
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/kernel.h>
+#include <drm/bridge/dw_hdmi.h>
+
+/* PHY registers */
+#define DW_HDMI_TX_PHY_GEN2_PWRCTRL                    0x00
+#define DW_HDMI_TX_PHY_GEN2_SERDIVCTRL                 0x01
+#define DW_HDMI_TX_PHY_GEN2_SERCKCTRL                  0x02
+#define DW_HDMI_TX_PHY_GEN2_SERCKKILLCTRL              0x03
+#define DW_HDMI_TX_PHY_GEN2_TXRESCALCTRL               0x04
+#define DW_HDMI_TX_PHY_GEN2_OPMODE_PLLCFG              0x06
+#define DW_HDMI_TX_PHY_GEN2_TXCKMEASCTRL               0x07
+#define DW_HDMI_TX_PHY_GEN2_TXMEASCTRL                 0x08
+#define DW_HDMI_TX_PHY_GEN2_CKSYMTXCTRL                        0x09
+#define DW_HDMI_TX_PHY_GEN2_CMPSEQCTRL                 0x0A
+#define DW_HDMI_TX_PHY_GEN2_CMPPWRCTRL                 0x0B
+#define DW_HDMI_TX_PHY_GEN2_CMPMODECTRL                        0x0C
+#define DW_HDMI_TX_PHY_GEN2_MEASCTRL                   0x0D
+#define DW_HDMI_TX_PHY_GEN2_VLEVCTRL_PLLMEASCTRL       0x0E
+#define DW_HDMI_TX_PHY_GEN2_D2ACTRL                    0x0F
+#define DW_HDMI_TX_PHY_GEN2_PLLCURRGMPCTRL             0x10
+#define DW_HDMI_TX_PHY_GEN2_PLLDIVCTRL                 0x11
+#define DW_HDMI_TX_PHY_GEN2_SUPPLLBGCTRL               0x12
+#define DW_HDMI_TX_PHY_GEN2_PLLCONFCTRL                        0x13
+#define DW_HDMI_TX_PHY_GEN2_PLLCTRL                    0x14
+#define DW_HDMI_TX_PHY_GEN2_PLLDIGTESTCTRL             0x15
+#define DW_HDMI_TX_PHY_GEN2_PLLMEASCTRL                        0x16
+#define DW_HDMI_TX_PHY_GEN2_PLLCLKBISTPHASE            0x17
+#define DW_HDMI_TX_PHY_GEN2_COMPRCAL                   0x18
+#define DW_HDMI_TX_PHY_GEN2_TXTERM                     0x19
+#define DW_HDMI_TX_PHY_GEN2_PWRSEQ_PATGENSKIP          0x1A
+#define DW_HDMI_TX_PHY_GEN2_PATTERNGEN_BISTSEL         0x1B
+#define DW_HDMI_TX_PHY_GEN2_CTRL_CLKALIGN_PGMODE_BISTPG        0x1C
+#define DW_HDMI_TX_PHY_GEN2_BISTCHSTATUS_CLKALIGN_RCAL 0x1D
+#define DW_HDMI_TX_PHY_GEN2_DIGTXMODE                  0x1E
+#define DW_HDMI_TX_PHY_GEN2_CLKALIGN_RCAL_SOFTRESET    0x1F
+#define DW_HDMI_TX_PHY_GEN2_PWRSEQ_PATGENSKIP_RD       0x2E
+#define DW_HDMI_TX_PHY_GEN2_PLLCLKBISTPHASE_RD         0x2F
+#define DW_HDMI_TX_PHY_GEN2_PLLSTATUS                  0x30
+#define DW_HDMI_TX_PHY_GEN2_SUPPLLBGSTATUS             0x31
+#define DW_HDMI_TX_PHY_GEN2_D2ASTATUS                  0x32
+#define DW_HDMI_TX_PHY_GEN2_CMPMODESTATUS              0x33
+#define DW_HDMI_TX_PHY_GEN2_CMPPWRSTATUS               0x34
+#define DW_HDMI_TX_PHY_GEN2_CMPSEQSTATUS               0x35
+#define DW_HDMI_TX_PHY_GEN2_CKSYMTXSTATUS              0x36
+#define DW_HDMI_TX_PHY_GEN2_TXRESCALSTATUS             0x37
+#define DW_HDMI_TX_PHY_GEN2_SERCKKILLSTATUS            0x38
+#define DW_HDMI_TX_PHY_GEN2_SERCKSTATUS                        0x39
+#define DW_HDMI_TX_PHY_GEN2_SERDIVSTATUS               0x3A
+#define DW_HDMI_TX_PHY_GEN2_PWRSTATUS                  0x3B
+
+/* PHY register bitfields */
+#define PWRCTRL_OVERRIDE_0                             BIT(15)
+#define PWRCTRL_BIAS_ON                                        BIT(5)
+#define PWRCTRL_TX_PWRON                               BIT(4)
+#define PWRCTRL_TX_PWRON0                              BIT(3)
+#define PWRCTRL_TX_PWRON1                              BIT(2)
+#define PWRCTRL_TX_PWRON2                              BIT(1)
+#define PWRCTRL_CK_PWRON                               BIT(0)
+
+#define OPMODE_PLLCFG_PREP_DIV                         GENMASK(13, 12)
+#define OPMODE_PLLCFG_MPLL_CKO_DIV                     GENMASK(10,  9)
+#define OPMODE_PLLCFG_OPMODE                           GENMASK( 7,  6)
+#define OPMODE_PLLCFG_REF_CNTRL                                GENMASK( 4,  3)
+#define OPMODE_PLLCFG_MPLL_N_CNTRL                     GENMASK( 1,  0)
+
+#define CKSYMTXCTRL_OVERRIDE_5                         BIT(15)
+#define CKSYMTXCTRL_SLOPE_BOOST                                GENMASK(13, 10)
+#define CKSYMTXCTRL_TX_TRAON                           BIT(9)
+#define CKSYMTXCTRL_TX_TRBON                           BIT(8)
+#define CKSYMTXCTRL_TX_SYMON                           GENMASK( 7,  4)
+#define CKSYMTXCTRL_CK_SYMON                           GENMASK( 3,  0)
+
+#define VLEVCTRL_PLLMEASCTRL_MPLL_MEAS_17_14           GENMASK(13, 10)
+#define VLEVCTRL_PLLMEASCTRL_SUP_TX_LVL                        GENMASK( 9,  5)
+
+#define PLLCURRGMPCTRL_MPLL_GMP_CNTRL                  GENMASK(13, 12)
+#define PLLCURRGMPCTRL_MPLL_PROP_CNTRL                 GENMASK(11,  6)
+#define PLLCURRGMPCTRL_MPLL_INT_CNTRL                  GENMASK( 5,  0)
+
+#define PLLDIVCTRL_VCO_CNTRL                           GENMASK(12,  9)
+#define PLLDIVCTRL_MPLL_MULTIPLIER                     GENMASK( 7,  0)
+
+#define TXTERM_D_TX_TERM                               GENMASK( 2,  0)
+
+#define TXTERM_50_OHM                                  0x0
+#define TXTERM_57_14_OHM                               0x1
+#define TXTERM_66_67_OHM                               0x2
+#define TXTERM_80_OHM                                  0x3
+#define TXTERM_100_OHM                                 0x4
+#define TXTERM_133_33_OHM                              0x5
+#define TXTERM_200_OHM                                 0x6
+#define TXTERM_OPEN_CIRCUIT                            0x7
+
+#define OPMODE_HDMI_14         0
+#define OPMODE_HDMI_20         1
+
+struct dw_hdmi_mpll_gen_config{
+       u64 pixelclock;         /* in kHz */
+       u8  colordepth;
+       u16 opmode:2;
+       struct {
+               u16 prep_div:2;
+               u16 mpll_cko_div:2;
+               u16 ref_cntrl:2;
+               u16 mpll_n_cntrl:2;
+               u16 vco_cntrl:4;
+               u16 mpll_multiplier:8;
+       } divider;
+       struct {
+               u16 gmp_cntrl:2;
+               u16 prop_cntrl:6;
+               u16 int_cntrl:6;
+       } charge_pump;
+       struct {
+               u16 txterm:3;
+               u16 sup_txlvl:5;
+               u16 tx_traon:1;
+               u16 tx_trbon:1;
+               u16 tx_symon:4;
+               u16 ck_symon:4;
+       } voltage;
+};
+
+struct dw_hdmi_light_private {
+       u32 max_pixclock;
+       u16 max_width;
+       u16 max_height;
+};
+
+static const struct dw_hdmi_mpll_gen_config mpll_configs[] = {
+       {
+               .pixelclock = 25175,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 27000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 31500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 33750,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 35500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x3, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 36000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x1, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x5, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 40000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x1, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x5, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 44900,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x3, .vco_cntrl = 0x1, .mpll_multiplier = 0x28, },
+               .charge_pump = { .gmp_cntrl = 0x0, .prop_cntrl = 0xa, .int_cntrl = 0x5, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 49500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 50000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 50350,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 54000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 56250,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 59400,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 65000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 68250,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 71000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x3, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x6, .int_cntrl = 0x3, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 72000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 73250,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 74250,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 75000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 78750,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 79500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 82500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 83500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 85500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 88750,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 90000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x2, .vco_cntrl = 0x1, .mpll_multiplier = 0x14, },
+               .charge_pump = { .gmp_cntrl = 0x1, .prop_cntrl = 0x5, .int_cntrl = 0x2, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 94500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 99000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 100700,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 101000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 102250,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 106500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 108000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 115500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 117500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 118800,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 119000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 121750,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x3, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x3, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 148500,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 154000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 162000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 165000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x1, .vco_cntrl = 0x1, .mpll_multiplier = 0xa, },
+               .charge_pump = { .gmp_cntrl = 0x2, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_OPEN_CIRCUIT, .sup_txlvl = 0xd, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0x8, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 185625,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x0, .vco_cntrl = 0x3, .mpll_multiplier = 0x5, },
+               .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_100_OHM, .sup_txlvl = 0x9, .tx_traon = 0x1,
+                            .tx_trbon = 0x1, .tx_symon = 0xc, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 198000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x0, .vco_cntrl = 0x3, .mpll_multiplier = 0x5, },
+               .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_100_OHM, .sup_txlvl = 0x9, .tx_traon = 0x1,
+                            .tx_trbon = 0x1, .tx_symon = 0xc, .ck_symon = 0x8, },
+       }, {
+               .pixelclock = 297000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_14,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x0, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x0, .vco_cntrl = 0x1, .mpll_multiplier = 0x5, },
+               .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x1, },
+               .voltage = { .txterm = TXTERM_133_33_OHM, .sup_txlvl = 0x10, .tx_traon = 0x0,
+                            .tx_trbon = 0x1, .tx_symon = 0xd, .ck_symon = 0xc, },
+       }, {
+               .pixelclock = 371250,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_20,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x3, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x0, .vco_cntrl = 0x1, .mpll_multiplier = 0x5, },
+               .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x1, .int_cntrl = 0x1, },
+               .voltage = { .txterm = TXTERM_50_OHM, .sup_txlvl = 0xa, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0xf, .ck_symon = 0x6, },
+       }, {
+               .pixelclock = 495000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_20,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x3, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x0, .vco_cntrl = 0x0, .mpll_multiplier = 0x5, },
+               .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_50_OHM, .sup_txlvl = 0xa, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0xf, .ck_symon = 0x6, },
+       }, {
+               .pixelclock = 594000,
+               .colordepth = 8,
+               .opmode = OPMODE_HDMI_20,
+               .divider = { .prep_div = 0x0, .mpll_cko_div = 0x3, .ref_cntrl = 0x0,
+                            .mpll_n_cntrl = 0x0, .vco_cntrl = 0x0, .mpll_multiplier = 0x5, },
+               .charge_pump = { .gmp_cntrl = 0x3, .prop_cntrl = 0x2, .int_cntrl = 0x0, },
+               .voltage = { .txterm = TXTERM_100_OHM, .sup_txlvl = 0xf, .tx_traon = 0x0,
+                            .tx_trbon = 0x0, .tx_symon = 0xf, .ck_symon = 0xa, },
+       },
+};
+
+static enum drm_mode_status
+dw_hdmi_tx_phy_gen2_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
+                              const struct drm_display_info *info,
+                              const struct drm_display_mode *mode)
+{
+       int i;
+
+       u32 max_clock  = 594000;
+       u16 max_height = 0;
+       u16 max_width  = 0;
+
+       struct dw_hdmi_light_private *dw_hdmi_private = (struct dw_hdmi_light_private*)data;
+       if(dw_hdmi_private != NULL){
+               max_clock  = dw_hdmi_private->max_pixclock;
+               max_height = dw_hdmi_private->max_height;
+               max_width  = dw_hdmi_private->max_width;
+       }
+
+       if (mode->clock < 13500)
+       {
+               return MODE_CLOCK_LOW;
+       }
+       else if (mode->clock > max_clock)
+       {
+               return MODE_CLOCK_HIGH;
+       }
+
+       if((max_width > 0) && (mode->hdisplay > max_width)){
+               return MODE_H_ILLEGAL;
+       }
+
+       if((max_height > 0) && (mode->vdisplay > max_height)){
+               return MODE_V_ILLEGAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(mpll_configs); i++) {
+       if (abs(mode->clock - mpll_configs[i].pixelclock) <= 100)
+                       return MODE_OK;
+               else if (mode->clock < mpll_configs[i].pixelclock)
+                       break;
+       }
+
+       return MODE_NOMODE;
+}
+
+static int dw_hdmi_tx_phy_gen2_configure(struct dw_hdmi *hdmi, void *data,
+                                        unsigned long mpixelclock)
+{
+       int i;
+       u16 opmode_pllcfg, pllcurrgmpctrl, plldivctrl;
+       u16 txterm, vlevctrl_pllmeasctrl, cksymtxctrl;
+       u64 pixclk = mpixelclock / 1000;        
+       const struct dw_hdmi_mpll_gen_config *config;
+
+       for (i = 0; i < ARRAY_SIZE(mpll_configs); i++) {
+               config = &mpll_configs[i];
+               /* TODO: add colordepth check later */
+               if (abs(config->pixelclock - pixclk) <= 100)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(mpll_configs))
+               return -1;
+
+       opmode_pllcfg = FIELD_PREP(OPMODE_PLLCFG_PREP_DIV, config->divider.prep_div)            |
+                       FIELD_PREP(OPMODE_PLLCFG_MPLL_CKO_DIV, config->divider.mpll_cko_div)    |
+                       FIELD_PREP(OPMODE_PLLCFG_OPMODE, config->opmode)                        |
+                       FIELD_PREP(OPMODE_PLLCFG_REF_CNTRL, config->divider.ref_cntrl)          |
+                       FIELD_PREP(OPMODE_PLLCFG_MPLL_N_CNTRL, config->divider.mpll_n_cntrl);
+       dw_hdmi_phy_i2c_write(
+               hdmi,
+               opmode_pllcfg,
+               DW_HDMI_TX_PHY_GEN2_OPMODE_PLLCFG
+       );
+
+       pllcurrgmpctrl = FIELD_PREP(PLLCURRGMPCTRL_MPLL_GMP_CNTRL, config->charge_pump.gmp_cntrl)       |
+                        FIELD_PREP(PLLCURRGMPCTRL_MPLL_PROP_CNTRL, config->charge_pump.prop_cntrl)     |
+                        FIELD_PREP(PLLCURRGMPCTRL_MPLL_INT_CNTRL, config->charge_pump.int_cntrl);
+       dw_hdmi_phy_i2c_write(
+               hdmi,
+               pllcurrgmpctrl,
+               DW_HDMI_TX_PHY_GEN2_PLLCURRGMPCTRL
+       );
+
+       plldivctrl = FIELD_PREP(PLLDIVCTRL_VCO_CNTRL, config->divider.vco_cntrl)        |
+                    FIELD_PREP(PLLDIVCTRL_MPLL_MULTIPLIER, config->divider.mpll_multiplier);
+       dw_hdmi_phy_i2c_write(
+               hdmi,
+               plldivctrl,
+               DW_HDMI_TX_PHY_GEN2_PLLDIVCTRL
+       );
+
+       txterm = FIELD_PREP(TXTERM_D_TX_TERM, config->voltage.txterm);
+       dw_hdmi_phy_i2c_write(
+               hdmi,
+               txterm,
+               DW_HDMI_TX_PHY_GEN2_TXTERM
+       );
+
+       vlevctrl_pllmeasctrl = FIELD_PREP(VLEVCTRL_PLLMEASCTRL_SUP_TX_LVL, config->voltage.sup_txlvl);
+       dw_hdmi_phy_i2c_write(
+               hdmi,
+               vlevctrl_pllmeasctrl,
+               DW_HDMI_TX_PHY_GEN2_VLEVCTRL_PLLMEASCTRL
+       );
+
+       cksymtxctrl = FIELD_PREP(CKSYMTXCTRL_OVERRIDE_5, 0x1)                           |
+                     FIELD_PREP(CKSYMTXCTRL_TX_TRAON, config->voltage.tx_traon)        |
+                     FIELD_PREP(CKSYMTXCTRL_TX_TRBON, config->voltage.tx_trbon)        |
+                     FIELD_PREP(CKSYMTXCTRL_TX_SYMON, config->voltage.tx_symon)        |
+                     FIELD_PREP(CKSYMTXCTRL_CK_SYMON, config->voltage.ck_symon);
+       dw_hdmi_phy_i2c_write(
+               hdmi,
+               cksymtxctrl,
+               DW_HDMI_TX_PHY_GEN2_CKSYMTXCTRL
+       );
+
+       return 0;
+}
+
+#endif
diff --git a/drivers/gpu/drm/verisilicon/dw_mipi_dsi.c b/drivers/gpu/drm/verisilicon/dw_mipi_dsi.c
new file mode 100644 (file)
index 0000000..59ed192
--- /dev/null
@@ -0,0 +1,1212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ *
+ * refer to bridge/synopsys/dw-mipi-dsi.c
+ */
+
+#include <linux/version.h>
+#include <linux/bitfield.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/iopoll.h>
+#include <linux/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_mipi_dsi.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_print.h>
+#else
+#include <drm/drmP.h>
+#endif
+
+#include "dw_mipi_dsi.h"
+
+#define DSI_VERSION         0x00
+
+#define DSI_PWR_UP          0x04
+#define RESET               0
+#define POWERUP             BIT(0)
+
+#define DSI_CLKMGR_CFG          0x08
+#define TO_CLK_DIVISION(div)        (((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVISION(div)    ((div) & 0xff)
+
+#define DSI_DPI_VCID            0x0c
+#define DPI_VCID(vcid)          ((vcid) & 0x3)
+
+#define DSI_DPI_COLOR_CODING        0x10
+#define LOOSELY18_EN            BIT(8)
+#define DPI_COLOR_CODING_16BIT_1    0x0
+#define DPI_COLOR_CODING_16BIT_2    0x1
+#define DPI_COLOR_CODING_16BIT_3    0x2
+#define DPI_COLOR_CODING_18BIT_1    0x3
+#define DPI_COLOR_CODING_18BIT_2    0x4
+#define DPI_COLOR_CODING_24BIT      0x5
+
+#define DSI_DPI_CFG_POL         0x14
+#define COLORM_ACTIVE_LOW       BIT(4)
+#define SHUTD_ACTIVE_LOW        BIT(3)
+#define HSYNC_ACTIVE_LOW        BIT(2)
+#define VSYNC_ACTIVE_LOW        BIT(1)
+#define DATAEN_ACTIVE_LOW       BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM      0x18
+#define OUTVACT_LPCMD_TIME(p)       (((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p)        ((p) & 0xff)
+
+#define DSI_DBI_VCID            0x1c
+#define DSI_DBI_CFG         0x20
+#define DSI_DBI_PARTITIONING_EN     0x24
+#define DSI_DBI_CMDSIZE         0x28
+
+#define DSI_PCKHDL_CFG          0x2c
+#define EOTP_TX_LP_EN           BIT(5)
+#define CRC_RX_EN           BIT(4)
+#define ECC_RX_EN           BIT(3)
+#define BTA_EN              BIT(2)
+#define EOTP_RX_EN          BIT(1)
+#define EOTP_TX_EN          BIT(0)
+
+#define DSI_GEN_VCID            0x30
+
+#define DSI_MODE_CFG            0x34
+#define ENABLE_VIDEO_MODE       0
+#define ENABLE_CMD_MODE         BIT(0)
+
+#define DSI_VID_MODE_CFG        0x38
+#define VPG_ORIENTATION         BIT(24)
+#define VPG_MODE                BIT(20)
+#define VPG_ENABLE              BIT(16)
+#define LP_CMD_EN               BIT(15)
+#define FRAME_BTA_ACK_EN        BIT(14)
+#define LP_HFP_EN               BIT(13)
+#define LP_HBP_EN               BIT(12)
+#define LP_VACT_EN              BIT(11)
+#define LP_VFP_EN               BIT(10)
+#define LP_VBP_EN               BIT(9)
+#define LP_VSA_EN               BIT(8)
+#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES 0x0
+#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS 0x1
+#define VID_MODE_TYPE_BURST         0x2
+#define VID_MODE_TYPE_MASK          0x3
+
+#define DSI_VID_PKT_SIZE        0x3c
+#define VID_PKT_SIZE(p)         ((p) & 0x3fff)
+
+#define DSI_VID_NUM_CHUNKS      0x40
+#define VID_NUM_CHUNKS(c)       ((c) & 0x1fff)
+
+#define DSI_VID_NULL_SIZE       0x44
+#define VID_NULL_SIZE(b)        ((b) & 0x1fff)
+
+#define DSI_VID_HSA_TIME        0x48
+#define DSI_VID_HBP_TIME        0x4c
+#define DSI_VID_HLINE_TIME      0x50
+#define DSI_VID_VSA_LINES       0x54
+#define DSI_VID_VBP_LINES       0x58
+#define DSI_VID_VFP_LINES       0x5c
+#define DSI_VID_VACTIVE_LINES       0x60
+#define DSI_EDPI_CMD_SIZE       0x64
+
+#define DSI_CMD_MODE_CFG        0x68
+#define MAX_RD_PKT_SIZE_LP      BIT(24)
+#define DCS_LW_TX_LP            BIT(19)
+#define DCS_SR_0P_TX_LP         BIT(18)
+#define DCS_SW_1P_TX_LP         BIT(17)
+#define DCS_SW_0P_TX_LP         BIT(16)
+#define GEN_LW_TX_LP            BIT(14)
+#define GEN_SR_2P_TX_LP         BIT(13)
+#define GEN_SR_1P_TX_LP         BIT(12)
+#define GEN_SR_0P_TX_LP         BIT(11)
+#define GEN_SW_2P_TX_LP         BIT(10)
+#define GEN_SW_1P_TX_LP         BIT(9)
+#define GEN_SW_0P_TX_LP         BIT(8)
+#define ACK_RQST_EN         BIT(1)
+#define TEAR_FX_EN          BIT(0)
+
+#define CMD_MODE_ALL_LP         (MAX_RD_PKT_SIZE_LP | \
+                     DCS_LW_TX_LP | \
+                     DCS_SR_0P_TX_LP | \
+                     DCS_SW_1P_TX_LP | \
+                     DCS_SW_0P_TX_LP | \
+                     GEN_LW_TX_LP | \
+                     GEN_SR_2P_TX_LP | \
+                     GEN_SR_1P_TX_LP | \
+                     GEN_SR_0P_TX_LP | \
+                     GEN_SW_2P_TX_LP | \
+                     GEN_SW_1P_TX_LP | \
+                     GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR         0x6c
+#define DSI_GEN_PLD_DATA        0x70
+
+#define DSI_CMD_PKT_STATUS      0x74
+#define GEN_RD_CMD_BUSY         BIT(6)
+#define GEN_PLD_R_FULL          BIT(5)
+#define GEN_PLD_R_EMPTY         BIT(4)
+#define GEN_PLD_W_FULL          BIT(3)
+#define GEN_PLD_W_EMPTY         BIT(2)
+#define GEN_CMD_FULL            BIT(1)
+#define GEN_CMD_EMPTY           BIT(0)
+
+#define DSI_TO_CNT_CFG          0x78
+#define HSTX_TO_CNT(p)          (((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p)          ((p) & 0xffff)
+
+#define DSI_HS_RD_TO_CNT        0x7c
+#define DSI_LP_RD_TO_CNT        0x80
+#define DSI_HS_WR_TO_CNT        0x84
+#define DSI_LP_WR_TO_CNT        0x88
+#define DSI_BTA_TO_CNT          0x8c
+
+#define DSI_LPCLK_CTRL          0x94
+#define AUTO_CLKLANE_CTRL       BIT(1)
+#define PHY_TXREQUESTCLKHS      BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG       0x98
+#define PHY_CLKHS2LP_TIME(lbcc)     (((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc)     ((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG         0x9c
+#define PHY_HS2LP_TIME(lbcc)        (((lbcc) & 0x3ff) << 16)
+#define PHY_LP2HS_TIME(lbcc)        ((lbcc) & 0x3ff)
+
+#define DSI_PHY_RSTZ            0xa0
+#define PHY_DISFORCEPLL         0
+#define PHY_ENFORCEPLL          BIT(3)
+#define PHY_DISABLECLK          0
+#define PHY_ENABLECLK           BIT(2)
+#define PHY_RSTZ            0
+#define PHY_UNRSTZ          BIT(1)
+#define PHY_SHUTDOWNZ           0
+#define PHY_UNSHUTDOWNZ         BIT(0)
+
+#define DSI_PHY_IF_CFG          0xa4
+#define PHY_STOP_WAIT_TIME(cycle)   (((cycle) & 0xff) << 8)
+#define N_LANES(n)          (((n) - 1) & 0x3)
+
+#define DSI_PHY_ULPS_CTRL       0xa8
+#define DSI_PHY_TX_TRIGGERS     0xac
+
+#define DSI_PHY_STATUS          0xb0
+#define PHY_STOP_STATE_CLK_LANE     BIT(2)
+#define PHY_LOCK            BIT(0)
+
+#define DSI_INT_ST0         0xbc
+#define DSI_INT_ST1         0xc0
+#define DSI_INT_MSK0            0xc4
+#define DSI_INT_MSK1            0xc8
+
+#define DSI_PHY_TMR_RD_CFG      0xf4
+#define MAX_RD_TIME(lbcc)       ((lbcc) & 0x7fff)
+
+#define PHY_STATUS_TIMEOUT_US       10000
+#define CMD_PKT_STATUS_TIMEOUT_US   20000
+
+#define MAX_LANE_COUNT          4
+
+struct dw_mipi_dsi;
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi);
+
+struct dw_mipi_dsi_funcs {
+    struct dw_mipi_dsi *(*get_dsi)(struct device *dev);
+    int (*bind)(struct dw_mipi_dsi *dsi);
+    void (*unbind)(struct dw_mipi_dsi *dsi);
+};
+
+struct dw_mipi_dsi {
+    struct device *dev;
+    struct regmap *regmap;
+    /* apb slave bus clock */
+    struct clk *pclk;
+    struct clk *pixclk;
+    struct phy *dphy;
+    union phy_configure_opts phy_opts;
+
+    struct mipi_dsi_host host;
+    unsigned int channel;
+    unsigned int lanes;
+    enum mipi_dsi_pixel_format format;
+    unsigned long mode_flags;
+
+    unsigned int lane_link_rate; /* kHz */
+
+    const struct dw_mipi_dsi_funcs *funcs;
+};
+
+struct dw_mipi_dsi_primary {
+    struct dw_mipi_dsi dsi;
+    struct dw_mipi_dsi *secondary_dsi;
+
+    struct drm_bridge bridge;
+    struct drm_bridge *panel_bridge;
+    u32 bus_format;
+};
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+    return container_of(host, struct dw_mipi_dsi, host);
+}
+
+static inline struct
+dw_mipi_dsi_primary *dsi_to_primary(struct dw_mipi_dsi *dsi)
+{
+    return container_of(dsi, struct dw_mipi_dsi_primary, dsi);
+}
+
+static inline struct
+dw_mipi_dsi_primary *bridge_to_primary(struct drm_bridge *bridge)
+{
+    return container_of(bridge, struct dw_mipi_dsi_primary, bridge);
+}
+
+static inline int dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+    int ret;
+
+    WARN_ON(ret = regmap_write(dsi->regmap, reg, val));
+
+    return ret;
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg, u32 *val)
+{
+    int ret;
+
+    WARN_ON(ret = regmap_read(dsi->regmap, reg, val));
+
+    return ret;
+}
+
+static int dsi_host_attach(struct mipi_dsi_host *host,
+               struct mipi_dsi_device *device)
+{
+    struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+    if (device->lanes > MAX_LANE_COUNT) {
+        DRM_ERROR("the number of data lanes(%u) is too many\n",
+              device->lanes);
+        return -EINVAL;
+    }
+
+    if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST))
+        DRM_WARN("This DSI driver only support burst mode\n");
+
+    dsi->lanes = device->lanes;
+    dsi->channel = device->channel;
+    dsi->format = device->format;
+    dsi->mode_flags = device->mode_flags;
+
+    return 0;
+}
+
+static int dsi_host_detach(struct mipi_dsi_host *host,
+               struct mipi_dsi_device *device)
+{
+    return 0;
+}
+
+static void dw_mipi_dsi_lpclk_config(struct dw_mipi_dsi *dsi, bool hsclk)
+{
+       u32 lpclk_ctrl = 0;
+
+       lpclk_ctrl |= hsclk ? PHY_TXREQUESTCLKHS : 0;
+       lpclk_ctrl |= dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS ?
+                     AUTO_CLKLANE_CTRL : 0;
+
+       dsi_write(dsi, DSI_LPCLK_CTRL, lpclk_ctrl);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+                                unsigned long mode_flags)
+{
+       u32 mode_cfg;
+
+       mode_cfg = mode_flags & MIPI_DSI_MODE_VIDEO ?
+                  ENABLE_VIDEO_MODE : ENABLE_CMD_MODE;
+       dsi_write(dsi, DSI_MODE_CFG, mode_cfg);
+}
+
+static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
+                   const struct mipi_dsi_msg *msg)
+{
+    bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
+    u32 val = 0;
+
+    if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
+        val |= ACK_RQST_EN;
+    if (lpm)
+        val |= CMD_MODE_ALL_LP | MAX_RD_PKT_SIZE_LP;
+
+    dsi_write(dsi, DSI_CMD_MODE_CFG, val);
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
+{
+    int ret;
+    u32 val, mask;
+
+    ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+                                  val, !(val & GEN_CMD_FULL), 1000,
+                                  CMD_PKT_STATUS_TIMEOUT_US);
+
+    if (ret) {
+        DRM_ERROR("failed to get available command FIFO\n");
+        return ret;
+    }
+
+    dsi_write(dsi, DSI_GEN_HDR, hdr_val);
+
+    mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
+    ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+                                  val, (val & mask) == mask,
+                                  1000, CMD_PKT_STATUS_TIMEOUT_US);
+    if (ret) {
+        DRM_ERROR("failed to write command FIFO\n");
+        return ret;
+    }
+
+    return 0;
+}
+
+static int dw_mipi_dsi_write(struct dw_mipi_dsi *dsi,
+                 const struct mipi_dsi_packet *packet)
+{
+    const u8 *tx_buf = packet->payload;
+    int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
+    __le32 word;
+    u32 val;
+
+    while (len) {
+        if (len < pld_data_bytes) {
+            word = 0;
+            memcpy(&word, tx_buf, len);
+            dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
+            len = 0;
+        } else {
+            memcpy(&word, tx_buf, pld_data_bytes);
+            dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
+            tx_buf += pld_data_bytes;
+            len -= pld_data_bytes;
+        }
+
+        ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+                                      val, !(val & GEN_PLD_W_FULL), 1000,
+                                      CMD_PKT_STATUS_TIMEOUT_US);
+        if (ret) {
+            DRM_ERROR("failed to get write payload FIFO\n");
+            return ret;
+        }
+    }
+
+    word = 0;
+    memcpy(&word, packet->header, sizeof(packet->header));
+    return dw_mipi_dsi_gen_pkt_hdr_write(dsi, le32_to_cpu(word));
+}
+
+static int dw_mipi_dsi_read(struct dw_mipi_dsi *dsi,
+                const struct mipi_dsi_msg *msg)
+{
+    int i, j, ret, len = msg->rx_len;
+    u8 *buf = msg->rx_buf;
+    u32 val;
+
+    /* Wait end of the read operation */
+    ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+                                  val, !(val & GEN_RD_CMD_BUSY),
+                                  1000, CMD_PKT_STATUS_TIMEOUT_US);
+    if (ret) {
+        DRM_ERROR("Timeout during read operation\n");
+        return ret;
+    }
+
+    for (i = 0; i < len; i += 4) {
+        /* Read fifo must not be empty before all bytes are read */
+        ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
+                                      val, !(val & GEN_PLD_R_EMPTY),
+                                      1000, CMD_PKT_STATUS_TIMEOUT_US);
+        if (ret) {
+            DRM_ERROR("Read payload FIFO is empty\n");
+            return ret;
+        }
+
+       ret = dsi_read(dsi, DSI_GEN_PLD_DATA, &val);
+       if (unlikely(ret))
+               return ret;
+
+        for (j = 0; j < 4 && j + i < len; j++)
+            buf[i + j] = val >> (8 * j);
+    }
+
+    return ret;
+}
+
+static ssize_t dsi_host_transfer(struct mipi_dsi_host *host,
+                 const struct mipi_dsi_msg *msg)
+{
+    struct dw_mipi_dsi *dsi = host_to_dsi(host);
+    struct mipi_dsi_packet packet;
+    int ret, nb_bytes;
+
+    ret = mipi_dsi_create_packet(&packet, msg);
+    if (ret) {
+        DRM_ERROR("failed to create packet: %d\n", ret);
+        return ret;
+    }
+
+    dw_mipi_message_config(dsi, msg);
+
+    ret = dw_mipi_dsi_write(dsi, &packet);
+    if (ret)
+        return ret;
+
+    if (msg->rx_buf && msg->rx_len) {
+        ret = dw_mipi_dsi_read(dsi, msg);
+        if (ret)
+            return ret;
+        nb_bytes = msg->rx_len;
+    } else {
+        nb_bytes = packet.size;
+    }
+
+    return nb_bytes;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+    .attach = dsi_host_attach,
+    .detach = dsi_host_detach,
+    .transfer = dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_power_config(struct dw_mipi_dsi *dsi, bool on)
+{
+    dsi_write(dsi, DSI_PWR_UP, on ? POWERUP : RESET);
+}
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+       u32 vid_mode_cfg = 0;
+
+       /* configure low-power transitions */
+       vid_mode_cfg |= LP_HFP_EN  |
+                       LP_HBP_EN   |
+                       LP_VACT_EN  |
+                       LP_VFP_EN   |
+                       LP_VBP_EN   |
+                       LP_VSA_EN;
+
+       /* configure commands transfer in high speed mode */
+       vid_mode_cfg &= ~LP_CMD_EN;
+
+       /* configure burs-mode or non-burst mode */
+       if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+               vid_mode_cfg |= VID_MODE_TYPE_BURST;
+       else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+               vid_mode_cfg |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
+       else
+               vid_mode_cfg |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
+
+ #if 0
+       /* enable colorbar mode */
+       vid_mode_cfg |= VPG_ENABLE;
+ #endif
+
+       dsi_write(dsi, DSI_VID_MODE_CFG, vid_mode_cfg);
+}
+
+static void dw_mipi_dsi_set_esc_div(struct dw_mipi_dsi *dsi)
+{
+    u8 esc_clk;
+    u32 esc_clk_division;
+
+    esc_clk = 20; /* 20MHz */
+
+    /*
+     * The maximum permitted escape clock is 20MHz and it is derived from
+     * lanebyteclk, which is running at "lane_link_rate / 8".  Thus we want:
+     *
+     *     (lane_link_rate >> 3) / esc_clk_division < 20
+     * which is:
+     *     (lane_link_rate >> 3) / 20 > esc_clk_division
+     */
+    esc_clk_division = ((dsi->lane_link_rate / 1000) >> 3) / esc_clk + 1;
+
+    /*
+     * TODO dw drv improvements
+     * timeout clock division should be computed with the
+     * high speed transmission counter timeout and byte lane...
+     */
+    dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
+            TX_ESC_CLK_DIVISION(esc_clk_division));
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+                   const struct drm_display_mode *mode)
+{
+    u32 dpi_cfg_pol = 0, dpi_color_coding = 0;
+
+    dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
+
+    switch (dsi->format) {
+    case MIPI_DSI_FMT_RGB888:
+       dpi_color_coding = DPI_COLOR_CODING_24BIT;
+        break;
+    case MIPI_DSI_FMT_RGB666:
+        dpi_color_coding = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
+        break;
+    case MIPI_DSI_FMT_RGB666_PACKED:
+        dpi_color_coding = DPI_COLOR_CODING_18BIT_1;
+        break;
+    case MIPI_DSI_FMT_RGB565:
+        dpi_color_coding = DPI_COLOR_CODING_16BIT_1;
+        break;
+    }
+    dsi_write(dsi, DSI_DPI_COLOR_CODING, dpi_color_coding);
+
+    /* TODO: negotiate bus flags for DE polarity */
+    if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+       dpi_cfg_pol |= VSYNC_ACTIVE_LOW;
+    if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+       dpi_cfg_pol |= HSYNC_ACTIVE_LOW;
+    dsi_write(dsi, DSI_DPI_CFG_POL, dpi_cfg_pol);
+
+    /*
+     * TODO dw drv improvements
+     * largest packet sizes during hfp or during vsa/vpb/vfp
+     * should be computed according to byte lane, lane number and only
+     * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS)
+     */
+    dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+          | INVACT_LPCMD_TIME(4));
+}
+
+static void __maybe_unused dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+    u32 pkthdl_cfg;
+
+    pkthdl_cfg = EOTP_TX_LP_EN | EOTP_TX_EN | BTA_EN;
+    dsi_write(dsi, DSI_PCKHDL_CFG, pkthdl_cfg);
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+    u32 cmd_mode_cfg;
+
+    /*
+     * TODO dw drv improvements
+     * compute high speed transmission counter timeout according
+     * to the timeout clock division (TO_CLK_DIVISION) and byte lane...
+     */
+    dsi_write(dsi, DSI_TO_CNT_CFG,
+            HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+    /*
+     * TODO dw drv improvements
+     * the Bus-Turn-Around Timeout Counter should be computed
+     * according to byte lane...
+     */
+    dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+
+    /* TODO use low power mode to transfer
+     * commands in command mode in default
+     */
+    cmd_mode_cfg = MAX_RD_PKT_SIZE_LP  |
+                  DCS_LW_TX_LP         |
+                  DCS_SR_0P_TX_LP      |
+                  DCS_SW_1P_TX_LP      |
+                  DCS_SW_0P_TX_LP      |
+                  GEN_LW_TX_LP         |
+                  GEN_SR_2P_TX_LP      |
+                  GEN_SR_1P_TX_LP      |
+                  GEN_SR_0P_TX_LP      |
+                  GEN_SW_2P_TX_LP      |
+                  GEN_SW_1P_TX_LP      |
+                  GEN_SW_0P_TX_LP;
+    dsi_write(dsi, DSI_CMD_MODE_CFG, cmd_mode_cfg);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+                       const struct drm_display_mode *mode,
+                       u32 hcomponent)
+{
+    u32 frac, lbcc;
+
+    lbcc = hcomponent * dsi->lane_link_rate / 8;
+
+    frac = lbcc % mode->clock;
+    lbcc = lbcc / mode->clock;
+    if (frac)
+        lbcc++;
+
+    return lbcc;
+}
+
+static void dw_mipi_dsi_timing_config(struct dw_mipi_dsi *dsi,
+                      const struct drm_display_mode *mode)
+{
+    u32 htotal, hsa, hbp, lbcc;
+
+    htotal = mode->htotal;
+    hsa = mode->hsync_end - mode->hsync_start;
+    hbp = mode->htotal - mode->hsync_end;
+
+    lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, htotal);
+    dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+    lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hsa);
+    dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+    lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hbp);
+    dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+
+    dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+
+    /* vertical */
+    dsi_write(dsi, DSI_VID_VACTIVE_LINES, mode->vdisplay);
+    dsi_write(dsi, DSI_VID_VSA_LINES, mode->vsync_end - mode->vsync_start);
+    dsi_write(dsi, DSI_VID_VFP_LINES, mode->vsync_start - mode->vdisplay);
+    dsi_write(dsi, DSI_VID_VBP_LINES, mode->vtotal - mode->vsync_end);
+}
+
+static void __maybe_unused dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+    /*
+     * TODO dw drv improvements
+     * stop wait time should be the maximum between host dsi
+     * and panel stop wait times
+     */
+    dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+          N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
+{
+    u32 val;
+    int ret;
+
+    phy_power_on(dsi->dphy);
+
+    ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS, val,
+                 val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+    if (ret)
+        DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
+
+    ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS,
+                                  val, val & PHY_STOP_STATE_CLK_LANE, 1000,
+                                  PHY_STATUS_TIMEOUT_US);
+    if (ret)
+        DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void __maybe_unused dw_mipi_dsi_wait_for_two_frames(const struct drm_display_mode *mode)
+{
+    int refresh, two_frames;
+
+    refresh = drm_mode_vrefresh(mode);
+    two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+    msleep(two_frames);
+}
+
+static void dw_mipi_dsi_dphy_init(struct dw_mipi_dsi *dsi)
+{
+    regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_UNRSTZ, PHY_RSTZ);
+    regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_UNSHUTDOWNZ, PHY_SHUTDOWNZ);
+
+    phy_init(dsi->dphy);
+    phy_configure(dsi->dphy, &dsi->phy_opts);
+    dw_mipi_dsi_set_esc_div(dsi);
+}
+
+static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
+                 const struct drm_display_mode *mode)
+{
+    pm_runtime_get_sync(dsi->dev);
+
+    /* configure DSI hosts */
+    dw_mipi_dsi_dpi_config(dsi, mode);
+    dw_mipi_dsi_video_mode_config(dsi);
+    dw_mipi_dsi_timing_config(dsi, mode);
+    dw_mipi_dsi_packet_handler_config(dsi);
+    dw_mipi_dsi_command_mode_config(dsi);
+
+    dw_mipi_dsi_dphy_init(dsi);
+    dw_mipi_dsi_wait_for_two_frames(mode);
+
+    /* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
+    dw_mipi_dsi_set_mode(dsi, 0);
+
+    dw_mipi_dsi_power_config(dsi, true);
+
+    pm_runtime_put(dsi->dev);
+}
+
+static enum drm_mode_status bridge_mode_valid(struct drm_bridge *bridge,
+                                             const struct drm_display_info *info,
+                                             const struct drm_display_mode *mode)
+{
+       int bpp, ret;
+       struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+       struct dw_mipi_dsi *dsi = &primary->dsi;
+       struct phy_configure_opts_mipi_dphy *dphy_cfg = &dsi->phy_opts.mipi_dphy;
+
+       bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+       if (bpp < 0)
+               return MODE_BAD;
+
+       phy_mipi_dphy_get_default_config(mode->clock * 1000, bpp,
+                                        dsi->lanes, dphy_cfg);
+
+       ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &dsi->phy_opts);
+       if (!ret)
+               dsi->lane_link_rate = dphy_cfg->hs_clk_rate / 1000;
+
+       return ret ? MODE_BAD : MODE_OK;
+}
+
+static void bridge_mode_set(struct drm_bridge *bridge,
+                const struct drm_display_mode *mode,
+                const struct drm_display_mode *adjusted_mode)
+{
+    struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+    struct drm_display_mode *new_mode =
+                drm_mode_duplicate(bridge->dev, adjusted_mode);
+
+    if (primary->secondary_dsi) {
+        new_mode->hdisplay /= 2;
+        new_mode->hsync_start /= 2;
+        new_mode->hsync_end /= 2;
+        new_mode->htotal /= 2;
+        new_mode->clock /= 2;
+    }
+
+    dw_mipi_dsi_mode_set(&primary->dsi, new_mode);
+    if (primary->secondary_dsi)
+        dw_mipi_dsi_mode_set(primary->secondary_dsi, new_mode);
+
+    drm_mode_destroy(bridge->dev, new_mode);
+}
+
+static void bridge_enable(struct drm_bridge *bridge)
+{
+    struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+    struct dw_mipi_dsi *dsi = &primary->dsi;
+
+#if 0
+    if (primary->secondary_dsi)
+        dw_mipi_dsi_mode_config(primary->secondary_dsi,
+                     MIPI_DSI_MODE_VIDEO);
+#endif
+
+    pm_runtime_get_sync(dsi->dev);
+
+    dw_mipi_dsi_dphy_enable(dsi);
+    dw_mipi_dsi_power_config(dsi, true);
+    dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
+    dw_mipi_dsi_lpclk_config(dsi, true);
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+    dw_mipi_dsi_power_config(dsi, false);
+    phy_power_off(dsi->dphy);
+
+    pm_runtime_put(dsi->dev);
+}
+
+static void bridge_post_disable(struct drm_bridge *bridge)
+{
+    struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+
+    /*
+     * Switch to command mode before panel-bridge post_disable &
+     * panel unprepare.
+     * Note: panel-bridge disable & panel disable has been called
+     * before by the drm framework.
+     */
+    dw_mipi_dsi_set_mode(&primary->dsi, 0);
+
+    /*
+     * TODO Only way found to call panel-bridge post_disable &
+     * panel unprepare before the dsi "final" disable...
+     * This needs to be fixed in the drm_bridge framework and the API
+     * needs to be updated to manage our own call chains...
+     */
+    primary->panel_bridge->funcs->post_disable(primary->panel_bridge);
+
+    if (primary->secondary_dsi)
+        dw_mipi_dsi_disable(primary->secondary_dsi);
+
+    dw_mipi_dsi_disable(&primary->dsi);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+static int bridge_attach(struct drm_bridge *bridge,
+                         enum drm_bridge_attach_flags flags)
+#else
+static int bridge_attach(struct drm_bridge *bridge)
+#endif
+{
+    struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+
+    if (!bridge->encoder) {
+        DRM_ERROR("Parent encoder object not found\n");
+        return -ENODEV;
+    }
+
+    /* Attach the panel-bridge to the dsi bridge */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+    return drm_bridge_attach(bridge->encoder, primary->panel_bridge,
+                 bridge, 0);
+#else
+    return drm_bridge_attach(bridge->encoder, primary->panel_bridge,
+                 bridge);
+#endif
+}
+
+static void bridge_update_bus_format(struct drm_bridge *bridge)
+{
+    struct dw_mipi_dsi_primary *primary = bridge_to_primary(bridge);
+    struct drm_connector *connector;
+    struct drm_connector_list_iter conn_iter;
+    struct drm_encoder *encoder;
+    u32 bus_format;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+    int i;
+#endif
+
+    /* set bus format according to DSI format */
+    switch (primary->dsi.format) {
+    case MIPI_DSI_FMT_RGB888:
+        bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+        break;
+    case MIPI_DSI_FMT_RGB666:
+        bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
+        break;
+    case MIPI_DSI_FMT_RGB666_PACKED:
+        bus_format = MEDIA_BUS_FMT_RGB666_1X18;
+        break;
+    case MIPI_DSI_FMT_RGB565:
+        bus_format = MEDIA_BUS_FMT_RGB565_1X16;
+        break;
+    default:
+        bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+    }
+
+    if (bus_format == primary->bus_format)
+        return;
+
+    drm_connector_list_iter_begin(bridge->dev, &conn_iter);
+
+    drm_for_each_connector_iter(connector, &conn_iter) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+        drm_connector_for_each_possible_encoder(connector, encoder, i) {
+#else
+        drm_connector_for_each_possible_encoder(connector, encoder) {
+#endif
+            if (encoder == bridge->encoder) {
+                drm_display_info_set_bus_formats(
+                        &connector->display_info,
+                        &bus_format, 1);
+                primary->bus_format = bus_format;
+                drm_connector_list_iter_end(&conn_iter);
+                return;
+            }
+        }
+    }
+    drm_connector_list_iter_end(&conn_iter);
+}
+
+#if 1
+static bool bridge_mode_fixup(struct drm_bridge *bridge,
+                  const struct drm_display_mode *mode,
+                  struct drm_display_mode *adjusted_mode)
+{
+    bridge_update_bus_format(bridge);
+    return true;
+}
+#else
+static int bridge_atomic_check(struct drm_bridge *bridge,
+                              struct drm_bridge_state *bridge_state,
+                              struct drm_crtc_state *crtc_state,
+                              struct drm_connector_state *conn_state)
+{
+       return 0;
+}
+#endif
+
+static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
+    .mode_valid        = bridge_mode_valid,
+    .mode_set   = bridge_mode_set,
+    .enable     = bridge_enable,
+    .post_disable   = bridge_post_disable,
+    .attach     = bridge_attach,
+#if 1
+    .mode_fixup = bridge_mode_fixup,
+#else
+    .atomic_check = bridge_atomic_check,
+    .atomic_reset = drm_atomic_helper_bridge_reset,
+    .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+    .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+    .atomic_get_input_bus_fmts = bridge_atomic_get_input_bus_fmts,
+#endif
+};
+
+static int dsi_attach_primary(struct dw_mipi_dsi *secondary, struct device *dev)
+{
+    struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+    struct dw_mipi_dsi_primary *primary = dsi_to_primary(dsi);
+
+    if (!of_device_is_compatible(dev->of_node, "verisilicon,dw-mipi-dsi0"))
+        return -EINVAL;
+
+    primary->secondary_dsi = secondary;
+    return 0;
+}
+
+struct dw_mipi_dsi *get_primary_dsi(struct device *dev)
+{
+    struct dw_mipi_dsi_primary *primary;
+
+    primary = devm_kzalloc(dev, sizeof(*primary), GFP_KERNEL);
+    if (!primary)
+        return NULL;
+
+    primary->dsi.dev = dev;
+
+    return &primary->dsi;
+}
+
+static int primary_bind(struct dw_mipi_dsi *dsi)
+{
+    struct dw_mipi_dsi_primary *primary = dsi_to_primary(dsi);
+    struct device *dev = primary->dsi.dev;
+    struct drm_bridge *bridge;
+    struct drm_panel *panel;
+    int ret;
+
+    ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge);
+    if (ret)
+        return ret;
+
+    if (panel) {
+        bridge = drm_panel_bridge_add(panel);
+        if (IS_ERR(bridge))
+            return PTR_ERR(bridge);
+    }
+
+    primary->panel_bridge = bridge;
+
+    primary->bridge.funcs = &dw_mipi_dsi_bridge_funcs;
+    primary->bridge.of_node = dev->of_node;
+    drm_bridge_add(&primary->bridge);
+    return 0;
+}
+
+static void primary_unbind(struct dw_mipi_dsi *dsi)
+{
+    struct dw_mipi_dsi_primary *primary = dsi_to_primary(dsi);
+    struct device *dev = primary->dsi.dev;
+
+    drm_bridge_remove(&primary->bridge);
+    drm_of_panel_bridge_remove(dev->of_node, 1, 0);
+}
+
+static const struct dw_mipi_dsi_funcs primary = {
+    .get_dsi = &get_primary_dsi,
+    .bind = &primary_bind,
+    .unbind = &primary_unbind,
+};
+
+struct dw_mipi_dsi *get_secondary_dsi(struct device *dev)
+{
+    struct dw_mipi_dsi *dsi;
+
+    dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+    if (!dsi)
+        return NULL;
+
+    dsi->dev = dev;
+    return dsi;
+}
+
+static int secondary_bind(struct dw_mipi_dsi *dsi)
+{
+    struct device_node *np;
+    struct platform_device *pdev;
+
+    np = of_find_compatible_node(NULL, NULL, "verisilicon,dw-mipi-dsi0");
+    if (!np)
+        return -ENODEV;
+
+    pdev = of_find_device_by_node(np);
+    of_node_put(np);
+    if (!pdev)
+        return -ENODEV;
+
+    return dsi_attach_primary(dsi, &pdev->dev);
+}
+
+static void secondary_unbind(struct dw_mipi_dsi *dsi)
+{
+
+}
+
+static const struct dw_mipi_dsi_funcs secondary = {
+    .get_dsi = &get_secondary_dsi,
+    .bind = &secondary_bind,
+    .unbind = &secondary_unbind,
+};
+
+static int dsi_bind(struct device *dev, struct device *primary, void *data)
+{
+    struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+    return dsi->funcs->bind(dsi);
+}
+
+static void dsi_unbind(struct device *dev, struct device *primary, void *data)
+{
+    struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+    dsi->funcs->unbind(dsi);
+}
+
+static const struct component_ops dsi_component_ops = {
+    .bind = dsi_bind,
+    .unbind = dsi_unbind,
+};
+
+static const struct of_device_id dw_mipi_dsi_dt_match[] = {
+    { .compatible = "verisilicon,dw-mipi-dsi", .data = &primary},
+    { .compatible = "verisilicon,dw-mipi-dsi-2nd", .data = &secondary},
+    {},
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_match);
+
+static int dsi_probe(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct device_node *np = dev->of_node;
+    const struct dw_mipi_dsi_funcs *funcs;
+    struct dw_mipi_dsi *dsi;
+    int ret;
+
+    funcs = of_device_get_match_data(dev);
+    dsi = funcs->get_dsi(dev);
+    if (!dsi)
+        return -ENOMEM;
+
+    dsi->regmap = syscon_regmap_lookup_by_phandle(np, "regmap");
+    if (IS_ERR(dsi->regmap))
+           return PTR_ERR(dsi->regmap);
+
+    dsi->pclk = devm_clk_get_optional(dev, "pclk");
+    if (IS_ERR(dsi->pclk))
+        return PTR_ERR(dsi->pclk);
+
+    dsi->pixclk = devm_clk_get_optional(dev, "pixclk");
+    if (IS_ERR(dsi->pixclk))
+       return PTR_ERR(dsi->pixclk);
+
+    dsi->dphy = devm_phy_get(dev, "dphy");
+    if (IS_ERR(dsi->dphy))
+        return PTR_ERR(dsi->dphy);
+
+    dsi->host.ops = &dw_mipi_dsi_host_ops;
+    dsi->host.dev = dev;
+    ret = mipi_dsi_host_register(&dsi->host);
+    if (ret)
+        return ret;
+
+    dsi->funcs = funcs;
+    dev_set_drvdata(dev, dsi);
+
+    pm_runtime_enable(dev);
+
+    ret = component_add(dev, &dsi_component_ops);
+    if (ret)
+       goto host_unregister;
+
+    return 0;
+
+host_unregister:
+    pm_runtime_disable(dev);
+
+    mipi_dsi_host_unregister(&dsi->host);
+
+    return ret;
+}
+
+static int dsi_remove(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+    mipi_dsi_host_unregister(&dsi->host);
+
+    pm_runtime_disable(dev);
+
+    component_del(dev, &dsi_component_ops);
+
+    dev_set_drvdata(dev, NULL);
+
+    return 0;
+}
+
+#ifdef CONFIG_PM
+static int dsi_runtime_suspend(struct device *dev)
+{
+    struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+    clk_disable_unprepare(dsi->pixclk);
+    clk_disable_unprepare(dsi->pclk);
+
+    return 0;
+}
+
+static int dsi_runtime_resume(struct device *dev)
+{
+    struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+    clk_prepare_enable(dsi->pclk);
+    clk_prepare_enable(dsi->pixclk);
+
+    return 0;
+}
+#endif
+
+static const struct dev_pm_ops dsi_pm_ops = {
+    SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                pm_runtime_force_resume)
+    SET_RUNTIME_PM_OPS(dsi_runtime_suspend, dsi_runtime_resume, NULL)
+};
+
+struct platform_driver dw_mipi_dsi_driver = {
+    .probe = dsi_probe,
+    .remove = dsi_remove,
+    .driver = {
+        .name = "dw-mipi-dsi",
+        .of_match_table = of_match_ptr(dw_mipi_dsi_dt_match),
+        .pm = &dsi_pm_ops,
+    },
+};
+
+MODULE_DESCRIPTION("DW MIPI DSI Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/verisilicon/dw_mipi_dsi.h b/drivers/gpu/drm/verisilicon/dw_mipi_dsi.h
new file mode 100644 (file)
index 0000000..ab533b1
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __DW_MIPI_DSI_H_
+#define __DW_MIPI_DSI_H_
+
+extern struct platform_driver dw_mipi_dsi_driver;
+#endif /* __DW_MIPI_DSI_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
new file mode 100644 (file)
index 0000000..da34168
--- /dev/null
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/vs_drm.h>
+
+#include "vs_crtc.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_vblank.h>
+#endif
+
+void vs_crtc_destroy(struct drm_crtc *crtc)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+    drm_crtc_cleanup(crtc);
+    kfree(vs_crtc);
+}
+
+static void vs_crtc_reset(struct drm_crtc *crtc)
+{
+    struct vs_crtc_state *state;
+
+    if (crtc->state) {
+        __drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+        state = to_vs_crtc_state(crtc->state);
+        kfree(state);
+        crtc->state = NULL;
+    }
+
+    state = kzalloc(sizeof(*state), GFP_KERNEL);
+    if (state == NULL)
+        return;
+
+    __drm_atomic_helper_crtc_reset(crtc, &state->base);
+
+    state->sync_mode = VS_SINGLE_DC;
+    state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+    state->encoder_type = DRM_MODE_ENCODER_NONE;
+#ifdef CONFIG_VERISILICON_MMU
+    state->mmu_prefetch = VS_MMU_PREFETCH_DISABLE;
+#endif
+}
+
+static struct drm_crtc_state *
+vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+    struct vs_crtc_state *ori_state;
+    struct vs_crtc_state *state;
+
+    if (WARN_ON(!crtc->state))
+        return NULL;
+
+    ori_state = to_vs_crtc_state(crtc->state);
+    state = kzalloc(sizeof(*state), GFP_KERNEL);
+    if (!state)
+        return NULL;
+
+    __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+    state->sync_mode = ori_state->sync_mode;
+    state->output_fmt = ori_state->output_fmt;
+    state->encoder_type = ori_state->encoder_type;
+    state->bg_color = ori_state->bg_color;
+    state->bpp = ori_state->bpp;
+    state->sync_enable = ori_state->sync_enable;
+    state->dither_enable = ori_state->dither_enable;
+    state->underflow = ori_state->underflow;
+#ifdef CONFIG_VERISILICON_MMU
+    state->mmu_prefetch = ori_state->mmu_prefetch;
+#endif
+
+    return &state->base;
+}
+
+static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+                     struct drm_crtc_state *state)
+{
+    __drm_atomic_helper_crtc_destroy_state(state);
+    kfree(to_vs_crtc_state(state));
+}
+
+static int vs_crtc_atomic_set_property(struct drm_crtc *crtc,
+                       struct drm_crtc_state *state,
+                       struct drm_property *property,
+                       uint64_t val)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state);
+
+    if (property == vs_crtc->sync_mode)
+        vs_crtc_state->sync_mode = val;
+    else if (property == vs_crtc->mmu_prefetch)
+        vs_crtc_state->mmu_prefetch = val;
+    else if (property == vs_crtc->bg_color)
+        vs_crtc_state->bg_color = val;
+    else if (property == vs_crtc->panel_sync)
+        vs_crtc_state->sync_enable = val;
+    else if (property == vs_crtc->dither)
+        vs_crtc_state->dither_enable = val;
+    else
+        return -EINVAL;
+
+    return 0;
+}
+
+static int vs_crtc_atomic_get_property(struct drm_crtc *crtc,
+                       const struct drm_crtc_state *state,
+                       struct drm_property *property,
+                       uint64_t *val)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+    const struct vs_crtc_state *vs_crtc_state =
+        container_of(state, const struct vs_crtc_state, base);
+
+    if (property == vs_crtc->sync_mode)
+        *val = vs_crtc_state->sync_mode;
+    else if (property == vs_crtc->mmu_prefetch)
+        *val = vs_crtc_state->mmu_prefetch;
+    else if (property == vs_crtc->bg_color)
+        *val = vs_crtc_state->bg_color;
+    else if (property == vs_crtc->panel_sync)
+        *val = vs_crtc_state->sync_enable;
+    else if (property == vs_crtc->dither)
+        *val = vs_crtc_state->dither_enable;
+    else
+        return -EINVAL;
+
+    return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int vs_crtc_debugfs_show(struct seq_file *s, void *data)
+{
+    struct drm_crtc *crtc = s->private;
+    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+
+    seq_printf(s, "crtc[%u]: %s\n", crtc->base.id, crtc->name);
+    seq_printf(s, "\tactive = %d\n", crtc->state->active);
+    seq_printf(s, "\tsize = %dx%d\n", mode->hdisplay, mode->vdisplay);
+    seq_printf(s, "\tbpp = %u\n", crtc_state->bpp);
+    seq_printf(s, "\tunderflow = %d\n", crtc_state->underflow);
+
+    return 0;
+}
+
+static int vs_crtc_debugfs_open(struct inode *inode, struct file *file)
+{
+    return single_open(file, vs_crtc_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations vs_crtc_debugfs_fops = {
+    .open       = vs_crtc_debugfs_open,
+    .read       = seq_read,
+    .llseek     = seq_lseek,
+    .release    = single_release,
+};
+
+static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
+{
+    debugfs_create_file("status", 0444, crtc->debugfs_entry,
+                crtc, &vs_crtc_debugfs_fops);
+
+    return 0;
+}
+#else
+static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
+{
+    return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int vs_crtc_late_register(struct drm_crtc *crtc)
+{
+    return vs_crtc_debugfs_init(crtc);
+}
+
+static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+    vs_crtc->funcs->enable_vblank(vs_crtc->dev, crtc, true);
+
+    return 0;
+}
+
+static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+    vs_crtc->funcs->enable_vblank(vs_crtc->dev, crtc, false);
+}
+
+static const struct drm_crtc_funcs vs_crtc_funcs = {
+    .set_config     = drm_atomic_helper_set_config,
+    .destroy        = vs_crtc_destroy,
+    .page_flip      = drm_atomic_helper_page_flip,
+    .reset          = vs_crtc_reset,
+    .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
+    .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
+    .atomic_set_property    = vs_crtc_atomic_set_property,
+    .atomic_get_property    = vs_crtc_atomic_get_property,
+    .late_register      = vs_crtc_late_register,
+    .enable_vblank      = vs_crtc_enable_vblank,
+    .disable_vblank     = vs_crtc_disable_vblank,
+};
+
+static u8 cal_pixel_bits(u32 bus_format)
+{
+    u8 bpp;
+
+    switch (bus_format) {
+    case MEDIA_BUS_FMT_RGB565_1X16:
+    case MEDIA_BUS_FMT_UYVY8_1X16:
+        bpp = 16;
+        break;
+    case MEDIA_BUS_FMT_RGB666_1X18:
+    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+        bpp = 18;
+        break;
+    case MEDIA_BUS_FMT_UYVY10_1X20:
+        bpp = 20;
+        break;
+    case MEDIA_BUS_FMT_BGR888_1X24:
+    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+    case MEDIA_BUS_FMT_YUV8_1X24:
+        bpp = 24;
+        break;
+    case MEDIA_BUS_FMT_RGB101010_1X30:
+    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+    case MEDIA_BUS_FMT_YUV10_1X30:
+        bpp = 30;
+        break;
+    default:
+        bpp = 24;
+        break;
+    }
+
+    return bpp;
+}
+
+static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
+                   const struct drm_display_mode *mode,
+                   struct drm_display_mode *adjusted_mode)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+    return vs_crtc->funcs->mode_fixup(vs_crtc->dev, crtc, mode, adjusted_mode);
+}
+
+static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
+                  struct drm_atomic_state *old_state)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+    vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
+
+    pm_runtime_get_sync(vs_crtc->dev);
+
+    vs_crtc->funcs->enable(vs_crtc->dev, crtc);
+
+    drm_crtc_vblank_on(crtc);
+}
+
+static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
+                   struct drm_atomic_state *old_state)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+    drm_crtc_vblank_off(crtc);
+
+    vs_crtc->funcs->disable(vs_crtc->dev, crtc);
+
+    pm_runtime_put(vs_crtc->dev);
+
+    spin_lock_irq(&crtc->dev->event_lock);
+    if (crtc->state->event && !crtc->state->active) {
+        drm_crtc_send_vblank_event(crtc, crtc->state->event);
+
+        crtc->state->event = NULL;
+    }
+    spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+                 struct drm_atomic_state *old_crtc_state)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+    struct device *dev = vs_crtc->dev;
+    struct drm_property_blob *blob = crtc->state->gamma_lut;
+    struct drm_color_lut *lut;
+
+    if (crtc->state->color_mgmt_changed) {
+        if ((blob) && (blob->length)) {
+            lut = blob->data;
+            vs_crtc->funcs->set_gamma(dev, crtc, lut,
+                          blob->length / sizeof(*lut));
+            vs_crtc->funcs->enable_gamma(dev, crtc, true);
+        } else {
+            vs_crtc->funcs->enable_gamma(dev, crtc, false);
+        }
+    }
+}
+
+static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
+                 struct drm_atomic_state *old_crtc_state)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+    struct drm_pending_vblank_event *event = crtc->state->event;
+
+    vs_crtc->funcs->commit(vs_crtc->dev);
+
+    if (event) {
+        WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+        spin_lock_irq(&crtc->dev->event_lock);
+        drm_crtc_arm_vblank_event(crtc, event);
+        spin_unlock_irq(&crtc->dev->event_lock);
+        crtc->state->event = NULL;
+    }
+}
+
+static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
+    .mode_fixup = vs_crtc_mode_fixup,
+    .atomic_enable  = vs_crtc_atomic_enable,
+    .atomic_disable = vs_crtc_atomic_disable,
+    .atomic_begin   = vs_crtc_atomic_begin,
+    .atomic_flush   = vs_crtc_atomic_flush,
+};
+
+static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
+    { VS_SINGLE_DC,         "single dc mode" },
+    { VS_MULTI_DC_PRIMARY,      "primary dc for multi dc mode" },
+    { VS_MULTI_DC_SECONDARY,    "secondary dc for multi dc mode" },
+};
+
+#ifdef CONFIG_VERISILICON_MMU
+static const struct drm_prop_enum_list vs_mmu_prefetch_enum_list[] = {
+    { VS_MMU_PREFETCH_DISABLE,  "disable mmu prefetch" },
+    { VS_MMU_PREFETCH_ENABLE,   "enable mmu prefetch" },
+};
+#endif
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+                   struct vs_dc_info *info)
+{
+    struct vs_crtc *crtc;
+    int ret;
+
+    if (!info)
+        return NULL;
+
+    crtc = kzalloc(sizeof(struct vs_crtc), GFP_KERNEL);
+    if (!crtc)
+        return NULL;
+
+    ret = drm_crtc_init_with_planes(drm_dev, &crtc->base,
+                    NULL, NULL, &vs_crtc_funcs,
+                    info->name ? info->name : NULL);
+    if (ret)
+        goto err_free_crtc;
+
+    drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
+
+    /* Set up the crtc properties */
+    if (info->pipe_sync) {
+        crtc->sync_mode = drm_property_create_enum(drm_dev, 0,
+                    "SYNC_MODE",
+                    vs_sync_mode_enum_list,
+                    ARRAY_SIZE(vs_sync_mode_enum_list));
+
+        if (!crtc->sync_mode)
+            goto err_cleanup_crts;
+
+        drm_object_attach_property(&crtc->base.base,
+                       crtc->sync_mode,
+                       VS_SINGLE_DC);
+    }
+
+#if 0
+    if (info->gamma_size) {
+        ret = drm_mode_crtc_set_gamma_size(&crtc->base,
+                           info->gamma_size);
+        if (ret)
+            goto err_cleanup_crts;
+
+        drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
+                       info->gamma_size);
+    }
+#endif
+
+    if (info->background) {
+        crtc->bg_color = drm_property_create_range(drm_dev, 0,
+                         "BG_COLOR", 0, 0xffffffff);
+
+        if (!crtc->bg_color)
+            goto err_cleanup_crts;
+
+        drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0);
+    }
+
+    if (info->panel_sync) {
+        crtc->panel_sync = drm_property_create_bool(drm_dev, 0, "SYNC_ENABLED");
+
+        if (!crtc->panel_sync)
+            goto err_cleanup_crts;
+
+        drm_object_attach_property(&crtc->base.base, crtc->panel_sync, 0);
+    }
+
+    crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED");
+    if (!crtc->dither)
+        goto err_cleanup_crts;
+
+    drm_object_attach_property(&crtc->base.base, crtc->dither, 0);
+
+#ifdef CONFIG_VERISILICON_MMU
+    if (info->mmu_prefetch) {
+        crtc->mmu_prefetch = drm_property_create_enum(drm_dev, 0,
+                                "MMU_PREFETCH",
+                                vs_mmu_prefetch_enum_list,
+                                ARRAY_SIZE(vs_mmu_prefetch_enum_list));
+        if (!crtc->mmu_prefetch)
+            goto err_cleanup_crts;
+
+        drm_object_attach_property(&crtc->base.base,
+                                   crtc->mmu_prefetch,
+                                   VS_MMU_PREFETCH_DISABLE);
+    }
+#endif
+
+    crtc->max_bpc = info->max_bpc;
+    crtc->color_formats = info->color_formats;
+    return crtc;
+
+err_cleanup_crts:
+    drm_crtc_cleanup(&crtc->base);
+
+err_free_crtc:
+    kfree(crtc);
+    return NULL;
+}
+
+void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
+{
+    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
+
+    drm_crtc_handle_vblank(crtc);
+
+    vs_crtc_state->underflow = underflow;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h
new file mode 100644 (file)
index 0000000..9d4079d
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_CRTC_H__
+#define __VS_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "vs_type.h"
+
+struct vs_crtc_funcs {
+    void (*enable)(struct device *dev, struct drm_crtc *crtc);
+    void (*disable)(struct device *dev, struct drm_crtc *crtc);
+    bool (*mode_fixup)(struct device *dev,
+               struct drm_crtc *crtc,
+               const struct drm_display_mode *mode,
+               struct drm_display_mode *adjusted_mode);
+    void (*set_gamma)(struct device *dev, struct drm_crtc *crtc,
+               struct drm_color_lut *lut, unsigned int size);
+    void (*enable_gamma)(struct device *dev, struct drm_crtc *crtc,
+               bool enable);
+    void (*enable_vblank)(struct device *dev, struct drm_crtc *crtc,
+               bool enable);
+    void (*commit)(struct device *dev);
+};
+
+struct vs_crtc_state {
+    struct drm_crtc_state base;
+
+    u32 sync_mode;
+    u32 output_fmt;
+    u32 bg_color;
+    u8 encoder_type;
+    u8 mmu_prefetch;
+    u8 bpp;
+    bool sync_enable;
+    bool dither_enable;
+    bool underflow;
+};
+
+struct vs_crtc {
+    struct drm_crtc base;
+    struct device *dev;
+    struct drm_pending_vblank_event *event;
+    unsigned int max_bpc;
+    unsigned int color_formats; /* supported color format */
+
+    struct drm_property *sync_mode;
+    struct drm_property *mmu_prefetch;
+    struct drm_property *bg_color;
+    struct drm_property *panel_sync;
+    struct drm_property *dither;
+
+    const struct vs_crtc_funcs *funcs;
+};
+
+void vs_crtc_destroy(struct drm_crtc *crtc);
+
+struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
+                   struct vs_dc_info *info);
+void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow);
+
+static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
+{
+    return container_of(crtc, struct vs_crtc, base);
+}
+
+static inline struct vs_crtc_state *
+to_vs_crtc_state(struct drm_crtc_state *state)
+{
+    return container_of(state, struct vs_crtc_state, base);
+}
+#endif /* __VS_CRTC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
new file mode 100644 (file)
index 0000000..d90e783
--- /dev/null
@@ -0,0 +1,1387 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/of_graph.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/vs_drm.h>
+
+#include "vs_type.h"
+#include "vs_dc_hw.h"
+#include "vs_dc.h"
+#include "vs_crtc.h"
+#include "vs_drv.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_fourcc.h>
+#include <drm/drm_vblank.h>
+#endif
+
+static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb)
+{
+    u8 f = FORMAT_A8R8G8B8;
+
+    switch (format) {
+    case DRM_FORMAT_XRGB4444:
+    case DRM_FORMAT_RGBX4444:
+    case DRM_FORMAT_XBGR4444:
+    case DRM_FORMAT_BGRX4444:
+        f = FORMAT_X4R4G4B4;
+        break;
+    case DRM_FORMAT_ARGB4444:
+    case DRM_FORMAT_RGBA4444:
+    case DRM_FORMAT_ABGR4444:
+    case DRM_FORMAT_BGRA4444:
+        f = FORMAT_A4R4G4B4;
+        break;
+    case DRM_FORMAT_XRGB1555:
+    case DRM_FORMAT_RGBX5551:
+    case DRM_FORMAT_XBGR1555:
+    case DRM_FORMAT_BGRX5551:
+        f = FORMAT_X1R5G5B5;
+        break;
+    case DRM_FORMAT_ARGB1555:
+    case DRM_FORMAT_RGBA5551:
+    case DRM_FORMAT_ABGR1555:
+    case DRM_FORMAT_BGRA5551:
+        f = FORMAT_A1R5G5B5;
+        break;
+    case DRM_FORMAT_RGB565:
+    case DRM_FORMAT_BGR565:
+        f = FORMAT_R5G6B5;
+        break;
+    case DRM_FORMAT_XRGB8888:
+    case DRM_FORMAT_RGBX8888:
+    case DRM_FORMAT_XBGR8888:
+    case DRM_FORMAT_BGRX8888:
+        f = FORMAT_X8R8G8B8;
+        break;
+    case DRM_FORMAT_ARGB8888:
+    case DRM_FORMAT_RGBA8888:
+    case DRM_FORMAT_ABGR8888:
+    case DRM_FORMAT_BGRA8888:
+        f = FORMAT_A8R8G8B8;
+        break;
+    case DRM_FORMAT_YUYV:
+    case DRM_FORMAT_YVYU:
+        f = FORMAT_YUY2;
+        break;
+    case DRM_FORMAT_UYVY:
+    case DRM_FORMAT_VYUY:
+        f = FORMAT_UYVY;
+        break;
+    case DRM_FORMAT_YUV420:
+    case DRM_FORMAT_YVU420:
+        f = FORMAT_YV12;
+        break;
+    case DRM_FORMAT_NV21:
+        f = FORMAT_NV12;
+        break;
+    case DRM_FORMAT_NV16:
+    case DRM_FORMAT_NV61:
+        f = FORMAT_NV16;
+        break;
+    case DRM_FORMAT_P010:
+        f = FORMAT_P010;
+        break;
+    case DRM_FORMAT_ARGB2101010:
+    case DRM_FORMAT_RGBA1010102:
+    case DRM_FORMAT_ABGR2101010:
+    case DRM_FORMAT_BGRA1010102:
+        f = FORMAT_A2R10G10B10;
+        break;
+    case DRM_FORMAT_NV12:
+        if (fourcc_mod_vs_get_type(mod) ==
+            DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
+            f = FORMAT_NV12_10BIT;
+        else
+            f = FORMAT_NV12;
+        break;
+    case DRM_FORMAT_YUV444:
+        if (fourcc_mod_vs_get_type(mod) ==
+            DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
+            f = FORMAT_YUV444_10BIT;
+        else
+            f = FORMAT_YUV444;
+        break;
+    default:
+        break;
+    }
+
+    fb->format = f;
+}
+
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
+{
+    fb->swizzle = SWIZZLE_ARGB;
+    fb->uv_swizzle = 0;
+
+    switch (format) {
+    case DRM_FORMAT_RGBX4444:
+    case DRM_FORMAT_RGBA4444:
+    case DRM_FORMAT_RGBX5551:
+    case DRM_FORMAT_RGBA5551:
+    case DRM_FORMAT_RGBX8888:
+    case DRM_FORMAT_RGBA8888:
+    case DRM_FORMAT_RGBA1010102:
+        fb->swizzle = SWIZZLE_RGBA;
+        break;
+    case DRM_FORMAT_XBGR4444:
+    case DRM_FORMAT_ABGR4444:
+    case DRM_FORMAT_XBGR1555:
+    case DRM_FORMAT_ABGR1555:
+    case DRM_FORMAT_BGR565:
+    case DRM_FORMAT_XBGR8888:
+    case DRM_FORMAT_ABGR8888:
+    case DRM_FORMAT_ABGR2101010:
+        fb->swizzle = SWIZZLE_ABGR;
+        break;
+    case DRM_FORMAT_BGRX4444:
+    case DRM_FORMAT_BGRA4444:
+    case DRM_FORMAT_BGRX5551:
+    case DRM_FORMAT_BGRA5551:
+    case DRM_FORMAT_BGRX8888:
+    case DRM_FORMAT_BGRA8888:
+    case DRM_FORMAT_BGRA1010102:
+        fb->swizzle = SWIZZLE_BGRA;
+        break;
+    case DRM_FORMAT_YVYU:
+    case DRM_FORMAT_VYUY:
+    case DRM_FORMAT_NV21:
+    case DRM_FORMAT_NV61:
+        fb->uv_swizzle = 1;
+        break;
+    default:
+        break;
+    }
+}
+
+static inline void update_watermark(struct drm_property_blob *watermark,
+                                    struct dc_hw_fb *fb)
+{
+    struct drm_vs_watermark *data;
+    fb->water_mark = 0;
+
+    if (watermark) {
+        data = watermark->data;
+        fb->water_mark = data->watermark & 0xFFFFF;
+    }
+}
+
+static inline u8 to_vs_rotation(unsigned int rotation)
+{
+    u8 rot;
+
+    switch (rotation & DRM_MODE_REFLECT_MASK) {
+    case DRM_MODE_REFLECT_X:
+        rot = FLIP_X;
+        return rot;
+    case DRM_MODE_REFLECT_Y:
+        rot = FLIP_Y;
+        return rot;
+    case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+        rot = FLIP_XY;
+        return rot;
+    default:
+        break;
+    }
+
+    switch (rotation & DRM_MODE_ROTATE_MASK) {
+    case DRM_MODE_ROTATE_0:
+        rot = ROT_0;
+        break;
+    case DRM_MODE_ROTATE_90:
+        rot = ROT_90;
+        break;
+    case DRM_MODE_ROTATE_180:
+        rot = ROT_180;
+        break;
+    case DRM_MODE_ROTATE_270:
+        rot = ROT_270;
+        break;
+    default:
+        rot = ROT_0;
+        break;
+    }
+
+    return rot;
+}
+
+static inline u8 to_vs_yuv_color_space(u32 color_space)
+{
+    u8 cs;
+
+    switch (color_space) {
+    case DRM_COLOR_YCBCR_BT601:
+        cs = COLOR_SPACE_601;
+        break;
+    case DRM_COLOR_YCBCR_BT709:
+        cs = COLOR_SPACE_709;
+        break;
+    case DRM_COLOR_YCBCR_BT2020:
+        cs = COLOR_SPACE_2020;
+        break;
+    default:
+        cs = COLOR_SPACE_601;
+        break;
+    }
+
+    return cs;
+}
+
+static inline u8 to_vs_tile_mode(u64 modifier)
+{
+    return (u8)(modifier & DRM_FORMAT_MOD_VS_NORM_MODE_MASK);
+}
+
+static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc)
+{
+    u8 panel_num = dc->hw.info->panel_num;
+    u32 index = drm_crtc_index(crtc);
+    int i;
+
+    for (i = 0; i < panel_num; i++) {
+        if (index == dc->crtc[i]->base.index)
+            return i;
+    }
+
+    return 0;
+}
+
+static void dc_deinit(struct device *dev)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+
+    pm_runtime_get_sync(dev);
+
+    dc_hw_enable_interrupt(&dc->hw, 0);
+    dc_hw_deinit(&dc->hw);
+
+    pm_runtime_put(dev);
+}
+
+static int dc_init(struct device *dev)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    int ret = 0, i;
+
+    dc->first_frame = true;
+
+    for (i = 0; i < DC_DISPLAY_NUM; i++)
+       dc->pix_clk_rate[i] = clk_get_rate(dc->pixclk[i]) / 1000;
+
+    pm_runtime_get_sync(dev);
+    ret = dc_hw_init(&dc->hw);
+    if (ret) {
+        dev_err(dev, "failed to init DC HW\n");
+       goto out;
+    }
+
+out:
+    pm_runtime_put(dev);
+
+    return ret;
+}
+
+static void vs_dc_dump_enable(struct device *dev, dma_addr_t addr,
+                   unsigned int pitch)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+
+    dc_hw_enable_dump(&dc->hw, addr, pitch);
+}
+
+static void vs_dc_dump_disable(struct device *dev)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+
+    dc_hw_disable_dump(&dc->hw);
+}
+
+static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
+    struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+    struct dc_hw_display display;
+
+    display.bus_format = crtc_state->output_fmt;
+    display.h_active = mode->hdisplay;
+    display.h_total = mode->htotal;
+    display.h_sync_start = mode->hsync_start;
+    display.h_sync_end = mode->hsync_end;
+    if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+        display.h_sync_polarity = true;
+    else
+        display.h_sync_polarity = false;
+
+    display.v_active = mode->vdisplay;
+    display.v_total = mode->vtotal;
+    display.v_sync_start = mode->vsync_start;
+    display.v_sync_end = mode->vsync_end;
+    if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+        display.v_sync_polarity = true;
+    else
+        display.v_sync_polarity = false;
+
+    display.sync_mode = crtc_state->sync_mode;
+    display.bg_color = crtc_state->bg_color;
+
+    display.id = to_vs_display_id(dc, crtc);
+    display.sync_enable = crtc_state->sync_enable;
+    display.dither_enable = crtc_state->dither_enable;
+
+    display.enable = true;
+
+    if (dc->pix_clk_rate[display.id] != mode->clock) {
+           clk_set_rate(dc->pixclk[display.id], mode->clock * 1000);
+        dc->pix_clk_rate[display.id] = mode->clock;
+    }
+
+    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI ||
+       crtc_state->encoder_type == DRM_MODE_ENCODER_DPI)
+        dc_hw_set_out(&dc->hw, OUT_DPI, display.id);
+    else
+        dc_hw_set_out(&dc->hw, OUT_DP, display.id);
+
+#ifdef CONFIG_VERISILICON_MMU
+    if (crtc_state->mmu_prefetch == VS_MMU_PREFETCH_ENABLE)
+        dc_hw_enable_mmu_prefetch(&dc->hw, true);
+    else
+        dc_hw_enable_mmu_prefetch(&dc->hw, false);
+#endif
+
+    dc_hw_setup_display(&dc->hw, &display);
+}
+
+static void vs_dc_disable(struct device *dev, struct drm_crtc *crtc)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    struct dc_hw_display display;
+
+    display.id = to_vs_display_id(dc, crtc);
+    display.enable = false;
+
+    dc_hw_setup_display(&dc->hw, &display);
+}
+
+static bool vs_dc_mode_fixup(struct device *dev,
+                  struct drm_crtc *crtc,
+                  const struct drm_display_mode *mode,
+                  struct drm_display_mode *adjusted_mode)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    int id = to_vs_display_id(dc, crtc);
+    long clk_rate;
+
+    if (unlikely(id >= DC_DISPLAY_NUM)) {
+       dev_err(dev, "invalid display id : %d\n", id);
+       return false;
+    }
+
+    if (dc->pixclk[id]) {
+        clk_rate = clk_round_rate(dc->pixclk[id],
+                      adjusted_mode->clock * 1000);
+        adjusted_mode->clock = DIV_ROUND_UP(clk_rate, 1000);
+    }
+
+    return true;
+}
+
+static void vs_dc_set_gamma(struct device *dev, struct drm_crtc *crtc,
+                 struct drm_color_lut *lut, unsigned int size)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    u16 i, r, g, b;
+    u8 bits, id;
+
+    if (size != dc->hw.info->gamma_size) {
+        dev_err(dev, "gamma size does not match!\n");
+        return;
+    }
+
+    id = to_vs_display_id(dc, crtc);
+
+    bits = dc->hw.info->gamma_bits;
+    for (i = 0; i < size; i++) {
+        r = drm_color_lut_extract(lut[i].red, bits);
+        g = drm_color_lut_extract(lut[i].green, bits);
+        b = drm_color_lut_extract(lut[i].blue, bits);
+        dc_hw_update_gamma(&dc->hw, id, i, r, g, b);
+    }
+}
+
+static void vs_dc_enable_gamma(struct device *dev, struct drm_crtc *crtc,
+                 bool enable)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    u8 id;
+
+    id = to_vs_display_id(dc, crtc);
+    dc_hw_enable_gamma(&dc->hw, id, enable);
+}
+
+static void vs_dc_enable_vblank(struct device *dev, struct drm_crtc *crtc,
+               bool enable)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+
+    dc_hw_enable_irq(&dc->hw, crtc->index, enable);
+}
+
+static u32 calc_factor(u32 src, u32 dest)
+{
+    u32 factor = 1 << 16;
+
+    if ((src > 1) && (dest > 1))
+        factor = ((src - 1) << 16) / (dest - 1);
+
+    return factor;
+}
+
+static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi,
+                         struct dc_hw_scale *scale)
+{
+    int dst_w = drm_rect_width(&state->dst);
+    int dst_h = drm_rect_height(&state->dst);
+    int src_w, src_h, temp;
+    scale->enable = false;
+
+    if (roi->enable) {
+        src_w = roi->width;
+        src_h = roi->height;
+    } else {
+        src_w = drm_rect_width(&state->src) >> 16;
+        src_h = drm_rect_height(&state->src) >> 16;
+    }
+
+    if (drm_rotation_90_or_270(state->rotation)) {
+        temp = src_w;
+        src_w = src_h;
+        src_h = temp;
+    }
+
+    if (src_w != dst_w) {
+        scale->scale_factor_x = calc_factor(src_w, dst_w);
+        scale->enable = true;
+    } else {
+        scale->scale_factor_x = 1 << 16;
+    }
+    if (src_h != dst_h) {
+        scale->scale_factor_y = calc_factor(src_h, dst_h);
+        scale->enable = true;
+    } else {
+        scale->scale_factor_y = 1 << 16;
+    }
+}
+
+static void update_fb(struct vs_plane *plane, u8 display_id,
+                      struct dc_hw_fb *fb)
+{
+    struct drm_plane_state *state = plane->base.state;
+    struct vs_plane_state *plane_state = to_vs_plane_state(state);
+    struct drm_framebuffer *drm_fb = state->fb;
+    struct drm_rect *src = &state->src;
+    u32 src_x, src_y;
+
+    src_y = src->y1 >> 16;
+    src_x = src->x1 >> 16;
+
+    fb->display_id = display_id;
+    fb->y_address = plane->dma_addr[0];
+    if (src_y)
+      fb->y_address += src_y * drm_fb->pitches[0];
+    if (src_x)
+      fb->y_address += src_x * drm_fb->format->cpp[0];
+
+    fb->y_stride = drm_fb->pitches[0];
+    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+        fb->u_address = plane->dma_addr[2];
+        if (src_y)
+          fb->u_address += src_y * drm_fb->pitches[2];
+        if (src_x)
+          fb->u_address += src_x * drm_fb->format->cpp[2];
+        fb->v_address = plane->dma_addr[1];
+        if (src_y)
+          fb->v_address += src_y * drm_fb->pitches[1];
+        if (src_x)
+          fb->v_address += src_x * drm_fb->format->cpp[1];
+        fb->u_stride = drm_fb->pitches[2];
+        fb->v_stride = drm_fb->pitches[1];
+    } else {
+        fb->u_address = plane->dma_addr[1];
+        if (src_y)
+          fb->u_address += src_y * drm_fb->pitches[1];
+        if (src_x)
+          fb->u_address += src_x * drm_fb->format->cpp[1];
+        fb->v_address = plane->dma_addr[2];
+        if (src_y)
+          fb->v_address += src_y * drm_fb->pitches[2];
+        if (src_x)
+          fb->v_address += src_x * drm_fb->format->cpp[2];
+        fb->u_stride = drm_fb->pitches[1];
+        fb->v_stride = drm_fb->pitches[2];
+    }
+    fb->width = drm_rect_width(src) >> 16;
+    fb->height = drm_rect_height(src) >> 16;
+    fb->tile_mode = to_vs_tile_mode(drm_fb->modifier);
+    fb->rotation = to_vs_rotation(state->rotation);
+    fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding);
+    fb->zpos = state->zpos;
+    fb->enable = state->visible;
+    update_format(drm_fb->format->format, drm_fb->modifier, fb);
+    update_swizzle(drm_fb->format->format, fb);
+    update_watermark(plane_state->watermark, fb);
+
+    plane_state->status.tile_mode = fb->tile_mode;
+}
+
+#ifdef CONFIG_VERISILICON_DEC
+static u8 get_stream_base(u8 id)
+{
+    u8 stream_base = 0;
+
+    switch (id) {
+    case OVERLAY_PLANE_0:
+        stream_base = 3;
+        break;
+    case OVERLAY_PLANE_1:
+        stream_base = 6;
+        break;
+    case PRIMARY_PLANE_1:
+        stream_base = 16;
+        break;
+    case OVERLAY_PLANE_2:
+        stream_base = 19;
+        break;
+    case OVERLAY_PLANE_3:
+        stream_base = 22;
+        break;
+    default:
+        break;
+    }
+
+    return stream_base;
+}
+
+static void update_fbc(struct vs_dc *dc, struct vs_plane *plane, bool *enable)
+{
+    struct dc_dec_fb dec_fb;
+    struct drm_plane_state *state = plane->base.state;
+    struct drm_framebuffer *drm_fb = state->fb;
+    struct vs_dc_plane *dc_plane = &dc->planes[plane->id];
+    u8 i, stream_id;
+
+    if (!dc->hw.info->cap_dec) {
+        *enable = false;
+        return;
+    }
+
+    stream_id = get_stream_base(dc_plane->id);
+    memset(&dec_fb, 0, sizeof(struct dc_dec_fb));
+    dec_fb.fb = drm_fb;
+
+    if (fourcc_mod_vs_get_type(drm_fb->modifier) !=
+                    DRM_FORMAT_MOD_VS_TYPE_COMPRESSED) {
+        *enable = false;
+    } else {
+        *enable = true;
+
+        for (i = 0; i < DEC_PLANE_MAX; i++) {
+            dec_fb.addr[i] = plane->dma_addr[i];
+            dec_fb.stride[i] = drm_fb->pitches[i];
+        }
+    }
+
+    dc_dec_config(&dc->dec400l, &dec_fb, stream_id);
+}
+
+static void disable_fbc(struct vs_dc *dc, struct vs_plane *plane)
+{
+    struct vs_dc_plane *dc_plane = &dc->planes[plane->id];
+    u8 stream_id;
+
+    if (!dc->hw.info->cap_dec)
+        return;
+
+    stream_id = get_stream_base(dc_plane->id);
+    dc_dec_config(&dc->dec400l, NULL, stream_id);
+}
+#endif
+
+static void update_degamma(struct vs_dc *dc, struct vs_plane *plane,
+               struct vs_plane_state *plane_state)
+{
+    dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
+    plane_state->degamma_changed = false;
+}
+
+static void update_roi(struct vs_dc *dc, u8 id,
+                       struct vs_plane_state *plane_state,
+                       struct dc_hw_roi *roi)
+{
+    struct drm_vs_roi *data;
+    struct drm_rect *src = &plane_state->base.src;
+    u16 src_w = drm_rect_width(src) >> 16;
+    u16 src_h = drm_rect_height(src) >> 16;
+
+    if (plane_state->roi) {
+        data = plane_state->roi->data;
+
+        if (data->enable) {
+            roi->x = data->roi_x;
+            roi->y = data->roi_y;
+            roi->width = (data->roi_x + data->roi_w > src_w) ?
+                         (src_w - data->roi_x) : data->roi_w;
+            roi->height = (data->roi_y + data->roi_h > src_h) ?
+                          (src_h - data->roi_y) : data->roi_h;
+            roi->enable = true;
+        } else {
+            roi->enable = false;
+        }
+
+        dc_hw_update_roi(&dc->hw, id, roi);
+    } else {
+        roi->enable = false;
+    }
+}
+
+static void update_color_mgmt(struct vs_dc *dc, u8 id,
+                            struct dc_hw_fb *fb,
+                            struct vs_plane_state *plane_state)
+{
+    struct drm_vs_color_mgmt *data;
+    struct dc_hw_colorkey colorkey;
+
+    if (plane_state->color_mgmt) {
+        data = plane_state->color_mgmt->data;
+
+        fb->clear_enable = data->clear_enable;
+        fb->clear_value = data->clear_value;
+
+        if (data->colorkey > data->colorkey_high)
+            data->colorkey = data->colorkey_high;
+
+        colorkey.colorkey = data->colorkey;
+        colorkey.colorkey_high = data->colorkey_high;
+        colorkey.transparency = (data->transparency) ?
+                                DC_TRANSPARENCY_KEY : DC_TRANSPARENCY_OPAQUE;
+        dc_hw_update_colorkey(&dc->hw, id, &colorkey);
+    }
+}
+
+static void update_plane(struct vs_dc *dc, struct vs_plane *plane)
+{
+    struct dc_hw_fb fb = {0};
+    struct dc_hw_scale scale;
+    struct dc_hw_position pos;
+    struct dc_hw_blend blend;
+    struct dc_hw_roi roi;
+    struct drm_plane_state *state = plane->base.state;
+    struct vs_plane_state *plane_state = to_vs_plane_state(state);
+    struct drm_rect *dest = &state->dst;
+    bool dec_enable = false;
+    u8 display_id = 0;
+
+#ifdef CONFIG_VERISILICON_DEC
+    update_fbc(dc, plane, &dec_enable);
+#endif
+
+    display_id = to_vs_display_id(dc, state->crtc);
+    update_fb(plane, display_id, &fb);
+    fb.dec_enable = dec_enable;
+
+
+    update_roi(dc, plane->id, plane_state, &roi);
+
+    update_scale(state, &roi, &scale);
+
+    if (plane_state->degamma_changed)
+        update_degamma(dc, plane, plane_state);
+
+    pos.start_x = dest->x1;
+    pos.start_y = dest->y1;
+    pos.end_x = dest->x2;
+    pos.end_y = dest->y2;
+
+    blend.alpha = (u8)(state->alpha >> 8);
+    blend.blend_mode = (u8)(state->pixel_blend_mode);
+
+    update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+    dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend);
+}
+
+static void update_qos(struct vs_dc *dc, struct vs_plane *plane)
+{
+    struct drm_plane_state *state = plane->base.state;
+    struct vs_plane_state *plane_state = to_vs_plane_state(state);
+    struct drm_vs_watermark *data;
+    struct dc_hw_qos qos;
+
+    if (plane_state->watermark){
+        data = plane_state->watermark->data;
+
+        if (data->qos_high) {
+            if (data->qos_low > data->qos_high)
+                data->qos_low = data->qos_high;
+
+            qos.low_value = data->qos_low & 0x0F;
+            qos.high_value = data->qos_high & 0x0F;
+            dc_hw_update_qos(&dc->hw, &qos);
+        }
+    }
+}
+
+static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor)
+{
+    u8 size_type;
+
+    switch (state->crtc_w) {
+    case 32:
+        size_type = CURSOR_SIZE_32X32;
+        break;
+    case 64:
+        size_type = CURSOR_SIZE_64X64;
+        break;
+    default:
+        size_type = CURSOR_SIZE_32X32;
+        break;
+    }
+
+    cursor->size = size_type;
+}
+
+static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane)
+{
+    struct drm_plane_state *state = plane->base.state;
+    struct dc_hw_cursor cursor;
+
+    cursor.address = plane->dma_addr[0];
+    if (state->crtc_x > 0) {
+        cursor.x = state->crtc_x;
+        cursor.hot_x = 0;
+    } else {
+        cursor.hot_x = -state->crtc_x;
+        cursor.x = 0;
+    }
+    if (state->crtc_y > 0) {
+        cursor.y = state->crtc_y;
+        cursor.hot_y = 0;
+    } else {
+        cursor.hot_y = -state->crtc_y;
+        cursor.y = 0;
+    }
+    cursor.display_id = to_vs_display_id(dc, state->crtc);
+    update_cursor_size(state, &cursor);
+    cursor.enable = true;
+
+    dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+}
+
+static void vs_dc_update_plane(struct device *dev, struct vs_plane *plane)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    enum drm_plane_type type = plane->base.type;
+
+    switch (type) {
+    case DRM_PLANE_TYPE_PRIMARY:
+    case DRM_PLANE_TYPE_OVERLAY:
+        update_plane(dc, plane);
+        update_qos(dc, plane);
+        break;
+    case DRM_PLANE_TYPE_CURSOR:
+        update_cursor_plane(dc, plane);
+        break;
+    default:
+        break;
+    }
+}
+
+static void vs_dc_disable_plane(struct device *dev, struct vs_plane *plane,
+                                struct drm_plane_state *old_state)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    enum drm_plane_type type = plane->base.type;
+    struct dc_hw_fb fb = {0};
+    struct dc_hw_cursor cursor = {0};
+
+    switch (type) {
+    case DRM_PLANE_TYPE_PRIMARY:
+    case DRM_PLANE_TYPE_OVERLAY:
+        fb.enable = false;
+        dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL);
+#ifdef CONFIG_VERISILICON_DEC
+        disable_fbc(dc, plane);
+#endif
+        break;
+    case DRM_PLANE_TYPE_CURSOR:
+        cursor.enable = false;
+        cursor.display_id = to_vs_display_id(dc, old_state->crtc);
+        dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor);
+        break;
+    default:
+        break;
+    }
+}
+
+static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info,
+                                u64 modifier)
+{
+    const u64 *mods;
+
+    if (plane_info->modifiers == NULL)
+        return false;
+
+    for(mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) {
+        if (*mods == modifier)
+            return true;
+    }
+
+    return false;
+}
+
+static int vs_dc_check_plane(struct device *dev, struct vs_plane *plane,
+                  struct drm_plane_state *state)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    struct drm_framebuffer *fb = state->fb;
+    const struct vs_plane_info *plane_info;
+    struct drm_crtc *crtc = state->crtc;
+    struct drm_crtc_state *crtc_state;
+
+    plane_info = &dc->hw.info->planes[plane->id];
+    if (plane_info == NULL)
+        return -EINVAL;
+
+    if (fb->width < plane_info->min_width ||
+        fb->width > plane_info->max_width ||
+        fb->height < plane_info->min_height ||
+        fb->height > plane_info->max_height)
+        dev_err_once(dev, "buffer size may not support on plane%d.\n",
+                 plane->id);
+
+    if ((plane->base.type != DRM_PLANE_TYPE_CURSOR) &&
+        (!vs_dc_mod_supported(plane_info, fb->modifier))) {
+        dev_err(dev, "unsupported modifier on plane%d.\n", plane->id);
+        return -EINVAL;
+    }
+
+    crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
+    if (IS_ERR(crtc_state))
+        return -EINVAL;
+
+    /* 90/270 degree rotation requires non-linear fb */
+    if ((state->rotation == DRM_MODE_ROTATE_90 ||
+       state->rotation == DRM_MODE_ROTATE_270) &&
+       (fb->modifier & DRM_FORMAT_MOD_LINEAR) == DRM_FORMAT_MOD_LINEAR)
+       return -EINVAL;
+
+    return drm_atomic_helper_check_plane_state(state, crtc_state,
+                          plane_info->min_scale,
+                          plane_info->max_scale,
+                          true, true);
+}
+
+static irqreturn_t dc_isr(int irq, void *data)
+{
+    struct vs_dc *dc = data;
+    struct vs_dc_info *dc_info = dc->hw.info;
+    u32 i, intr_vec;
+
+    intr_vec = dc_hw_get_interrupt(&dc->hw);
+
+    for (i = 0; i < dc_info->panel_num; i++) {
+       if (intr_vec & BIT(i))
+           vs_crtc_handle_vblank(&dc->crtc[i]->base,
+                       dc_hw_check_underflow(&dc->hw));
+    }
+
+    if (intr_vec & ~0x3)
+       pr_warn("%s: unhandled interrupt %#x\n", __func__, intr_vec);
+
+    return IRQ_HANDLED;
+}
+
+static void vs_dc_commit(struct device *dev)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+
+#ifdef CONFIG_VERISILICON_DEC
+    if (dc->hw.info->cap_dec)
+        dc_dec_commit(&dc->dec400l, &dc->hw);
+#endif
+
+    dc_hw_enable_shadow_register(&dc->hw, false);
+
+    dc_hw_commit(&dc->hw);
+
+    if (dc->first_frame)
+        dc->first_frame = false;
+
+    dc_hw_enable_shadow_register(&dc->hw, true);
+}
+
+static const struct vs_crtc_funcs dc_crtc_funcs = {
+    .enable         = vs_dc_enable,
+    .disable        = vs_dc_disable,
+    .mode_fixup     = vs_dc_mode_fixup,
+    .set_gamma      = vs_dc_set_gamma,
+    .enable_gamma       = vs_dc_enable_gamma,
+    .enable_vblank      = vs_dc_enable_vblank,
+    .commit         = vs_dc_commit,
+};
+
+static const struct vs_plane_funcs dc_plane_funcs = {
+    .update         = vs_dc_update_plane,
+    .disable        = vs_dc_disable_plane,
+    .check          = vs_dc_check_plane,
+};
+
+static const struct vs_dc_funcs dc_funcs = {
+    .dump_enable        = vs_dc_dump_enable,
+    .dump_disable       = vs_dc_dump_disable,
+};
+
+static int dc_bind(struct device *dev, struct device *master, void *data)
+{
+    struct drm_device *drm_dev = data;
+#ifdef CONFIG_VERISILICON_MMU
+    struct vs_drm_private *priv = drm_dev->dev_private;
+#endif
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    struct device_node *port;
+    struct vs_crtc *crtc;
+    struct drm_crtc *drm_crtc;
+    struct vs_dc_info *dc_info;
+    struct vs_plane *plane;
+    struct drm_plane *drm_plane, *tmp;
+    struct vs_plane_info *plane_info;
+    int i, ret;
+    u32 ctrc_mask = 0;
+    if (!drm_dev || !dc) {
+        dev_err(dev, "devices are not created.\n");
+        return -ENODEV;
+    }
+
+    ret = dc_init(dev);
+    if (ret < 0) {
+        dev_err(dev, "Failed to initialize DC hardware.\n");
+        return ret;
+    }
+
+#ifdef CONFIG_VERISILICON_MMU
+    ret = dc_mmu_construct(priv->dma_dev, &priv->mmu);
+    if (ret) {
+        dev_err(dev, "failed to construct DC MMU\n");
+        goto err_clean_dc;
+    }
+
+    ret = dc_hw_mmu_init(&dc->hw, priv->mmu);
+    if (ret) {
+        dev_err(dev, "failed to init DC MMU\n");
+        goto err_clean_dc;
+    }
+#endif
+
+    ret = vs_drm_iommu_attach_device(drm_dev, dev);
+    if (ret < 0) {
+        dev_err(dev, "Failed to attached iommu device.\n");
+        goto err_clean_dc;
+    }
+
+    if (!of_graph_is_present(dev->of_node)) {
+        dev_err(dev, "no port node found\n");
+       ret = -ENODEV;
+        goto err_detach_dev;
+    }
+
+    dc_info = dc->hw.info;
+
+    for(i = 0; i < dc_info->panel_num; i++) {
+       port = of_graph_get_port_by_id(dev->of_node, i);
+       if (!port) {
+           dev_err(dev, "no port node found for panel %d\n", i);
+           ret = -ENODEV;
+           goto err_detach_dev;
+       };
+
+        crtc = vs_crtc_create(drm_dev, dc_info);
+        if (!crtc) {
+            dev_err(dev, "Failed to create CRTC.\n");
+            ret = -ENOMEM;
+            goto err_detach_dev;
+        }
+
+        crtc->base.port = port;
+        crtc->dev = dev;
+        crtc->funcs = &dc_crtc_funcs;
+        dc->crtc[i] = crtc;
+        ctrc_mask |= drm_crtc_mask(&crtc->base);
+    }
+
+    for (i = 0; i < dc_info->plane_num; i++) {
+        plane_info = (struct vs_plane_info *)&dc_info->planes[i];
+
+        if (!strcmp(plane_info->name, "Primary") ||
+            !strcmp(plane_info->name, "Cursor"))
+            plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+                                    drm_crtc_mask(&dc->crtc[0]->base));
+        else if (!strcmp(plane_info->name, "Primary_1") ||
+                 !strcmp(plane_info->name, "Cursor_1"))
+            plane = vs_plane_create(drm_dev, plane_info, dc_info->layer_num,
+                                    drm_crtc_mask(&dc->crtc[1]->base));
+        else
+            plane = vs_plane_create(drm_dev, plane_info,
+                                    dc_info->layer_num, ctrc_mask);
+
+        if (!plane)
+            goto err_cleanup_planes;
+
+        plane->id = i;
+        dc->planes[i].id = plane_info->id;
+
+        plane->funcs = &dc_plane_funcs;
+
+        if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
+            if (!strcmp(plane_info->name, "Primary"))
+                dc->crtc[0]->base.primary = &plane->base;
+            else
+                dc->crtc[1]->base.primary = &plane->base;
+            drm_dev->mode_config.min_width = plane_info->min_width;
+            drm_dev->mode_config.min_height =
+                            plane_info->min_height;
+            drm_dev->mode_config.max_width = plane_info->max_width;
+            drm_dev->mode_config.max_height =
+                            plane_info->max_height;
+        }
+
+        if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+            if (!strcmp(plane_info->name, "Cursor"))
+                dc->crtc[0]->base.cursor = &plane->base;
+            else
+                dc->crtc[1]->base.cursor = &plane->base;
+            drm_dev->mode_config.cursor_width =
+                            plane_info->max_width;
+            drm_dev->mode_config.cursor_height =
+                            plane_info->max_height;
+        }
+    }
+
+    dc->funcs = &dc_funcs;
+    vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
+    return 0;
+
+err_cleanup_planes:
+    list_for_each_entry_safe(drm_plane, tmp,
+                 &drm_dev->mode_config.plane_list, head)
+        if (drm_plane->possible_crtcs & ctrc_mask)
+            vs_plane_destory(drm_plane);
+
+    drm_for_each_crtc(drm_crtc, drm_dev)
+        vs_crtc_destroy(drm_crtc);
+err_detach_dev:
+    vs_drm_iommu_detach_device(drm_dev, dev);
+err_clean_dc:
+    dc_deinit(dev);
+    return ret;
+}
+
+static void dc_unbind(struct device *dev, struct device *master, void *data)
+{
+    struct drm_device *drm_dev = data;
+
+    dc_deinit(dev);
+
+    vs_drm_iommu_detach_device(drm_dev, dev);
+}
+
+const struct component_ops dc_component_ops = {
+    .bind = dc_bind,
+    .unbind = dc_unbind,
+};
+
+static const struct of_device_id dc_driver_dt_match[] = {
+    { .compatible = "verisilicon,dc8200", },
+    {},
+};
+MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+
+static void dc_get_display_pll(struct device *dev, struct vs_dc *dc)
+{
+       struct device_node *np;
+
+       np = of_find_node_by_name(NULL, "dw-mipi-dsi0");
+       if (!np)
+               dev_err(dev, "Failed to get dsi0\n");
+       else
+               dc->dpu0pll_on = of_device_is_available(np);
+
+       np = of_find_node_by_name(NULL, "dw-mipi-dsi1");
+       if (!np)
+               dev_err(dev, "Failed to get dsi1\n");
+       else
+               dc->dpu1pll_on = of_device_is_available(np);
+
+       /* dsi1/hdmi share the same pll1, hdmi detect again if dsi1 not use */
+       if (!dc->dpu1pll_on) {
+               np = of_find_node_by_name(NULL, "dw-hdmi-tx");
+               if (!np)
+                       dev_err(dev, "Failed to get hdmi\n");
+               else
+                       dc->dpu1pll_on = of_device_is_available(np);
+       }
+
+
+       dev_info(dev, "dpu0pll_on:%d dpu1pll_on:%d\n", dc->dpu0pll_on,
+                       dc->dpu1pll_on);
+}
+
+static int dc_probe(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct vs_dc *dc;
+    int irq, ret, i;
+    char pixclk[16];
+    struct device_node *np = dev->of_node;
+
+    dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
+    if (!dc)
+        return -ENOMEM;
+
+    dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
+    if (IS_ERR(dc->hw.hi_base))
+        return PTR_ERR(dc->hw.hi_base);
+
+    dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1);
+    if (IS_ERR(dc->hw.reg_base))
+        return PTR_ERR(dc->hw.reg_base);
+
+#ifdef CONFIG_VERISILICON_MMU
+    dc->hw.mmu_base = devm_platform_ioremap_resource(pdev, 2);
+    if (IS_ERR(dc->hw.mmu_base))
+        return PTR_ERR(dc->hw.mmu_base);
+#endif
+
+    dc->hw.vosys_regmap = syscon_regmap_lookup_by_phandle(np, "vosys-regmap");
+    if (IS_ERR(dc->hw.vosys_regmap))
+    {
+        dev_err(dev, "Failed to remap vosys\n");
+        return PTR_ERR(dc->hw.vosys_regmap);
+    }
+
+    irq = platform_get_irq(pdev, 0);
+    ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
+    if (ret < 0) {
+        dev_err(dev, "Failed to install irq:%u.\n", irq);
+        return ret;
+    }
+
+    dc->core_clk = devm_clk_get_optional(dev, "core_clk");
+    if (IS_ERR(dc->core_clk)) {
+        dev_err(dev, "failed to get core_clk source\n");
+        return PTR_ERR(dc->core_clk);
+    }
+
+    for (i = 0; i < DC_DISPLAY_NUM; i++) {
+        snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pix_clk", i);
+        dc->pix_clk[i] = devm_clk_get_optional(dev, pixclk);
+
+        if (IS_ERR(dc->pix_clk[i])) {
+            dev_err(dev, "failed to get pix_clk %d source\n", i);
+            return PTR_ERR(dc->pix_clk[i]);
+        }
+
+        snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pixclk", i);
+        dc->pixclk[i] = devm_clk_get_optional(dev, pixclk);
+        if (IS_ERR(dc->pixclk[i])) {
+            dev_err(dev, "failed to get pixclk %d source\n", i);
+            return PTR_ERR(dc->pixclk[i]);
+        }
+    }
+
+    dc->axi_clk = devm_clk_get_optional(dev, "axi_clk");
+    if (IS_ERR(dc->axi_clk)) {
+        dev_err(dev, "failed to get axi_clk source\n");
+        return PTR_ERR(dc->axi_clk);
+    }
+
+    dc->cfg_clk = devm_clk_get_optional(dev, "cfg_clk");
+    if (IS_ERR(dc->cfg_clk)) {
+        dev_err(dev, "failed to get cfg_clk source\n");
+        return PTR_ERR(dc->cfg_clk);
+    }
+
+    dc->dpu0pll_clk = devm_clk_get_optional(dev, "dpu0_pll_foutpostdiv");
+    if (IS_ERR(dc->dpu0pll_clk)) {
+        dev_err(dev, "failed to get dpu0pll_clk source\n");
+        return PTR_ERR(dc->dpu0pll_clk);
+    }
+
+    dc->dpu1pll_clk = devm_clk_get_optional(dev, "dpu1_pll_foutpostdiv");
+    if (IS_ERR(dc->dpu1pll_clk)) {
+        dev_err(dev, "failed to get dpu1pll_clk source\n");
+        return PTR_ERR(dc->dpu1pll_clk);
+    }
+
+    dc->def_pix_clk_rate[0] = clk_get_rate(dc->dpu0pll_clk)/1000;
+    dc->def_pix_clk_rate[1] = clk_get_rate(dc->dpu1pll_clk)/1000;
+
+    dc_get_display_pll(dev, dc);
+
+    dev_set_drvdata(dev, dc);
+    pm_runtime_enable(dev);
+
+    return component_add(dev, &dc_component_ops);
+}
+
+static int dc_remove(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+
+    component_del(dev, &dc_component_ops);
+
+    pm_runtime_disable(dev);
+
+    dev_set_drvdata(dev, NULL);
+
+    return 0;
+}
+
+#ifdef CONFIG_PM
+static int dc_runtime_suspend(struct device *dev)
+{
+       int i;
+       struct vs_dc *dc = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+       clk_disable_unprepare(dc->axi_clk);
+
+       for (i = 0; i < DC_DISPLAY_NUM; i++){
+               clk_disable_unprepare(dc->pix_clk[i]);
+        clk_disable_unprepare(dc->pixclk[i]);
+    }
+
+       clk_disable_unprepare(dc->core_clk);
+
+       clk_disable_unprepare(dc->cfg_clk);
+
+       if (dc->dpu0pll_on)
+               clk_disable_unprepare(dc->dpu0pll_clk);
+       if (dc->dpu1pll_on)
+               clk_disable_unprepare(dc->dpu1pll_clk);
+
+       return 0;
+}
+
+static int dc_runtime_resume(struct device *dev)
+{
+       int i, ret;
+       struct vs_dc *dc = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+       if (dc->dpu0pll_on)
+               clk_prepare_enable(dc->dpu0pll_clk);
+       if (dc->dpu1pll_on)
+               clk_prepare_enable(dc->dpu1pll_clk);
+
+       ret = clk_prepare_enable(dc->cfg_clk);
+       if (ret < 0) {
+               dev_err(dev, "failed to prepare/enable cfg_clk\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(dc->core_clk);
+       if (ret < 0) {
+               dev_err(dev, "failed to prepare/enable core_clk\n");
+               return ret;
+       }
+
+       for (i = 0; i < DC_DISPLAY_NUM; i++) {
+               ret = clk_prepare_enable(dc->pix_clk[i]);
+        if (ret < 0) {
+                       dev_err(dev, "failed to prepare/enable pix_clk %d\n", i);
+                       return ret;
+               }
+
+        ret = clk_prepare_enable(dc->pixclk[i]);
+               if (ret < 0) {
+                       dev_err(dev, "failed to prepare/enable pixclk %d\n", i);
+                       return ret;
+               }
+       }
+
+       ret = clk_prepare_enable(dc->axi_clk);
+       if (ret < 0) {
+               dev_err(dev, "failed to prepare/enable axi_clk\n");
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+#ifdef CONFIG_PM_SLEEP
+
+static int dc_suspend(struct device *dev)
+{
+    int i;
+       struct vs_dc *dc = dev_get_drvdata(dev);
+
+    for (i = 0; i < DC_DISPLAY_NUM; i++){
+        dc->pix_clk_rate[i] = dc->def_pix_clk_rate[i];
+        clk_set_rate(dc->pixclk[i], dc->pix_clk_rate[i] * 1000);
+    }
+
+    return 0;
+}
+
+static int dc_resume(struct device *dev)
+{
+    int i, ret;
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    dev_info(dev,"dc resume\n");
+    regmap_write(dc->hw.vosys_regmap,0x4,0x7);  //release dpu reset
+
+    ret = dc_hw_init(&dc->hw);
+    if (ret)
+        dev_err(dev, "failed to init DC HW\n");
+    return 0;
+}
+
+#endif
+static const struct dev_pm_ops dc_pm_ops = {
+    SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                pm_runtime_force_resume)
+    SET_RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL)
+    #ifdef CONFIG_PM_SLEEP
+        SET_LATE_SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume)
+    #endif
+};
+
+struct platform_driver dc_platform_driver = {
+    .probe = dc_probe,
+    .remove = dc_remove,
+    .driver = {
+        .name = "vs-dc",
+        .of_match_table = of_match_ptr(dc_driver_dt_match),
+        .pm = &dc_pm_ops,
+    },
+};
+
+MODULE_DESCRIPTION("VeriSilicon DC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
new file mode 100644 (file)
index 0000000..95cf957
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_H__
+#define __VS_DC_H__
+
+#include <linux/version.h>
+#include <linux/mm_types.h>
+
+#include <drm/drm_modes.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+
+#include "vs_plane.h"
+#include "vs_crtc.h"
+#include "vs_dc_hw.h"
+#include "vs_dc_dec.h"
+#ifdef CONFIG_VERISILICON_MMU
+#include "vs_dc_mmu.h"
+#endif
+
+struct vs_dc_funcs {
+    void (*dump_enable)(struct device *dev, dma_addr_t addr,
+                unsigned int pitch);
+    void (*dump_disable)(struct device *dev);
+};
+
+struct vs_dc_plane {
+    enum dc_hw_plane_id id;
+};
+
+struct vs_dc {
+    struct vs_crtc      *crtc[DC_DISPLAY_NUM];
+    struct dc_hw        hw;
+#ifdef CONFIG_VERISILICON_DEC
+    struct dc_dec400l   dec400l;
+#endif
+
+    struct clk      *core_clk;
+    struct clk      *pix_clk[DC_DISPLAY_NUM];
+    struct clk      *pixclk[DC_DISPLAY_NUM];
+    struct clk      *axi_clk;
+    struct clk      *cfg_clk;
+    struct clk      *dpu0pll_clk;
+    struct clk      *dpu1pll_clk;
+
+    unsigned int    pix_clk_rate[DC_DISPLAY_NUM]; /* in KHz */
+    unsigned int    def_pix_clk_rate[DC_DISPLAY_NUM];
+
+    bool            first_frame;
+    bool            dpu0pll_on;
+    bool            dpu1pll_on;
+
+    struct vs_dc_plane planes[PLANE_NUM];
+
+    const struct vs_dc_funcs *funcs;
+};
+
+extern struct platform_driver dc_platform_driver;
+#endif /* __VS_DC_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_dec.c b/drivers/gpu/drm/verisilicon/vs_dc_dec.c
new file mode 100644 (file)
index 0000000..08a5cd7
--- /dev/null
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+
+#include "vs_dc_dec.h"
+
+#define fourcc_mod_vs_get_tile_mode(val) (u8)((val) & \
+                DRM_FORMAT_MOD_VS_DEC_TILE_MODE_MASK)
+
+static inline bool _is_stream_changed(struct dc_dec400l *dec400l, u8 stream_id)
+{
+    return dec400l->stream[stream_id].dirty;
+}
+
+static inline bool _is_stream_valid(struct dc_dec400l *dec400l, u8 stream_id)
+{
+    return !!(dec400l->stream[stream_id].main_base_addr);
+}
+
+static void _enable_stream(struct dc_dec400l *dec400l, struct dc_hw *hw,
+                           u8 stream_id)
+{
+    if (!(dec400l->stream_status & (1 << stream_id))) {
+        if (!(dec400l->stream_status))
+            /* the first enabled steram */
+            dc_hw_dec_init(hw);
+
+        dec400l->stream_status |= 1 << stream_id;
+    }
+}
+static void _disable_stream(struct dc_dec400l *dec400l, struct dc_hw *hw,
+                            u8 stream_id)
+{
+    if ((dec400l->stream_status & (1 << stream_id)))
+        dec400l->stream_status &= ~(1 << stream_id);
+}
+
+static u16 get_dec_tile_size(u8 tile_mode, u8 cpp)
+{
+    u16 multi = 0;
+
+    switch (tile_mode) {
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_16X1:
+        multi = 16;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X4:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_4X8:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_32X1:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X4_S:
+        multi = 32;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_YMAJOR:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_16X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_16X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_32X2:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_16X4_S:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_16X4_LSB:
+        multi = 64;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_32X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X16:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_32X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_64X2:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_32X4_S:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_32X4_LSB:
+        multi = 128;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_64X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_64X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_128X2:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_16X16:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
+        multi = 256;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_256X2:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_128X4:
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_512X1:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_128X4:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_32X16:
+        multi = 512;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_256X4:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_64X16:
+    case DRM_FORMAT_MOD_VS_DEC_TILE_128X8:
+        multi = 1024;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_512X4:
+        multi = 2048;
+        break;
+    default:
+        break;
+    }
+
+    return multi * cpp;
+}
+
+static void update_fb_format(struct dc_dec_fb *dec_fb)
+{
+    struct drm_framebuffer *drm_fb = dec_fb->fb;
+    u8 tile_mod = fourcc_mod_vs_get_tile_mode(drm_fb->modifier);
+    u8 norm_mod = DRM_FORMAT_MOD_VS_LINEAR;
+
+    switch (tile_mod) {
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_32X1:
+        norm_mod = DRM_FORMAT_MOD_VS_TILE_32X1;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
+        norm_mod = DRM_FORMAT_MOD_VS_TILE_64X1;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+        norm_mod = DRM_FORMAT_MOD_VS_TILE_128X1;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+        norm_mod = DRM_FORMAT_MOD_VS_TILE_256X1;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X4:
+        norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_4X8:
+        norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
+        if (drm_fb->format->format == DRM_FORMAT_YUYV ||
+            drm_fb->format->format == DRM_FORMAT_UYVY ||
+            drm_fb->format->format == DRM_FORMAT_P010)
+            norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
+        else
+            norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+        if (drm_fb->format->format == DRM_FORMAT_NV12 ||
+            drm_fb->format->format == DRM_FORMAT_P010)
+            norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
+        break;
+    case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
+        if (drm_fb->format->format == DRM_FORMAT_NV12)
+            norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
+        break;
+    default:
+        break;
+    }
+
+    drm_fb->modifier = fourcc_mod_vs_norm_code(norm_mod);
+}
+
+static void _stream_config(struct dc_dec_fb *dec_fb,
+                           struct dc_dec_stream *stream, u8 index)
+{
+    struct drm_framebuffer *drm_fb = dec_fb->fb;
+    const struct drm_format_info *info = drm_fb->format;
+    u32 plane_height = drm_format_info_plane_height(info,
+                           drm_fb->height, index);
+    u16 tile_size;
+
+    stream->main_base_addr = dec_fb->addr[index];
+    stream->tile_mode = fourcc_mod_vs_get_tile_mode(drm_fb->modifier);
+    if (drm_fb->modifier & DRM_FORMAT_MOD_VS_DEC_ALIGN_32)
+        stream->align_mode = DEC_ALIGN_32;
+    else
+        stream->align_mode = DEC_ALIGN_64;
+
+    switch (drm_fb->format->format) {
+    case DRM_FORMAT_ARGB8888:
+        stream->format = DEC_FORMAT_ARGB8;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_XRGB8888:
+        stream->format = DEC_FORMAT_XRGB8;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_RGB565:
+        stream->format = DEC_FORMAT_R5G6B5;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_ARGB1555:
+        stream->format = DEC_FORMAT_A1RGB5;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_XRGB1555:
+        stream->format = DEC_FORMAT_X1RGB5;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_ARGB4444:
+        stream->format = DEC_FORMAT_ARGB4;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_XRGB4444:
+        stream->format = DEC_FORMAT_XRGB4;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_ARGB2101010:
+        stream->format = DEC_FORMAT_A2R10G10B10;
+        stream->depth = DEC_DEPTH_10;
+        break;
+    case DRM_FORMAT_YUYV:
+        stream->format = DEC_FORMAT_YUY2;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_UYVY:
+        stream->format = DEC_FORMAT_UYVY;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_YVU420:
+        stream->format = DEC_FORMAT_YUV_ONLY;
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_NV12:
+        WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_32X8 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_16X8);
+        if (index) {
+            stream->format = DEC_FORMAT_UV_MIX;
+            switch (stream->tile_mode) {
+            case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_RASTER_128X1;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_TILE_32X4;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_TILE_16X4;
+                break;
+            default:
+                break;
+            }
+        } else {
+            stream->format = DEC_FORMAT_YUV_ONLY;
+        }
+
+        stream->depth = DEC_DEPTH_8;
+        break;
+    case DRM_FORMAT_P010:
+        WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_64X1 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_16X8 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR);
+        if (index) {
+            stream->format = DEC_FORMAT_UV_MIX;
+            switch (stream->tile_mode) {
+            case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_RASTER_32X1;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_TILE_16X4;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_TILE_8X4;
+                break;
+            default:
+                break;
+            }
+        } else {
+            stream->format = DEC_FORMAT_YUV_ONLY;
+        }
+
+        stream->depth = DEC_DEPTH_10;
+        break;
+    case DRM_FORMAT_NV16:
+        WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 &&
+                stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1);
+        if (index) {
+            stream->format = DEC_FORMAT_UV_MIX;
+            switch (stream->tile_mode) {
+            case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_RASTER_128X1;
+                break;
+            case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
+                stream->tile_mode =
+                    DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
+                break;
+            default:
+                break;
+            }
+        } else {
+            stream->format = DEC_FORMAT_YUV_ONLY;
+        }
+
+        stream->depth = DEC_DEPTH_8;
+        break;
+    }
+
+    tile_size = get_dec_tile_size(stream->tile_mode, info->cpp[index]);
+    stream->aligned_stride = ALIGN(dec_fb->stride[index], tile_size);
+    stream->ts_base_addr = stream->main_base_addr +
+                           stream->aligned_stride * plane_height;
+}
+
+static int _dec400l_config(struct dc_dec400l *dec400l,
+                          struct dc_dec_fb *dec_fb, u8 stream_base)
+{
+    struct dc_dec_stream stream;
+    u8 i, stream_id, num_planes = 0;
+
+    if (dec_fb) {
+        const struct drm_format_info *info = dec_fb->fb->format;
+
+        num_planes = info->num_planes;
+    }
+
+    for (i = 0; i < STREAM_COUNT; i ++) {
+        stream_id = stream_base + i;
+
+        memset(&dec400l->stream[stream_id], 0, sizeof(struct dc_dec_stream));
+
+        if (i < num_planes) {
+            memset(&stream, 0, sizeof(struct dc_dec_stream));
+            _stream_config(dec_fb, &stream, i);
+            memcpy(&dec400l->stream[stream_id], &stream,
+                   sizeof(struct dc_dec_stream));
+        }
+        dec400l->stream[stream_id].dirty = true;
+    }
+
+    if (dec_fb)
+        update_fb_format(dec_fb);
+
+    return 0;
+}
+
+int dc_dec_config(struct dc_dec400l *dec400l, struct dc_dec_fb *dec_fb,
+                  u8 stream_base)
+{
+    if (dec_fb && !dec_fb->fb)
+        return -EINVAL;
+
+    if (dec_fb && (fourcc_mod_vs_get_type(dec_fb->fb->modifier) !=
+                   DRM_FORMAT_MOD_VS_TYPE_COMPRESSED))
+        _dec400l_config(dec400l, NULL, stream_base);
+    else
+        _dec400l_config(dec400l, dec_fb, stream_base);
+
+    return 0;
+}
+
+int dc_dec_commit(struct dc_dec400l *dec400l, struct dc_hw *hw)
+{
+    u8 i;
+
+    for (i = 0; i < STREAM_TOTAL; i++) {
+        if (!_is_stream_changed(dec400l, i))
+            continue;
+
+        if (_is_stream_valid(dec400l, i)) {
+            _enable_stream(dec400l, hw, i);
+            dc_hw_dec_stream_set(hw, dec400l->stream[i].main_base_addr,
+                dec400l->stream[i].ts_base_addr, dec400l->stream[i].tile_mode,
+                dec400l->stream[i].align_mode, dec400l->stream[i].format,
+                dec400l->stream[i].depth, i);
+        } else {
+            dc_hw_dec_stream_disable(hw, i);
+            _disable_stream(dec400l, hw, i);
+        }
+
+        dec400l->stream[i].dirty = false;
+    }
+
+    return 0;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_dec.h b/drivers/gpu/drm/verisilicon/vs_dc_dec.h
new file mode 100644 (file)
index 0000000..2cb3fc7
--- /dev/null
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef _VS_DC_DEC_H_
+#define _VS_DC_DEC_H_
+
+#include <drm/drm_fourcc.h>
+
+#include "vs_dc_hw.h"
+
+#define fourcc_mod_vs_get_type(val) \
+            (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+
+/* DEC_READ_CONFIG */
+#define COMPRESSION_EN              BIT(0)
+#define COMPRESSION_FORMAT_MASK     GENMASK(7, 3)
+#define COMPRESSION_ALIGN_MODE_MASK GENMASK(17, 16)
+#define TILE_MODE_MASK              GENMASK(30, 25)
+
+/* DEC_READ_EX_CONFIG */
+#define BIT_DEPTH_MASK          GENMASK(18, 16)
+
+/* DEC_CONTROL */
+#define FLUSH_ENABLE            BIT(0)
+#define COMPRESSION_DISABLE     BIT(1)
+
+/* DEC_CONTROL_EX */
+#define WRITE_MISS_POLICY1      1
+#define WRITE_MISS_POLICY_MASK  BIT(19)
+#define READ_MISS_POLICY0       0
+#define READ_MISS_POLICY_MASK   BIT(29)
+
+/* DEC_CONTROL_EX2 */
+#define TILE_STATUS_READ_ID         16
+#define TILE_STATUS_READ_ID_MASK    GENMASK(6, 0)
+#define TILE_STATUS_READ_ID_H       0
+#define TILE_STATUS_READ_ID_H_MASK  GENMASK(23, 22)
+#define DISABLE_HW_DEC_FLUSH        BIT(28)
+
+#define STREAM_COUNT       3
+#define STREAM_TOTAL       32
+
+#define DEC_PLANE_MAX      3
+
+enum dc_dec_align_mode {
+    DEC_ALIGN_32  = 0x02,
+    DEC_ALIGN_64,
+};
+
+enum dc_dec_format {
+    DEC_FORMAT_ARGB8,
+    DEC_FORMAT_XRGB8,
+    DEC_FORMAT_AYUV,
+    DEC_FORMAT_UYVY,
+    DEC_FORMAT_YUY2,
+    DEC_FORMAT_YUV_ONLY,
+    DEC_FORMAT_UV_MIX,
+    DEC_FORMAT_ARGB4,
+    DEC_FORMAT_XRGB4,
+    DEC_FORMAT_A1RGB5,
+    DEC_FORMAT_X1RGB5,
+    DEC_FORMAT_R5G6B5,
+    DEC_FORMAT_A2R10G10B10 = 0x0F,
+    DEC_FORMAT_BAYER,
+    DEC_FORMAT_COEFFICIENT = 0x12,
+    DEC_FORMAT_ARGB16,
+    DEC_FORMAT_X2RGB10 = 0x15,
+};
+
+enum dc_dec_depth {
+    DEC_DEPTH_8,
+    DEC_DEPTH_10,
+    DEC_DEPTH_12,
+    DEC_DEPTH_14,
+    DEC_DEPTH_16,
+};
+
+struct dc_dec_stream {
+    u32  main_base_addr;
+    u32  aligned_stride;
+    u32  ts_base_addr;
+    u8   tile_mode;
+    u8   align_mode;
+    u8   format;
+    u8   depth;
+    bool dirty;
+};
+
+struct dc_dec_fb {
+    struct drm_framebuffer *fb;
+    u32 addr[DEC_PLANE_MAX];
+    u32 stride[DEC_PLANE_MAX];
+};
+
+struct dc_dec400l {
+    struct dc_dec_stream stream[STREAM_TOTAL];
+    u32 stream_status;
+};
+
+int dc_dec_config(struct dc_dec400l *dec400l, struct dc_dec_fb *dec_fb,
+                  u8 stream_base);
+int dc_dec_commit(struct dc_dec400l *dec400l, struct dc_hw *hw);
+
+#endif /* _VS_DC_DEC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c
new file mode 100644 (file)
index 0000000..cf813b0
--- /dev/null
@@ -0,0 +1,2286 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/io.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/vs_drm.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "vs_type.h"
+#include "vs_dc_hw.h"
+#include "vs_dc_dec.h"
+
+static const u32 horKernel[] = {
+    0x00000000, 0x20000000, 0x00002000, 0x00000000,
+    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+    0x00000000, 0x00000000, 0x00000000, 0x01470000,
+    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00004000, 0x00000000,
+    0x00000000, 0x00000000, 0x20002000, 0x00000000,
+    0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
+    0x00000000, 0x00000000, 0x00000000, 0x27e1181f,
+    0x00000000, 0x00000000, 0x00000000, 0x14680000,
+    0x00002b98, 0x00000000, 0x00000000, 0x00000000,
+    0x2f1010f0, 0x00000000, 0x00000000, 0x00000000,
+    0x0dc70000, 0x00003239, 0x00000000, 0x00000000,
+    0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
+    0x00000000, 0x087f0000, 0x00003781, 0x00000000,
+    0x00000000, 0x00000000, 0x399a0666, 0x00000000,
+    0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
+    0x00000000, 0x00000000, 0x00000000, 0x3cc4033c,
+    0x00000000, 0x00000000, 0x00000000, 0x021f0000,
+};
+#define H_COEF_SIZE (sizeof(horKernel) / sizeof(u32))
+
+static const u32 verKernel[] = {
+    0x00000000, 0x20000000, 0x00002000, 0x00000000,
+    0x00000000, 0x00000000, 0x23fd1c03, 0x00000000,
+    0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+    0x00000000, 0x00000000, 0x00000000, 0x2b981468,
+    0x00000000, 0x00000000, 0x00000000, 0x10f00000,
+    0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+    0x32390dc7, 0x00000000, 0x00000000, 0x00000000,
+    0x0af50000, 0x0000350b, 0x00000000, 0x00000000,
+    0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+    0x00000000, 0x06660000, 0x0000399a, 0x00000000,
+    0x00000000, 0x00000000, 0x3b5904a7, 0x00000000,
+    0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+    0x00000000, 0x00000000, 0x00000000, 0x3de1021f,
+    0x00000000, 0x00000000, 0x00000000, 0x01470000,
+    0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+    0x3f5300ad, 0x00000000, 0x00000000, 0x00000000,
+    0x00480000, 0x00003fb8, 0x00000000, 0x00000000,
+    0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00004000, 0x00000000,
+    0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab,
+    0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
+    0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+    0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+};
+#define V_COEF_SIZE (sizeof(verKernel) / sizeof(u32))
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = {
+    10279,  5395,   709,
+    1132,   15065,  187,
+    269,    1442,   14674
+};
+
+/*
+ * YUV601 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+    1196,    0,       1640,    1196,
+    -404,   -836,     1196,    2076,
+    0,      -916224,  558336, -1202944,
+    64,      940,     64,      960
+};
+
+/*
+ * YUV709 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+    1196,    0,       1844,     1196,
+    -220,   -548,     1196,     2172,
+    0,      -1020672, 316672,  -1188608,
+    64,      940,     64,       960
+};
+
+/*
+ * YUV2020 to RGB conversion parameters
+ * YUV2RGB[0]  - [8] : C0 - C8;
+ * YUV2RGB[9]  - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+    1196,    0,       1724,     1196,
+    -192,   -668,     1196,     2200,
+    0,      -959232,  363776,  -1202944,
+    64,     940,      64,       960
+};
+
+/*
+ * RGB to YUV2020 conversion parameters
+ * RGB2YUV[0] - [8] : C0 - C8;
+ * RGB2YUV[9] - [11]: D0 - D2;
+ */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = {
+    230,    594,    52,
+    -125,  -323,    448,
+    448,   -412,   -36,
+    64,     512,    512
+};
+
+/*
+ * Degamma table for 709 color space data.
+ */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005,
+    0x0007, 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024,
+    0x002a, 0x0030, 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064,
+    0x006f, 0x007b, 0x0087, 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce,
+    0x00de, 0x00ef, 0x0101, 0x0114, 0x0127, 0x013b, 0x0150, 0x0166,
+    0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de, 0x01f9, 0x0214, 0x0230,
+    0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec, 0x030e, 0x0331,
+    0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441, 0x046c,
+    0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
+    0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d,
+    0x07d9, 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999,
+    0x09dd, 0x0a23, 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb,
+    0x0c28, 0x0c76, 0x0cc6, 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65,
+    0x0ebb, 0x0f13, 0x0f6c, 0x0fc6, 0x1021, 0x107d, 0x10db, 0x113a,
+    0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325, 0x138c, 0x13f3, 0x145b,
+    0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8, 0x175a, 0x17cc,
+    0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11, 0x1b8e,
+    0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
+    0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e,
+    0x24a1, 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf,
+    0x296e, 0x2a0e, 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea,
+    0x2e93, 0x2f3e, 0x2feb, 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f,
+    0x3414, 0x34ca, 0x3582, 0x363c, 0x36f7, 0x37b3, 0x3871, 0x3930,
+    0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02, 0x3dcb, 0x3e94, 0x3f5f,
+    0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442, 0x4517, 0x45ee,
+    0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb, 0x4cde,
+    0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
+    0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9,
+    0x5ce7, 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406,
+    0x6510, 0x661d, 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a,
+    0x6da2, 0x6ebb, 0x6fd6, 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576,
+    0x769b, 0x77c2, 0x78ea, 0x7a14, 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd,
+    0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+
+/*
+ * Degamma table for 2020 color space data.
+ */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+    0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
+    0x0003, 0x0003, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006,
+    0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 0x0009, 0x000a,
+    0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f, 0x000f,
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
+    0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024,
+    0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035,
+    0x0038, 0x003a, 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d,
+    0x0050, 0x0054, 0x0057, 0x005b, 0x005f, 0x0064, 0x0068, 0x006d,
+    0x0071, 0x0076, 0x007c, 0x0081, 0x0086, 0x008c, 0x0092, 0x0098,
+    0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb, 0x00c3, 0x00cb, 0x00d3,
+    0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c, 0x0117, 0x0123,
+    0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e, 0x018d,
+    0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
+    0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6,
+    0x02f2, 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1,
+    0x03f5, 0x041b, 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d,
+    0x054e, 0x0580, 0x05b4, 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6,
+    0x0717, 0x075a, 0x079f, 0x07e7, 0x0831, 0x087e, 0x08cd, 0x0920,
+    0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec, 0x0b52, 0x0bbc, 0x0c2a,
+    0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15, 0x0fa1, 0x1033,
+    0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1, 0x1593,
+    0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
+    0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656,
+    0x27be, 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332,
+    0x3516, 0x370d, 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480,
+    0x470d, 0x49b3, 0x4c73, 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1,
+    0x5f55, 0x62eb, 0x66a6, 0x6a86, 0x6e8c, 0x72bb, 0x7714, 0x7b99,
+    0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+
+/* one is for primary plane and the other is for all overlay planes */
+static const struct dc_hw_plane_reg dc_plane_reg[] = {
+    {
+    .y_address      = DC_FRAMEBUFFER_ADDRESS,
+    .u_address      = DC_FRAMEBUFFER_U_ADDRESS,
+    .v_address      = DC_FRAMEBUFFER_V_ADDRESS,
+    .y_stride       = DC_FRAMEBUFFER_STRIDE,
+    .u_stride       = DC_FRAMEBUFFER_U_STRIDE,
+    .v_stride       = DC_FRAMEBUFFER_V_STRIDE,
+    .size           = DC_FRAMEBUFFER_SIZE,
+    .top_left       = DC_FRAMEBUFFER_TOP_LEFT,
+    .bottom_right   = DC_FRAMEBUFFER_BOTTOM_RIGHT,
+    .scale_factor_x      = DC_FRAMEBUFFER_SCALE_FACTOR_X,
+    .scale_factor_y      = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
+    .h_filter_coef_index = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
+    .h_filter_coef_data  = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
+    .v_filter_coef_index = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
+    .v_filter_coef_data  = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
+    .init_offset         = DC_FRAMEBUFFER_INIT_OFFSET,
+    .color_key           = DC_FRAMEBUFFER_COLOR_KEY,
+    .color_key_high      = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
+    .clear_value         = DC_FRAMEBUFFER_CLEAR_VALUE,
+    .color_table_index   = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
+    .color_table_data    = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
+    .scale_config        = DC_FRAMEBUFFER_SCALE_CONFIG,
+    .water_mark          = DC_FRAMEBUFFER_WATER_MARK,
+    .degamma_index       = DC_FRAMEBUFFER_DEGAMMA_INDEX,
+    .degamma_data        = DC_FRAMEBUFFER_DEGAMMA_DATA,
+    .degamma_ex_data     = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
+    .src_global_color    = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR,
+    .dst_global_color    = DC_FRAMEBUFFER_DST_GLOBAL_COLOR,
+    .blend_config        = DC_FRAMEBUFFER_BLEND_CONFIG,
+    .roi_origin          = DC_FRAMEBUFFER_ROI_ORIGIN,
+    .roi_size            = DC_FRAMEBUFFER_ROI_SIZE,
+    .YUVToRGBCoef0       = DC_FRAMEBUFFER_YUVTORGB_COEF0,
+    .YUVToRGBCoef1       = DC_FRAMEBUFFER_YUVTORGB_COEF1,
+    .YUVToRGBCoef2       = DC_FRAMEBUFFER_YUVTORGB_COEF2,
+    .YUVToRGBCoef3       = DC_FRAMEBUFFER_YUVTORGB_COEF3,
+    .YUVToRGBCoef4       = DC_FRAMEBUFFER_YUVTORGB_COEF4,
+    .YUVToRGBCoefD0      = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
+    .YUVToRGBCoefD1      = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
+    .YUVToRGBCoefD2      = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
+    .YClampBound         = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
+    .UVClampBound        = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
+    .RGBToRGBCoef0       = DC_FRAMEBUFFER_RGBTORGB_COEF0,
+    .RGBToRGBCoef1       = DC_FRAMEBUFFER_RGBTORGB_COEF1,
+    .RGBToRGBCoef2       = DC_FRAMEBUFFER_RGBTORGB_COEF2,
+    .RGBToRGBCoef3       = DC_FRAMEBUFFER_RGBTORGB_COEF3,
+    .RGBToRGBCoef4       = DC_FRAMEBUFFER_RGBTORGB_COEF4,
+    },
+    {
+    .y_address      = DC_OVERLAY_ADDRESS,
+    .u_address      = DC_OVERLAY_U_ADDRESS,
+    .v_address      = DC_OVERLAY_V_ADDRESS,
+    .y_stride       = DC_OVERLAY_STRIDE,
+    .u_stride       = DC_OVERLAY_U_STRIDE,
+    .v_stride       = DC_OVERLAY_V_STRIDE,
+    .size           = DC_OVERLAY_SIZE,
+    .top_left       = DC_OVERLAY_TOP_LEFT,
+    .bottom_right   = DC_OVERLAY_BOTTOM_RIGHT,
+    .scale_factor_x      = DC_OVERLAY_SCALE_FACTOR_X,
+    .scale_factor_y      = DC_OVERLAY_SCALE_FACTOR_Y,
+    .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
+    .h_filter_coef_data  = DC_OVERLAY_H_FILTER_COEF_DATA,
+    .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
+    .v_filter_coef_data  = DC_OVERLAY_V_FILTER_COEF_DATA,
+    .init_offset         = DC_OVERLAY_INIT_OFFSET,
+    .color_key           = DC_OVERLAY_COLOR_KEY,
+    .color_key_high      = DC_OVERLAY_COLOR_KEY_HIGH,
+    .clear_value         = DC_OVERLAY_CLEAR_VALUE,
+    .color_table_index   = DC_OVERLAY_COLOR_TABLE_INDEX,
+    .color_table_data    = DC_OVERLAY_COLOR_TABLE_DATA,
+    .scale_config        = DC_OVERLAY_SCALE_CONFIG,
+    .water_mark          = DC_OVERLAY_WATER_MARK,
+    .degamma_index       = DC_OVERLAY_DEGAMMA_INDEX,
+    .degamma_data        = DC_OVERLAY_DEGAMMA_DATA,
+    .degamma_ex_data     = DC_OVERLAY_DEGAMMA_EX_DATA,
+    .src_global_color    = DC_OVERLAY_SRC_GLOBAL_COLOR,
+    .dst_global_color    = DC_OVERLAY_DST_GLOBAL_COLOR,
+    .blend_config        = DC_OVERLAY_BLEND_CONFIG,
+    .roi_origin          = DC_OVERLAY_ROI_ORIGIN,
+    .roi_size            = DC_OVERLAY_ROI_SIZE,
+    .YUVToRGBCoef0       = DC_OVERLAY_YUVTORGB_COEF0,
+    .YUVToRGBCoef1       = DC_OVERLAY_YUVTORGB_COEF1,
+    .YUVToRGBCoef2       = DC_OVERLAY_YUVTORGB_COEF2,
+    .YUVToRGBCoef3       = DC_OVERLAY_YUVTORGB_COEF3,
+    .YUVToRGBCoef4       = DC_OVERLAY_YUVTORGB_COEF4,
+    .YUVToRGBCoefD0      = DC_OVERLAY_YUVTORGB_COEFD0,
+    .YUVToRGBCoefD1      = DC_OVERLAY_YUVTORGB_COEFD1,
+    .YUVToRGBCoefD2      = DC_OVERLAY_YUVTORGB_COEFD2,
+    .YClampBound         = DC_OVERLAY_Y_CLAMP_BOUND,
+    .UVClampBound        = DC_OVERLAY_UV_CLAMP_BOUND,
+    .RGBToRGBCoef0       = DC_OVERLAY_RGBTORGB_COEF0,
+    .RGBToRGBCoef1       = DC_OVERLAY_RGBTORGB_COEF1,
+    .RGBToRGBCoef2       = DC_OVERLAY_RGBTORGB_COEF2,
+    .RGBToRGBCoef3       = DC_OVERLAY_RGBTORGB_COEF3,
+    .RGBToRGBCoef4       = DC_OVERLAY_RGBTORGB_COEF4,
+    },
+};
+
+#ifdef CONFIG_VERISILICON_MMU
+static const u32 mmu_reg_base = SE_MMU_REG_BASE;
+
+static const struct dc_hw_mmu_reg dc_mmu_reg = {
+    .mmu_config = SE_MMU_REG_CONFIG,
+    .mmu_control = SE_MMU_REG_CONTROL,
+    .table_array_size = SE_MMU_REG_TABLE_ARRAY_SIZE,
+    .safe_non_secure = SE_MMU_REG_SAFE_NON_SECUR,
+    .safe_secure = SE_MMU_REG_SAFE_SECURE,
+    .safe_ex = SE_MMU_REG_SAFE_EXT_ADDRESS,
+    .context_pd_entry = SE_MMU_REG_CONTEXT_PD,
+};
+#endif
+
+static const u32 primary_overlay_format0[] = {
+    DRM_FORMAT_XRGB4444,
+    DRM_FORMAT_XBGR4444,
+    DRM_FORMAT_RGBX4444,
+    DRM_FORMAT_BGRX4444,
+    DRM_FORMAT_ARGB4444,
+    DRM_FORMAT_ABGR4444,
+    DRM_FORMAT_RGBA4444,
+    DRM_FORMAT_BGRA4444,
+    DRM_FORMAT_XRGB1555,
+    DRM_FORMAT_XBGR1555,
+    DRM_FORMAT_RGBX5551,
+    DRM_FORMAT_BGRX5551,
+    DRM_FORMAT_ARGB1555,
+    DRM_FORMAT_ABGR1555,
+    DRM_FORMAT_RGBA5551,
+    DRM_FORMAT_BGRA5551,
+    DRM_FORMAT_RGB565,
+    DRM_FORMAT_BGR565,
+    DRM_FORMAT_XRGB8888,
+    DRM_FORMAT_XBGR8888,
+    DRM_FORMAT_RGBX8888,
+    DRM_FORMAT_BGRX8888,
+    DRM_FORMAT_ARGB8888,
+    DRM_FORMAT_ABGR8888,
+    DRM_FORMAT_RGBA8888,
+    DRM_FORMAT_BGRA8888,
+    DRM_FORMAT_ARGB2101010,
+    DRM_FORMAT_ABGR2101010,
+    DRM_FORMAT_RGBA1010102,
+    DRM_FORMAT_BGRA1010102,
+    DRM_FORMAT_YUYV,
+    DRM_FORMAT_YVYU,
+    DRM_FORMAT_UYVY,
+    DRM_FORMAT_VYUY,
+    DRM_FORMAT_YVU420,
+    DRM_FORMAT_YUV420,
+    DRM_FORMAT_NV12,
+    DRM_FORMAT_NV21,
+    DRM_FORMAT_NV16,
+    DRM_FORMAT_NV61,
+    DRM_FORMAT_P010,
+};
+
+static const u32 primary_overlay_format1[] = {
+    DRM_FORMAT_XRGB4444,
+    DRM_FORMAT_XBGR4444,
+    DRM_FORMAT_RGBX4444,
+    DRM_FORMAT_BGRX4444,
+    DRM_FORMAT_ARGB4444,
+    DRM_FORMAT_ABGR4444,
+    DRM_FORMAT_RGBA4444,
+    DRM_FORMAT_BGRA4444,
+    DRM_FORMAT_XRGB1555,
+    DRM_FORMAT_XBGR1555,
+    DRM_FORMAT_RGBX5551,
+    DRM_FORMAT_BGRX5551,
+    DRM_FORMAT_ARGB1555,
+    DRM_FORMAT_ABGR1555,
+    DRM_FORMAT_RGBA5551,
+    DRM_FORMAT_BGRA5551,
+    DRM_FORMAT_RGB565,
+    DRM_FORMAT_BGR565,
+    DRM_FORMAT_XRGB8888,
+    DRM_FORMAT_XBGR8888,
+    DRM_FORMAT_RGBX8888,
+    DRM_FORMAT_BGRX8888,
+    DRM_FORMAT_ARGB8888,
+    DRM_FORMAT_ABGR8888,
+    DRM_FORMAT_RGBA8888,
+    DRM_FORMAT_BGRA8888,
+    DRM_FORMAT_ARGB2101010,
+    DRM_FORMAT_ABGR2101010,
+    DRM_FORMAT_RGBA1010102,
+    DRM_FORMAT_BGRA1010102,
+    DRM_FORMAT_YUYV,
+    DRM_FORMAT_YVYU,
+    DRM_FORMAT_UYVY,
+    DRM_FORMAT_VYUY,
+    DRM_FORMAT_YVU420,
+    DRM_FORMAT_YUV420,
+    DRM_FORMAT_NV12,
+    DRM_FORMAT_NV21,
+    DRM_FORMAT_NV16,
+    DRM_FORMAT_NV61,
+    DRM_FORMAT_P010,
+    DRM_FORMAT_YUV444,
+};
+
+static const u32 cursor_formats[] = {
+    DRM_FORMAT_ARGB8888
+};
+
+static const u64 format_modifier0[] = {
+    DRM_FORMAT_MOD_LINEAR,
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8),
+#ifdef CONFIG_VERISILICON_DEC
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_8X4,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_4X8,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_256X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_128X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_64X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_16X8,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_32X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_TILE_32X8,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+#endif
+    DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 format_modifier1[] = {
+    DRM_FORMAT_MOD_LINEAR,
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_LINEAR),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X8),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_8X4),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8),
+    fourcc_mod_vs_norm_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4),
+    fourcc_mod_vs_custom_code(DRM_FORMAT_MOD_VS_TILE_MODE4X4),
+    DRM_FORMAT_MOD_INVALID
+};
+
+static const u64 secondary_format_modifiers[] = {
+    DRM_FORMAT_MOD_LINEAR,
+#ifdef CONFIG_VERISILICON_DEC
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_256X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_128X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_64X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+    fourcc_mod_vs_dec_code(DRM_FORMAT_MOD_VS_DEC_RASTER_32X1,
+                    DRM_FORMAT_MOD_VS_DEC_ALIGN_32),
+#endif
+    DRM_FORMAT_MOD_INVALID
+};
+
+#define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
+
+static const struct vs_plane_info dc_hw_planes[][PLANE_NUM] = {
+    {
+        /* DC_REV_0 */
+        {
+        .name        = "Primary",
+        .id          = PRIMARY_PLANE_0,
+        .type        = DRM_PLANE_TYPE_PRIMARY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 0,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay",
+        .id          = OVERLAY_PLANE_0,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 1,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_1",
+        .id          = OVERLAY_PLANE_1,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+        .modifiers   = secondary_format_modifiers,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = 0,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 2,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Primary_1",
+        .id          = PRIMARY_PLANE_1,
+        .type        = DRM_PLANE_TYPE_PRIMARY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 3,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_2",
+        .id          = OVERLAY_PLANE_2,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 4,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_3",
+        .id          = OVERLAY_PLANE_3,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+        .modifiers   = secondary_format_modifiers,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = 0,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 5,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Cursor",
+        .id          = CURSOR_PLANE_0,
+        .type        = DRM_PLANE_TYPE_CURSOR,
+        .num_formats = ARRAY_SIZE(cursor_formats),
+        .formats     = cursor_formats,
+        .num_modifiers = 0,
+        .modifiers   = NULL,
+        .min_width   = 32,
+        .min_height  = 32,
+        .max_width   = 64,
+        .max_height  = 64,
+        .rotation    = 0,
+        .degamma_size = 0,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 255,
+        .watermark   = false,
+        .color_mgmt  = false,
+        .roi         = false,
+        },
+        {
+        .name        = "Cursor_1",
+        .id          = CURSOR_PLANE_1,
+        .type        = DRM_PLANE_TYPE_CURSOR,
+        .num_formats = ARRAY_SIZE(cursor_formats),
+        .formats     = cursor_formats,
+        .num_modifiers = 0,
+        .modifiers   = NULL,
+        .min_width   = 32,
+        .min_height  = 32,
+        .max_width   = 64,
+        .max_height  = 64,
+        .rotation    = 0,
+        .degamma_size = 0,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 255,
+        .watermark   = false,
+        .color_mgmt  = false,
+        .roi         = false,
+        },
+    },
+    {
+        /* DC_REV_1 */
+        {
+        .name        = "Primary",
+        .id          = PRIMARY_PLANE_0,
+        .type        = DRM_PLANE_TYPE_PRIMARY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 0,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay",
+        .id          = OVERLAY_PLANE_0,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 1,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Primary_1",
+        .id          = PRIMARY_PLANE_1,
+        .type        = DRM_PLANE_TYPE_PRIMARY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 2,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_2",
+        .id          = OVERLAY_PLANE_2,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format0),
+        .formats     = primary_overlay_format0,
+        .num_modifiers = ARRAY_SIZE(format_modifier0),
+        .modifiers   = format_modifier0,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 3,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Cursor",
+        .id          = CURSOR_PLANE_0,
+        .type        = DRM_PLANE_TYPE_CURSOR,
+        .num_formats = ARRAY_SIZE(cursor_formats),
+        .formats     = cursor_formats,
+        .num_modifiers = 0,
+        .modifiers   = NULL,
+        .min_width   = 32,
+        .min_height  = 32,
+        .max_width   = 64,
+        .max_height  = 64,
+        .rotation    = 0,
+        .degamma_size = 0,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 255,
+        .watermark   = false,
+        .color_mgmt  = false,
+        .roi         = false,
+        },
+        {
+        .name        = "Cursor_1",
+        .id          = CURSOR_PLANE_1,
+        .type        = DRM_PLANE_TYPE_CURSOR,
+        .num_formats = ARRAY_SIZE(cursor_formats),
+        .formats     = cursor_formats,
+        .num_modifiers = 0,
+        .modifiers   = NULL,
+        .min_width   = 32,
+        .min_height  = 32,
+        .max_width   = 64,
+        .max_height  = 64,
+        .rotation    = 0,
+        .degamma_size = 0,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 255,
+        .watermark   = false,
+        .color_mgmt  = false,
+        .roi         = false,
+        },
+    },
+    {
+        /* DC_REV_2 */
+        {
+        .name        = "Primary",
+        .id          = PRIMARY_PLANE_0,
+        .type        = DRM_PLANE_TYPE_PRIMARY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format1),
+        .formats     = primary_overlay_format1,
+        .num_modifiers = ARRAY_SIZE(format_modifier1),
+        .modifiers   = format_modifier1,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 0,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay",
+        .id          = OVERLAY_PLANE_0,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format1),
+        .formats     = primary_overlay_format1,
+        .num_modifiers = ARRAY_SIZE(format_modifier1),
+        .modifiers   = format_modifier1,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 1,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_1",
+        .id          = OVERLAY_PLANE_1,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format1),
+        .formats     = primary_overlay_format1,
+        .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+        .modifiers   = secondary_format_modifiers,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = 0,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 2,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Primary_1",
+        .id          = PRIMARY_PLANE_1,
+        .type        = DRM_PLANE_TYPE_PRIMARY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format1),
+        .formats     = primary_overlay_format1,
+        .num_modifiers = ARRAY_SIZE(format_modifier1),
+        .modifiers   = format_modifier1,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 3,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_2",
+        .id          = OVERLAY_PLANE_2,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format1),
+        .formats     = primary_overlay_format1,
+        .num_modifiers = ARRAY_SIZE(format_modifier1),
+        .modifiers   = format_modifier1,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = DRM_MODE_ROTATE_0 |
+                       DRM_MODE_ROTATE_90 |
+                       DRM_MODE_ROTATE_180 |
+                       DRM_MODE_ROTATE_270 |
+                       DRM_MODE_REFLECT_X |
+                       DRM_MODE_REFLECT_Y,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = FRAC_16_16(1, 3),
+        .max_scale   = FRAC_16_16(10, 1),
+        .zpos        = 4,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Overlay_3",
+        .id          = OVERLAY_PLANE_3,
+        .type        = DRM_PLANE_TYPE_OVERLAY,
+        .num_formats = ARRAY_SIZE(primary_overlay_format1),
+        .formats     = primary_overlay_format1,
+        .num_modifiers = ARRAY_SIZE(secondary_format_modifiers),
+        .modifiers   = secondary_format_modifiers,
+        .min_width   = 0,
+        .min_height  = 0,
+        .max_width   = 4096,
+        .max_height  = 4096,
+        .rotation    = 0,
+        .blend_mode  = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                       BIT(DRM_MODE_BLEND_COVERAGE),
+        .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+                          BIT(DRM_COLOR_YCBCR_BT2020),
+        .degamma_size   = DEGAMMA_SIZE,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 5,
+        .watermark   = true,
+        .color_mgmt  = true,
+        .roi         = true,
+        },
+        {
+        .name        = "Cursor",
+        .id          = CURSOR_PLANE_0,
+        .type        = DRM_PLANE_TYPE_CURSOR,
+        .num_formats = ARRAY_SIZE(cursor_formats),
+        .formats     = cursor_formats,
+        .num_modifiers = 0,
+        .modifiers   = NULL,
+        .min_width   = 32,
+        .min_height  = 32,
+        .max_width   = 64,
+        .max_height  = 64,
+        .rotation    = 0,
+        .degamma_size = 0,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 255,
+        .watermark   = false,
+        .color_mgmt  = false,
+        .roi         = false,
+        },
+        {
+        .name        = "Cursor_1",
+        .id          = CURSOR_PLANE_1,
+        .type        = DRM_PLANE_TYPE_CURSOR,
+        .num_formats = ARRAY_SIZE(cursor_formats),
+        .formats     = cursor_formats,
+        .num_modifiers = 0,
+        .modifiers   = NULL,
+        .min_width   = 32,
+        .min_height  = 32,
+        .max_width   = 64,
+        .max_height  = 64,
+        .rotation    = 0,
+        .degamma_size = 0,
+        .min_scale   = DRM_PLANE_NO_SCALING,
+        .max_scale   = DRM_PLANE_NO_SCALING,
+        .zpos        = 255,
+        .watermark   = false,
+        .color_mgmt  = false,
+        .roi         = false,
+        },
+    },
+};
+
+static const struct vs_dc_info dc_info[] = {
+    {
+        /* DC_REV_0 */
+        .name           = "DC8200",
+        .panel_num      = 2,
+        .plane_num      = 8,
+        .planes         = dc_hw_planes[DC_REV_0],
+        .layer_num      = 6,
+        .max_bpc        = 10,
+        .color_formats  = DRM_COLOR_FORMAT_RGB444 |
+                          DRM_COLOR_FORMAT_YCBCR444 |
+                          DRM_COLOR_FORMAT_YCBCR422 |
+                          DRM_COLOR_FORMAT_YCBCR420,
+        .gamma_size     = GAMMA_EX_SIZE,
+        .gamma_bits     = 12,
+        .pitch_alignment    = 128,
+        .pipe_sync      = false,
+        .mmu_prefetch   = false,
+        .background     = true,
+        .panel_sync     = true,
+        .cap_dec        = true,
+    },
+    {
+        /* DC_REV_1 */
+        .name           = "DC8200",
+        .panel_num      = 2,
+        .plane_num      = 6,
+        .planes         = dc_hw_planes[DC_REV_1],
+        .layer_num      = 4,
+        .max_bpc        = 10,
+        .color_formats  = DRM_COLOR_FORMAT_RGB444 |
+                          DRM_COLOR_FORMAT_YCBCR444 |
+                          DRM_COLOR_FORMAT_YCBCR422 |
+                          DRM_COLOR_FORMAT_YCBCR420,
+        .gamma_size     = GAMMA_EX_SIZE,
+        .gamma_bits     = 12,
+        .pitch_alignment    = 128,
+        .pipe_sync      = false,
+        .mmu_prefetch   = false,
+        .background     = true,
+        .panel_sync     = true,
+        .cap_dec        = true,
+    },
+    {
+        /* DC_REV_2 */
+        .name           = "DC8200",
+        .panel_num      = 2,
+        .plane_num      = 8,
+        .planes         = dc_hw_planes[DC_REV_1],
+        .layer_num      = 6,
+        .max_bpc        = 10,
+        .color_formats  = DRM_COLOR_FORMAT_RGB444 |
+                          DRM_COLOR_FORMAT_YCBCR444 |
+                          DRM_COLOR_FORMAT_YCBCR422 |
+                          DRM_COLOR_FORMAT_YCBCR420,
+        .gamma_size     = GAMMA_EX_SIZE,
+        .gamma_bits     = 12,
+        .pitch_alignment    = 128,
+        .pipe_sync      = false,
+        .mmu_prefetch   = false,
+        .background     = true,
+        .panel_sync     = true,
+        .cap_dec        = false,
+    },
+};
+
+static const struct dc_hw_funcs hw_func;
+
+static inline u32 hi_read(struct dc_hw *hw, u32 reg)
+{
+    return readl(hw->hi_base + reg);
+}
+
+static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+    writel(value, hw->hi_base + reg);
+}
+
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+    writel(value, hw->reg_base + reg - DC_REG_BASE);
+}
+
+static inline u32 dc_read(struct dc_hw *hw, u32 reg)
+{
+    u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
+
+    return value;
+}
+
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+    u32 value = dc_read(hw, reg);
+
+    value &= ~clear;
+    value |= set;
+    dc_write(hw, reg, value);
+}
+
+static void load_default_filter(struct dc_hw *hw,
+                const struct dc_hw_plane_reg *reg, u32 offset)
+{
+    u8 i;
+
+    dc_write(hw, reg->scale_config + offset, 0x33);
+    dc_write(hw, reg->init_offset + offset, 0x80008000);
+    dc_write(hw, reg->h_filter_coef_index + offset, 0x00);
+    for (i = 0; i < H_COEF_SIZE; i++)
+        dc_write(hw, reg->h_filter_coef_data + offset, horKernel[i]);
+
+    dc_write(hw, reg->v_filter_coef_index + offset, 0x00);
+    for (i = 0; i < V_COEF_SIZE; i++)
+        dc_write(hw, reg->v_filter_coef_data + offset, verKernel[i]);
+}
+
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+                u32 offset, u16 *table)
+{
+    dc_write(hw, reg->RGBToRGBCoef0 + offset, table[0] | (table[1] << 16));
+    dc_write(hw, reg->RGBToRGBCoef1 + offset, table[2] | (table[3] << 16));
+    dc_write(hw, reg->RGBToRGBCoef2 + offset, table[4] | (table[5] << 16));
+    dc_write(hw, reg->RGBToRGBCoef3 + offset, table[6] | (table[7] << 16));
+    dc_write(hw, reg->RGBToRGBCoef4 + offset, table[8]);
+}
+
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+                            u32 offset, s32 *table)
+{
+    dc_write(hw, reg->YUVToRGBCoef0 + offset,
+             (0xFFFF & table[0]) | (table[1] << 16));
+    dc_write(hw, reg->YUVToRGBCoef1 + offset,
+             (0xFFFF & table[2]) | (table[3] << 16));
+    dc_write(hw, reg->YUVToRGBCoef2 + offset,
+             (0xFFFF & table[4]) | (table[5] << 16));
+    dc_write(hw, reg->YUVToRGBCoef3 + offset,
+             (0xFFFF & table[6]) | (table[7] << 16));
+    dc_write(hw, reg->YUVToRGBCoef4 + offset, table[8]);
+    dc_write(hw, reg->YUVToRGBCoefD0 + offset, table[9]);
+    dc_write(hw, reg->YUVToRGBCoefD1 + offset, table[10]);
+    dc_write(hw, reg->YUVToRGBCoefD2 + offset, table[11]);
+    dc_write(hw, reg->YClampBound + offset, table[12] | (table[13] << 16));
+    dc_write(hw, reg->UVClampBound + offset, table[14] | (table[15] << 16));
+}
+
+static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table)
+{
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset,
+             table[0] | (table[1] << 16));
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset,
+             table[2] | (table[3] << 16));
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset,
+             table[4] | (table[5] << 16));
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset,
+             table[6] | (table[7] << 16));
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]);
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]);
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]);
+    dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]);
+}
+
+static bool is_rgb(enum dc_hw_color_format format)
+{
+    switch (format) {
+    case FORMAT_X4R4G4B4:
+    case FORMAT_A4R4G4B4:
+    case FORMAT_X1R5G5B5:
+    case FORMAT_A1R5G5B5:
+    case FORMAT_R5G6B5:
+    case FORMAT_X8R8G8B8:
+    case FORMAT_A8R8G8B8:
+    case FORMAT_A2R10G10B10:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static void load_degamma_table(struct dc_hw *hw,
+                   const struct dc_hw_plane_reg *reg,
+                   u32 offset, u16 *table)
+{
+    u16 i;
+    u32 value;
+
+    dc_write(hw, reg->degamma_index + offset, 0);
+
+    for (i = 0; i < DEGAMMA_SIZE; i++) {
+        value = table[i] | (table[i] << 16);
+        dc_write(hw, reg->degamma_data + offset, value);
+        dc_write(hw, reg->degamma_ex_data + offset, table[i]);
+    }
+}
+
+static u32 get_addr_offset(u32 id)
+{
+    u32 offset = 0;
+
+    switch(id) {
+    case PRIMARY_PLANE_1:
+    case OVERLAY_PLANE_1:
+        offset = 0x04;
+        break;
+    case OVERLAY_PLANE_2:
+        offset = 0x08;
+        break;
+    case OVERLAY_PLANE_3:
+        offset = 0x0C;
+        break;
+    default:
+        break;
+    }
+
+    return offset;
+}
+
+int dc_hw_init(struct dc_hw *hw)
+{
+    u8 i, id, panel_num, layer_num;
+    u32 offset;
+    u32 revision = hi_read(hw, DC_HW_REVISION);
+    u32 cid = hi_read(hw, DC_HW_CHIP_CID);
+    const struct dc_hw_plane_reg *reg;
+
+    switch (revision) {
+    case 0x5720:
+        hw->rev = DC_REV_0;
+        break;
+    case 0x5721:
+        switch (cid) {
+        case 0x30B:
+            hw->rev = DC_REV_1;
+            break;
+        case 0x310:
+            hw->rev = DC_REV_2;
+            break;
+        default:
+            hw->rev = DC_REV_0;
+            break;
+        }
+        break;
+    default:
+        return -ENXIO;
+    }
+
+    hw->info = (struct vs_dc_info *)&dc_info[hw->rev];
+    hw->func = (struct dc_hw_funcs *)&hw_func;
+
+    layer_num = hw->info->layer_num;
+    for (i = 0; i < layer_num; i++) {
+        id = hw->info->planes[i].id;
+        offset = get_addr_offset(id);
+        if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+            reg = &dc_plane_reg[0];
+        else
+            reg = &dc_plane_reg[1];
+
+        load_default_filter(hw, reg, offset);
+        load_rgb_to_rgb(hw, reg, offset, RGB2RGB);
+    }
+
+    panel_num = hw->info->panel_num;
+    for (i = 0; i < panel_num; i++) {
+        offset = i << 2;
+        load_rgb_to_yuv(hw, offset, RGB2YUV);
+        dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111);
+
+        offset = i ? DC_CURSOR_OFFSET : 0;
+        dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF);
+        dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA);
+    }
+
+    dc_write(hw, DC_FRAMEBUFFER_WATER_MARK, 0x3000);
+    dc_write(hw, DC_FRAMEBUFFER_WATER_MARK + 0x4, 0x3000);
+    dc_write(hw, DC_OVERLAY_WATER_MARK, 0x3000);
+    dc_write(hw, DC_OVERLAY_WATER_MARK + 0x4, 0x3000);
+    dc_write(hw, DC_OVERLAY_WATER_MARK + 0x8, 0x3000);
+    dc_write(hw, DC_OVERLAY_WATER_MARK + 0xc, 0x3000);
+    dc_write(hw, DC_QOS_CONFIG, 0xc0);
+
+    dc_write(hw, DC_CLK_GATTING, 0x0);
+    dc_write(hw, DC_CLK_GATTING + 0x4, 0x0);
+
+    return 0;
+}
+
+void dc_hw_deinit(struct dc_hw *hw)
+{
+    /* Nothing to do */
+}
+
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+            struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+            struct dc_hw_position *pos, struct dc_hw_blend *blend)
+{
+    struct dc_hw_plane *plane = &hw->plane[id];
+
+    if (plane) {
+        if (fb) {
+            if (fb->enable == false)
+                plane->fb.enable = false;
+            else
+                memcpy(&plane->fb, fb,
+                       sizeof(*fb) - sizeof(fb->dirty));
+            plane->fb.dirty = true;
+        }
+        if (scale) {
+            memcpy(&plane->scale, scale,
+                   sizeof(*scale) - sizeof(scale->dirty));
+            plane->scale.dirty = true;
+        }
+        if (pos) {
+            memcpy(&plane->pos, pos,
+                   sizeof(*pos) - sizeof(pos->dirty));
+            plane->pos.dirty = true;
+        }
+        if (blend) {
+            memcpy(&plane->blend, blend,
+                   sizeof(*blend) - sizeof(blend->dirty));
+            plane->blend.dirty = true;
+        }
+    }
+}
+
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode)
+{
+    struct dc_hw_plane *plane = &hw->plane[id];
+
+    if (plane) {
+        if (hw->info->planes[id].degamma_size) {
+            plane->degamma.mode = mode;
+            plane->degamma.dirty = true;
+        } else {
+            plane->degamma.dirty = false;
+        }
+    }
+}
+
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi)
+{
+    struct dc_hw_plane *plane = &hw->plane[id];
+
+    if (plane) {
+        memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
+        plane->roi.dirty = true;
+    }
+}
+
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+                           struct dc_hw_colorkey *colorkey)
+{
+    struct dc_hw_plane *plane = &hw->plane[id];
+
+    if (plane) {
+        memcpy(&plane->colorkey, colorkey,
+               sizeof(*colorkey) - sizeof(colorkey->dirty));
+        plane->colorkey.dirty = true;
+    }
+}
+
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos)
+{
+    memcpy(&hw->qos, qos, sizeof(*qos) - sizeof(qos->dirty));
+    hw->qos.dirty = true;
+}
+
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor)
+{
+    memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty));
+    hw->cursor[id].dirty = true;
+}
+
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+                        u16 r, u16 g, u16 b)
+{
+    if (index >= hw->info->gamma_size)
+        return;
+
+    hw->gamma[id].gamma[index][0] = r;
+    hw->gamma[id].gamma[index][1] = g;
+    hw->gamma[id].gamma[index][2] = b;
+    hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable)
+{
+    hw->gamma[id].enable = enable;
+    hw->gamma[id].dirty = true;
+}
+
+void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch)
+{
+    dc_write(hw, 0x14F0, addr);
+    dc_write(hw, 0x14E8, addr);
+    dc_write(hw, 0x1500, pitch);
+    dc_write(hw, 0x14F8, 0x30000);
+}
+
+void dc_hw_disable_dump(struct dc_hw *hw)
+{
+    dc_write(hw, 0x14F8, 0x00);
+}
+
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+    u8 id = display->id;
+
+    memcpy(&hw->display[id], display, sizeof(*display));
+
+    hw->func->display(hw, display);
+}
+
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
+{
+    if (enable)
+        hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF);
+    else
+        hi_write(hw, AQ_INTR_ENBL, 0);
+}
+
+void dc_hw_enable_irq(struct dc_hw *hw, u32 irq, bool enable)
+{
+    u32 intr_enbl = hi_read(hw, AQ_INTR_ENBL);
+
+    intr_enbl = enable ? intr_enbl | BIT(irq) : intr_enbl & ~BIT(irq);
+
+    hi_write(hw, AQ_INTR_ENBL, intr_enbl);
+}
+
+u32 dc_hw_get_interrupt(struct dc_hw *hw)
+{
+    return hi_read(hw, AQ_INTR_ACKNOWLEDGE);
+}
+
+bool dc_hw_check_underflow(struct dc_hw *hw)
+{
+    return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
+{
+    u32 i, offset;
+    u8 id, layer_num = hw->info->layer_num;
+    u8 panel_num = hw->info->panel_num;
+
+    for (i = 0; i< layer_num; i++) {
+        id = hw->info->planes[i].id;
+        offset = get_addr_offset(id);
+        if (enable) {
+            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, BIT(12), 0);
+            else
+                dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, BIT(31), 0);
+        } else {
+            if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1)
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, 0, BIT(12));
+            else
+                dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, 0, BIT(31));
+        }
+    }
+
+    for (i = 0; i< panel_num; i++) {
+        offset = i << 2;
+        if (enable)
+            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, BIT(0));
+        else
+            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, BIT(0), 0);
+    }
+}
+
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id)
+{
+    if (out <= OUT_DP)
+        hw->out[id] = out;
+}
+
+static void gamma_ex_commit(struct dc_hw *hw)
+{
+    u8 panel_num = hw->info->panel_num;
+    u16 i, j;
+    u32 value;
+
+    for (j = 0; j < panel_num; j++) {
+        if (hw->gamma[j].dirty) {
+            if (hw->gamma[j].enable) {
+                dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00);
+                for (i = 0; i < GAMMA_EX_SIZE; i++) {
+                    value = hw->gamma[j].gamma[i][2] |
+                        (hw->gamma[j].gamma[i][1] << 12);
+                    dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value);
+                    dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2),
+                         hw->gamma[j].gamma[i][0]);
+                }
+                dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+                             BIT(13), 0);
+            } else {
+                dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2),
+                             0, BIT(13));
+            }
+            hw->gamma[j].dirty = false;
+        }
+    }
+}
+
+static void plane_commit(struct dc_hw *hw)
+{
+    struct dc_hw_plane *plane;
+    const struct dc_hw_plane_reg *reg;
+    bool primary = false;
+    u8 id, layer_num = hw->info->layer_num;
+    u32 i, offset;
+
+    for (i = 0; i < layer_num; i++) {
+        plane = &hw->plane[i];
+        id = hw->info->planes[i].id;
+        offset = get_addr_offset(id);
+        if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+            reg = &dc_plane_reg[0];
+            primary = true;
+        } else {
+            reg = &dc_plane_reg[1];
+            primary = false;
+        }
+
+        if (plane->fb.dirty) {
+            if (plane->fb.enable) {
+                dc_write(hw, reg->y_address + offset,
+                     plane->fb.y_address);
+                dc_write(hw, reg->u_address + offset,
+                     plane->fb.u_address);
+                dc_write(hw, reg->v_address + offset,
+                     plane->fb.v_address);
+                dc_write(hw, reg->y_stride + offset,
+                     plane->fb.y_stride);
+                dc_write(hw, reg->u_stride + offset,
+                     plane->fb.u_stride);
+                dc_write(hw, reg->v_stride + offset,
+                     plane->fb.v_stride);
+                dc_write(hw, reg->size + offset,
+                     plane->fb.width |
+                     (plane->fb.height << 15));
+
+                if (plane->fb.clear_enable)
+                    dc_write(hw, reg->clear_value + offset,
+                        plane->fb.clear_value);
+            }
+
+            if (primary) {
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+                         (plane->fb.format << 26) |
+                         (plane->fb.uv_swizzle << 25) |
+                         (plane->fb.swizzle << 23) |
+                         (plane->fb.tile_mode << 17) |
+                         (plane->fb.yuv_color_space << 14) |
+                         (plane->fb.rotation << 11) |
+                         (plane->fb.clear_enable << 8),
+                         (0x1F << 26) |
+                         BIT(25) |
+                         (0x03 << 23) |
+                         (0x1F << 17) |
+                         (0x07 << 14) |
+                         (0x07 << 11) |
+                         BIT(8));
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+                    (plane->fb.dec_enable << 1) |
+                    (plane->fb.enable << 13) | 
+                    (plane->fb.zpos << 16) |
+                    (plane->fb.display_id << 19),
+                    BIT(1) | BIT(13) |(0x07 << 16) | BIT(19));
+            } else {
+                dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+                         (plane->fb.dec_enable << 27) |
+                         (plane->fb.clear_enable << 25) |
+                         (plane->fb.enable << 24) |
+                         (plane->fb.format << 16) |
+                         (plane->fb.uv_swizzle << 15) |
+                         (plane->fb.swizzle << 13) |
+                         (plane->fb.tile_mode << 8) |
+                         (plane->fb.yuv_color_space << 5) |
+                         (plane->fb.rotation << 2),
+                         BIT(27) |
+                         BIT(25) |
+                         BIT(24) |
+                         (0x1F << 16) |
+                         BIT(15) |
+                         (0x03 << 13) |
+                         (0x1F << 8) |
+                         (0x07 << 5) |
+                         (0x07 << 2));
+                dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset,
+                         plane->fb.zpos | (plane->fb.display_id << 3),
+                         0x07| BIT(3));
+            }
+            plane->fb.dirty = false;
+        }
+
+        if (plane->scale.dirty) {
+            if (plane->scale.enable) {
+                dc_write(hw, reg->scale_factor_x + offset,
+                    plane->scale.scale_factor_x);
+                dc_write(hw, reg->scale_factor_y + offset,
+                    plane->scale.scale_factor_y);
+                if (primary)
+                    dc_set_clear(hw,
+                             DC_FRAMEBUFFER_CONFIG + offset,
+                             BIT(22), 0);
+                else
+                    dc_set_clear(hw,
+                             DC_OVERLAY_SCALE_CONFIG + offset,
+                             BIT(8), 0);
+            } else {
+                if (primary)
+                    dc_set_clear(hw,
+                             DC_FRAMEBUFFER_CONFIG + offset,
+                             0, BIT(22));
+                else
+                    dc_set_clear(hw,
+                             DC_OVERLAY_SCALE_CONFIG + offset,
+                             0, BIT(8));
+            }
+            plane->scale.dirty = false;
+        }
+
+        if (plane->pos.dirty) {
+            dc_write(hw, reg->top_left + offset,
+                plane->pos.start_x |
+                (plane->pos.start_y << 15));
+            dc_write(hw, reg->bottom_right + offset,
+                plane->pos.end_x |
+                (plane->pos.end_y << 15));
+            plane->pos.dirty = false;
+        }
+
+        if (plane->blend.dirty) {
+            dc_write(hw, reg->src_global_color + offset,
+                     plane->blend.alpha << 24);
+            dc_write(hw, reg->dst_global_color + offset,
+                     plane->blend.alpha << 24);
+            switch (plane->blend.blend_mode) {
+            case BLEND_PREMULTI:
+                dc_write(hw, reg->blend_config + offset, 0x3450);
+                break;
+            case BLEND_COVERAGE:
+                dc_write(hw, reg->blend_config + offset, 0x3950);
+                break;
+            case BLEND_PIXEL_NONE:
+                dc_write(hw, reg->blend_config + offset, 0x3548);
+                break;
+            default:
+                break;
+            }
+            plane->blend.dirty = false;
+        }
+
+        if (plane->colorkey.dirty) {
+            dc_write(hw, reg->color_key + offset, plane->colorkey.colorkey);
+            dc_write(hw, reg->color_key_high + offset,
+                     plane->colorkey.colorkey_high);
+
+            if (primary)
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset,
+                             plane->colorkey.transparency << 9, 0x03 << 9);
+            else
+                dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+                             plane->colorkey.transparency, 0x03);
+
+            plane->colorkey.dirty = false;
+        }
+
+        if (plane->roi.dirty) {
+            if (plane->roi.enable) {
+                dc_write(hw, reg->roi_origin + offset,
+                         plane->roi.x | (plane->roi.y << 16));
+                dc_write(hw, reg->roi_size + offset,
+                         plane->roi.width | (plane->roi.height << 16));
+                if (primary)
+                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+                                 BIT(0), 0);
+                else
+                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+                                 BIT(22), 0);
+            } else {
+                if (primary)
+                    dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset,
+                                 0, BIT(0));
+                else
+                    dc_set_clear(hw, DC_OVERLAY_CONFIG + offset,
+                                 0, BIT(22));
+            }
+            plane->roi.dirty = false;
+        }
+    }
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+    struct dc_hw_plane *plane;
+    const struct dc_hw_plane_reg *reg;
+    bool primary = false;
+    u8 id, layer_num = hw->info->layer_num;
+    u32 i, offset;
+
+    for (i = 0; i < layer_num; i++) {
+        plane = &hw->plane[i];
+        id = hw->info->planes[i].id;
+        offset = get_addr_offset(id);
+        if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) {
+            reg = &dc_plane_reg[0];
+            primary = true;
+        } else {
+            reg = &dc_plane_reg[1];
+            primary = false;
+        }
+
+        if (plane->fb.dirty) {
+            if (is_rgb(plane->fb.format)) {
+                if (primary)
+                    dc_set_clear(hw,
+                             DC_FRAMEBUFFER_CONFIG_EX + offset,
+                             0, BIT(8));
+                else
+                    dc_set_clear(hw,
+                             DC_OVERLAY_CONFIG + offset,
+                             0, BIT(30));
+            } else {
+                if (primary)
+                    dc_set_clear(hw,
+                             DC_FRAMEBUFFER_CONFIG_EX + offset,
+                             BIT(8), BIT(6));
+                else
+                    dc_set_clear(hw,
+                             DC_OVERLAY_CONFIG + offset,
+                             BIT(30), BIT(29));
+                switch (plane->fb.yuv_color_space) {
+                case COLOR_SPACE_601:
+                    load_yuv_to_rgb(hw, reg, offset, YUV601_2RGB);
+                    break;
+                case COLOR_SPACE_709:
+                    load_yuv_to_rgb(hw, reg, offset, YUV709_2RGB);
+                    break;
+                case COLOR_SPACE_2020:
+                    load_yuv_to_rgb(hw, reg, offset, YUV2020_2RGB);
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+        if (plane->degamma.dirty) {
+            switch (plane->degamma.mode) {
+            case VS_DEGAMMA_DISABLE:
+                if (primary)
+                    dc_set_clear(hw,
+                         DC_FRAMEBUFFER_CONFIG_EX + offset,
+                         0, BIT(5));
+                else
+                    dc_set_clear(hw,
+                         DC_OVERLAY_CONFIG + offset,
+                         0, BIT(28));
+                break;
+            case VS_DEGAMMA_BT709:
+                load_degamma_table(hw, reg, offset, DEGAMMA_709);
+                if (primary)
+                    dc_set_clear(hw,
+                         DC_FRAMEBUFFER_CONFIG_EX + offset,
+                         BIT(5), 0);
+                else
+                    dc_set_clear(hw,
+                         DC_OVERLAY_CONFIG + offset,
+                         BIT(28), 0);
+                break;
+            case VS_DEGAMMA_BT2020:
+                load_degamma_table(hw, reg, offset, DEGAMMA_2020);
+                if (primary)
+                    dc_set_clear(hw,
+                         DC_FRAMEBUFFER_CONFIG_EX + offset,
+                         BIT(5), 0);
+                else
+                    dc_set_clear(hw,
+                         DC_OVERLAY_CONFIG + offset,
+                         BIT(28), 0);
+                break;
+            default:
+                break;
+            }
+            plane->degamma.dirty = false;
+        }
+    }
+    plane_commit(hw);
+}
+
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+    u8 id = display->id;
+    u32 i, dpi_cfg, offset = id << 2;
+    u32 loffset, config, config_ex;
+
+    if (hw->display[id].enable) {
+        switch (display->bus_format) {
+        case MEDIA_BUS_FMT_RGB565_1X16:
+            dpi_cfg = 0;
+            break;
+        case MEDIA_BUS_FMT_RGB666_1X18:
+            dpi_cfg = 3;
+            break;
+        case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+            dpi_cfg = 4;
+            break;
+        case MEDIA_BUS_FMT_RGB888_1X24:
+            dpi_cfg = 5;
+            break;
+        case MEDIA_BUS_FMT_RGB101010_1X30:
+            dpi_cfg = 6;
+            break;
+        default:
+            dpi_cfg = 5;
+            break;
+        }
+        dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg);
+
+        if (id == 0)
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+        else
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+
+        dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active |
+                (hw->display[id].h_total << 16));
+        dc_write(hw, DC_DISPLAY_H_SYNC + offset,
+                hw->display[id].h_sync_start |
+                (hw->display[id].h_sync_end << 15) |
+                (hw->display[id].h_sync_polarity ? 0 : BIT(31)) |
+                BIT(30));
+        dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active |
+                (hw->display[id].v_total << 16));
+        dc_write(hw, DC_DISPLAY_V_SYNC + offset,
+                hw->display[id].v_sync_start |
+                (hw->display[id].v_sync_end << 15) |
+                (hw->display[id].v_sync_polarity ? 0 : BIT(31)) |
+                BIT(30));
+
+        if (hw->info->pipe_sync) {
+            switch (display->sync_mode) {
+            case VS_SINGLE_DC:
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+                         0, BIT(3) | BIT(4));
+                break;
+            case VS_MULTI_DC_PRIMARY:
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+                         BIT(3) | BIT(4), 0);
+                break;
+            case VS_MULTI_DC_SECONDARY:
+                dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+                         BIT(3), BIT(4));
+                break;
+            default:
+                break;
+            }
+        }
+
+        if (hw->info->background)
+            dc_write(hw, DC_FRAMEBUFFER_BG_COLOR + offset,
+                     hw->display[id].bg_color);
+
+        if (hw->display[id].dither_enable) {
+            dc_write(hw, DC_DISPLAY_DITHER_TABLE_LOW + offset,
+                     DC_DISPLAY_DITHERTABLE_LOW);
+            dc_write(hw, DC_DISPLAY_DITHER_TABLE_HIGH + offset,
+                     DC_DISPLAY_DITHERTABLE_HIGH);
+            dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, BIT(31));
+        } else {
+            dc_write(hw, DC_DISPLAY_DITHER_CONFIG + offset, 0);
+        }
+
+        dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(12), 0);
+        if (hw->display[id].sync_enable)
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(2) | BIT(3), 0);
+        else if (id == 0)
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(0), BIT(3));
+        else
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, BIT(1), BIT(3));
+    } else {
+       /* disable layers enabled for 'id' display which is required
+        * by DPU IP to avoid possible dma drop issue
+        */
+       for (i = 0; i < hw->info->layer_num; i++) {
+           loffset = get_addr_offset(i);
+
+           if (i == PRIMARY_PLANE_0 || i == PRIMARY_PLANE_1) {
+               config_ex = dc_read(hw, DC_FRAMEBUFFER_CONFIG_EX + loffset);
+
+               if (!!(config_ex & BIT(19)) == id && config_ex & BIT(13)) {
+                   dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + loffset, 0, BIT(13));
+                   dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + loffset, BIT(12), 0);
+               }
+           } else {
+               config_ex = dc_read(hw, DC_OVERLAY_CONFIG_EX + loffset);
+
+               if (!!(config_ex & BIT(3)) == id) {
+                   config = dc_read(hw, DC_OVERLAY_CONFIG + loffset);
+
+                   if (config & BIT(24)) {
+                       dc_set_clear(hw, DC_OVERLAY_CONFIG + loffset, 0, BIT(24));
+                       dc_set_clear(hw, DC_OVERLAY_CONFIG + loffset, BIT(31), 0);
+                   }
+               }
+           }
+       }
+       mdelay(20);
+
+        dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(12));
+        if (id == 0)
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(0) | BIT(2));
+        else
+            dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2));
+    }
+}
+
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
+{
+    u8 id = display->id;
+    u32 dp_cfg, offset = id << 2;
+    bool is_yuv = false;
+
+    if (hw->display[id].enable && hw->out[id] == OUT_DP) {
+        switch (display->bus_format) {
+        case MEDIA_BUS_FMT_RGB565_1X16:
+            dp_cfg = 0;
+            break;
+        case MEDIA_BUS_FMT_RGB666_1X18:
+            dp_cfg = 1;
+            break;
+        case MEDIA_BUS_FMT_RGB888_1X24:
+            dp_cfg = 2;
+            break;
+        case MEDIA_BUS_FMT_RGB101010_1X30:
+            dp_cfg = 3;
+            break;
+        case MEDIA_BUS_FMT_UYVY8_1X16:
+            dp_cfg = 2 << 4;
+            is_yuv = true;
+            break;
+        case MEDIA_BUS_FMT_YUV8_1X24:
+            dp_cfg = 4 << 4;
+            is_yuv = true;
+            break;
+        case MEDIA_BUS_FMT_UYVY10_1X20:
+            dp_cfg = 8 << 4;
+            is_yuv = true;
+            break;
+        case MEDIA_BUS_FMT_YUV10_1X30:
+            dp_cfg = 10 << 4;
+            is_yuv = true;
+            break;
+        case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+            dp_cfg = 12 << 4;
+            is_yuv = true;
+            break;
+        case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+            dp_cfg = 13 << 4;
+            is_yuv = true;
+            break;
+        default:
+            dp_cfg = 2;
+            break;
+        }
+        if (is_yuv)
+            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, BIT(16), 0);
+        else
+            dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, BIT(16));
+        dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | BIT(3));
+    }
+
+    if (hw->out[id] == OUT_DPI)
+        dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, BIT(3));
+
+    setup_display(hw, display);
+}
+
+static const struct dc_hw_funcs hw_func= {
+    .gamma = &gamma_ex_commit,
+    .plane = &plane_ex_commit,
+    .display = setup_display_ex,
+};
+
+void dc_hw_commit(struct dc_hw *hw)
+{
+    u32 i, offset = 0;
+    u8 plane_num = hw->info->plane_num;
+    u8 layer_num = hw->info->layer_num;
+    u8 cursor_num = plane_num - layer_num;
+
+    hw->func->gamma(hw);
+    hw->func->plane(hw);
+
+    for (i = 0; i < cursor_num; i++) {
+        if (hw->cursor[i].dirty) {
+            offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0;
+            if (hw->cursor[i].enable) {
+                dc_write(hw, DC_CURSOR_ADDRESS + offset,
+                         hw->cursor[i].address);
+                dc_write(hw, DC_CURSOR_LOCATION + offset, hw->cursor[i].x |
+                     (hw->cursor[i].y << 16));
+                dc_set_clear(hw, DC_CURSOR_CONFIG + offset,
+                         (hw->cursor[i].hot_x << 16) |
+                         (hw->cursor[i].hot_y << 8) |
+                         (hw->cursor[i].size << 5) |
+                         BIT(3) | BIT(2) | 0x02,
+                         (0xFF << 16) |
+                         (0xFF << 8) |
+                         (0x07 << 5) | 0x1F);
+            } else {
+                dc_set_clear(hw, DC_CURSOR_CONFIG + offset, BIT(3), 0x03);
+            }
+            hw->cursor[i].dirty = false;
+        }
+    }
+
+    if (hw->qos.dirty) {
+        dc_set_clear(hw, DC_QOS_CONFIG, (hw->qos.high_value << 4) |
+                     hw->qos.low_value, 0xFF);
+        hw->qos.dirty = false;
+    }
+}
+
+#ifdef CONFIG_VERISILICON_DEC
+void dc_hw_dec_init(struct dc_hw *hw)
+{
+    u32 config = 0;
+
+    config = DEC_CONTROL_RESET & (~COMPRESSION_DISABLE);
+    dc_write(hw, DEC_CONTROL, config | FLUSH_ENABLE);
+
+    config = DEC_CONTROL_EX2_RESET &
+             (~TILE_STATUS_READ_ID_MASK) &
+             (~TILE_STATUS_READ_ID_H_MASK) &
+             (~DISABLE_HW_DEC_FLUSH);
+    dc_write(hw, DEC_CONTROL_EX2,
+             config | (TILE_STATUS_READ_ID_H << 22) |
+             TILE_STATUS_READ_ID);
+
+    config = DEC_CONTROL_EX_RESET &
+             (~WRITE_MISS_POLICY_MASK) &
+             (~READ_MISS_POLICY_MASK);
+    dc_write(hw, DEC_CONTROL_EX, config | (WRITE_MISS_POLICY1 << 19));
+}
+
+void dc_hw_dec_stream_set(struct dc_hw *hw,u32  main_base_addr,
+                          u32 ts_base_addr, u8 tile_mode, u8 align_mode,
+                          u8 format, u8 depth, u8 stream_id)
+{
+    u32 offset = stream_id << 2;
+
+    dc_set_clear(hw, DEC_READ_CONFIG + offset,
+                 (tile_mode << 25) |
+                 (align_mode << 16) |
+                 (format << 3) |
+                 COMPRESSION_EN,
+                 TILE_MODE_MASK |
+                 COMPRESSION_ALIGN_MODE_MASK |
+                 COMPRESSION_FORMAT_MASK);
+
+    dc_set_clear(hw, DEC_READ_EX_CONFIG + offset,
+                 (depth << 16), BIT_DEPTH_MASK);
+
+    dc_write(hw, DEC_READ_BUFFER_BASE + offset, main_base_addr);
+    dc_write(hw, DEC_READ_BUFFER_END + offset, ts_base_addr - 128);
+    dc_write(hw, DEC_READ_CACHE_BASE + offset, ts_base_addr);
+}
+
+void dc_hw_dec_stream_disable(struct dc_hw *hw, u8 stream_id)
+{
+    u32 offset = stream_id << 2;
+
+    dc_write(hw, DEC_READ_CONFIG + offset, DEC_READ_CONFIG_RESET);
+    dc_write(hw, DEC_READ_BUFFER_BASE + offset, 0xFFFFFFFF);
+    dc_write(hw, DEC_READ_BUFFER_END + offset, 0xFFFFFFFF);
+}
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+static u32 mmu_read(struct dc_hw *hw, u32 reg)
+{
+    return readl(hw->mmu_base + reg - mmu_reg_base);
+}
+
+static void mmu_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+    writel(value, hw->mmu_base + reg - mmu_reg_base);
+}
+
+static void mmu_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+    u32 value = mmu_read(hw, reg);
+
+    value &= ~clear;
+    value |= set;
+    mmu_write(hw, reg, value);
+}
+
+int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu)
+{
+    const struct dc_hw_mmu_reg *reg;
+    u32 mtlb = 0, ext_mtlb = 0;
+    u32 safe_addr = 0, ext_safe_addr = 0;
+    u32 config = 0;
+
+    reg = &dc_mmu_reg;
+
+    mtlb = (u32)(mmu->mtlb_physical & 0xFFFFFFFF);
+    ext_mtlb = (u32)(mmu->mtlb_physical >> 32);
+
+    /* more than 40bit physical address */
+    if (ext_mtlb & 0xFFFFFF00) {
+        pr_err("Mtlb address out of range.\n");
+        return -EFAULT;
+    }
+
+    config = (ext_mtlb << 20) | (mtlb >> 12);
+    if (mmu->mode == MMU_MODE_1K)
+        mmu_set_clear(hw, reg->context_pd_entry,
+                  (config << 4) |BIT(0),
+                  (0xFFFFFFF << 4) | (0x07));
+    else
+        mmu_set_clear(hw, reg->context_pd_entry,
+                  (config << 4),
+                  (0xFFFFFFF << 4) | (0x07));
+
+    safe_addr = (u32)(mmu->safe_page_physical & 0xFFFFFFFF);
+    ext_safe_addr = (u32)(mmu->safe_page_physical >> 32);
+
+    if ((safe_addr & 0x3F) || (ext_safe_addr & 0xFFFFFF00)) {
+        pr_err("Invalid safe_address.\n");
+        return -EFAULT;
+    }
+
+    mmu_write(hw, reg->table_array_size, 1);
+    mmu_write(hw, reg->safe_secure, safe_addr);
+    mmu_write(hw, reg->safe_non_secure, safe_addr);
+
+    mmu_set_clear(hw, reg->safe_ex,
+              (ext_safe_addr << 16) |ext_safe_addr,
+              BIT(31) |(0xFF << 16) | BIT(15) | 0xFF);
+
+    /* mmu configuration for ree driver */
+    mmu_write(hw, reg->mmu_control, BIT(5) | BIT(0));
+
+    mmu_write(hw, SE_MMU_REG_INTR_ENBL, 0xFFFFFFFF);
+
+    return 0;
+}
+
+void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable)
+{
+    if (!hw->info->mmu_prefetch)
+        return;
+
+    if (enable)
+        dc_write(hw, DC_MMU_PREFETCH, BIT(0));
+    else
+        dc_write(hw, DC_MMU_PREFETCH, 0);
+}
+
+void dc_hw_mmu_flush(struct dc_hw *hw)
+{
+    const struct dc_hw_mmu_reg *reg = &dc_mmu_reg;
+    u32 value = mmu_read(hw, reg->mmu_config);
+
+    mmu_write(hw, reg->mmu_config, value | BIT(4));
+    mmu_write(hw, reg->mmu_config, value);
+}
+#endif
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h
new file mode 100644 (file)
index 0000000..37399f8
--- /dev/null
@@ -0,0 +1,583 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DC_HW_H__
+#define __VS_DC_HW_H__
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+#include "vs_dc_mmu.h"
+#endif
+
+#define AQ_INTR_ACKNOWLEDGE        0x0010
+#define AQ_INTR_ENBL               0x0014
+#define DC_HW_REVISION             0x0024
+#define DC_HW_CHIP_CID             0x0030
+
+#define DC_REG_BASE             0x0800
+#define DC_REG_RANGE            0x2000
+#define DC_SEC_REG_OFFSET       0x100000
+
+#define DC_FRAMEBUFFER_CONFIG           0x1518
+#define DC_FRAMEBUFFER_CONFIG_EX        0x1CC0
+#define DC_FRAMEBUFFER_SCALE_CONFIG     0x1520
+#define DC_FRAMEBUFFER_TOP_LEFT         0x24D8
+#define DC_FRAMEBUFFER_BOTTOM_RIGHT     0x24E0
+#define DC_FRAMEBUFFER_ADDRESS          0x1400
+#define DC_FRAMEBUFFER_U_ADDRESS        0x1530
+#define DC_FRAMEBUFFER_V_ADDRESS        0x1538
+#define DC_FRAMEBUFFER_STRIDE           0x1408
+#define DC_FRAMEBUFFER_U_STRIDE         0x1800
+#define DC_FRAMEBUFFER_V_STRIDE         0x1808
+#define DC_FRAMEBUFFER_SIZE             0x1810
+#define DC_FRAMEBUFFER_SCALE_FACTOR_X       0x1828
+#define DC_FRAMEBUFFER_SCALE_FACTOR_Y       0x1830
+#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX  0x1838
+#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA   0x1A00
+#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX  0x1A08
+#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA   0x1A10
+#define DC_FRAMEBUFFER_INIT_OFFSET          0x1A20
+#define DC_FRAMEBUFFER_COLOR_KEY            0x1508
+#define DC_FRAMEBUFFER_COLOR_KEY_HIGH       0x1510
+#define DC_FRAMEBUFFER_CLEAR_VALUE          0x1A18
+#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX    0x1818
+#define DC_FRAMEBUFFER_COLOR_TABLE_DATA     0x1820
+#define DC_FRAMEBUFFER_BG_COLOR             0x1528
+#define DC_FRAMEBUFFER_ROI_ORIGIN           0x1CB0
+#define DC_FRAMEBUFFER_ROI_SIZE             0x1CB8
+#define DC_FRAMEBUFFER_WATER_MARK           0x1CE8
+#define DC_FRAMEBUFFER_DEGAMMA_INDEX        0x1D88
+#define DC_FRAMEBUFFER_DEGAMMA_DATA         0x1D90
+#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA      0x1D98
+#define DC_FRAMEBUFFER_YUVTORGB_COEF0       0x1DA0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF1       0x1DA8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF2       0x1DB0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF3       0x1DB8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF4       0x1E00
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD0      0x1E08
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD1      0x1E10
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD2      0x1E18
+#define DC_FRAMEBUFFER_Y_CLAMP_BOUND        0x1E88
+#define DC_FRAMEBUFFER_UV_CLAMP_BOUND       0x1E90
+#define DC_FRAMEBUFFER_RGBTORGB_COEF0       0x1E20
+#define DC_FRAMEBUFFER_RGBTORGB_COEF1       0x1E28
+#define DC_FRAMEBUFFER_RGBTORGB_COEF2       0x1E30
+#define DC_FRAMEBUFFER_RGBTORGB_COEF3       0x1E38
+#define DC_FRAMEBUFFER_RGBTORGB_COEF4       0x1E40
+#define DC_FRAMEBUFFER_BLEND_CONFIG         0x2510
+#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR     0x2500
+#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR     0x2508
+
+#define DC_OVERLAY_CONFIG            0x1540
+#define DC_OVERLAY_CONFIG_EX         0x2540
+#define DC_OVERLAY_SCALE_CONFIG      0x1C00
+#define DC_OVERLAY_BLEND_CONFIG      0x1580
+#define DC_OVERLAY_TOP_LEFT          0x1640
+#define DC_OVERLAY_BOTTOM_RIGHT      0x1680
+#define DC_OVERLAY_ADDRESS           0x15C0
+#define DC_OVERLAY_U_ADDRESS         0x1840
+#define DC_OVERLAY_V_ADDRESS         0x1880
+#define DC_OVERLAY_STRIDE            0x1600
+#define DC_OVERLAY_U_STRIDE          0x18C0
+#define DC_OVERLAY_V_STRIDE          0x1900
+#define DC_OVERLAY_SIZE              0x17C0
+#define DC_OVERLAY_SCALE_FACTOR_X    0x1A40
+#define DC_OVERLAY_SCALE_FACTOR_Y    0x1A80
+#define DC_OVERLAY_H_FILTER_COEF_INDEX      0x1AC0
+#define DC_OVERLAY_H_FILTER_COEF_DATA       0x1B00
+#define DC_OVERLAY_V_FILTER_COEF_INDEX      0x1B40
+#define DC_OVERLAY_V_FILTER_COEF_DATA       0x1B80
+#define DC_OVERLAY_INIT_OFFSET              0x1BC0
+#define DC_OVERLAY_COLOR_KEY                0x1740
+#define DC_OVERLAY_COLOR_KEY_HIGH           0x1780
+#define DC_OVERLAY_CLEAR_VALUE              0x1940
+#define DC_OVERLAY_COLOR_TABLE_INDEX        0x1980
+#define DC_OVERLAY_COLOR_TABLE_DATA         0x19C0
+#define DC_OVERLAY_SRC_GLOBAL_COLOR         0x16C0
+#define DC_OVERLAY_DST_GLOBAL_COLOR         0x1700
+#define DC_OVERLAY_ROI_ORIGIN               0x1D00
+#define DC_OVERLAY_ROI_SIZE                 0x1D40
+#define DC_OVERLAY_WATER_MARK               0x1DC0
+#define DC_OVERLAY_DEGAMMA_INDEX            0x2200
+#define DC_OVERLAY_DEGAMMA_DATA             0x2240
+#define DC_OVERLAY_DEGAMMA_EX_DATA          0x2280
+#define DC_OVERLAY_YUVTORGB_COEF0           0x1EC0
+#define DC_OVERLAY_YUVTORGB_COEF1           0x1F00
+#define DC_OVERLAY_YUVTORGB_COEF2           0x1F40
+#define DC_OVERLAY_YUVTORGB_COEF3           0x1F80
+#define DC_OVERLAY_YUVTORGB_COEF4           0x1FC0
+#define DC_OVERLAY_YUVTORGB_COEFD0          0x2000
+#define DC_OVERLAY_YUVTORGB_COEFD1          0x2040
+#define DC_OVERLAY_YUVTORGB_COEFD2          0x2080
+#define DC_OVERLAY_Y_CLAMP_BOUND            0x22C0
+#define DC_OVERLAY_UV_CLAMP_BOUND           0x2300
+#define DC_OVERLAY_RGBTORGB_COEF0           0x20C0
+#define DC_OVERLAY_RGBTORGB_COEF1           0x2100
+#define DC_OVERLAY_RGBTORGB_COEF2           0x2140
+#define DC_OVERLAY_RGBTORGB_COEF3           0x2180
+#define DC_OVERLAY_RGBTORGB_COEF4           0x21C0
+
+#define DC_CURSOR_CONFIG             0x1468
+#define DC_CURSOR_ADDRESS            0x146C
+#define DC_CURSOR_LOCATION           0x1470
+#define DC_CURSOR_BACKGROUND         0x1474
+#define DC_CURSOR_FOREGROUND         0x1478
+#define DC_CURSOR_CLK_GATING         0x1484
+#define DC_CURSOR_CONFIG_EX          0x24E8
+#define DC_CURSOR_OFFSET             0x1080
+
+#define DC_DISPLAY_DITHER_CONFIG        0x1410
+#define DC_DISPLAY_PANEL_CONFIG         0x1418
+#define DC_DISPLAY_PANEL_CONFIG_EX      0x2518
+#define DC_DISPLAY_DITHER_TABLE_LOW     0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH    0x1428
+#define DC_DISPLAY_H                    0x1430
+#define DC_DISPLAY_H_SYNC               0x1438
+#define DC_DISPLAY_V                    0x1440
+#define DC_DISPLAY_V_SYNC               0x1448
+#define DC_DISPLAY_CURRENT_LOCATION     0x1450
+#define DC_DISPLAY_GAMMA_INDEX          0x1458
+#define DC_DISPLAY_GAMMA_DATA           0x1460
+#define DC_DISPLAY_INT                  0x147C
+#define DC_DISPLAY_INT_ENABLE           0x1480
+#define DC_DISPLAY_DBI_CONFIG           0x1488
+#define DC_DISPLAY_GENERAL_CONFIG       0x14B0
+#define DC_DISPLAY_DPI_CONFIG           0x14B8
+#define DC_DISPLAY_PANEL_START          0x1CCC
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT     0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE      0x14D8
+#define DC_DISPLAY_DP_CONFIG                0x1CD0
+#define DC_DISPLAY_GAMMA_EX_INDEX           0x1CF0
+#define DC_DISPLAY_GAMMA_EX_DATA            0x1CF8
+#define DC_DISPLAY_GAMMA_EX_ONE_DATA        0x1D80
+#define DC_DISPLAY_RGBTOYUV_COEF0           0x1E48
+#define DC_DISPLAY_RGBTOYUV_COEF1           0x1E50
+#define DC_DISPLAY_RGBTOYUV_COEF2           0x1E58
+#define DC_DISPLAY_RGBTOYUV_COEF3           0x1E60
+#define DC_DISPLAY_RGBTOYUV_COEF4           0x1E68
+#define DC_DISPLAY_RGBTOYUV_COEFD0          0x1E70
+#define DC_DISPLAY_RGBTOYUV_COEFD1          0x1E78
+#define DC_DISPLAY_RGBTOYUV_COEFD2          0x1E80
+
+#define DC_CLK_GATTING              0x1A28
+#define DC_QOS_CONFIG               0x1A38
+
+#define DC_TRANSPARENCY_OPAQUE       0x00
+#define DC_TRANSPARENCY_KEY          0x02
+#define DC_DISPLAY_DITHERTABLE_LOW   0x7B48F3C0
+#define DC_DISPLAY_DITHERTABLE_HIGH  0x596AD1E2
+
+#define GAMMA_SIZE              256
+#define GAMMA_EX_SIZE               300
+#define DEGAMMA_SIZE                260
+
+#define RGB_TO_RGB_TABLE_SIZE           9
+#define YUV_TO_RGB_TABLE_SIZE           16
+#define RGB_TO_YUV_TABLE_SIZE           12
+
+#ifdef CONFIG_VERISILICON_DEC
+/* DEC400 register */
+#define DEC_CONTROL               0x0800
+#define DEC_CONTROL_EX            0x0804
+#define DEC_CONTROL_EX2           0x0808
+
+#define DEC_READ_CONFIG           0x0880
+#define DEC_READ_EX_CONFIG        0x0900
+#define DEC_READ_BUFFER_BASE      0x0A80
+#define DEC_READ_BUFFER_END       0x0B80
+#define DEC_READ_CACHE_BASE       0x1080
+
+#define DEC_CONTROL_RESET         0x0301018A
+#define DEC_CONTROL_EX_RESET      0x00080000
+#define DEC_CONTROL_EX2_RESET     0x103FC810
+#define DEC_READ_CONFIG_RESET     0x00020000
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+#define DC_MMU_PREFETCH           0x1E98
+
+#define MMU_REG_BASE              0x0180
+#define MMU_REG_RANGE             0x700
+
+#define MMU_REG_CONFIG            0x0184
+#define MMU_REG_CONTROL           0x0388
+#define MMU_REG_TABLE_ARRAY_SIZE  0x0394
+#define MMU_REG_SAFE_NON_SECURE   0x0398
+#define MMU_REG_SAFE_SECURE       0x039C
+#define MMU_REG_SAFE_EXT_ADDRESS  0x03A0
+#define MMU_REG_CONTEXT_PD        0x03B4
+
+#define DEC_REG_CONTROL           0x0800
+#define DEC_REG_CONTROL_VALUE     0x02010188
+
+#define SE_MMU_REG_BASE           0x10010
+#define SE_MMU_REG_RANGE          0x60
+
+#define SE_MMU_REG_CONFIG            0x10010
+#define SE_MMU_REG_CONTROL           0x10024
+#define SE_MMU_REG_TABLE_ARRAY_SIZE  0x10030
+#define SE_MMU_REG_SAFE_NON_SECUR    0x10034
+#define SE_MMU_REG_SAFE_SECURE       0x10038
+#define SE_MMU_REG_SAFE_EXT_ADDRESS  0x1003C
+#define SE_MMU_REG_CONTEXT_PD        0x10040
+#define SE_MMU_REG_INTR_ENBL         0x10044
+#endif
+
+#define DC_LAYER_NUM    6
+#define DC_DISPLAY_NUM  2
+#define DC_CURSOR_NUM   2
+
+enum dc_chip_rev {
+    DC_REV_0,    /* For HW_REV_5720;
+                  * HW_REV_5721_311 */
+    DC_REV_1,    /* For HW_REV_5721_30B */
+    DC_REV_2,    /* For HW_REV_5721_310 */
+};
+
+enum dc_hw_plane_id {
+    PRIMARY_PLANE_0,
+    OVERLAY_PLANE_0,
+    OVERLAY_PLANE_1,
+    PRIMARY_PLANE_1,
+    OVERLAY_PLANE_2,
+    OVERLAY_PLANE_3,
+    CURSOR_PLANE_0,
+    CURSOR_PLANE_1,
+    PLANE_NUM
+};
+
+enum dc_hw_color_format {
+    FORMAT_X4R4G4B4,
+    FORMAT_A4R4G4B4,
+    FORMAT_X1R5G5B5,
+    FORMAT_A1R5G5B5,
+    FORMAT_R5G6B5,
+    FORMAT_X8R8G8B8,
+    FORMAT_A8R8G8B8,
+    FORMAT_YUY2,
+    FORMAT_UYVY,
+    FORMAT_INDEX8,
+    FORMAT_MONOCHROME,
+    FORMAT_YV12 = 0xf,
+    FORMAT_A8,
+    FORMAT_NV12,
+    FORMAT_NV16,
+    FORMAT_RG16,
+    FORMAT_R8,
+    FORMAT_NV12_10BIT,
+    FORMAT_A2R10G10B10,
+    FORMAT_NV16_10BIT,
+    FORMAT_INDEX1,
+    FORMAT_INDEX2,
+    FORMAT_INDEX4,
+    FORMAT_P010,
+    FORMAT_YUV444,
+    FORMAT_YUV444_10BIT,
+};
+
+enum dc_hw_yuv_color_space {
+    COLOR_SPACE_601 = 0,
+    COLOR_SPACE_709 = 1,
+    COLOR_SPACE_2020 = 3,
+};
+
+enum dc_hw_rotation {
+    ROT_0 = 0,
+    ROT_90 = 4,
+    ROT_180 = 5,
+    ROT_270 = 6,
+    FLIP_X = 1,
+    FLIP_Y = 2,
+    FLIP_XY = 3,
+};
+
+enum dc_hw_swizzle {
+    SWIZZLE_ARGB = 0,
+    SWIZZLE_RGBA,
+    SWIZZLE_ABGR,
+    SWIZZLE_BGRA,
+};
+
+enum dc_hw_out {
+    OUT_DPI,
+    OUT_DP,
+};
+
+enum dc_hw_cursor_size {
+    CURSOR_SIZE_32X32 = 0,
+    CURSOR_SIZE_64X64,
+};
+
+enum dc_hw_blend_mode {
+    /* out.rgb = plane_alpha * fg.rgb +
+     *      (1 - (plane_alpha * fg.alpha)) * bg.rgb
+     */
+    BLEND_PREMULTI,
+    /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
+     *      (1 - (plane_alpha * fg.alpha)) * bg.rgb
+     */
+    BLEND_COVERAGE,
+    /* out.rgb = plane_alpha * fg.rgb +
+     *      (1 - plane_alpha) * bg.rgb
+     */
+    BLEND_PIXEL_NONE,
+};
+
+struct dc_hw_plane_reg {
+    u32 y_address;
+    u32 u_address;
+    u32 v_address;
+    u32 y_stride;
+    u32 u_stride;
+    u32 v_stride;
+    u32 size;
+    u32 top_left;
+    u32 bottom_right;
+    u32 scale_factor_x;
+    u32 scale_factor_y;
+    u32 h_filter_coef_index;
+    u32 h_filter_coef_data;
+    u32 v_filter_coef_index;
+    u32 v_filter_coef_data;
+    u32 init_offset;
+    u32 color_key;
+    u32 color_key_high;
+    u32 clear_value;
+    u32 color_table_index;
+    u32 color_table_data;
+    u32 scale_config;
+    u32 water_mark;
+    u32 degamma_index;
+    u32 degamma_data;
+    u32 degamma_ex_data;
+    u32 src_global_color;
+    u32 dst_global_color;
+    u32 blend_config;
+    u32 roi_origin;
+    u32 roi_size;
+    u32 YUVToRGBCoef0;
+    u32 YUVToRGBCoef1;
+    u32 YUVToRGBCoef2;
+    u32 YUVToRGBCoef3;
+    u32 YUVToRGBCoef4;
+    u32 YUVToRGBCoefD0;
+    u32 YUVToRGBCoefD1;
+    u32 YUVToRGBCoefD2;
+    u32 YClampBound;
+    u32 UVClampBound;
+    u32 RGBToRGBCoef0;
+    u32 RGBToRGBCoef1;
+    u32 RGBToRGBCoef2;
+    u32 RGBToRGBCoef3;
+    u32 RGBToRGBCoef4;
+};
+
+#ifdef CONFIG_VERISILICON_MMU
+struct dc_hw_mmu_reg {
+    u32 mmu_config;
+    u32 mmu_control;
+    u32 table_array_size;
+    u32 safe_non_secure;
+    u32 safe_secure;
+    u32 safe_ex;
+    u32 context_pd_entry;
+};
+#endif
+
+struct dc_hw_fb {
+    u32 y_address;
+    u32 u_address;
+    u32 v_address;
+    u32 clear_value;
+    u32 water_mark;
+    u16 y_stride;
+    u16 u_stride;
+    u16 v_stride;
+    u16 width;
+    u16 height;
+    u8  format;
+    u8  tile_mode;
+    u8  rotation;
+    u8  yuv_color_space;
+    u8  swizzle;
+    u8  uv_swizzle;
+    u8  zpos;
+    u8  display_id;
+    bool    clear_enable;
+    bool    dec_enable;
+    bool    enable;
+    bool    dirty;
+};
+
+struct dc_hw_scale {
+    u32 scale_factor_x;
+    u32 scale_factor_y;
+    bool    enable;
+    bool    dirty;
+};
+
+struct dc_hw_position {
+    u16 start_x;
+    u16 start_y;
+    u16 end_x;
+    u16 end_y;
+    bool    dirty;
+};
+
+struct dc_hw_blend {
+    u8  alpha;
+    u8  blend_mode;
+    bool    dirty;
+};
+
+struct dc_hw_colorkey {
+    u32 colorkey;
+    u32 colorkey_high;
+    u8  transparency;
+    bool dirty;
+};
+
+struct dc_hw_roi {
+    u16 x;
+    u16 y;
+    u16 width;
+    u16 height;
+    bool enable;
+    bool dirty;
+};
+
+struct dc_hw_cursor {
+    u32 address;
+    u16 x;
+    u16 y;
+    u16 hot_x;
+    u16 hot_y;
+    u8  size;
+    u8  display_id;
+    bool    enable;
+    bool    dirty;
+};
+
+struct dc_hw_display {
+    u32 bus_format;
+    u16 h_active;
+    u16 h_total;
+    u16 h_sync_start;
+    u16 h_sync_end;
+    u16 v_active;
+    u16 v_total;
+    u16 v_sync_start;
+    u16 v_sync_end;
+    u16 sync_mode;
+    u32 bg_color;
+    u8  id;
+    bool    h_sync_polarity;
+    bool    v_sync_polarity;
+    bool    enable;
+    bool    sync_enable;
+    bool    dither_enable;
+};
+
+struct dc_hw_gamma {
+    u16 gamma[GAMMA_EX_SIZE][3];
+    bool    enable;
+    bool    dirty;
+};
+
+struct dc_hw_degamma {
+    u16 degamma[DEGAMMA_SIZE][3];
+    u32 mode;
+    bool    dirty;
+};
+
+struct dc_hw_plane {
+    struct dc_hw_fb         fb;
+    struct dc_hw_position   pos;
+    struct dc_hw_scale      scale;
+    struct dc_hw_blend      blend;
+    struct dc_hw_roi        roi;
+    struct dc_hw_colorkey   colorkey;
+    struct dc_hw_degamma    degamma;
+};
+
+struct dc_hw_qos {
+    u8    low_value;
+    u8    high_value;
+    bool  dirty;
+};
+
+struct dc_hw_read {
+    u32         reg;
+    u32         value;
+};
+
+struct dc_hw;
+struct dc_hw_funcs {
+    void (*gamma)(struct dc_hw *hw);
+    void (*plane)(struct dc_hw *hw);
+    void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+
+struct dc_hw {
+    enum dc_chip_rev rev;
+    enum dc_hw_out      out[DC_DISPLAY_NUM];
+    void            *hi_base;
+    void            *reg_base;
+#ifdef CONFIG_VERISILICON_MMU
+    void            *mmu_base;
+#endif
+    struct dc_hw_display display[DC_DISPLAY_NUM];
+    struct dc_hw_gamma   gamma[DC_DISPLAY_NUM];
+    struct dc_hw_plane   plane[DC_LAYER_NUM];
+    struct dc_hw_cursor  cursor[DC_CURSOR_NUM];
+    struct dc_hw_qos     qos;
+    struct dc_hw_funcs   *func;
+    struct vs_dc_info    *info;
+    struct regmap *vosys_regmap;
+};
+
+int dc_hw_init(struct dc_hw *hw);
+void dc_hw_deinit(struct dc_hw *hw);
+void dc_hw_update_plane(struct dc_hw *hw, u8 id,
+            struct dc_hw_fb *fb, struct dc_hw_scale *scale,
+            struct dc_hw_position *pos, struct dc_hw_blend *blend);
+void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
+void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
+void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
+                           struct dc_hw_colorkey *colorkey);
+void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
+void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
+void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
+                        u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
+void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch);
+void dc_hw_disable_dump(struct dc_hw *hw);
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
+void dc_hw_enable_irq(struct dc_hw *hw, u32 irq, bool enable);
+u32 dc_hw_get_interrupt(struct dc_hw *hw);
+bool dc_hw_check_underflow(struct dc_hw *hw);
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
+void dc_hw_commit(struct dc_hw *hw);
+#ifdef CONFIG_VERISILICON_DEC
+void dc_hw_dec_init(struct dc_hw *hw);
+void dc_hw_dec_stream_set(struct dc_hw *hw,u32  main_base_addr,
+                          u32 ts_base_addr, u8 tile_mode, u8 align_mode,
+                          u8 format, u8 depth, u8 stream_id);
+void dc_hw_dec_stream_disable(struct dc_hw *hw, u8 stream_id);
+#endif
+
+#ifdef CONFIG_VERISILICON_MMU
+int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu);
+void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable);
+#endif
+
+#endif /* __VS_DC_HW_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_mmu.c b/drivers/gpu/drm/verisilicon/vs_dc_mmu.c
new file mode 100644 (file)
index 0000000..e1986cb
--- /dev/null
@@ -0,0 +1,724 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "vs_dc_mmu.h"
+
+static bool mmu_construct = false;
+
+int _allocate_memory(u32 bytes, void **memory)
+{
+    void *mem = NULL;
+
+    if (bytes == 0 || memory == NULL) {
+        pr_err("%s has invalid arguments.\n", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    if (bytes > PAGE_SIZE) {
+        mem = vmalloc(bytes);
+    }
+    else {
+        mem = kmalloc(bytes, GFP_KERNEL);
+    }
+
+    if (!mem) {
+        pr_err("%s out of memory.\n", __FUNCTION__);
+        return -ENOMEM;
+    }
+
+    memset((u8 *)mem, 0, bytes);
+    *memory = mem;
+
+    return 0;
+}
+
+static int _create_mutex(void **mutex)
+{
+    int ret =0;
+
+    if (mutex == NULL) {
+        return -EINVAL;
+    }
+
+    ret = _allocate_memory(sizeof(struct mutex), mutex);
+    if (ret)
+        return ret;
+
+    mutex_init(*(struct mutex **)mutex);
+
+    return 0;
+}
+
+static int _acquire_mutex(void *mutex, u32 timeout)
+{
+    if (mutex == NULL) {
+        pr_err("%s has invalid argument.\n", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    if (timeout == DC_INFINITE) {
+        mutex_lock(mutex);
+        return 0;
+    }
+
+    for (;;) {
+        /* Try to acquire the mutex. */
+        if (mutex_trylock(mutex)) {
+            /* Success. */
+            return 0;
+        }
+
+        if (timeout-- == 0) {
+            break;
+        }
+
+        /* Wait for 1 millisecond. */
+        udelay(1000);
+    }
+
+    return -ETIMEDOUT;
+}
+
+static int _release_mutex(void *mutex)
+{
+    if (mutex == NULL) {
+        pr_err("%s has invalid argument.\n", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    mutex_unlock(mutex);
+
+    return 0;
+}
+
+static u32 _mtlb_offset(u32 address)
+{
+    return (address & MMU_MTLB_MASK) >> MMU_MTLB_SHIFT;
+}
+
+static u32 _stlb_offset(u32 address)
+{
+    return (address & MMU_STLB_4K_MASK) >> MMU_STLB_4K_SHIFT;
+}
+
+static u32 _address_to_index(dc_mmu_pt mmu, u32 address)
+{
+    return _mtlb_offset(address) * MMU_STLB_4K_ENTRY_NUM + _stlb_offset(address);
+}
+
+static u32 _set_page(u32 page_address, u32 page_address_ext, bool writable)
+{
+    u32 entry = page_address
+        /* AddressExt */
+        | (page_address_ext << 4)
+        /* Ignore exception */
+        | (0 << 1)
+        /* Present */
+        | (1 << 0);
+
+    if (writable) {
+        /* writable */
+        entry |= (1 << 2);
+    }
+
+    return entry;
+}
+
+static void _write_page_entry(u32 *page_entry, u32 entry_value)
+{
+    *page_entry = entry_value;
+}
+
+static u32 _read_page_entry(u32 *page_entry)
+{
+    return *page_entry;
+}
+
+int _allocate_stlb(dc_mmu_stlb_pt *stlb)
+{
+    dc_mmu_stlb_pt stlb_t = NULL;
+    void *mem = NULL;
+
+    mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
+    if (!mem)
+        return -ENOMEM;
+
+    stlb_t = (dc_mmu_stlb_pt)mem;
+
+    stlb_t->size = MMU_STLB_4K_SIZE;
+
+    *stlb = stlb_t;
+
+    return 0;
+}
+
+int _allocate_all_stlb(struct device *dev, dc_mmu_stlb_pt *stlb)
+{
+    dc_mmu_stlb_pt stlb_t = NULL;
+    void *mem = NULL;
+    void *cookie = NULL;
+    dma_addr_t dma_addr;
+    size_t size;
+
+    mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
+    if (!mem)
+        return -ENOMEM;
+
+    stlb_t = (dc_mmu_stlb_pt)mem;
+
+    stlb_t->size = MMU_STLB_4K_SIZE * MMU_MTLB_ENTRY_NUM;
+    size = PAGE_ALIGN(stlb_t->size);
+
+    cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
+    if (!cookie) {
+        dev_err(dev, "Failed to alloc stlb buffer.\n");
+        return -ENOMEM;
+    }
+
+    stlb_t->logical = cookie;
+    stlb_t->physBase = (u64)dma_addr;
+    memset(stlb_t->logical, 0, size);
+
+    *stlb = stlb_t;
+
+    return 0;
+}
+
+int _setup_process_address_space(struct device *dev, dc_mmu_pt mmu)
+{
+    u32 *map = NULL;
+    u32 free, i;
+    u32 dynamic_mapping_entries, address;
+    dc_mmu_stlb_pt all_stlb;
+    int ret =0;
+
+    dynamic_mapping_entries = MMU_MTLB_ENTRY_NUM;
+    mmu->dynamic_mapping_start = 0;
+    mmu->page_table_size = dynamic_mapping_entries * MMU_STLB_4K_SIZE;
+
+    mmu->page_table_entries = mmu->page_table_size / sizeof(u32);
+
+    ret = _allocate_memory(mmu->page_table_size,
+            (void **)&mmu->map_logical);
+    if (ret) {
+        pr_err("Failed to alloc mmu map buffer.\n");
+        return ret;;
+    }
+
+    map = mmu->map_logical;
+
+    /* Initialize free area*/
+    free = mmu->page_table_entries;
+    _write_page_entry(map, (free << 8) | DC_MMU_FREE);
+    _write_page_entry(map + 1, ~0U);
+
+    mmu->heap_list  = 0;
+    mmu->free_nodes = false;
+
+    ret = _allocate_all_stlb(dev, &all_stlb);
+    if (ret)
+        return ret;
+
+    for (i = 0; i < dynamic_mapping_entries; i++) {
+        dc_mmu_stlb_pt stlb;
+        dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
+
+        ret = _allocate_stlb(&stlb);
+        if (ret) 
+            return ret;
+
+        stlb->physBase = all_stlb->physBase + i * MMU_STLB_4K_SIZE;
+        stlb->logical = all_stlb->logical + i * MMU_STLB_4K_SIZE / sizeof(u32);
+
+        stlbs[i] = stlb;
+    }
+
+    address = (u32)all_stlb->physBase;
+
+    ret = _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+    if (ret)
+        return ret;
+
+    for (i = mmu->dynamic_mapping_start;
+         i < mmu->dynamic_mapping_start + dynamic_mapping_entries;
+         i++) {
+        u32 mtlb_entry;
+
+        mtlb_entry = address
+               | MMU_MTLB_4K_PAGE
+               | MMU_MTLB_PRESENT;
+
+        address += MMU_STLB_4K_SIZE;
+
+        /* Insert Slave TLB address to Master TLB entry.*/
+        _write_page_entry(mmu->mtlb_logical + i, mtlb_entry);
+    }
+
+    _release_mutex(mmu->page_table_mutex);
+
+    return 0;
+}
+
+/* MMU Construct */
+int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu)
+{
+    dc_mmu_pt mmu_t = NULL;
+    void *mem = NULL;
+    void *cookie = NULL, *cookie_safe =NULL;
+    dma_addr_t dma_addr, dma_addr_safe;
+    u32 size = 0;
+    int ret = 0;
+
+    if (mmu_construct)
+        return 0;
+
+    mem = kzalloc(sizeof(dc_mmu), GFP_KERNEL);
+    if (!mem)
+        return -ENOMEM;
+
+    mmu_t = (dc_mmu_pt)mem;
+    mmu_t->mtlb_bytes = MMU_MTLB_SIZE;
+    size = PAGE_ALIGN(mmu_t->mtlb_bytes);
+
+    /* Allocate MTLB */
+    cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
+    if (!cookie) {
+        dev_err(dev, "Failed to alloc mtlb buffer.\n");
+        return -ENOMEM;
+    }
+
+    mmu_t->mtlb_logical = cookie;
+    mmu_t->mtlb_physical = (u64)dma_addr;
+    memset(mmu_t->mtlb_logical, 0, size);
+
+    size = MMU_MTLB_ENTRY_NUM * sizeof(dc_mmu_stlb_pt);
+
+    ret = _allocate_memory(size, &mmu_t->stlbs);
+    if (ret)
+        return ret;
+
+    ret = _create_mutex(&mmu_t->page_table_mutex);
+    if (ret)
+        return ret;
+
+    mmu_t->mode = MMU_MODE_1K;
+
+    ret = _setup_process_address_space(dev, mmu_t);
+    if (ret)
+        return ret;
+
+    /* Allocate safe page */
+    cookie_safe = dma_alloc_wc(dev, 4096, &dma_addr_safe, GFP_KERNEL);
+    if (!cookie_safe) {
+        dev_err(dev, "Failed to alloc safe page.\n");
+        return -ENOMEM;
+    }
+
+    mmu_t->safe_page_logical = cookie_safe;
+    mmu_t->safe_page_physical = (u64)dma_addr_safe;
+    memset(mmu_t->safe_page_logical, 0, size);
+
+    *mmu = mmu_t;
+    mmu_construct = true;
+
+    return 0;
+}
+
+int dc_mmu_get_page_entry(dc_mmu_pt mmu, u32 address, u32 **page_table)
+{
+    dc_mmu_stlb_pt stlb;
+    dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
+    u32 mtlb_offset = _mtlb_offset(address);
+    u32 stlb_offset = _stlb_offset(address);
+
+    stlb = stlbs[mtlb_offset - mmu->dynamic_mapping_start];
+    if (stlb == NULL) {
+        pr_err("BUG: invalid stlb,  mmu=%p stlbs=%p  mtlb_offset=0x%x %s(%d)\n",
+            mmu, stlbs ,mtlb_offset,__FUNCTION__,__LINE__);
+        return -ENXIO;
+    }
+
+    *page_table = &stlb->logical[stlb_offset];
+
+    return 0;
+}
+
+int _link(dc_mmu_pt mmu, u32 index, u32 node)
+{
+    if (index >= mmu->page_table_entries) {
+        mmu->heap_list = node;
+    }
+    else {
+        u32 *map = mmu->map_logical;
+
+        switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+        case DC_MMU_SINGLE:
+            /* Previous is a single node, link to it*/
+            _write_page_entry(&map[index], (node << 8) | DC_MMU_SINGLE);
+            break;
+        case DC_MMU_FREE:
+            /* Link to FREE TYPE node */
+            _write_page_entry(&map[index + 1], node);
+            break;
+        default:
+            pr_err("MMU table corrupted at index %u!", index);
+            return -EINVAL;
+        }
+    }
+
+    return 0;
+}
+
+int _add_free(dc_mmu_pt mmu, u32 index, u32 node, u32 count)
+{
+    u32 *map = mmu->map_logical;
+
+    if (count == 1) {
+        /* Initialize a single page node */
+        _write_page_entry(map + node, DC_SINGLE_PAGE_NODE_INITIALIZE | DC_MMU_SINGLE);
+    }
+    else {
+        /* Initialize the FREE node*/
+        _write_page_entry(map + node, (count << 8) | DC_MMU_FREE);
+        _write_page_entry(map + node + 1, ~0U);
+    }
+
+    return _link(mmu, index, node);
+}
+
+/* Collect free nodes */
+int _collect(dc_mmu_pt mmu)
+{
+    u32 *map = mmu->map_logical;
+    u32 count = 0, start = 0, i = 0;
+    u32 previous = ~0U;
+    int ret = 0;
+
+    mmu->heap_list = ~0U;
+    mmu->free_nodes = false;
+
+    /* Walk the entire page table */
+    for (i = 0; i < mmu->page_table_entries; i++) {
+        switch (DC_ENTRY_TYPE(_read_page_entry(&map[i]))) {
+        case DC_MMU_SINGLE:
+            if (count++ == 0) {
+                /* Set new start node */
+                start = i;
+            }
+            break;
+        case DC_MMU_FREE:
+            if (count == 0) {
+                /* Set new start node */
+                start = i;
+            }
+
+            count += _read_page_entry(&map[i]) >> 8;
+            /* Advance the index of the page table */
+            i += (_read_page_entry(&map[i]) >> 8) - 1;
+            break;
+        case DC_MMU_USED:
+            /* Meet used node, start to collect */
+            if (count > 0) {
+                /* Add free node to list*/
+                ret = _add_free(mmu, previous, start, count);
+                if (ret)
+                    return ret;
+                /* Reset previous unused node index */
+                previous = start;
+                count = 0;
+            }
+            break;
+        default:
+            pr_err("MMU page table corrupted at index %u!", i);
+            return -EINVAL;
+        }
+    }
+
+    /* If left node is an open node. */
+    if (count > 0) {
+        ret = _add_free(mmu, previous, start, count);
+        if (ret)
+            return ret;
+    }
+
+    return 0;
+}
+
+int _fill_page_table(u32 *page_table, u32 page_count, u32 entry_value)
+{
+    u32 i;
+
+    for (i = 0; i < page_count; i++) {
+        _write_page_entry(page_table + i, entry_value);
+    }
+
+    return 0;
+}
+
+int dc_mmu_allocate_pages(dc_mmu_pt mmu, u32 page_count, u32 *address)
+{
+    bool got = false, acquired = false;
+    u32 *map;
+    u32 index = 0, vaddr, left;
+    u32 previous = ~0U;
+    u32 mtlb_offset, stlb_offset;
+    int ret = 0;
+
+    if (page_count == 0 || page_count > mmu->page_table_entries) {
+        pr_err("%s has invalid arguments.\n", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+    acquired = true;
+
+    for (map = mmu->map_logical; !got;) {
+        for (index = mmu->heap_list; !got && (index < mmu->page_table_entries);) {
+            switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+            case DC_MMU_SINGLE:
+                if (page_count == 1) {
+                    got = true;
+                }
+                else {
+                    /* Move to next node */
+                    previous = index;
+                    index = _read_page_entry(&map[index]) >> 8;
+                }
+                break;
+            case DC_MMU_FREE:
+                if (page_count <= (_read_page_entry(&map[index]) >> 8)) {
+                    got = true;
+                }
+                else {
+                    /* Move to next node */
+                    previous = index;
+                    index = _read_page_entry(&map[index + 1]);
+                }
+                break;
+            default:
+                /* Only link SINGLE and FREE node */
+                pr_err("MMU table corrupted at index %u!", index);
+                ret = -EINVAL;
+                goto OnError;
+            }
+        }
+
+        /* If out of index */
+        if (index >= mmu->page_table_entries) {
+            if (mmu->free_nodes) {
+                /* Collect the free node */
+                ret = _collect(mmu);
+                if (ret)
+                    goto OnError;
+            }
+            else {
+                ret = -ENODATA;
+                goto OnError;
+            }
+        }
+    }
+
+    switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+    case DC_MMU_SINGLE:
+        /* Unlink single node from node list */
+        ret = _link(mmu, previous, _read_page_entry(&map[index]) >> 8);
+        if (ret)
+            goto OnError;
+        break;
+
+    case DC_MMU_FREE:
+        left = (_read_page_entry(&map[index]) >> 8) - page_count;
+        switch (left) {
+        case 0:
+            /* Unlink the entire FREE type node */
+            ret = _link(mmu, previous, _read_page_entry(&map[index + 1]));
+            if (ret)
+                goto OnError;
+            break;
+        case 1:
+            /* Keep the map[index] as a single node,
+             * mark the left as used
+             */
+            _write_page_entry(&map[index], 
+                (_read_page_entry(&map[index + 1]) << 8) |
+                DC_MMU_SINGLE);
+            index++;
+            break;
+        default:
+            /* FREE type node left */
+            _write_page_entry(&map[index],
+                      (left << 8) | DC_MMU_FREE);
+            index += left;
+            break;
+        }
+        break;
+    default:
+        /* Only link SINGLE and FREE node */
+        pr_err("MMU table corrupted at index %u!", index);
+        ret = -EINVAL;
+        goto OnError;
+    }
+
+    /* Mark node as used */
+    ret = _fill_page_table(&map[index], page_count, DC_MMU_USED);
+    if (ret)
+        goto OnError;
+
+    _release_mutex(mmu->page_table_mutex);
+
+    mtlb_offset = index / MMU_STLB_4K_ENTRY_NUM + mmu->dynamic_mapping_start;
+    stlb_offset = index % MMU_STLB_4K_ENTRY_NUM;
+
+    vaddr = (mtlb_offset << MMU_MTLB_SHIFT) | (stlb_offset << MMU_STLB_4K_SHIFT);
+
+    if (address != NULL) {
+        *address = vaddr;
+    }
+
+    return 0;
+
+OnError:
+    if (acquired) {
+        _release_mutex(mmu->page_table_mutex);
+    }
+
+    return ret;
+}
+
+int dc_mmu_free_pages(dc_mmu_pt mmu, u32 address, u32 page_count)
+{
+    u32 *node;
+
+    if (page_count == 0)
+        return -EINVAL;
+
+    node = mmu->map_logical + _address_to_index(mmu, address);
+
+    _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+
+    if (page_count == 1) {
+        /* Mark the Single page node free */
+        _write_page_entry(node, DC_SINGLE_PAGE_NODE_INITIALIZE | DC_MMU_SINGLE);
+    }
+    else {
+         /* Mark the FREE type node free */
+        _write_page_entry(node, (page_count << 8) | DC_MMU_FREE);
+        _write_page_entry(node + 1, ~0U);
+    }
+
+    mmu->free_nodes = true;
+
+    _release_mutex(mmu->page_table_mutex);
+
+    return 0;
+}
+
+int dc_mmu_set_page(dc_mmu_pt mmu, u64 page_address, u32 *page_entry)
+{
+    u32 address_ext;
+    u32 address;
+
+    if (page_entry == NULL || (page_address & 0xFFF)) {
+        return -EINVAL;
+    }
+
+    /* [31:0]. */
+    address = (u32)(page_address & 0xFFFFFFFF);
+    /* [39:32]. */
+    address_ext = (u32)((page_address >> 32) & 0xFF);
+
+    _write_page_entry(page_entry, _set_page(address, address_ext, true));
+
+    return 0;
+}
+
+int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count,
+              u32 *address, bool continuous, bool security)
+{
+    u32 virutal_address, i= 0;
+    u32 mtlb_num, mtlb_entry, mtlb_offset;
+    bool allocated = false;
+    int ret = 0;
+
+    ret = dc_mmu_allocate_pages(mmu, page_count, &virutal_address);
+    if (ret)
+        goto OnError;
+
+    *address = virutal_address;
+    allocated = true;
+
+    /*Fill mtlb security bit*/
+    mtlb_num = _mtlb_offset(virutal_address + page_count * MMU_PAGE_4K_SIZE - 1) -
+        _mtlb_offset(virutal_address) + 1;
+    mtlb_offset = _mtlb_offset(virutal_address);
+    mtlb_entry = mmu->mtlb_logical[mtlb_offset];
+
+    for (i = 0; i < mtlb_num ; i++) {
+        mtlb_entry = mmu->mtlb_logical[mtlb_offset + i];
+        if(security) {
+            mtlb_entry = mtlb_entry
+                       | MMU_MTLB_SECURITY
+                       | MMU_MTLB_EXCEPTION;
+            _write_page_entry(&mmu->mtlb_logical[mtlb_offset + i], mtlb_entry);
+        } else {
+            mtlb_entry = mtlb_entry & (~MMU_MTLB_SECURITY);
+            _write_page_entry(&mmu->mtlb_logical[mtlb_offset + i], mtlb_entry);
+        }
+    }
+
+    /* Fill in page table */
+    for (i = 0; i < page_count; i++) {
+        u64 page_phy;
+        u32 *page_entry;
+        struct page **pages;
+
+        if (continuous == true) {
+            page_phy = physical + i * MMU_PAGE_4K_SIZE;
+        }
+        else {
+            pages = (struct page **)physical;
+            page_phy = page_to_phys(pages[i]);
+        }
+
+        ret = dc_mmu_get_page_entry(mmu, virutal_address, &page_entry);
+        if (ret)
+            goto OnError;
+
+        /* Write the page address to the page entry */
+        ret = dc_mmu_set_page(mmu, page_phy, page_entry);
+        if (ret)
+            goto OnError;
+
+        /* Get next page */
+        virutal_address += MMU_PAGE_4K_SIZE;
+    }
+
+    return 0;
+
+OnError:
+    if (allocated)
+        dc_mmu_free_pages(mmu, virutal_address, page_count);
+    pr_info("%s fail!\n", __FUNCTION__);
+
+    return ret;
+}
+
+int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count)
+{
+    return dc_mmu_free_pages(mmu, gpu_address, page_count);
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_dc_mmu.h b/drivers/gpu/drm/verisilicon/vs_dc_mmu.h
new file mode 100644 (file)
index 0000000..d2ba4f6
--- /dev/null
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef _VS_DC_MMU_H_
+#define _VS_DC_MMU_H_
+
+#include "vs_type.h"
+
+#define DC_INFINITE ((u32)(~0U))
+
+#define DC_ENTRY_TYPE(x) (x & 0xF0)
+#define DC_SINGLE_PAGE_NODE_INITIALIZE (~((1U << 8) - 1))
+
+#define DC_INVALID_PHYSICAL_ADDRESS ~0ULL
+#define DC_INVALID_ADDRESS ~0U
+
+/* 1k mode */
+#define MMU_MTLB_SHIFT           24
+#define MMU_STLB_4K_SHIFT        12
+
+#define MMU_MTLB_BITS            (32 - MMU_MTLB_SHIFT)
+#define MMU_PAGE_4K_BITS         MMU_STLB_4K_SHIFT
+#define MMU_STLB_4K_BITS         (32 - MMU_MTLB_BITS - MMU_PAGE_4K_BITS)
+
+#define MMU_MTLB_ENTRY_NUM       (1 << MMU_MTLB_BITS)
+#define MMU_MTLB_SIZE            (MMU_MTLB_ENTRY_NUM << 2)
+#define MMU_STLB_4K_ENTRY_NUM    (1 << MMU_STLB_4K_BITS)
+#define MMU_STLB_4K_SIZE         (MMU_STLB_4K_ENTRY_NUM << 2)
+#define MMU_PAGE_4K_SIZE         (1 << MMU_STLB_4K_SHIFT)
+
+#define MMU_MTLB_MASK            (~((1U << MMU_MTLB_SHIFT)-1))
+#define MMU_STLB_4K_MASK         ((~0U << MMU_STLB_4K_SHIFT) ^ MMU_MTLB_MASK)
+#define MMU_PAGE_4K_MASK         (MMU_PAGE_4K_SIZE - 1)
+
+/* page offset definitions. */
+#define MMU_OFFSET_4K_BITS       (32 - MMU_MTLB_BITS - MMU_STLB_4K_BITS)
+#define MMU_OFFSET_4K_MASK       ((1U << MMU_OFFSET_4K_BITS) - 1)
+
+#define MMU_MTLB_PRESENT         0x00000001
+#define MMU_MTLB_EXCEPTION       0x00000002
+#define MMU_MTLB_SECURITY        0x00000010
+#define MMU_MTLB_4K_PAGE         0x00000000
+
+typedef enum _dc_mmu_type
+{
+    DC_MMU_USED   = (0 << 4),
+    DC_MMU_SINGLE = (1 << 4),
+    DC_MMU_FREE   = (2 << 4),
+}
+dc_mmu_type;
+
+typedef enum _dc_mmu_mode
+{
+    MMU_MODE_1K,
+    MMU_MODE_4K,
+}
+dc_mmu_mode;
+
+typedef struct _dc_mmu_stlb
+{
+    u32  *logical;
+    void *physical;
+    u32  size;
+    u64  physBase;
+    u32  pageCount;
+}
+dc_mmu_stlb, *dc_mmu_stlb_pt;
+
+typedef struct _dc_mmu {
+    u32    mtlb_bytes;
+    u64    mtlb_physical;
+    u32    *mtlb_logical;
+
+    void   *safe_page_logical;
+    u64    safe_page_physical;
+
+    u32    dynamic_mapping_start;
+
+    void   *stlbs;
+
+    u64    stlb_physicals[MMU_MTLB_ENTRY_NUM];
+
+    u32    page_table_entries;
+    u32    page_table_size;
+    u32    heap_list;
+
+    u32    *map_logical;
+    bool   free_nodes;
+
+    void   *page_table_mutex;
+
+    dc_mmu_mode mode;
+
+    void   *static_stlb;
+}
+dc_mmu, *dc_mmu_pt;
+
+int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu);
+int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count,
+              u32 *address, bool continuous, bool security);
+int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count);
+
+#endif /* _VS_DC_MMU_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c
new file mode 100644 (file)
index 0000000..4f17c39
--- /dev/null
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/of_graph.h>
+#include <linux/component.h>
+#include <linux/iommu.h>
+#include <linux/version.h>
+
+#include <drm/drm_of.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_modeset_helper.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_prime.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_debugfs.h>
+#endif
+
+#include "vs_drv.h"
+#include "vs_fb.h"
+#include "vs_gem.h"
+#include "vs_plane.h"
+#include "vs_crtc.h"
+#include "vs_simple_enc.h"
+#include "vs_dc.h"
+#include "vs_virtual.h"
+#include "dw_mipi_dsi.h"
+#include "dw_hdmi_light.h"
+
+#define DRV_NAME    "vs-drm"
+#define DRV_DESC    "VeriSilicon DRM driver"
+#define DRV_DATE    "20191101"
+#define DRV_MAJOR   1
+#define DRV_MINOR   0
+
+static bool has_iommu = true;
+static struct platform_driver vs_drm_platform_driver;
+
+static const struct file_operations 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,
+    .mmap           = vs_gem_mmap,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int vs_debugfs_planes_show(struct seq_file *s, void *data)
+{
+    struct drm_info_node *node = (struct drm_info_node *)s->private;
+    struct drm_device *dev = node->minor->dev;
+    struct drm_plane *plane;
+
+    list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+        struct drm_plane_state *state = plane->state;
+        struct vs_plane_state *plane_state = to_vs_plane_state(state);
+
+        seq_printf(s, "plane[%u]: %s\n", plane->base.id, plane->name);
+        seq_printf(s, "\tcrtc = %s\n", state->crtc ?
+               state->crtc->name : "(null)");
+        seq_printf(s, "\tcrtc id = %u\n", state->crtc ?
+               state->crtc->base.id : 0);
+        seq_printf(s, "\tcrtc-pos = " DRM_RECT_FMT "\n",
+               DRM_RECT_ARG(&plane_state->status.dest));
+        seq_printf(s, "\tsrc-pos = " DRM_RECT_FP_FMT "\n",
+               DRM_RECT_FP_ARG(&plane_state->status.src));
+        seq_printf(s, "\tformat = %p4cc\n", state->fb ?
+               state->fb->format->format : "(null)");
+        seq_printf(s, "\trotation = 0x%x\n", state->rotation);
+        seq_printf(s, "\ttiling = %u\n",
+               plane_state->status.tile_mode);
+
+        seq_puts(s, "\n");
+    }
+
+    return 0;
+}
+
+static struct drm_info_list vs_debugfs_list[] = {
+    { "planes", vs_debugfs_planes_show, 0, NULL },
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+static void vs_debugfs_init(struct drm_minor *minor)
+{
+    drm_debugfs_create_files(vs_debugfs_list,
+                 ARRAY_SIZE(vs_debugfs_list),
+                 minor->debugfs_root, minor);
+}
+#else
+static int vs_debugfs_init(struct drm_minor *minor)
+{
+    struct drm_device *dev = minor->dev;
+    int ret;
+
+    ret = drm_debugfs_create_files(vs_debugfs_list,
+                       ARRAY_SIZE(vs_debugfs_list),
+                       minor->debugfs_root, minor);
+    if (ret)
+        dev_err(dev->dev, "could not install vs_debugfs_list.\n");
+
+    return ret;
+}
+#endif
+#endif
+
+static struct drm_driver vs_drm_driver = {
+    .driver_features    = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
+    .lastclose      = drm_fb_helper_lastclose,
+    .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+    .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+    .gem_prime_import   = vs_gem_prime_import,
+    .gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
+    .dumb_create        = vs_gem_dumb_create,
+#ifdef CONFIG_DEBUG_FS
+    .debugfs_init       = vs_debugfs_init,
+#endif
+    .fops           = &fops,
+    .name           = DRV_NAME,
+    .desc           = DRV_DESC,
+    .date           = DRV_DATE,
+    .major          = DRV_MAJOR,
+    .minor          = DRV_MINOR,
+};
+
+int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
+                   struct device *dev)
+{
+    struct vs_drm_private *priv = drm_dev->dev_private;
+    int ret;
+
+    if (!has_iommu)
+        return 0;
+
+    if (!priv->domain) {
+        priv->domain = iommu_get_domain_for_dev(dev);
+        if (IS_ERR(priv->domain))
+            return PTR_ERR(priv->domain);
+        priv->dma_dev = dev;
+    }
+
+    ret = iommu_attach_device(priv->domain, dev);
+    if (ret) {
+        DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
+        return ret;
+    }
+
+    return 0;
+}
+
+void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
+                struct device *dev)
+{
+    struct vs_drm_private *priv = drm_dev->dev_private;
+
+    if (!has_iommu)
+        return;
+
+    iommu_detach_device(priv->domain, dev);
+
+    if (priv->dma_dev == dev)
+        priv->dma_dev = drm_dev->dev;
+}
+
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+                   unsigned int alignment)
+{
+    struct vs_drm_private *priv = drm_dev->dev_private;
+
+    if (alignment > priv->pitch_alignment)
+        priv->pitch_alignment = alignment;
+}
+
+static int vs_drm_bind(struct device *dev)
+{
+    struct drm_device *drm_dev;
+    struct vs_drm_private *priv;
+    int ret;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#ifdef CONFIG_VERISILICON_MMU
+    static u64 dma_mask = DMA_BIT_MASK(40);
+#else
+    static u64 dma_mask = DMA_BIT_MASK(32);
+#endif
+#else
+    static u64 dma_mask = DMA_40BIT_MASK;
+#endif
+
+    drm_dev = drm_dev_alloc(&vs_drm_driver, dev);
+    if (IS_ERR(drm_dev))
+        return PTR_ERR(drm_dev);
+
+    dev_set_drvdata(dev, drm_dev);
+
+    priv = devm_kzalloc(drm_dev->dev, sizeof(struct vs_drm_private),
+                GFP_KERNEL);
+    if (!priv) {
+        ret = -ENOMEM;
+        goto err_put_dev;
+    }
+
+    priv->pitch_alignment = 64;
+    priv->dma_dev = drm_dev->dev;
+    priv->dma_dev->coherent_dma_mask = dma_mask;
+
+    drm_dev->dev_private = priv;
+
+    drm_mode_config_init(drm_dev);
+
+    /* Now try and bind all our sub-components */
+    ret = component_bind_all(dev, drm_dev);
+    if (ret)
+        goto err_mode;
+
+    vs_mode_config_init(drm_dev);
+
+    ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+    if (ret)
+        goto err_bind;
+
+    drm_mode_config_reset(drm_dev);
+
+    drm_dev->irq_enabled = true;
+
+    drm_kms_helper_poll_init(drm_dev);
+
+    ret = drm_dev_register(drm_dev, 0);
+    if (ret)
+        goto err_helper;
+
+    drm_fbdev_generic_setup(drm_dev, 32);
+
+    return 0;
+
+err_helper:
+    drm_kms_helper_poll_fini(drm_dev);
+err_bind:
+    component_unbind_all(drm_dev->dev, drm_dev);
+err_mode:
+    drm_mode_config_cleanup(drm_dev);
+    if (priv->domain)
+        iommu_domain_free(priv->domain);
+err_put_dev:
+    drm_dev->dev_private = NULL;
+    dev_set_drvdata(dev, NULL);
+    drm_dev_put(drm_dev);
+    return ret;
+}
+
+static void vs_drm_unbind(struct device *dev)
+{
+    struct drm_device *drm_dev = dev_get_drvdata(dev);
+    struct vs_drm_private *priv = drm_dev->dev_private;
+
+    drm_dev_unregister(drm_dev);
+
+    drm_kms_helper_poll_fini(drm_dev);
+
+    component_unbind_all(drm_dev->dev, drm_dev);
+
+    drm_mode_config_cleanup(drm_dev);
+
+    if (priv->domain) {
+        iommu_domain_free(priv->domain);
+        priv->domain = NULL;
+    }
+
+    drm_dev->dev_private = NULL;
+    dev_set_drvdata(dev, NULL);
+    drm_dev_put(drm_dev);
+}
+
+static const struct component_master_ops vs_drm_ops = {
+    .bind = vs_drm_bind,
+    .unbind = vs_drm_unbind,
+};
+
+static struct platform_driver *drm_sub_drivers[] = {
+    /* put display control driver at start */
+    &dc_platform_driver,
+
+    /* connector */
+
+    /* bridge */
+#ifdef CONFIG_VERISILICON_DW_MIPI_DSI
+    &dw_mipi_dsi_driver,
+#endif
+
+#ifdef CONFIG_VERISILICON_DW_HDMI_LIGHT
+    &dw_hdmi_light_platform_driver,
+#endif
+
+    /* encoder */
+    &simple_encoder_driver,
+
+#ifdef CONFIG_VERISILICON_VIRTUAL_DISPLAY
+    &virtual_display_platform_driver,
+#endif
+};
+#define NUM_DRM_DRIVERS \
+        (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
+
+static int compare_dev(struct device *dev, void *data)
+{
+    return dev == (struct device *)data;
+}
+
+static struct component_match *vs_drm_match_add(struct device *dev)
+{
+    struct component_match *match = NULL;
+    int i;
+
+    for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
+        struct platform_driver *drv = drm_sub_drivers[i];
+        struct device *p = NULL, *d;
+
+        while ((d = platform_find_device_by_driver(p, &drv->driver))) {
+            put_device(p);
+
+            component_match_add(dev, &match, compare_dev, d);
+
+            p = d;
+        }
+        put_device(p);
+    }
+
+    return match ?: ERR_PTR(-ENODEV);
+}
+
+static int vs_drm_platform_of_probe(struct device *dev)
+{
+    struct device_node *np = dev->of_node;
+    struct device_node *port;
+    bool found = false;
+    int i;
+
+    if (!np)
+        return -ENODEV;
+
+    for (i = 0;; i++) {
+        struct device_node *iommu;
+
+        port = of_parse_phandle(np, "ports", i);
+        if (!port)
+            break;
+
+        if (!of_device_is_available(port->parent)) {
+            of_node_put(port);
+            continue;
+        }
+
+        iommu = of_parse_phandle(port->parent, "iommus", 0);
+
+        /*
+         * if there is a crtc not support iommu, force set all
+         * crtc use non-iommu buffer.
+         */
+        if (!iommu || !of_device_is_available(iommu->parent))
+            has_iommu = false;
+
+        found = true;
+
+        of_node_put(iommu);
+        of_node_put(port);
+    }
+
+    if (i == 0) {
+        DRM_DEV_ERROR(dev, "missing 'ports' property\n");
+        return -ENODEV;
+    }
+
+    if (!found) {
+        DRM_DEV_ERROR(dev, "No available DC found.\n");
+        return -ENODEV;
+    }
+
+    return 0;
+}
+
+static int vs_drm_platform_probe(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct component_match *match;
+    int ret;
+
+    ret = vs_drm_platform_of_probe(dev);
+    if (ret)
+        return ret;
+
+    match = vs_drm_match_add(dev);
+    if (IS_ERR(match))
+        return PTR_ERR(match);
+
+    return component_master_add_with_match(dev, &vs_drm_ops, match);
+}
+
+static int vs_drm_platform_remove(struct platform_device *pdev)
+{
+    component_master_del(&pdev->dev, &vs_drm_ops);
+    return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vs_drm_suspend(struct device *dev)
+{
+    struct drm_device *drm = dev_get_drvdata(dev);
+
+    return drm_mode_config_helper_suspend(drm);
+}
+
+static int vs_drm_resume(struct device *dev)
+{
+    struct drm_device *drm = dev_get_drvdata(dev);
+
+    return drm_mode_config_helper_resume(drm);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
+
+
+static const struct of_device_id vs_drm_dt_ids[] = {
+
+    { .compatible = "verisilicon,display-subsystem", },
+
+    { /* sentinel */ },
+
+};
+
+MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
+
+static struct platform_driver vs_drm_platform_driver = {
+    .probe = vs_drm_platform_probe,
+    .remove = vs_drm_platform_remove,
+
+    .driver = {
+        .name = DRV_NAME,
+        .of_match_table = vs_drm_dt_ids,
+        .pm = &vs_drm_pm_ops,
+    },
+};
+
+static int __init vs_drm_init(void)
+{
+    int ret;
+
+    ret = platform_register_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+    if (ret)
+        return ret;
+
+    ret = platform_driver_register(&vs_drm_platform_driver);
+    if (ret)
+        platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+
+    return ret;
+}
+
+static void __exit vs_drm_fini(void)
+{
+    platform_driver_unregister(&vs_drm_platform_driver);
+    platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+}
+
+module_init(vs_drm_init);
+module_exit(vs_drm_fini);
+
+MODULE_DESCRIPTION("VeriSilicon DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/verisilicon/vs_drv.h b/drivers/gpu/drm/verisilicon/vs_drv.h
new file mode 100644 (file)
index 0000000..07e7e34
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRV_H__
+#define __VS_DRV_H__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+
+#include "vs_plane.h"
+#ifdef CONFIG_VERISILICON_MMU
+#include "vs_dc_mmu.h"
+#endif
+
+
+/*
+ *
+ * @dma_dev: device for DMA API.
+ *  - use the first attached device if support iommu
+    else use drm device (only contiguous buffer support)
+ * @domain: iommu domain for DRM.
+ *  - all DC IOMMU share same domain to reduce mapping
+ * @pitch_alignment: buffer pitch alignment required by sub-devices.
+ *
+ */
+struct vs_drm_private {
+    struct device *dma_dev;
+    struct iommu_domain *domain;
+#ifdef CONFIG_VERISILICON_MMU
+    dc_mmu *mmu;
+#endif
+
+    unsigned int pitch_alignment;
+};
+
+int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
+                   struct device *dev);
+
+void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
+                struct device *dev);
+
+void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
+                   unsigned int alignment);
+
+static inline struct device *to_dma_dev(struct drm_device *dev)
+{
+    struct vs_drm_private *priv = dev->dev_private;
+
+    return priv->dma_dev;
+}
+
+static inline bool is_iommu_enabled(struct drm_device *dev)
+{
+    struct vs_drm_private *priv = dev->dev_private;
+
+    return priv->domain != NULL ? true : false;
+}
+#endif /* __VS_DRV_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_fb.c b/drivers/gpu/drm/verisilicon/vs_fb.c
new file mode 100644 (file)
index 0000000..d873d21
--- /dev/null
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_fourcc.h>
+#else
+#include <drm/drmP.h>
+#endif
+#include <drm/drm_gem.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "vs_fb.h"
+#include "vs_gem.h"
+
+#define fourcc_mod_vs_get_type(val) \
+    (((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
+
+static struct drm_framebuffer_funcs vs_fb_funcs = {
+    .create_handle  = drm_gem_fb_create_handle,
+    .destroy    = drm_gem_fb_destroy,
+};
+
+static struct drm_framebuffer *
+vs_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
+         struct vs_gem_object **obj, unsigned int num_planes)
+{
+    struct drm_framebuffer *fb;
+    int ret, i;
+
+    fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+    if (!fb)
+        return ERR_PTR(-ENOMEM);
+
+    drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
+
+    for (i = 0; i < num_planes; i++)
+        fb->obj[i] = &obj[i]->base;
+
+    ret = drm_framebuffer_init(dev, fb, &vs_fb_funcs);
+    if (ret) {
+        dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+            ret);
+        kfree(fb);
+        return ERR_PTR(ret);
+    }
+
+    return fb;
+}
+
+static struct drm_framebuffer *vs_fb_create(struct drm_device *dev,
+                      struct drm_file *file_priv,
+                      const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+    struct drm_framebuffer *fb;
+    const struct drm_format_info *info;
+    struct vs_gem_object *objs[MAX_NUM_PLANES];
+    struct drm_gem_object *obj;
+    unsigned int height, size;
+    unsigned char i, num_planes;
+    int ret = 0;
+
+    info = drm_get_format_info(dev, mode_cmd);
+    if (!info)
+        return ERR_PTR(-EINVAL);
+
+    num_planes = info->num_planes;
+    if (num_planes > MAX_NUM_PLANES)
+        return ERR_PTR(-EINVAL);
+
+    for (i = 0; i < num_planes; i++) {
+        obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
+        if (!obj) {
+            dev_err(dev->dev, "Failed to lookup GEM object.\n");
+            ret = -ENXIO;
+            goto err;
+        }
+
+        height = drm_format_info_plane_height(info,
+                              mode_cmd->height, i);
+
+        size = height * mode_cmd->pitches[i] + mode_cmd->offsets[i];
+
+        if (obj->size < size) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+            drm_gem_object_put(obj);
+#else
+            drm_gem_object_put_unlocked(obj);
+#endif
+            ret = -EINVAL;
+            goto err;
+        }
+
+        objs[i] = to_vs_gem_object(obj);
+    }
+
+    fb = vs_fb_alloc(dev, mode_cmd, objs, i);
+    if (IS_ERR(fb)) {
+        ret = PTR_ERR(fb);
+        goto err;
+    }
+
+    return fb;
+
+err:
+    for (; i > 0; i--)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+        drm_gem_object_put(&objs[i-1]->base);
+#else
+        drm_gem_object_put_unlocked(&objs[i-1]->base);
+#endif
+
+    return ERR_PTR(ret);
+}
+
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
+                    unsigned char plane)
+{
+    if (plane > MAX_NUM_PLANES)
+        return NULL;
+
+    return to_vs_gem_object(fb->obj[plane]);
+}
+
+static const struct drm_format_info vs_formats[] = {
+    {.format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .char_per_block = { 20, 40, 0 },
+     .block_w = { 4, 4, 0 }, .block_h = { 4, 4, 0 },.hsub = 2, .vsub = 2, .is_yuv = true},
+    {.format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .char_per_block = { 20, 20, 20 },
+     .block_w = { 4, 4, 4 }, .block_h = { 4, 4, 4 },.hsub = 1, .vsub = 1, .is_yuv = true},
+};
+
+static const struct drm_format_info *
+vs_lookup_format_info(const struct drm_format_info formats[],
+                   int num_formats, u32 format)
+{
+    int i;
+
+    for (i = 0; i < num_formats; i++) {
+        if (formats[i].format == format)
+            return &formats[i];
+    }
+
+    return NULL;
+}
+
+static const struct drm_format_info *
+vs_get_format_info(const struct drm_mode_fb_cmd2 *cmd)
+{
+    if (fourcc_mod_vs_get_type(cmd->modifier[0]) ==
+        DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
+        return vs_lookup_format_info(vs_formats, ARRAY_SIZE(vs_formats),
+                                     cmd->pixel_format);
+    else
+        return NULL;
+}
+
+static const struct drm_mode_config_funcs vs_mode_config_funcs = {
+    .fb_create           = vs_fb_create,
+    .get_format_info     = vs_get_format_info,
+    .atomic_check        = drm_atomic_helper_check,
+    .atomic_commit       = drm_atomic_helper_commit,
+};
+
+static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
+    .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+void vs_mode_config_init(struct drm_device *dev)
+{
+    dev->mode_config.fb_modifiers_not_supported = false;
+
+    if (dev->mode_config.max_width == 0 ||
+        dev->mode_config.max_height == 0) {
+        dev->mode_config.min_width  = 0;
+        dev->mode_config.min_height = 0;
+        dev->mode_config.max_width = 4096;
+        dev->mode_config.max_height = 4096;
+    }
+    dev->mode_config.funcs = &vs_mode_config_funcs;
+    dev->mode_config.helper_private = &vs_mode_config_helpers;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_fb.h b/drivers/gpu/drm/verisilicon/vs_fb.h
new file mode 100644 (file)
index 0000000..16e80f2
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_FB_H__
+#define __VS_FB_H__
+
+struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
+                    unsigned char plane);
+
+void vs_mode_config_init(struct drm_device *dev);
+#endif /* __VS_FB_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_gem.c b/drivers/gpu/drm/verisilicon/vs_gem.c
new file mode 100644 (file)
index 0000000..6233d6d
--- /dev/null
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/dma-buf.h>
+
+#include "vs_drv.h"
+#include "vs_gem.h"
+
+static const struct drm_gem_object_funcs vs_gem_default_funcs;
+
+static void nonseq_free(struct page **pages, unsigned int nr_page)
+{
+       u32 i;
+
+       if (!pages)
+               return;
+
+       for (i = 0; i < nr_page; i++)
+               __free_page(pages[i]);
+}
+
+static int __maybe_unused get_pages(unsigned int nr_page,
+                                   struct vs_gem_object *vs_obj)
+{
+       struct page *pages;
+       u32 i, num_page, page_count = 0;
+       int order = 0;
+       gfp_t gfp = GFP_KERNEL;
+
+       if (!vs_obj->pages)
+               return -EINVAL;
+
+       gfp &= ~__GFP_HIGHMEM;
+       gfp |= __GFP_DMA32;
+
+       num_page = nr_page;
+
+       do {
+               pages = NULL;
+               order = get_order(num_page * PAGE_SIZE);
+               num_page = 1 << order;
+
+               if ((num_page + page_count > nr_page) || (order >= MAX_ORDER)) {
+                       num_page = num_page >> 1;
+                       continue;
+               }
+
+               pages = alloc_pages(gfp, order);
+               if (!pages) {
+                       if (num_page == 1) {
+                               nonseq_free(vs_obj->pages, page_count);
+                               return -ENOMEM;
+                       }
+
+                       num_page = num_page >> 1;
+               } else {
+                       for (i = 0; i < num_page; i++) {
+                               vs_obj->pages[page_count + i] = &pages[i];
+                               SetPageReserved(vs_obj->pages[page_count + i]);
+                       }
+
+                       page_count += num_page;
+                       num_page = nr_page - page_count;
+               }
+
+       } while (page_count < nr_page);
+
+       vs_obj->get_pages = true;
+
+       return 0;
+}
+
+static void put_pages(unsigned int nr_page, struct vs_gem_object *vs_obj)
+{
+       u32 i;
+
+       for (i = 0; i < nr_page; i++)
+               ClearPageReserved(vs_obj->pages[i]);
+
+       nonseq_free(vs_obj->pages, nr_page);
+
+       return;
+}
+
+static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj)
+{
+       struct drm_device *dev = vs_obj->base.dev;
+       unsigned int nr_pages;
+       struct sg_table sgt;
+       int ret = -ENOMEM;
+#ifdef CONFIG_VERISILICON_MMU
+       struct vs_drm_private *priv = dev->dev_private;
+#endif
+
+       if (vs_obj->dma_addr) {
+               DRM_DEV_DEBUG_KMS(dev->dev, "already allocated.\n");
+               return 0;
+       }
+
+       vs_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
+
+       if (!is_iommu_enabled(dev))
+               vs_obj->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
+
+       nr_pages = vs_obj->size >> PAGE_SHIFT;
+
+       vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
+                                      GFP_KERNEL | __GFP_ZERO);
+       if (!vs_obj->pages) {
+               DRM_DEV_ERROR(dev->dev, "failed to allocate pages.\n");
+               return -ENOMEM;
+       }
+
+       vs_obj->cookie = dma_alloc_attrs(to_dma_dev(dev), vs_obj->size,
+                                        &vs_obj->dma_addr, GFP_KERNEL,
+                                        vs_obj->dma_attrs);
+       if (!vs_obj->cookie) {
+#ifdef CONFIG_VERISILICON_MMU
+               ret = get_pages(nr_pages, vs_obj);
+               if (ret) {
+                       DRM_DEV_ERROR(dev->dev, "fail to allocate buffer.\n");
+                       goto err_free;
+               }
+#else
+               DRM_DEV_ERROR(dev->dev, "failed to allocate buffer.\n");
+               goto err_free;
+#endif
+       }
+
+#ifdef CONFIG_VERISILICON_MMU
+       /* MMU map*/
+       if (!priv->mmu) {
+               DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
+               ret = -EINVAL;
+               goto err_mem_free;
+       }
+
+       /* mmu for ree driver */
+       if (!vs_obj->get_pages)
+               ret = dc_mmu_map_memory(priv->mmu, (u64)vs_obj->dma_addr,
+                                       nr_pages, &vs_obj->iova, true, false);
+       else
+               ret = dc_mmu_map_memory(priv->mmu, (u64)vs_obj->pages, nr_pages,
+                                       &vs_obj->iova, false, false);
+
+       if (ret) {
+               DRM_DEV_ERROR(dev->dev, "failed to do mmu map.\n");
+               goto err_mem_free;
+       }
+#else
+       vs_obj->iova = vs_obj->dma_addr;
+#endif
+
+       if (!vs_obj->get_pages) {
+               ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt,
+                                           vs_obj->cookie, vs_obj->dma_addr,
+                                           vs_obj->size, vs_obj->dma_attrs);
+               if (ret < 0) {
+                       DRM_DEV_ERROR(dev->dev, "failed to get sgtable.\n");
+                       goto err_mem_free;
+               }
+
+               if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) {
+                       DRM_DEV_ERROR(dev->dev, "invalid sgtable.\n");
+                       ret = -EINVAL;
+                       goto err_sgt_free;
+               }
+
+               sg_free_table(&sgt);
+       }
+
+       return 0;
+
+err_sgt_free:
+       sg_free_table(&sgt);
+err_mem_free:
+       if (!vs_obj->get_pages)
+               dma_free_attrs(to_dma_dev(dev), vs_obj->size, vs_obj->cookie,
+                              vs_obj->dma_addr, vs_obj->dma_attrs);
+       else
+               put_pages(nr_pages, vs_obj);
+err_free:
+       kvfree(vs_obj->pages);
+
+       return ret;
+}
+
+static void vs_gem_free_buf(struct vs_gem_object *vs_obj)
+{
+       struct drm_device *dev = vs_obj->base.dev;
+#ifdef CONFIG_VERISILICON_MMU
+       struct vs_drm_private *priv = dev->dev_private;
+       unsigned int nr_pages;
+#endif
+
+       if ((!vs_obj->get_pages) && (!vs_obj->dma_addr)) {
+               DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n");
+               return;
+       }
+
+#ifdef CONFIG_VERISILICON_MMU
+       if (!priv->mmu) {
+               DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
+               return;
+       }
+
+       nr_pages = vs_obj->size >> PAGE_SHIFT;
+       dc_mmu_unmap_memory(priv->mmu, vs_obj->iova, nr_pages);
+#endif
+
+       if (!vs_obj->get_pages)
+               dma_free_attrs(to_dma_dev(dev), vs_obj->size, vs_obj->cookie,
+                              (dma_addr_t)vs_obj->dma_addr, vs_obj->dma_attrs);
+       else
+               put_pages(vs_obj->size >> PAGE_SHIFT, vs_obj);
+
+       kvfree(vs_obj->pages);
+}
+
+static void vs_gem_free_object(struct drm_gem_object *obj)
+{
+       struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+
+       if (obj->import_attach) {
+               drm_prime_gem_destroy(obj, vs_obj->sgt);
+               kvfree(vs_obj->pages);
+       } else {
+               vs_gem_free_buf(vs_obj);
+       }
+
+       drm_gem_object_release(obj);
+
+       kfree(vs_obj);
+}
+
+static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev,
+                                                size_t size)
+{
+       struct vs_gem_object *vs_obj;
+       struct drm_gem_object *obj;
+       int ret;
+
+       vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL);
+       if (!vs_obj)
+               return ERR_PTR(-ENOMEM);
+
+       vs_obj->size = size;
+       obj = &vs_obj->base;
+
+       ret = drm_gem_object_init(dev, obj, size);
+       if (ret)
+               goto err_free;
+
+       vs_obj->base.funcs = &vs_gem_default_funcs;
+
+       ret = drm_gem_create_mmap_offset(obj);
+       if (ret) {
+               drm_gem_object_release(obj);
+               goto err_free;
+       }
+
+       return vs_obj;
+
+err_free:
+       kfree(vs_obj);
+       return ERR_PTR(ret);
+}
+
+struct vs_gem_object *vs_gem_create_object(struct drm_device *dev, size_t size)
+{
+       struct vs_gem_object *vs_obj;
+       int ret;
+
+       size = PAGE_ALIGN(size);
+
+       vs_obj = vs_gem_alloc_object(dev, size);
+       if (IS_ERR(vs_obj))
+               return vs_obj;
+
+       ret = vs_gem_alloc_buf(vs_obj);
+       if (ret) {
+               drm_gem_object_release(&vs_obj->base);
+               kfree(vs_obj);
+               return ERR_PTR(ret);
+       }
+
+       return vs_obj;
+}
+
+static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev,
+                                                      struct drm_file *file,
+                                                      size_t size,
+                                                      unsigned int *handle)
+{
+       struct vs_gem_object *vs_obj;
+       struct drm_gem_object *obj;
+       int ret;
+
+       vs_obj = vs_gem_create_object(dev, size);
+       if (IS_ERR(vs_obj))
+               return vs_obj;
+
+       obj = &vs_obj->base;
+
+       ret = drm_gem_handle_create(file, obj, handle);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+       drm_gem_object_put(obj);
+#else
+       drm_gem_object_put_unlocked(obj);
+#endif
+
+       if (ret)
+               return ERR_PTR(ret);
+
+       return vs_obj;
+}
+
+static int vs_gem_mmap_obj(struct drm_gem_object *obj,
+                          struct vm_area_struct *vma)
+{
+       struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+       struct drm_device *drm_dev = vs_obj->base.dev;
+       unsigned long vm_size;
+       int ret = 0;
+
+       vm_size = vma->vm_end - vma->vm_start;
+       if (vm_size > vs_obj->size)
+               return -EINVAL;
+
+       vma->vm_pgoff = 0;
+
+       if (!vs_obj->get_pages) {
+               vm_flags_clear(vma, VM_PFNMAP);
+
+               ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, vs_obj->cookie,
+                                    vs_obj->dma_addr, vs_obj->size,
+                                    vs_obj->dma_attrs);
+       } else {
+               u32 i, nr_pages, pfn = 0U;
+               unsigned long start;
+
+               vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+               vm_flags_set(vma,
+                            VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
+               start = vma->vm_start;
+               vm_size = PAGE_ALIGN(vm_size);
+               nr_pages = vm_size >> PAGE_SHIFT;
+
+               for (i = 0; i < nr_pages; i++) {
+                       pfn = page_to_pfn(vs_obj->pages[i]);
+
+                       ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+                                             vma->vm_page_prot);
+                       if (ret < 0)
+                               break;
+
+                       start += PAGE_SIZE;
+               }
+       }
+
+       if (ret)
+               drm_gem_vm_close(vma);
+
+       return ret;
+}
+
+struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+       struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+       return drm_prime_pages_to_sg(obj->dev, vs_obj->pages,
+                                    vs_obj->size >> PAGE_SHIFT);
+#else
+       return drm_prime_pages_to_sg(vs_obj->pages, vs_obj->size >> PAGE_SHIFT);
+#endif
+}
+
+static int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+       struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
+
+       return vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ?
+                      page_address(vs_obj->cookie) :
+                      vs_obj->cookie;
+}
+
+static void vs_gem_prime_vunmap(struct drm_gem_object *obj,
+                               struct iosys_map *map)
+{
+       /* Nothing to do */
+}
+
+static const struct vm_operations_struct vs_vm_ops = {
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
+static const struct drm_gem_object_funcs vs_gem_default_funcs = {
+       .free = vs_gem_free_object,
+       .get_sg_table = vs_gem_prime_get_sg_table,
+       .vmap = vs_gem_prime_vmap,
+       .vunmap = vs_gem_prime_vunmap,
+       .vm_ops = &vs_vm_ops,
+};
+
+int vs_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+                      struct drm_mode_create_dumb *args)
+{
+       struct vs_drm_private *priv = dev->dev_private;
+       struct vs_gem_object *vs_obj;
+       unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+       if (args->bpp % 10)
+               args->pitch = ALIGN(pitch, priv->pitch_alignment);
+       else
+               /* for costum 10bit format with no bit gaps */
+               args->pitch = pitch;
+       args->size = PAGE_ALIGN(args->pitch * args->height);
+
+       vs_obj =
+               vs_gem_create_with_handle(dev, file, args->size, &args->handle);
+       return PTR_ERR_OR_ZERO(vs_obj);
+}
+
+struct drm_gem_object *vs_gem_prime_import(struct drm_device *dev,
+                                          struct dma_buf *dma_buf)
+{
+       return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev));
+}
+
+struct drm_gem_object *
+vs_gem_prime_import_sg_table(struct drm_device *dev,
+                            struct dma_buf_attachment *attach,
+                            struct sg_table *sgt)
+{
+       struct vs_gem_object *vs_obj;
+       int npages;
+       int ret;
+       struct scatterlist *s;
+       u32 i;
+       dma_addr_t expected;
+       size_t size = attach->dmabuf->size;
+#ifdef CONFIG_VERISILICON_MMU
+       u32 iova;
+       struct vs_drm_private *priv = dev->dev_private;
+
+       if (!priv->mmu) {
+               DRM_ERROR("invalid mmu.\n");
+               ret = -EINVAL;
+               return ERR_PTR(ret);
+       }
+#endif
+
+       size = PAGE_ALIGN(size);
+
+       vs_obj = vs_gem_alloc_object(dev, size);
+       if (IS_ERR(vs_obj))
+               return ERR_CAST(vs_obj);
+
+       expected = sg_dma_address(sgt->sgl);
+       for_each_sg(sgt->sgl, s, sgt->nents, i) {
+               if (sg_dma_address(s) != expected) {
+#ifndef CONFIG_VERISILICON_MMU
+                       DRM_ERROR("sg_table is not contiguous");
+                       ret = -EINVAL;
+                       goto err;
+#endif
+               }
+
+               if (sg_dma_len(s) & (PAGE_SIZE - 1)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+#ifdef CONFIG_VERISILICON_MMU
+               iova = 0;
+               npages = sg_dma_len(s) >> PAGE_SHIFT;
+               ret = dc_mmu_map_memory(priv->mmu, (u64)sg_dma_address(s),
+                                       npages, &iova, true, false);
+               if (ret) {
+                       DRM_ERROR("failed to do mmu map.\n");
+                       goto err;
+               }
+
+               if (i == 0)
+                       vs_obj->iova = iova;
+#else
+               if (i == 0)
+                       vs_obj->iova = sg_dma_address(s);
+#endif
+
+               expected = sg_dma_address(s) + sg_dma_len(s);
+       }
+
+       vs_obj->dma_addr = sg_dma_address(sgt->sgl);
+
+       npages = vs_obj->size >> PAGE_SHIFT;
+       vs_obj->pages =
+               kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
+       if (!vs_obj->pages) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages);
+       if (ret)
+               goto err_free_page;
+
+       vs_obj->sgt = sgt;
+
+       return &vs_obj->base;
+
+err_free_page:
+       kvfree(vs_obj->pages);
+err:
+       vs_gem_free_object(&vs_obj->base);
+
+       return ERR_PTR(ret);
+}
+
+int vs_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+       int ret = 0;
+
+       ret = drm_gem_mmap_obj(obj, obj->size, vma);
+       if (ret < 0)
+               return ret;
+
+       return vs_gem_mmap_obj(obj, vma);
+}
+
+int vs_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct drm_gem_object *obj;
+       int ret;
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret)
+               return ret;
+
+       obj = vma->vm_private_data;
+
+       if (obj->import_attach)
+               return dma_buf_mmap(obj->dma_buf, vma, 0);
+
+       return vs_gem_mmap_obj(obj, vma);
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_gem.h b/drivers/gpu/drm/verisilicon/vs_gem.h
new file mode 100644 (file)
index 0000000..a02e09e
--- /dev/null
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_GEM_H__
+#define __VS_GEM_H__
+
+#include <linux/dma-buf.h>
+
+#include <drm/drm_gem.h>
+
+#include "vs_drv.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <drm/drm_prime.h>
+#endif
+
+/*
+ *
+ * @base: drm gem object.
+ * @size: size requested from user
+ * @cookie: cookie returned by dma_alloc_attrs
+ *  - not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING
+ * @dma_addr: bus address(accessed by dma) to allocated memory region.
+ *  - this address could be physical address without IOMMU and
+ *  device address with IOMMU.
+ * @dma_attrs: attribute for DMA API
+ * @get_pages: flag for manually applying for non-contiguous memory.
+ * @pages: Array of backing pages.
+ * @sgt: Imported sg_table.
+ *
+ */
+struct vs_gem_object {
+    struct drm_gem_object   base;
+    size_t          size;
+    void            *cookie;
+    dma_addr_t      dma_addr;
+    u32             iova;
+    unsigned long   dma_attrs;
+    bool            get_pages;
+    struct page     **pages;
+    struct sg_table *sgt;
+};
+
+static inline
+struct vs_gem_object *to_vs_gem_object(struct drm_gem_object *obj)
+{
+    return container_of(obj, struct vs_gem_object, base);
+}
+
+struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
+                       size_t size);
+
+int vs_gem_prime_mmap(struct drm_gem_object *obj,
+              struct vm_area_struct *vma);
+
+int vs_gem_dumb_create(struct drm_file *file_priv,
+               struct drm_device *drm,
+               struct drm_mode_create_dumb *args);
+
+int vs_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+
+struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj);
+
+struct drm_gem_object *vs_gem_prime_import(struct drm_device *dev,
+                        struct dma_buf *dma_buf);
+struct drm_gem_object *
+vs_gem_prime_import_sg_table(struct drm_device *dev,
+                 struct dma_buf_attachment *attach,
+                 struct sg_table *sgt);
+
+#endif /* __VS_GEM_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
new file mode 100644 (file)
index 0000000..076627c
--- /dev/null
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/vs_drm.h>
+
+#include "vs_type.h"
+#include "vs_crtc.h"
+#include "vs_plane.h"
+#include "vs_gem.h"
+#include "vs_fb.h"
+
+void vs_plane_destory(struct drm_plane *plane)
+{
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+
+       drm_plane_cleanup(plane);
+       kfree(vs_plane);
+}
+
+static void vs_plane_reset(struct drm_plane *plane)
+{
+       struct vs_plane_state *state;
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+
+       if (plane->state) {
+               __drm_atomic_helper_plane_destroy_state(plane->state);
+
+               state = to_vs_plane_state(plane->state);
+               kfree(state);
+               plane->state = NULL;
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return;
+
+       __drm_atomic_helper_plane_reset(plane, &state->base);
+
+       state->degamma = VS_DEGAMMA_DISABLE;
+       state->degamma_changed = false;
+       state->base.zpos = vs_plane->id;
+
+       memset(&state->status, 0, sizeof(state->status));
+}
+
+static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
+                                    struct vs_plane_state *ori_state)
+{
+       state->watermark = ori_state->watermark;
+       state->color_mgmt = ori_state->color_mgmt;
+       state->roi = ori_state->roi;
+
+       if (state->watermark)
+               drm_property_blob_get(state->watermark);
+       if (state->color_mgmt)
+               drm_property_blob_get(state->color_mgmt);
+       if (state->roi)
+               drm_property_blob_get(state->roi);
+}
+
+static int _vs_plane_set_property_blob_from_id(struct drm_device *dev,
+                                              struct drm_property_blob **blob,
+                                              uint64_t blob_id,
+                                              size_t expected_size)
+{
+       struct drm_property_blob *new_blob = NULL;
+
+       if (blob_id) {
+               new_blob = drm_property_lookup_blob(dev, blob_id);
+               if (new_blob == NULL)
+                       return -EINVAL;
+
+               if (new_blob->length != expected_size) {
+                       drm_property_blob_put(new_blob);
+                       return -EINVAL;
+               }
+       }
+
+       drm_property_replace_blob(blob, new_blob);
+       drm_property_blob_put(new_blob);
+
+       return 0;
+}
+
+static struct drm_plane_state *
+vs_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+       struct vs_plane_state *ori_state;
+       struct vs_plane_state *state;
+
+       if (WARN_ON(!plane->state))
+               return NULL;
+
+       ori_state = to_vs_plane_state(plane->state);
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+
+       __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+       state->degamma = ori_state->degamma;
+       state->degamma_changed = ori_state->degamma_changed;
+
+       _vs_plane_duplicate_blob(state, ori_state);
+       memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
+
+       return &state->base;
+}
+
+static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
+                                         struct drm_plane_state *state)
+{
+       struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+
+       __drm_atomic_helper_plane_destroy_state(state);
+
+       drm_property_blob_put(vs_plane_state->watermark);
+       drm_property_blob_put(vs_plane_state->color_mgmt);
+       drm_property_blob_put(vs_plane_state->roi);
+       kfree(vs_plane_state);
+}
+
+static int vs_plane_atomic_set_property(struct drm_plane *plane,
+                                       struct drm_plane_state *state,
+                                       struct drm_property *property,
+                                       uint64_t val)
+{
+       struct drm_device *dev = plane->dev;
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+       struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
+       int ret = 0;
+
+       if (property == vs_plane->degamma_mode) {
+               if (vs_plane_state->degamma != val) {
+                       vs_plane_state->degamma = val;
+                       vs_plane_state->degamma_changed = true;
+               } else {
+                       vs_plane_state->degamma_changed = false;
+               }
+       } else if (property == vs_plane->watermark_prop) {
+               ret = _vs_plane_set_property_blob_from_id(
+                       dev, &vs_plane_state->watermark, val,
+                       sizeof(struct drm_vs_watermark));
+               return ret;
+       } else if (property == vs_plane->color_mgmt_prop) {
+               ret = _vs_plane_set_property_blob_from_id(
+                       dev, &vs_plane_state->color_mgmt, val,
+                       sizeof(struct drm_vs_color_mgmt));
+               return ret;
+       } else if (property == vs_plane->roi_prop) {
+               ret = _vs_plane_set_property_blob_from_id(
+                       dev, &vs_plane_state->roi, val,
+                       sizeof(struct drm_vs_roi));
+               return ret;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vs_plane_atomic_get_property(struct drm_plane *plane,
+                                       const struct drm_plane_state *state,
+                                       struct drm_property *property,
+                                       uint64_t *val)
+{
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+       const struct vs_plane_state *vs_plane_state =
+               container_of(state, const struct vs_plane_state, base);
+
+       if (property == vs_plane->degamma_mode)
+               *val = vs_plane_state->degamma;
+       else if (property == vs_plane->watermark_prop)
+               *val = (vs_plane_state->watermark) ?
+                              vs_plane_state->watermark->base.id :
+                              0;
+       else if (property == vs_plane->color_mgmt_prop)
+               *val = (vs_plane_state->color_mgmt) ?
+                              vs_plane_state->color_mgmt->base.id :
+                              0;
+       else if (property == vs_plane->roi_prop)
+               *val = (vs_plane_state->roi) ? vs_plane_state->roi->base.id : 0;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+const struct drm_plane_funcs vs_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .destroy = vs_plane_destory,
+       .reset = vs_plane_reset,
+       .atomic_duplicate_state = vs_plane_atomic_duplicate_state,
+       .atomic_destroy_state = vs_plane_atomic_destroy_state,
+       .atomic_set_property = vs_plane_atomic_set_property,
+       .atomic_get_property = vs_plane_atomic_get_property,
+};
+
+static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
+{
+       const struct drm_format_info *info;
+
+       if (!fb)
+               return 0;
+
+       info = drm_format_info(fb->format->format);
+       if (!info || info->num_planes > MAX_NUM_PLANES)
+               return 0;
+
+       return info->num_planes;
+}
+
+static int vs_plane_atomic_check(struct drm_plane *plane,
+                                struct drm_atomic_state *state)
+{
+       struct drm_plane_state *plane_state =
+               drm_atomic_get_existing_plane_state(state, plane);
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+       struct drm_framebuffer *fb = plane_state->fb;
+       struct drm_crtc *crtc = plane_state->crtc;
+       struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+       if (!crtc || !fb)
+               return 0;
+
+       return vs_plane->funcs->check(vs_crtc->dev, vs_plane, plane_state);
+}
+
+static void vs_plane_atomic_update(struct drm_plane *plane,
+                                  struct drm_atomic_state *old_state)
+{
+       unsigned char i, num_planes;
+       struct drm_framebuffer *fb;
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+       struct drm_plane_state *state = plane->state;
+       struct vs_crtc *vs_crtc = to_vs_crtc(state->crtc);
+       struct vs_plane_state *plane_state = to_vs_plane_state(state);
+
+       if (!state->fb || !state->crtc)
+               return;
+
+       fb = state->fb;
+
+       num_planes = vs_get_plane_number(fb);
+
+       for (i = 0; i < num_planes; i++) {
+               struct vs_gem_object *vs_obj;
+
+               vs_obj = vs_fb_get_gem_obj(fb, i);
+               vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i];
+       }
+
+       plane_state->status.src = drm_plane_state_src(state);
+       plane_state->status.dest = drm_plane_state_dest(state);
+
+       vs_plane->funcs->update(vs_crtc->dev, vs_plane);
+}
+
+static void vs_plane_atomic_disable(struct drm_plane *plane,
+                                   struct drm_atomic_state *old_state)
+{
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+       struct drm_plane_state *old_plane_state =
+               drm_atomic_get_old_plane_state(old_state, plane);
+       struct vs_crtc *vs_crtc = to_vs_crtc(old_plane_state->crtc);
+
+       vs_plane->funcs->disable(vs_crtc->dev, vs_plane, old_plane_state);
+}
+
+const struct drm_plane_helper_funcs vs_plane_helper_funcs = {
+       .atomic_check = vs_plane_atomic_check,
+       .atomic_update = vs_plane_atomic_update,
+       .atomic_disable = vs_plane_atomic_disable,
+};
+
+static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
+       { VS_DEGAMMA_DISABLE, "disabled" },
+       { VS_DEGAMMA_BT709, "preset degamma for BT709" },
+       { VS_DEGAMMA_BT2020, "preset degamma for BT2020" },
+};
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+                                struct vs_plane_info *info,
+                                unsigned int layer_num,
+                                unsigned int possible_crtcs)
+{
+       struct vs_plane *plane;
+       int ret;
+
+       if (!info)
+               return NULL;
+
+       plane = kzalloc(sizeof(struct vs_plane), GFP_KERNEL);
+       if (!plane)
+               return NULL;
+
+       ret = drm_universal_plane_init(drm_dev, &plane->base, possible_crtcs,
+                                      &vs_plane_funcs, info->formats,
+                                      info->num_formats, info->modifiers,
+                                      info->type,
+                                      info->name ? info->name : NULL);
+       if (ret)
+               goto err_free_plane;
+
+       drm_plane_helper_add(&plane->base, &vs_plane_helper_funcs);
+
+       /* Set up the plane properties */
+       if (info->degamma_size) {
+               plane->degamma_mode = drm_property_create_enum(
+                       drm_dev, 0, "DEGAMMA_MODE", vs_degamma_mode_enum_list,
+                       ARRAY_SIZE(vs_degamma_mode_enum_list));
+
+               if (!plane->degamma_mode)
+                       goto error_cleanup_plane;
+
+               drm_object_attach_property(&plane->base.base,
+                                          plane->degamma_mode,
+                                          VS_DEGAMMA_DISABLE);
+       }
+
+       if (info->rotation) {
+               ret = drm_plane_create_rotation_property(
+                       &plane->base, DRM_MODE_ROTATE_0, info->rotation);
+               if (ret)
+                       goto error_cleanup_plane;
+       }
+
+       if (info->blend_mode) {
+               ret = drm_plane_create_blend_mode_property(&plane->base,
+                                                          info->blend_mode);
+               if (ret)
+                       goto error_cleanup_plane;
+               ret = drm_plane_create_alpha_property(&plane->base);
+               if (ret)
+                       goto error_cleanup_plane;
+       }
+
+       if (info->color_encoding) {
+               ret = drm_plane_create_color_properties(
+                       &plane->base, info->color_encoding,
+                       BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
+                       DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE);
+               if (ret)
+                       goto error_cleanup_plane;
+       }
+
+       if (info->zpos != 255) {
+               ret = drm_plane_create_zpos_property(&plane->base, info->zpos,
+                                                    0, layer_num - 1);
+               if (ret)
+                       goto error_cleanup_plane;
+       }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+       else {
+               ret = drm_plane_create_zpos_immutable_property(&plane->base,
+                                                              info->zpos);
+               if (ret)
+                       goto error_cleanup_plane;
+       }
+#endif
+
+       if (info->watermark) {
+               plane->watermark_prop = drm_property_create(
+                       drm_dev, DRM_MODE_PROP_BLOB, "WATERMARK", 0);
+               if (!plane->watermark_prop)
+                       goto error_cleanup_plane;
+
+               drm_object_attach_property(&plane->base.base,
+                                          plane->watermark_prop, 0);
+       }
+
+       if (info->color_mgmt) {
+               plane->color_mgmt_prop = drm_property_create(
+                       drm_dev, DRM_MODE_PROP_BLOB, "COLOR_CONFIG", 0);
+               if (!plane->color_mgmt_prop)
+                       goto error_cleanup_plane;
+
+               drm_object_attach_property(&plane->base.base,
+                                          plane->color_mgmt_prop, 0);
+       }
+
+       if (info->roi) {
+               plane->roi_prop = drm_property_create(
+                       drm_dev, DRM_MODE_PROP_BLOB, "ROI", 0);
+               if (!plane->roi_prop)
+                       goto error_cleanup_plane;
+
+               drm_object_attach_property(&plane->base.base, plane->roi_prop,
+                                          0);
+       }
+
+       return plane;
+
+error_cleanup_plane:
+       drm_plane_cleanup(&plane->base);
+err_free_plane:
+       kfree(plane);
+       return NULL;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
new file mode 100644 (file)
index 0000000..87ddd8f
--- /dev/null
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_PLANE_H__
+#define __VS_PLANE_H__
+
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_fourcc.h>
+
+#include "vs_type.h"
+#include "vs_fb.h"
+
+#define MAX_NUM_PLANES      3 /* colour format plane */
+
+struct vs_plane;
+
+struct vs_plane_funcs {
+    void (*update)(struct device *dev, struct vs_plane *plane);
+    void (*disable)(struct device *dev, struct vs_plane *plane,
+                    struct drm_plane_state *old_state);
+    int (*check)(struct device *dev, struct vs_plane *plane,
+                 struct drm_plane_state *state);
+};
+
+struct vs_plane_status {
+    u32 tile_mode;
+    struct drm_rect src;
+    struct drm_rect dest;
+};
+
+struct vs_plane_state {
+    struct drm_plane_state base;
+    struct vs_plane_status status; /* for debugfs */
+
+    struct drm_property_blob *watermark;
+    struct drm_property_blob *color_mgmt;
+    struct drm_property_blob *roi;
+
+    u32 degamma;
+    bool degamma_changed;
+};
+
+struct vs_plane {
+    struct drm_plane base;
+    u8 id;
+    dma_addr_t dma_addr[MAX_NUM_PLANES];
+
+    struct drm_property *degamma_mode;
+    struct drm_property *watermark_prop;
+    struct drm_property *color_mgmt_prop;
+    struct drm_property *roi_prop;
+
+    const struct vs_plane_funcs *funcs;
+};
+
+void vs_plane_destory(struct drm_plane *plane);
+
+struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
+                 struct vs_plane_info *info,
+                 unsigned int layer_num,
+                 unsigned int possible_crtcs);
+
+static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
+{
+    return container_of(plane, struct vs_plane, base);
+}
+
+static inline struct vs_plane_state *
+to_vs_plane_state(struct drm_plane_state *state)
+{
+    return container_of(state, struct vs_plane_state, base);
+}
+#endif /* __VS_PLANE_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c b/drivers/gpu/drm/verisilicon/vs_simple_enc.c
new file mode 100644 (file)
index 0000000..56d1adb
--- /dev/null
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+#include <linux/version.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/media-bus-format.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <linux/module.h>
+
+#include <drm/drm_bridge.h>
+#else
+#include <drm/drmP.h>
+#endif
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "vs_crtc.h"
+#include "vs_simple_enc.h"
+
+static const struct simple_encoder_priv hdmi_priv = {
+    .encoder_type = DRM_MODE_ENCODER_TMDS
+};
+
+static const struct simple_encoder_priv dsi_priv = {
+    .encoder_type = DRM_MODE_ENCODER_DSI
+};
+
+static const struct simple_encoder_priv dpi_priv = {
+    .encoder_type = DRM_MODE_ENCODER_DPI
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+    .destroy = drm_encoder_cleanup
+};
+
+static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
+{
+    return container_of(enc, struct simple_encoder, encoder);
+}
+
+static int encoder_parse_dt(struct device *dev)
+{
+    struct simple_encoder *simple = dev_get_drvdata(dev);
+    int ret = 0;
+    int cnt, i;
+    u32 *vals;
+    u32 *masks;
+
+    simple->dss_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+                        "verisilicon,dss-syscon");
+
+    if (IS_ERR(simple->dss_regmap)) {
+        if (PTR_ERR(simple->dss_regmap) != -ENODEV) {
+            dev_err(dev, "failed to get dss-syscon\n");
+            ret = PTR_ERR(simple->dss_regmap);
+            goto err;
+        }
+        simple->dss_regmap = NULL;
+        goto err;
+    }
+
+    cnt = of_property_count_elems_of_size(dev->of_node,
+                "verisilicon,mux-mask", 4);
+    if (!cnt) {
+        ret = cnt;
+        goto err;
+    }
+
+    simple->dss_regdatas = devm_kzalloc(dev,
+        sizeof(*simple->dss_regdatas) * cnt, GFP_KERNEL);
+
+    masks = kcalloc(cnt, sizeof(*masks), GFP_KERNEL);
+    if (!masks) {
+        ret = -ENOMEM;
+        goto err;
+    }
+
+    vals = kcalloc(cnt, sizeof(*vals), GFP_KERNEL);
+    if (!vals) {
+        ret = -ENOMEM;
+        goto err_free_masks;
+    }
+
+    ret = of_property_read_u32_array(
+            dev->of_node, "verisilicon,mux-mask", masks, cnt);
+    if (ret)
+        goto err_free_vals;
+
+    ret = of_property_read_u32_array(
+            dev->of_node, "verisilicon,mux-val", vals, cnt);
+    if (ret)
+        goto err_free_vals;
+
+    for (i = 0; i < cnt; i++) {
+        simple->dss_regdatas[i].mask = masks[i];
+        simple->dss_regdatas[i].value = vals[i];
+    }
+
+err_free_vals:
+    kfree(vals);
+err_free_masks:
+    kfree(masks);
+err:
+    return ret;
+}
+
+void encoder_atomic_enable(struct drm_encoder *encoder,
+                        struct drm_atomic_state *state)
+{
+    struct simple_encoder *simple = to_simple_encoder(encoder);
+    struct dss_data *data = simple->dss_regdatas;
+    int crtc_id;
+
+    if (!simple->dss_regmap)
+        return;
+
+    crtc_id = drm_of_encoder_active_endpoint_id(
+                simple->dev->of_node, encoder);
+
+    regmap_update_bits(simple->dss_regmap, 0, data[crtc_id].mask,
+            data[crtc_id].value);
+}
+
+int encoder_atomic_check(struct drm_encoder *encoder,
+            struct drm_crtc_state *crtc_state,
+            struct drm_connector_state *conn_state)
+{
+    struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
+    struct drm_connector *connector = conn_state->connector;
+    int ret = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+    struct drm_bridge *first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+    struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
+#endif
+
+    vs_crtc_state->encoder_type = encoder->encoder_type;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+    if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
+        bridge_state = drm_atomic_get_bridge_state(
+                       crtc_state->state, first_bridge);
+
+    if (IS_ERR(bridge_state)) {
+        if (connector->display_info.num_bus_formats)
+            vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
+        else
+            vs_crtc_state->output_fmt = MEDIA_BUS_FMT_FIXED;
+    } else {
+        vs_crtc_state->output_fmt = bridge_state->input_bus_cfg.format;
+    }
+#else
+    if (connector->display_info.num_bus_formats)
+        vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
+    else
+        vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+#endif
+
+    switch (vs_crtc_state->output_fmt) {
+    case MEDIA_BUS_FMT_FIXED:
+    case MEDIA_BUS_FMT_RGB565_1X16:
+    case MEDIA_BUS_FMT_RGB666_1X18:
+    case MEDIA_BUS_FMT_RGB888_1X24:
+    case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+    case MEDIA_BUS_FMT_RGB101010_1X30:
+    case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+    case MEDIA_BUS_FMT_UYVY8_1X16:
+    case MEDIA_BUS_FMT_YUV8_1X24:
+    case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+    case MEDIA_BUS_FMT_UYVY10_1X20:
+    case MEDIA_BUS_FMT_YUV10_1X30:
+        ret = 0;
+        break;
+    default:
+        ret = -EINVAL;
+        break;
+    }
+
+    /* If MEDIA_BUS_FMT_FIXED, set it to default value */
+    if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
+        vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+    return ret;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+    .atomic_enable = encoder_atomic_enable,
+    .atomic_check = encoder_atomic_check,
+};
+
+static int encoder_bind(struct device *dev, struct device *master, void *data)
+{
+    struct drm_device *drm_dev = data;
+    struct simple_encoder *simple = dev_get_drvdata(dev);
+    struct drm_encoder *encoder;
+    struct drm_bridge *bridge;
+    struct drm_panel *panel;
+    int ret;
+
+    encoder = &simple->encoder;
+
+    /* Encoder. */
+    ret = drm_encoder_init(drm_dev, encoder, &encoder_funcs,
+                   simple->priv->encoder_type, NULL);
+    if (ret)
+        return ret;
+
+    drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+    encoder->possible_crtcs =
+            drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+    /* output port is port1*/
+    ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, &panel, &bridge);
+    if (ret)
+        goto err;
+
+    if (panel) {
+       bridge = drm_panel_bridge_add(panel);
+       if (IS_ERR(bridge))
+           return PTR_ERR(bridge);
+    }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+    ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+#else
+    ret = drm_bridge_attach(encoder, bridge, NULL);
+#endif
+    if (ret)
+        goto err;
+
+    return 0;
+err:
+    drm_encoder_cleanup(encoder);
+
+    return ret;
+}
+
+static void encoder_unbind(struct device *dev, struct device *master,
+                  void *data)
+{
+    struct simple_encoder *simple = dev_get_drvdata(dev);
+
+    drm_encoder_cleanup(&simple->encoder);
+}
+
+static const struct component_ops encoder_component_ops = {
+    .bind = encoder_bind,
+    .unbind = encoder_unbind,
+};
+
+static const struct of_device_id simple_encoder_dt_match[] = {
+    { .compatible = "verisilicon,hdmi-encoder", .data = &hdmi_priv},
+    { .compatible = "verisilicon,dp-encoder", .data = &hdmi_priv},
+    { .compatible = "verisilicon,dsi-encoder", .data = &dsi_priv},
+    { .compatible = "verisilicon,dpi-encoder", .data = &dpi_priv},
+    {},
+};
+MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
+
+static int encoder_probe(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct simple_encoder *simple;
+    int ret;
+
+    simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
+    if (!simple)
+        return -ENOMEM;
+
+    simple->priv = of_device_get_match_data(dev);
+
+    simple->dev = dev;
+
+    dev_set_drvdata(dev, simple);
+
+    ret = encoder_parse_dt(dev);
+    if (ret)
+        return ret;
+
+    return component_add(dev, &encoder_component_ops);
+}
+
+static int encoder_remove(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+
+    component_del(dev, &encoder_component_ops);
+
+    dev_set_drvdata(dev, NULL);
+
+    return 0;
+}
+
+struct platform_driver simple_encoder_driver = {
+    .probe = encoder_probe,
+    .remove = encoder_remove,
+    .driver = {
+        .name = "vs-simple-encoder",
+        .of_match_table = of_match_ptr(simple_encoder_dt_match),
+    },
+};
+
+MODULE_DESCRIPTION("Simple Encoder Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.h b/drivers/gpu/drm/verisilicon/vs_simple_enc.h
new file mode 100644 (file)
index 0000000..5a47bd4
--- /dev/null
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_SIMPLE_ENC_H_
+#define __VS_SIMPLE_ENC_H_
+
+struct simple_encoder_priv {
+    unsigned char encoder_type;
+};
+
+struct dss_data {
+    u32 mask;
+    u32 value;
+};
+
+struct simple_encoder {
+    struct drm_encoder encoder;
+    struct device *dev;
+    const struct simple_encoder_priv *priv;
+    struct regmap *dss_regmap;
+    struct dss_data *dss_regdatas;
+};
+
+extern struct platform_driver simple_encoder_driver;
+#endif /* __VS_SIMPLE_ENC_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h
new file mode 100644 (file)
index 0000000..830ad91
--- /dev/null
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_TYPE_H__
+#define __VS_TYPE_H__
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#include <drm/drmP.h>
+#endif
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+struct vs_plane_info {
+    const char *name;
+    u8 id;
+    enum drm_plane_type type;
+    unsigned int num_formats;
+    const u32 *formats;
+    u8 num_modifiers;
+    const u64 *modifiers;
+    unsigned int min_width;
+    unsigned int min_height;
+    unsigned int max_width;
+    unsigned int max_height;
+    unsigned int rotation;
+    unsigned int blend_mode;
+    unsigned int color_encoding;
+
+    /* 0 means no de-gamma LUT */
+    unsigned int degamma_size;
+
+    int min_scale; /* 16.16 fixed point */
+    int max_scale; /* 16.16 fixed point */
+
+    u8   zpos; /* default zorder value,
+                * and 255 means unsupported zorder capability */
+
+    bool watermark;
+    bool color_mgmt;
+    bool roi;
+};
+
+struct vs_dc_info {
+    const char *name;
+
+    u8 panel_num;
+
+    /* planes */
+    u8 plane_num;
+    const struct vs_plane_info *planes;
+
+    u8 layer_num;
+    unsigned int max_bpc;
+    unsigned int color_formats;
+
+    /* 0 means no gamma LUT */
+    u16 gamma_size;
+    u8 gamma_bits;
+
+    u16 pitch_alignment;
+
+    bool pipe_sync;
+    bool mmu_prefetch;
+    bool background;
+    bool panel_sync;
+    bool cap_dec;
+};
+
+#endif /* __VS_TYPE_H__ */
diff --git a/drivers/gpu/drm/verisilicon/vs_virtual.c b/drivers/gpu/drm/verisilicon/vs_virtual.c
new file mode 100644 (file)
index 0000000..389a0b2
--- /dev/null
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "vs_virtual.h"
+#include "vs_dc.h"
+#include "vs_gem.h"
+
+static unsigned char __get_bpp(struct vs_virtual_display *vd)
+{
+    if (vd->bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
+        return 10;
+    return 8;
+}
+
+static void vd_dump_destroy(struct vs_virtual_display *vd)
+{
+    if (vd->dump_blob.data) {
+        vunmap(vd->dump_blob.data);
+        vd->dump_blob.data = NULL;
+    }
+    vd->dump_blob.size = 0;
+
+    debugfs_remove(vd->dump_debugfs);
+    vd->dump_debugfs = NULL;
+
+    if (vd->dump_obj) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+        drm_gem_object_put(&vd->dump_obj->base);
+#else
+        drm_gem_object_put_unlocked(&vd->dump_obj->base);
+#endif
+        vd->dump_obj = NULL;
+    }
+}
+
+static void vd_dump_create(struct vs_virtual_display *vd,
+               struct drm_display_mode *mode)
+{
+    struct drm_device *drm_dev = vd->encoder.dev;
+    struct vs_dc *dc = dev_get_drvdata(vd->dc);
+    struct vs_gem_object *obj;
+    unsigned int pitch, size;
+    void *kvaddr;
+    char *name;
+
+    if (!dc->funcs)
+        return;
+
+    vd_dump_destroy(vd);
+
+    /* dump in 4bytes XRGB format */
+    pitch = mode->hdisplay * 4;
+    pitch = ALIGN(pitch, dc->hw.info->pitch_alignment);
+    size = PAGE_ALIGN(pitch * mode->vdisplay);
+
+    obj = vs_gem_create_object(drm_dev, size);
+    if (IS_ERR(obj))
+        return;
+
+    vd->dump_obj = obj;
+    vd->pitch = pitch;
+
+    kvaddr = vmap(obj->pages, obj->size >> PAGE_SHIFT, VM_MAP,
+              pgprot_writecombine(PAGE_KERNEL));
+    if (!kvaddr)
+        goto err;
+
+    vd->dump_blob.data = kvaddr;
+    vd->dump_blob.size = obj->size;
+
+    name = kasprintf(GFP_KERNEL, "%dx%d-XRGB-%d.raw",
+             mode->hdisplay, mode->vdisplay,
+             __get_bpp(vd));
+    if (!name)
+        goto err;
+
+    vd->dump_debugfs = debugfs_create_blob(name, 0444,
+                    vd->connector.debugfs_entry,
+                    &vd->dump_blob);
+    kfree(name);
+
+    return;
+
+err:
+    vd_dump_destroy(vd);
+
+}
+
+static void vd_encoder_destroy(struct drm_encoder *encoder)
+{
+    struct vs_virtual_display *vd;
+
+    drm_encoder_cleanup(encoder);
+    vd = to_virtual_display_with_encoder(encoder);
+    vd_dump_destroy(vd);
+}
+
+static const struct drm_encoder_funcs vd_encoder_funcs = {
+    .destroy = vd_encoder_destroy
+};
+
+static void vd_mode_set(struct drm_encoder *encoder,
+            struct drm_display_mode *mode,
+            struct drm_display_mode *adjusted_mode)
+{
+    struct vs_virtual_display *vd;
+
+    vd = to_virtual_display_with_encoder(encoder);
+    vd_dump_create(vd, adjusted_mode);
+}
+
+static void vd_encoder_disable(struct drm_encoder *encoder)
+{
+    struct vs_virtual_display *vd;
+    struct vs_dc *dc;
+
+    vd = to_virtual_display_with_encoder(encoder);
+    dc = dev_get_drvdata(vd->dc);
+    if (dc->funcs && dc->funcs->dump_disable)
+        dc->funcs->dump_disable(vd->dc);
+}
+
+static void vd_encoder_enable(struct drm_encoder *encoder)
+{
+    struct vs_virtual_display *vd;
+    struct vs_dc *dc;
+
+    vd = to_virtual_display_with_encoder(encoder);
+    dc = dev_get_drvdata(vd->dc);
+    if (dc->funcs && dc->funcs->dump_enable && vd->dump_obj)
+        dc->funcs->dump_enable(vd->dc, vd->dump_obj->iova,
+                      vd->pitch);
+}
+
+static const struct drm_encoder_helper_funcs vd_encoder_helper_funcs = {
+    .mode_set = vd_mode_set,
+    .enable = vd_encoder_enable,
+    .disable = vd_encoder_disable,
+};
+
+static int vd_get_modes(struct drm_connector *connector)
+{
+    struct drm_device *dev = connector->dev;
+    struct drm_display_mode *mode = NULL;
+    unsigned int i;
+    static const struct display_mode {
+        int w, h;
+    } cvt_mode[] = {
+        {640, 480},
+        {720, 480},
+        {800, 600},
+        {1024, 768},
+        {1280, 720},
+        {1280, 1024},
+        {1400, 1050},
+        {1680, 1050},
+        {1600, 1200},
+        {1920, 1080},
+        {1920, 1200},
+        {3840, 2160}
+    };
+
+    for (i = 0; i < ARRAY_SIZE(cvt_mode); i++) {
+        mode = drm_cvt_mode(dev, cvt_mode[i].w, cvt_mode[i].h,
+                    60, false, false, false);
+        drm_mode_probed_add(connector, mode);
+    }
+    return 0;
+}
+
+static struct drm_encoder *vd_best_encoder(struct drm_connector *connector)
+{
+    struct vs_virtual_display *vd;
+
+    vd = to_virtual_display_with_connector(connector);
+    return &vd->encoder;
+}
+
+static enum drm_mode_status vd_mode_valid(struct drm_connector *connector,
+                      struct drm_display_mode *mode)
+{
+    return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs vd_connector_helper_funcs = {
+    .get_modes = vd_get_modes,
+    .mode_valid = vd_mode_valid,
+    .best_encoder = vd_best_encoder,
+};
+
+static void vd_connector_destroy(struct drm_connector *connector)
+{
+    drm_connector_unregister(connector);
+    drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+vd_connector_detect(struct drm_connector *connector, bool force)
+{
+    return connector_status_connected;
+}
+
+static const struct drm_connector_funcs vd_connector_funcs = {
+    .fill_modes = drm_helper_probe_single_connector_modes,
+    .destroy = vd_connector_destroy,
+    .detect = vd_connector_detect,
+    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+    .reset = drm_atomic_helper_connector_reset,
+};
+
+static int vd_bind(struct device *dev, struct device *master, void *data)
+{
+    struct drm_device *drm_dev = data;
+    struct vs_virtual_display *vd = dev_get_drvdata(dev);
+    struct drm_encoder *encoder;
+    struct drm_connector *connector;
+    struct device_node *ep, *np;
+    struct platform_device *pdev;
+    int ret;
+
+    /* Encoder */
+    encoder = &vd->encoder;
+    ret = drm_encoder_init(drm_dev, encoder, &vd_encoder_funcs,
+                   DRM_MODE_ENCODER_VIRTUAL, NULL);
+    if (ret)
+        return ret;
+    drm_encoder_helper_add(encoder, &vd_encoder_helper_funcs);
+
+    encoder->possible_crtcs =
+            drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+    /* Connector */
+    connector = &vd->connector;
+    ret = drm_connector_init(drm_dev, connector, &vd_connector_funcs,
+                 DRM_MODE_CONNECTOR_VIRTUAL);
+    if (ret)
+        goto connector_init_err;
+    drm_connector_helper_add(connector, &vd_connector_helper_funcs);
+    connector->interlace_allowed = false;
+    connector->doublescan_allowed = false;
+    connector->dpms = DRM_MODE_DPMS_OFF;
+    connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+                DRM_CONNECTOR_POLL_DISCONNECT;
+    ret = drm_connector_register(connector);
+    if (ret)
+        goto connector_reg_err;
+
+    drm_display_info_set_bus_formats(&connector->display_info,
+                     &vd->bus_format, 1);
+
+    /* attach */
+    ret = drm_connector_attach_encoder(connector, encoder);
+    if (ret)
+        goto attach_err;
+
+    ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+    if (!ep) {
+        ret = -EINVAL;
+        goto attach_err;
+    }
+
+    np = of_graph_get_remote_port_parent(ep);
+    of_node_put(ep);
+    if (!np) {
+        ret = -EINVAL;
+        goto attach_err;
+    }
+
+    pdev = of_find_device_by_node(np);
+    of_node_put(np);
+    if (!pdev) {
+        ret = -EPROBE_DEFER;
+        goto attach_err;
+    }
+    get_device(&pdev->dev);
+    vd->dc = &pdev->dev;
+
+    return 0;
+
+attach_err:
+    drm_connector_unregister(connector);
+connector_reg_err:
+    drm_connector_cleanup(connector);
+connector_init_err:
+    drm_encoder_cleanup(encoder);
+    return ret;
+}
+
+static void vd_unbind(struct device *dev, struct device *master, void *data)
+{
+    struct vs_virtual_display *vd = dev_get_drvdata(dev);
+
+    drm_connector_unregister(&vd->connector);
+    drm_connector_cleanup(&vd->connector);
+    drm_encoder_cleanup(&vd->encoder);
+}
+
+const struct component_ops vd_component_ops = {
+    .bind = vd_bind,
+    .unbind = vd_unbind,
+};
+
+static const struct of_device_id vd_driver_dt_match[] = {
+    { .compatible = "verisilicon,virtual_display", },
+    {},
+};
+MODULE_DEVICE_TABLE(of, vd_driver_dt_match);
+
+static int vd_probe(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct vs_virtual_display *vd;
+    unsigned char bpp;
+
+    vd = devm_kzalloc(dev, sizeof(*vd), GFP_KERNEL);
+    if (!vd)
+        return -ENOMEM;
+
+    vd->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
+    of_property_read_u8(dev->of_node, "bpp", &bpp);
+    if (bpp == 8)
+        vd->bus_format = MEDIA_BUS_FMT_RBG888_1X24;
+
+    dev_set_drvdata(dev, vd);
+
+    return component_add(dev, &vd_component_ops);
+}
+
+static int vd_remove(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+
+    component_del(dev, &vd_component_ops);
+
+    dev_set_drvdata(dev, NULL);
+
+    return 0;
+}
+
+struct platform_driver virtual_display_platform_driver = {
+    .probe = vd_probe,
+    .remove = vd_remove,
+    .driver = {
+        .name = "vs-virtual-display",
+        .of_match_table = of_match_ptr(vd_driver_dt_match),
+    },
+};
+
+MODULE_DESCRIPTION("VeriSilicon Virtual Display Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/verisilicon/vs_virtual.h b/drivers/gpu/drm/verisilicon/vs_virtual.h
new file mode 100644 (file)
index 0000000..3bdabe6
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+
+#ifndef __VS_VIRTUAL_H_
+#define __VS_VIRTUAL_H_
+
+#include <linux/debugfs.h>
+
+struct vs_virtual_display {
+    struct drm_encoder encoder;
+    struct drm_connector connector;
+    struct device *dc;
+    u32 bus_format;
+
+    struct dentry *dump_debugfs;
+    struct debugfs_blob_wrapper dump_blob;
+    struct vs_gem_object *dump_obj;
+    unsigned int pitch;
+};
+
+static inline struct vs_virtual_display *
+to_virtual_display_with_connector(struct drm_connector *connector)
+{
+    return container_of(connector, struct vs_virtual_display, connector);
+}
+
+static inline struct vs_virtual_display *
+to_virtual_display_with_encoder(struct drm_encoder *encoder)
+{
+    return container_of(encoder, struct vs_virtual_display, encoder);
+}
+
+extern struct platform_driver virtual_display_platform_driver;
+#endif /* __VS_VIRTUAL_H_ */
index 8db7fd3f743e4ffe12c4572eca3c0b10494a8138..67f40b10de93ccf60d2321cb27c51f5607627dde 100644 (file)
@@ -419,6 +419,7 @@ extern "C" {
 #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
+#define DRM_FORMAT_MOD_VENDOR_VS      0x0b
 
 /* add more to the end as needed */
 
@@ -1562,6 +1563,95 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
 #define AMD_FMT_MOD_CLEAR(field) \
        (~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
 
+/*
+ * VeriSilicon Compressed Format
+ *
+ * DEC(Decompressor-Compressor) format consists of category (2 bits),
+ * tile mode (6 bits) and align mode (2 bits).
+ */
+
+#define DRM_FORMAT_MOD_VS_TYPE_NORMAL        0x00
+#define DRM_FORMAT_MOD_VS_TYPE_COMPRESSED    0x01
+#define DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT  0x02
+#define DRM_FORMAT_MOD_VS_TYPE_MASK     ((__u64)0x3 << 54)
+
+#define fourcc_mod_vs_code(type, val) \
+    fourcc_mod_code(VS, ((((__u64)type) << 54) | (val)))
+
+#define DRM_FORMAT_MOD_VS_DEC_TILE_MODE_MASK    0x3F
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR   0x00
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X8_YMAJOR   0x01
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4     0x02
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X4      0x03
+#define DRM_FORMAT_MOD_VS_DEC_TILE_4X8      0x04
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_16X4   0x06
+#define DRM_FORMAT_MOD_VS_DEC_TILE_64X4     0x07
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4     0x08
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_256X1  0x09
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X1  0x0A
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X4   0x0B
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_256X2  0x0C
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X2  0x0D
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X4  0x0E
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X1   0x0F
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X8     0x10
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X16     0x11
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_512X1  0x12
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X4   0x13
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X2   0x14
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X2   0x15
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X1   0x16
+#define DRM_FORMAT_MOD_VS_DEC_RASTER_16X1   0x17
+#define DRM_FORMAT_MOD_VS_DEC_TILE_128X4    0x18
+#define DRM_FORMAT_MOD_VS_DEC_TILE_256X4    0x19
+#define DRM_FORMAT_MOD_VS_DEC_TILE_512X4    0x1A
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X16    0x1B
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X16    0x1C
+#define DRM_FORMAT_MOD_VS_DEC_TILE_64X16    0x1D
+#define DRM_FORMAT_MOD_VS_DEC_TILE_128X8    0x1E
+#define DRM_FORMAT_MOD_VS_DEC_TILE_8X4_S    0x1F
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4_S   0x20
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4_S   0x21
+#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4_LSB 0x22
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4_LSB 0x23
+#define DRM_FORMAT_MOD_VS_DEC_TILE_32X8     0x24
+
+#define DRM_FORMAT_MOD_VS_DEC_ALIGN_32      (0x01 << 6)
+#define DRM_FORMAT_MOD_VS_DEC_ALIGN_64      (0x01 << 7)
+
+#define fourcc_mod_vs_dec_code(tile, align) \
+    fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_COMPRESSED, \
+                ((tile) | (align)))
+
+#define DRM_FORMAT_MOD_VS_NORM_MODE_MASK        0x1F
+#define DRM_FORMAT_MOD_VS_LINEAR                0x00
+#define DRM_FORMAT_MOD_VS_TILED4x4              0x01
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR    0x02
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR    0x03
+#define DRM_FORMAT_MOD_VS_TILE_8X8              0x04
+#define DRM_FORMAT_MOD_VS_TILE_MODE1            0x05
+#define DRM_FORMAT_MOD_VS_TILE_MODE2            0x06
+#define DRM_FORMAT_MOD_VS_TILE_8X4              0x07
+#define DRM_FORMAT_MOD_VS_TILE_MODE4            0x08
+#define DRM_FORMAT_MOD_VS_TILE_MODE5            0x09
+#define DRM_FORMAT_MOD_VS_TILE_MODE6            0x0A
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4    0x0B
+#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8    0x0C
+#define DRM_FORMAT_MOD_VS_TILE_Y                0x0D
+#define DRM_FORMAT_MOD_VS_TILE_128X1            0x0F
+#define DRM_FORMAT_MOD_VS_TILE_256X1            0x10
+#define DRM_FORMAT_MOD_VS_TILE_32X1             0x11
+#define DRM_FORMAT_MOD_VS_TILE_64X1             0x12
+#define DRM_FORMAT_MOD_VS_TILE_MODE4X4          0x15
+
+#define fourcc_mod_vs_norm_code(tile) \
+    fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_NORMAL, \
+                (tile))
+
+#define fourcc_mod_vs_custom_code(tile) \
+    fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT, \
+                (tile))
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/include/uapi/drm/vs_drm.h b/include/uapi/drm/vs_drm.h
new file mode 100644 (file)
index 0000000..72bb9e7
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef __VS_DRM_H__
+#define __VS_DRM_H__
+
+#include "drm.h"
+
+enum drm_vs_degamma_mode {
+    VS_DEGAMMA_DISABLE = 0,
+    VS_DEGAMMA_BT709 = 1,
+    VS_DEGAMMA_BT2020 = 2,
+};
+
+enum drm_vs_sync_dc_mode {
+    VS_SINGLE_DC = 0,
+    VS_MULTI_DC_PRIMARY = 1,
+    VS_MULTI_DC_SECONDARY = 2,
+};
+
+enum drm_vs_mmu_prefetch_mode {
+    VS_MMU_PREFETCH_DISABLE = 0,
+    VS_MMU_PREFETCH_ENABLE = 1,
+};
+
+struct drm_vs_watermark {
+    __u32 watermark;
+    __u8 qos_low;
+    __u8 qos_high;
+};
+
+struct drm_vs_color_mgmt {
+    __u32 colorkey;
+    __u32 colorkey_high;
+    __u32 clear_value;
+    bool  clear_enable;
+    bool  transparency;
+};
+
+struct drm_vs_roi {
+    bool enable;
+    __u16 roi_x;
+    __u16 roi_y;
+    __u16 roi_w;
+    __u16 roi_h;
+};
+
+#endif /* __VS_DRM_H__ */