riscv:uboot:starfive:dc8200
authorkeith.zhao <keith.zhao@starfivetech.com>
Fri, 14 Jan 2022 12:46:25 +0000 (20:46 +0800)
committerkeith.zhao <keith.zhao@starfivetech.com>
Fri, 14 Jan 2022 12:46:25 +0000 (20:46 +0800)
update  drdc8200iver kenerl version from 5.10 to 5.13

Signed-off-by:keith.zhao<keith.zhao@statfivetech.com>

50 files changed:
arch/riscv/boot/dts/starfive/starfive_jh7110.dts [changed mode: 0644->0755]
drivers/gpu/drm/Kconfig [changed mode: 0644->0755]
drivers/gpu/drm/Makefile [changed mode: 0644->0755]
drivers/gpu/drm/drm_plane.c [changed mode: 0644->0755]
drivers/gpu/drm/verisilicon/Kconfig [new file with mode: 0755]
drivers/gpu/drm/verisilicon/Makefile [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/Kconfig [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/Makefile [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/adv7511.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/adv7511_audio.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/adv7511_cec.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/adv7511_drv.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/adv7511/adv7533.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/dw_mipi_dsi.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/dw_mipi_dsi.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/inno_hdmi.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/inno_hdmi.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/starfive_drm_dsi.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/starfive_drm_encoder.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/starfive_drm_encoder.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_crtc.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_crtc.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc_dec.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc_dec.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc_hw.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc_hw.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc_mmu.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_dc_mmu.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_drv.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_drv.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_fb.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_fb.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_gem.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_gem.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_plane.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_plane.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_simple_enc.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_simple_enc.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_type.h [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_virtual.c [new file with mode: 0755]
drivers/gpu/drm/verisilicon/vs_virtual.h [new file with mode: 0755]
drivers/phy/Kconfig [changed mode: 0644->0755]
drivers/phy/Makefile [changed mode: 0644->0755]
drivers/phy/m31/Kconfig [new file with mode: 0755]
drivers/phy/m31/Makefile [new file with mode: 0755]
drivers/phy/m31/phy-m31-dphy-tx0.c [new file with mode: 0755]
include/uapi/drm/drm_fourcc.h [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index 4d6696f..8aa3573
                        };
                };
 
+               i2c1: i2c@10040000 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0x10040000 0x0 0x10000>;
+                       interrupt-parent = <&plic>;
+                       interrupts = <36>;
+                       /*clocks = <&hfclk>; */
+                       clock-frequency = <100000>;
+                       i2c-sda-hold-time-ns = <300>;
+                       i2c-sda-falling-time-ns = <3000>;
+                       i2c-scl-falling-time-ns = <3000>;
+                       auto_calc_scl_lhcnt;
+
+                       adv7513@39 {
+                                       compatible = "adi,adv7513";
+                                       reg = <0x39>, <0x49>, <0x29>, <0x59>;
+                                       reg-names = "main", "edid", "cec", "packet";
+
+                                       adi,input-depth = <8>;
+                                       adi,input-colorspace = "rgb";
+                                       adi,input-clock = "1x";
+                                       ports {
+                                               #address-cells = <1>;
+                                               #size-cells = <0>;
+
+                                               port@0 {
+                                                       reg = <0>;
+                                                       adv7513_0_in: endpoint {
+                                                       };
+                                               };
+
+                                               port@1 {
+                                                       reg = <1>;
+                                                       adv7513_1_out: endpoint {
+                                                       };
+                                               };
+                                       };
+                       };
+                       /*
+                       seeed_plane_i2c@45 { //next to do handle this node to i2cX
+                                       compatible = "seeed_panel";
+                                       reg = <0x45>;
+
+                                       port {
+                                               panel_dsi_port: endpoint {
+                                                       remote-endpoint = <&dsi_out_port>;
+                                               };
+                                       };
+                       };*/
+               };
                /*emmc*/
                sdio0:sdio0@16010000{
                        compatible = "snps,dw-mshc";
                        mbox-names = "rx", "tx";
                        mboxes = <&mailbox_contrl0 0 1>,<&mailbox_contrl0 1 0>;
                };
+
+               display-subsystem {
+                       compatible = "verisilicon,display-subsystem";
+                       //memory-region = <&sffb_reserved>;
+                       ports = <&dc_out_dpi0>;
+                       status = "okay";
+               };
+
+               display-encoder {
+                       compatible = "starfive,display-encoder";
+                       encoder-type = <2>;     //2-TMDS, 3-LVDS, 6-DSI, 8-DPI
+                       status = "okay";
+
+                       ports {
+                               port@0 {
+                                       endpoint {
+                                               remote-endpoint = <&adv7513_0_in>;
+                                               //remote-endpoint = <&dsi_0_in>;
+                                       };
+                               };
+
+                               port@1 {
+                                       endpoint {
+                                               remote-endpoint = <&dc_out_dpi0>;
+                                       };
+                               };
+
+                       };
+               };
+
+               dc8200@29400000 {
+                       compatible = "verisilicon,dc8200";
+                       reg = <0x0 0x29400000 0x0 0x100>,<0x0 0x29400800 0x0 0x2000>;
+                       interrupt-parent = <&plic>;
+                       interrupts = <95>;
+
+                       port {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               dc_out_dpi0: endpoint@0 {
+                                       /*reg = <0>;
+                                       remote-endpoint = <&hdmi_input>;*/
+                               };
+                               dc_out_dpi1: endpoint@1 {
+                                       /*reg = <1>;
+                                       remote-endpoint = <&vd_input>;*/
+                               };
+                       };
+
+                       /*port: port@0 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0>;
+
+                               crtc_0_out: endpoint {
+                               };
+                       };*/
+               };
        };
 };
 
old mode 100644 (file)
new mode 100755 (executable)
index cea777a..355e0b5
@@ -381,6 +381,8 @@ source "drivers/gpu/drm/xlnx/Kconfig"
 
 source "drivers/gpu/drm/gud/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
old mode 100644 (file)
new mode 100755 (executable)
index ad11121..770e724
@@ -128,3 +128,4 @@ obj-$(CONFIG_DRM_TIDSS) += tidss/
 obj-y                  += xlnx/
 obj-y                  += gud/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
+obj-$(CONFIG_DRM_VERISILICON) += verisilicon/
old mode 100644 (file)
new mode 100755 (executable)
index 82afb85..0e20ed7
@@ -290,7 +290,7 @@ static int __drm_universal_plane_init(struct drm_device *dev,
                            !list_empty(&config->plane_list));
                config->allow_fb_modifiers = true;
        } else {
-               drm_WARN_ON(dev, config->allow_fb_modifiers);
+               //drm_WARN_ON(dev, config->allow_fb_modifiers);
        }
 
        plane->modifier_count = format_modifier_count;
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
new file mode 100755 (executable)
index 0000000..cbed5e0
--- /dev/null
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_VERISILICON
+       tristate "DRM Support for VeriSilicon"
+       depends on DRM
+       select DRM_KMS_HELPER
+       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
+       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_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.
+
+config STARFIVE_INNO_HDMI
+       bool "HDMI2.0"
+       help
+         This selects support for Rockchip SoC specific extensions
+         for the Innosilicon HDMI driver. If you want to enable
+         HDMI on RK3036 based SoC, you should select this option.
+config STARFIVE_DSI
+       bool "DSI"
+       help
+         enable dsi, you should select this option.
+
+source "drivers/gpu/drm/verisilicon/adv7511/Kconfig"
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
new file mode 100755 (executable)
index 0000000..04b2d15
--- /dev/null
@@ -0,0 +1,20 @@
+# 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 \
+               starfive_drm_encoder.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_MMU) += vs_dc_mmu.o
+vs_drm-$(CONFIG_VERISILICON_DEC) += vs_dc_dec.o
+vs_drm-$(CONFIG_STARFIVE_INNO_HDMI) += inno_hdmi.o
+vs_drm-$(CONFIG_STARFIVE_DSI) += starfive_drm_dsi.o starfive_drm_seeedpanel.o
+obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o
+
+obj-y+= adv7511/
diff --git a/drivers/gpu/drm/verisilicon/adv7511/Kconfig b/drivers/gpu/drm/verisilicon/adv7511/Kconfig
new file mode 100755 (executable)
index 0000000..c631736
--- /dev/null
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DRM_I2C_ADV7513
+       tristate "ADV7513 encoder"
+       depends on OF
+       select DRM_KMS_HELPER
+       select REGMAP_I2C
+       select DRM_MIPI_DSI
+       help
+         Support for the Analog Devices ADV7511(W)/13/33/35 HDMI encoders.
+
+config DRM_I2C_ADV7511_AUDIO
+       bool "ADV7511 HDMI Audio driver"
+       depends on DRM_I2C_ADV7511 && SND_SOC
+       select SND_SOC_HDMI_CODEC
+       help
+         Support the ADV7511 HDMI Audio interface. This is used in
+         conjunction with the AV7511  HDMI driver.
+
+config DRM_I2C_ADV7511_CEC
+       bool "ADV7511/33/35 HDMI CEC driver"
+       depends on DRM_I2C_ADV7513
+       select CEC_CORE
+       default y
+       help
+         When selected the HDMI transmitter will support the CEC feature.
diff --git a/drivers/gpu/drm/verisilicon/adv7511/Makefile b/drivers/gpu/drm/verisilicon/adv7511/Makefile
new file mode 100755 (executable)
index 0000000..70a90e4
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+adv7513-y := adv7511_drv.o adv7533.o
+adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
+adv7511-$(CONFIG_DRM_I2C_ADV7511_CEC) += adv7511_cec.o
+obj-y += adv7513.o
diff --git a/drivers/gpu/drm/verisilicon/adv7511/adv7511.h b/drivers/gpu/drm/verisilicon/adv7511/adv7511.h
new file mode 100755 (executable)
index 0000000..9f30c93
--- /dev/null
@@ -0,0 +1,420 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Analog Devices ADV7511 HDMI transmitter driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ */
+
+#ifndef __DRM_I2C_ADV7511_H__
+#define __DRM_I2C_ADV7511_H__
+
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+
+#define ADV7511_REG_CHIP_REVISION              0x00
+#define ADV7511_REG_N0                         0x01
+#define ADV7511_REG_N1                         0x02
+#define ADV7511_REG_N2                         0x03
+#define ADV7511_REG_SPDIF_FREQ                 0x04
+#define ADV7511_REG_CTS_AUTOMATIC1             0x05
+#define ADV7511_REG_CTS_AUTOMATIC2             0x06
+#define ADV7511_REG_CTS_MANUAL0                        0x07
+#define ADV7511_REG_CTS_MANUAL1                        0x08
+#define ADV7511_REG_CTS_MANUAL2                        0x09
+#define ADV7511_REG_AUDIO_SOURCE               0x0a
+#define ADV7511_REG_AUDIO_CONFIG               0x0b
+#define ADV7511_REG_I2S_CONFIG                 0x0c
+#define ADV7511_REG_I2S_WIDTH                  0x0d
+#define ADV7511_REG_AUDIO_SUB_SRC0             0x0e
+#define ADV7511_REG_AUDIO_SUB_SRC1             0x0f
+#define ADV7511_REG_AUDIO_SUB_SRC2             0x10
+#define ADV7511_REG_AUDIO_SUB_SRC3             0x11
+#define ADV7511_REG_AUDIO_CFG1                 0x12
+#define ADV7511_REG_AUDIO_CFG2                 0x13
+#define ADV7511_REG_AUDIO_CFG3                 0x14
+#define ADV7511_REG_I2C_FREQ_ID_CFG            0x15
+#define ADV7511_REG_VIDEO_INPUT_CFG1           0x16
+#define ADV7511_REG_CSC_UPPER(x)               (0x18 + (x) * 2)
+#define ADV7511_REG_CSC_LOWER(x)               (0x19 + (x) * 2)
+#define ADV7511_REG_SYNC_DECODER(x)            (0x30 + (x))
+#define ADV7511_REG_DE_GENERATOR               (0x35 + (x))
+#define ADV7511_REG_PIXEL_REPETITION           0x3b
+#define ADV7511_REG_VIC_MANUAL                 0x3c
+#define ADV7511_REG_VIC_SEND                   0x3d
+#define ADV7511_REG_VIC_DETECTED               0x3e
+#define ADV7511_REG_AUX_VIC_DETECTED           0x3f
+#define ADV7511_REG_PACKET_ENABLE0             0x40
+#define ADV7511_REG_POWER                      0x41
+#define ADV7511_REG_STATUS                     0x42
+#define ADV7511_REG_EDID_I2C_ADDR              0x43
+#define ADV7511_REG_PACKET_ENABLE1             0x44
+#define ADV7511_REG_PACKET_I2C_ADDR            0x45
+#define ADV7511_REG_DSD_ENABLE                 0x46
+#define ADV7511_REG_VIDEO_INPUT_CFG2           0x48
+#define ADV7511_REG_INFOFRAME_UPDATE           0x4a
+#define ADV7511_REG_GC(x)                      (0x4b + (x)) /* 0x4b - 0x51 */
+#define ADV7511_REG_AVI_INFOFRAME_VERSION      0x52
+#define ADV7511_REG_AVI_INFOFRAME_LENGTH       0x53
+#define ADV7511_REG_AVI_INFOFRAME_CHECKSUM     0x54
+#define ADV7511_REG_AVI_INFOFRAME(x)           (0x55 + (x)) /* 0x55 - 0x6f */
+#define ADV7511_REG_AUDIO_INFOFRAME_VERSION    0x70
+#define ADV7511_REG_AUDIO_INFOFRAME_LENGTH     0x71
+#define ADV7511_REG_AUDIO_INFOFRAME_CHECKSUM   0x72
+#define ADV7511_REG_AUDIO_INFOFRAME(x)         (0x73 + (x)) /* 0x73 - 0x7c */
+#define ADV7511_REG_INT_ENABLE(x)              (0x94 + (x))
+#define ADV7511_REG_INT(x)                     (0x96 + (x))
+#define ADV7511_REG_INPUT_CLK_DIV              0x9d
+#define ADV7511_REG_PLL_STATUS                 0x9e
+#define ADV7511_REG_HDMI_POWER                 0xa1
+#define ADV7511_REG_HDCP_HDMI_CFG              0xaf
+#define ADV7511_REG_AN(x)                      (0xb0 + (x)) /* 0xb0 - 0xb7 */
+#define ADV7511_REG_HDCP_STATUS                        0xb8
+#define ADV7511_REG_BCAPS                      0xbe
+#define ADV7511_REG_BKSV(x)                    (0xc0 + (x)) /* 0xc0 - 0xc3 */
+#define ADV7511_REG_EDID_SEGMENT               0xc4
+#define ADV7511_REG_DDC_STATUS                 0xc8
+#define ADV7511_REG_EDID_READ_CTRL             0xc9
+#define ADV7511_REG_BSTATUS(x)                 (0xca + (x)) /* 0xca - 0xcb */
+#define ADV7511_REG_TIMING_GEN_SEQ             0xd0
+#define ADV7511_REG_POWER2                     0xd6
+#define ADV7511_REG_HSYNC_PLACEMENT_MSB                0xfa
+
+#define ADV7511_REG_SYNC_ADJUSTMENT(x)         (0xd7 + (x)) /* 0xd7 - 0xdc */
+#define ADV7511_REG_TMDS_CLOCK_INV             0xde
+#define ADV7511_REG_ARC_CTRL                   0xdf
+#define ADV7511_REG_CEC_I2C_ADDR               0xe1
+#define ADV7511_REG_CEC_CTRL                   0xe2
+#define ADV7511_REG_CHIP_ID_HIGH               0xf5
+#define ADV7511_REG_CHIP_ID_LOW                        0xf6
+
+/* Hardware defined default addresses for I2C register maps */
+#define ADV7511_CEC_I2C_ADDR_DEFAULT           0x3c
+#define ADV7511_EDID_I2C_ADDR_DEFAULT          0x3f
+#define ADV7511_PACKET_I2C_ADDR_DEFAULT                0x38
+
+#define ADV7511_CSC_ENABLE                     BIT(7)
+#define ADV7511_CSC_UPDATE_MODE                        BIT(5)
+
+#define ADV7511_INT0_HPD                       BIT(7)
+#define ADV7511_INT0_VSYNC                     BIT(5)
+#define ADV7511_INT0_AUDIO_FIFO_FULL           BIT(4)
+#define ADV7511_INT0_EDID_READY                        BIT(2)
+#define ADV7511_INT0_HDCP_AUTHENTICATED                BIT(1)
+
+#define ADV7511_INT1_DDC_ERROR                 BIT(7)
+#define ADV7511_INT1_BKSV                      BIT(6)
+#define ADV7511_INT1_CEC_TX_READY              BIT(5)
+#define ADV7511_INT1_CEC_TX_ARBIT_LOST         BIT(4)
+#define ADV7511_INT1_CEC_TX_RETRY_TIMEOUT      BIT(3)
+#define ADV7511_INT1_CEC_RX_READY3             BIT(2)
+#define ADV7511_INT1_CEC_RX_READY2             BIT(1)
+#define ADV7511_INT1_CEC_RX_READY1             BIT(0)
+
+#define ADV7511_ARC_CTRL_POWER_DOWN            BIT(0)
+
+#define ADV7511_CEC_CTRL_POWER_DOWN            BIT(0)
+
+#define ADV7511_POWER_POWER_DOWN               BIT(6)
+
+#define ADV7511_HDMI_CFG_MODE_MASK             0x2
+#define ADV7511_HDMI_CFG_MODE_DVI              0x0
+#define ADV7511_HDMI_CFG_MODE_HDMI             0x2
+
+#define ADV7511_AUDIO_SELECT_I2C               0x0
+#define ADV7511_AUDIO_SELECT_SPDIF             0x1
+#define ADV7511_AUDIO_SELECT_DSD               0x2
+#define ADV7511_AUDIO_SELECT_HBR               0x3
+#define ADV7511_AUDIO_SELECT_DST               0x4
+
+#define ADV7511_I2S_SAMPLE_LEN_16              0x2
+#define ADV7511_I2S_SAMPLE_LEN_20              0x3
+#define ADV7511_I2S_SAMPLE_LEN_18              0x4
+#define ADV7511_I2S_SAMPLE_LEN_22              0x5
+#define ADV7511_I2S_SAMPLE_LEN_19              0x8
+#define ADV7511_I2S_SAMPLE_LEN_23              0x9
+#define ADV7511_I2S_SAMPLE_LEN_24              0xb
+#define ADV7511_I2S_SAMPLE_LEN_17              0xc
+#define ADV7511_I2S_SAMPLE_LEN_21              0xd
+
+#define ADV7511_SAMPLE_FREQ_44100              0x0
+#define ADV7511_SAMPLE_FREQ_48000              0x2
+#define ADV7511_SAMPLE_FREQ_32000              0x3
+#define ADV7511_SAMPLE_FREQ_88200              0x8
+#define ADV7511_SAMPLE_FREQ_96000              0xa
+#define ADV7511_SAMPLE_FREQ_176400             0xc
+#define ADV7511_SAMPLE_FREQ_192000             0xe
+
+#define ADV7511_STATUS_POWER_DOWN_POLARITY     BIT(7)
+#define ADV7511_STATUS_HPD                     BIT(6)
+#define ADV7511_STATUS_MONITOR_SENSE           BIT(5)
+#define ADV7511_STATUS_I2S_32BIT_MODE          BIT(3)
+
+#define ADV7511_PACKET_ENABLE_N_CTS            BIT(8+6)
+#define ADV7511_PACKET_ENABLE_AUDIO_SAMPLE     BIT(8+5)
+#define ADV7511_PACKET_ENABLE_AVI_INFOFRAME    BIT(8+4)
+#define ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME  BIT(8+3)
+#define ADV7511_PACKET_ENABLE_GC               BIT(7)
+#define ADV7511_PACKET_ENABLE_SPD              BIT(6)
+#define ADV7511_PACKET_ENABLE_MPEG             BIT(5)
+#define ADV7511_PACKET_ENABLE_ACP              BIT(4)
+#define ADV7511_PACKET_ENABLE_ISRC             BIT(3)
+#define ADV7511_PACKET_ENABLE_GM               BIT(2)
+#define ADV7511_PACKET_ENABLE_SPARE2           BIT(1)
+#define ADV7511_PACKET_ENABLE_SPARE1           BIT(0)
+
+#define ADV7511_REG_POWER2_HPD_SRC_MASK                0xc0
+#define ADV7511_REG_POWER2_HPD_SRC_BOTH                0x00
+#define ADV7511_REG_POWER2_HPD_SRC_HPD         0x40
+#define ADV7511_REG_POWER2_HPD_SRC_CEC         0x80
+#define ADV7511_REG_POWER2_HPD_SRC_NONE                0xc0
+#define ADV7511_REG_POWER2_TDMS_ENABLE         BIT(4)
+#define ADV7511_REG_POWER2_GATE_INPUT_CLK      BIT(0)
+
+#define ADV7511_LOW_REFRESH_RATE_NONE          0x0
+#define ADV7511_LOW_REFRESH_RATE_24HZ          0x1
+#define ADV7511_LOW_REFRESH_RATE_25HZ          0x2
+#define ADV7511_LOW_REFRESH_RATE_30HZ          0x3
+
+#define ADV7511_AUDIO_CFG3_LEN_MASK            0x0f
+#define ADV7511_I2C_FREQ_ID_CFG_RATE_MASK      0xf0
+
+#define ADV7511_AUDIO_SOURCE_I2S               0
+#define ADV7511_AUDIO_SOURCE_SPDIF             1
+
+#define ADV7511_I2S_FORMAT_I2S                 0
+#define ADV7511_I2S_FORMAT_RIGHT_J             1
+#define ADV7511_I2S_FORMAT_LEFT_J              2
+
+#define ADV7511_PACKET(p, x)       ((p) * 0x20 + (x))
+#define ADV7511_PACKET_SDP(x)      ADV7511_PACKET(0, x)
+#define ADV7511_PACKET_MPEG(x)     ADV7511_PACKET(1, x)
+#define ADV7511_PACKET_ACP(x)      ADV7511_PACKET(2, x)
+#define ADV7511_PACKET_ISRC1(x)            ADV7511_PACKET(3, x)
+#define ADV7511_PACKET_ISRC2(x)            ADV7511_PACKET(4, x)
+#define ADV7511_PACKET_GM(x)       ADV7511_PACKET(5, x)
+#define ADV7511_PACKET_SPARE(x)            ADV7511_PACKET(6, x)
+
+#define ADV7511_REG_CEC_TX_FRAME_HDR   0x00
+#define ADV7511_REG_CEC_TX_FRAME_DATA0 0x01
+#define ADV7511_REG_CEC_TX_FRAME_LEN   0x10
+#define ADV7511_REG_CEC_TX_ENABLE      0x11
+#define ADV7511_REG_CEC_TX_RETRY       0x12
+#define ADV7511_REG_CEC_TX_LOW_DRV_CNT 0x14
+#define ADV7511_REG_CEC_RX_FRAME_HDR   0x15
+#define ADV7511_REG_CEC_RX_FRAME_DATA0 0x16
+#define ADV7511_REG_CEC_RX_FRAME_LEN   0x25
+#define ADV7511_REG_CEC_RX_ENABLE      0x26
+#define ADV7511_REG_CEC_RX_BUFFERS     0x4a
+#define ADV7511_REG_CEC_LOG_ADDR_MASK  0x4b
+#define ADV7511_REG_CEC_LOG_ADDR_0_1   0x4c
+#define ADV7511_REG_CEC_LOG_ADDR_2     0x4d
+#define ADV7511_REG_CEC_CLK_DIV                0x4e
+#define ADV7511_REG_CEC_SOFT_RESET     0x50
+
+#define ADV7533_REG_CEC_OFFSET         0x70
+
+enum adv7511_input_clock {
+       ADV7511_INPUT_CLOCK_1X,
+       ADV7511_INPUT_CLOCK_2X,
+       ADV7511_INPUT_CLOCK_DDR,
+};
+
+enum adv7511_input_justification {
+       ADV7511_INPUT_JUSTIFICATION_EVENLY = 0,
+       ADV7511_INPUT_JUSTIFICATION_RIGHT = 1,
+       ADV7511_INPUT_JUSTIFICATION_LEFT = 2,
+};
+
+enum adv7511_input_sync_pulse {
+       ADV7511_INPUT_SYNC_PULSE_DE = 0,
+       ADV7511_INPUT_SYNC_PULSE_HSYNC = 1,
+       ADV7511_INPUT_SYNC_PULSE_VSYNC = 2,
+       ADV7511_INPUT_SYNC_PULSE_NONE = 3,
+};
+
+/**
+ * enum adv7511_sync_polarity - Polarity for the input sync signals
+ * @ADV7511_SYNC_POLARITY_PASSTHROUGH:  Sync polarity matches that of
+ *                                    the currently configured mode.
+ * @ADV7511_SYNC_POLARITY_LOW:     Sync polarity is low
+ * @ADV7511_SYNC_POLARITY_HIGH:            Sync polarity is high
+ *
+ * If the polarity is set to either LOW or HIGH the driver will configure the
+ * ADV7511 to internally invert the sync signal if required to match the sync
+ * polarity setting for the currently selected output mode.
+ *
+ * If the polarity is set to PASSTHROUGH, the ADV7511 will route the signal
+ * unchanged. This is used when the upstream graphics core already generates
+ * the sync signals with the correct polarity.
+ */
+enum adv7511_sync_polarity {
+       ADV7511_SYNC_POLARITY_PASSTHROUGH,
+       ADV7511_SYNC_POLARITY_LOW,
+       ADV7511_SYNC_POLARITY_HIGH,
+};
+
+/**
+ * struct adv7511_link_config - Describes adv7511 hardware configuration
+ * @input_color_depth:         Number of bits per color component (8, 10 or 12)
+ * @input_colorspace:          The input colorspace (RGB, YUV444, YUV422)
+ * @input_clock:               The input video clock style (1x, 2x, DDR)
+ * @input_style:               The input component arrangement variant
+ * @input_justification:       Video input format bit justification
+ * @clock_delay:               Clock delay for the input clock (in ps)
+ * @embedded_sync:             Video input uses BT.656-style embedded sync
+ * @sync_pulse:                        Select the sync pulse
+ * @vsync_polarity:            vsync input signal configuration
+ * @hsync_polarity:            hsync input signal configuration
+ */
+struct adv7511_link_config {
+       unsigned int input_color_depth;
+       enum hdmi_colorspace input_colorspace;
+       enum adv7511_input_clock input_clock;
+       unsigned int input_style;
+       enum adv7511_input_justification input_justification;
+
+       int clock_delay;
+
+       bool embedded_sync;
+       enum adv7511_input_sync_pulse sync_pulse;
+       enum adv7511_sync_polarity vsync_polarity;
+       enum adv7511_sync_polarity hsync_polarity;
+};
+
+/**
+ * enum adv7511_csc_scaling - Scaling factor for the ADV7511 CSC
+ * @ADV7511_CSC_SCALING_1: CSC results are not scaled
+ * @ADV7511_CSC_SCALING_2: CSC results are scaled by a factor of two
+ * @ADV7511_CSC_SCALING_4: CSC results are scalled by a factor of four
+ */
+enum adv7511_csc_scaling {
+       ADV7511_CSC_SCALING_1 = 0,
+       ADV7511_CSC_SCALING_2 = 1,
+       ADV7511_CSC_SCALING_4 = 2,
+};
+
+/**
+ * struct adv7511_video_config - Describes adv7511 hardware configuration
+ * @csc_enable:                        Whether to enable color space conversion
+ * @csc_scaling_factor:                Color space conversion scaling factor
+ * @csc_coefficents:           Color space conversion coefficents
+ * @hdmi_mode:                 Whether to use HDMI or DVI output mode
+ * @avi_infoframe:             HDMI infoframe
+ */
+struct adv7511_video_config {
+       bool csc_enable;
+       enum adv7511_csc_scaling csc_scaling_factor;
+       const uint16_t *csc_coefficents;
+
+       bool hdmi_mode;
+       struct hdmi_avi_infoframe avi_infoframe;
+};
+
+enum adv7511_type {
+       ADV7511,
+       ADV7533,
+       ADV7535,
+};
+
+#define ADV7511_MAX_ADDRS 3
+
+struct adv7511 {
+       struct i2c_client *i2c_main;
+       struct i2c_client *i2c_edid;
+       struct i2c_client *i2c_packet;
+       struct i2c_client *i2c_cec;
+
+       struct regmap *regmap;
+       struct regmap *regmap_cec;
+       enum drm_connector_status status;
+       bool powered;
+
+       struct drm_display_mode curr_mode;
+
+       unsigned int f_tmds;
+       unsigned int f_audio;
+       unsigned int audio_source;
+
+       unsigned int current_edid_segment;
+       uint8_t edid_buf[256];
+       bool edid_read;
+
+       wait_queue_head_t wq;
+       struct work_struct hpd_work;
+
+       struct drm_encoder encoder;
+       struct drm_bridge bridge;
+       struct drm_connector connector;
+
+       bool embedded_sync;
+       enum adv7511_sync_polarity vsync_polarity;
+       enum adv7511_sync_polarity hsync_polarity;
+       bool rgb;
+
+       struct gpio_desc *gpio_pd;
+
+       struct regulator_bulk_data *supplies;
+       unsigned int num_supplies;
+
+       /* ADV7533 DSI RX related params */
+       struct device_node *host_node;
+       struct mipi_dsi_device *dsi;
+       u8 num_dsi_lanes;
+       bool use_timing_gen;
+
+       enum adv7511_type type;
+       struct platform_device *audio_pdev;
+
+       struct cec_adapter *cec_adap;
+       u8   cec_addr[ADV7511_MAX_ADDRS];
+       u8   cec_valid_addrs;
+       bool cec_enabled_adap;
+       struct clk *cec_clk;
+       u32 cec_clk_freq;
+};
+
+#ifdef CONFIG_DRM_I2C_ADV7511_CEC
+int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511);
+void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
+#else
+static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
+{
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                               ADV7533_REG_CEC_OFFSET : 0;
+
+       regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
+                    ADV7511_CEC_CTRL_POWER_DOWN);
+       return 0;
+}
+#endif
+
+void adv7533_dsi_power_on(struct adv7511 *adv);
+void adv7533_dsi_power_off(struct adv7511 *adv);
+void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode);
+int adv7533_patch_registers(struct adv7511 *adv);
+int adv7533_patch_cec_registers(struct adv7511 *adv);
+int adv7533_attach_dsi(struct adv7511 *adv);
+void adv7533_detach_dsi(struct adv7511 *adv);
+int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
+
+#ifdef CONFIG_DRM_I2C_ADV7511_AUDIO
+int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511);
+void adv7511_audio_exit(struct adv7511 *adv7511);
+#else /*CONFIG_DRM_I2C_ADV7511_AUDIO */
+static inline int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
+{
+       return 0;
+}
+static inline void adv7511_audio_exit(struct adv7511 *adv7511)
+{
+}
+#endif /* CONFIG_DRM_I2C_ADV7511_AUDIO */
+
+#endif /* __DRM_I2C_ADV7511_H__ */
diff --git a/drivers/gpu/drm/verisilicon/adv7511/adv7511_audio.c b/drivers/gpu/drm/verisilicon/adv7511/adv7511_audio.c
new file mode 100755 (executable)
index 0000000..f101dd2
--- /dev/null
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADV7511 HDMI transmitter driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Copyright (c) 2016, Linaro Limited
+ */
+
+#include <sound/core.h>
+#include <sound/hdmi-codec.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/of_graph.h>
+
+#include "adv7511.h"
+
+static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs,
+                              unsigned int *cts, unsigned int *n)
+{
+       switch (fs) {
+       case 32000:
+       case 48000:
+       case 96000:
+       case 192000:
+               *n = fs * 128 / 1000;
+               break;
+       case 44100:
+       case 88200:
+       case 176400:
+               *n = fs * 128 / 900;
+               break;
+       }
+
+       *cts = ((f_tmds * *n) / (128 * fs)) * 1000;
+}
+
+static int adv7511_update_cts_n(struct adv7511 *adv7511)
+{
+       unsigned int cts = 0;
+       unsigned int n = 0;
+
+       adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n);
+
+       regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf);
+       regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff);
+       regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff);
+
+       regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0,
+                    (cts >> 16) & 0xf);
+       regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1,
+                    (cts >> 8) & 0xff);
+       regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2,
+                    cts & 0xff);
+
+       return 0;
+}
+
+int adv7511_hdmi_hw_params(struct device *dev, void *data,
+                          struct hdmi_codec_daifmt *fmt,
+                          struct hdmi_codec_params *hparms)
+{
+       struct adv7511 *adv7511 = dev_get_drvdata(dev);
+       unsigned int audio_source, i2s_format = 0;
+       unsigned int invert_clock;
+       unsigned int rate;
+       unsigned int len;
+
+       switch (hparms->sample_rate) {
+       case 32000:
+               rate = ADV7511_SAMPLE_FREQ_32000;
+               break;
+       case 44100:
+               rate = ADV7511_SAMPLE_FREQ_44100;
+               break;
+       case 48000:
+               rate = ADV7511_SAMPLE_FREQ_48000;
+               break;
+       case 88200:
+               rate = ADV7511_SAMPLE_FREQ_88200;
+               break;
+       case 96000:
+               rate = ADV7511_SAMPLE_FREQ_96000;
+               break;
+       case 176400:
+               rate = ADV7511_SAMPLE_FREQ_176400;
+               break;
+       case 192000:
+               rate = ADV7511_SAMPLE_FREQ_192000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (hparms->sample_width) {
+       case 16:
+               len = ADV7511_I2S_SAMPLE_LEN_16;
+               break;
+       case 18:
+               len = ADV7511_I2S_SAMPLE_LEN_18;
+               break;
+       case 20:
+               len = ADV7511_I2S_SAMPLE_LEN_20;
+               break;
+       case 24:
+               len = ADV7511_I2S_SAMPLE_LEN_24;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt->fmt) {
+       case HDMI_I2S:
+               audio_source = ADV7511_AUDIO_SOURCE_I2S;
+               i2s_format = ADV7511_I2S_FORMAT_I2S;
+               break;
+       case HDMI_RIGHT_J:
+               audio_source = ADV7511_AUDIO_SOURCE_I2S;
+               i2s_format = ADV7511_I2S_FORMAT_RIGHT_J;
+               break;
+       case HDMI_LEFT_J:
+               audio_source = ADV7511_AUDIO_SOURCE_I2S;
+               i2s_format = ADV7511_I2S_FORMAT_LEFT_J;
+               break;
+       case HDMI_SPDIF:
+               audio_source = ADV7511_AUDIO_SOURCE_SPDIF;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       invert_clock = fmt->bit_clk_inv;
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70,
+                          audio_source << 4);
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6),
+                          invert_clock << 6);
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03,
+                          i2s_format);
+
+       adv7511->audio_source = audio_source;
+
+       adv7511->f_audio = hparms->sample_rate;
+
+       adv7511_update_cts_n(adv7511);
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3,
+                          ADV7511_AUDIO_CFG3_LEN_MASK, len);
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG,
+                          ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4);
+       regmap_write(adv7511->regmap, 0x73, 0x1);
+
+       return 0;
+}
+
+static int audio_startup(struct device *dev, void *data)
+{
+       struct adv7511 *adv7511 = dev_get_drvdata(dev);
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
+                               BIT(7), 0);
+
+       /* hide Audio infoframe updates */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
+                               BIT(5), BIT(5));
+       /* enable N/CTS, enable Audio sample packets */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+                               BIT(5), BIT(5));
+       /* enable N/CTS */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+                               BIT(6), BIT(6));
+       /* not copyrighted */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1,
+                               BIT(5), BIT(5));
+       /* enable audio infoframes */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+                               BIT(3), BIT(3));
+       /* AV mute disable */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0),
+                               BIT(7) | BIT(6), BIT(7));
+       /* use Audio infoframe updated info */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(1),
+                               BIT(5), 0);
+       /* enable SPDIF receiver */
+       if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
+                                  BIT(7), BIT(7));
+
+       return 0;
+}
+
+static void audio_shutdown(struct device *dev, void *data)
+{
+       struct adv7511 *adv7511 = dev_get_drvdata(dev);
+
+       if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
+                                  BIT(7), 0);
+}
+
+static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
+                                       struct device_node *endpoint)
+{
+       struct of_endpoint of_ep;
+       int ret;
+
+       ret = of_graph_parse_endpoint(endpoint, &of_ep);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * HDMI sound should be located as reg = <2>
+        * Then, it is sound port 0
+        */
+       if (of_ep.port == 2)
+               return 0;
+
+       return -EINVAL;
+}
+
+static const struct hdmi_codec_ops adv7511_codec_ops = {
+       .hw_params      = adv7511_hdmi_hw_params,
+       .audio_shutdown = audio_shutdown,
+       .audio_startup  = audio_startup,
+       .get_dai_id     = adv7511_hdmi_i2s_get_dai_id,
+};
+
+static const struct hdmi_codec_pdata codec_data = {
+       .ops = &adv7511_codec_ops,
+       .max_i2s_channels = 2,
+       .i2s = 1,
+       .spdif = 1,
+};
+
+int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
+{
+       adv7511->audio_pdev = platform_device_register_data(dev,
+                                       HDMI_CODEC_DRV_NAME,
+                                       PLATFORM_DEVID_AUTO,
+                                       &codec_data,
+                                       sizeof(codec_data));
+       return PTR_ERR_OR_ZERO(adv7511->audio_pdev);
+}
+
+void adv7511_audio_exit(struct adv7511 *adv7511)
+{
+       if (adv7511->audio_pdev) {
+               platform_device_unregister(adv7511->audio_pdev);
+               adv7511->audio_pdev = NULL;
+       }
+}
diff --git a/drivers/gpu/drm/verisilicon/adv7511/adv7511_cec.c b/drivers/gpu/drm/verisilicon/adv7511/adv7511_cec.c
new file mode 100755 (executable)
index 0000000..a20a45c
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * adv7511_cec.c - Analog Devices ADV7511/33 cec driver
+ *
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <media/cec.h>
+
+#include "adv7511.h"
+
+#define ADV7511_INT1_CEC_MASK \
+       (ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
+        ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1)
+
+static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
+{
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                       ADV7533_REG_CEC_OFFSET : 0;
+       unsigned int val;
+
+       if (regmap_read(adv7511->regmap_cec,
+                       ADV7511_REG_CEC_TX_ENABLE + offset, &val))
+               return;
+
+       if ((val & 0x01) == 0)
+               return;
+
+       if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
+               cec_transmit_attempt_done(adv7511->cec_adap,
+                                         CEC_TX_STATUS_ARB_LOST);
+               return;
+       }
+       if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
+               u8 status;
+               u8 err_cnt = 0;
+               u8 nack_cnt = 0;
+               u8 low_drive_cnt = 0;
+               unsigned int cnt;
+
+               /*
+                * We set this status bit since this hardware performs
+                * retransmissions.
+                */
+               status = CEC_TX_STATUS_MAX_RETRIES;
+               if (regmap_read(adv7511->regmap_cec,
+                           ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
+                       err_cnt = 1;
+                       status |= CEC_TX_STATUS_ERROR;
+               } else {
+                       nack_cnt = cnt & 0xf;
+                       if (nack_cnt)
+                               status |= CEC_TX_STATUS_NACK;
+                       low_drive_cnt = cnt >> 4;
+                       if (low_drive_cnt)
+                               status |= CEC_TX_STATUS_LOW_DRIVE;
+               }
+               cec_transmit_done(adv7511->cec_adap, status,
+                                 0, nack_cnt, low_drive_cnt, err_cnt);
+               return;
+       }
+       if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
+               cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
+               return;
+       }
+}
+
+void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
+{
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                       ADV7533_REG_CEC_OFFSET : 0;
+       const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
+                               ADV7511_INT1_CEC_TX_ARBIT_LOST |
+                               ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
+       struct cec_msg msg = {};
+       unsigned int len;
+       unsigned int val;
+       u8 i;
+
+       if (irq1 & irq_tx_mask)
+               adv_cec_tx_raw_status(adv7511, irq1);
+
+       if (!(irq1 & ADV7511_INT1_CEC_RX_READY1))
+               return;
+
+       if (regmap_read(adv7511->regmap_cec,
+                       ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len))
+               return;
+
+       msg.len = len & 0x1f;
+
+       if (msg.len > 16)
+               msg.len = 16;
+
+       if (!msg.len)
+               return;
+
+       for (i = 0; i < msg.len; i++) {
+               regmap_read(adv7511->regmap_cec,
+                           i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val);
+               msg.msg[i] = val;
+       }
+
+       /* toggle to re-enable rx 1 */
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_RX_BUFFERS + offset, 1);
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
+       cec_received_msg(adv7511->cec_adap, &msg);
+}
+
+static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct adv7511 *adv7511 = cec_get_drvdata(adap);
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                       ADV7533_REG_CEC_OFFSET : 0;
+
+       if (adv7511->i2c_cec == NULL)
+               return -EIO;
+
+       if (!adv7511->cec_enabled_adap && enable) {
+               /* power up cec section */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_CLK_DIV + offset,
+                                  0x03, 0x01);
+               /* legacy mode and clear all rx buffers */
+               regmap_write(adv7511->regmap_cec,
+                            ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07);
+               regmap_write(adv7511->regmap_cec,
+                            ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
+               /* initially disable tx */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
+               /* enabled irqs: */
+               /* tx: ready */
+               /* tx: arbitration lost */
+               /* tx: retry timeout */
+               /* rx: ready 1 */
+               regmap_update_bits(adv7511->regmap,
+                                  ADV7511_REG_INT_ENABLE(1), 0x3f,
+                                  ADV7511_INT1_CEC_MASK);
+       } else if (adv7511->cec_enabled_adap && !enable) {
+               regmap_update_bits(adv7511->regmap,
+                                  ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
+               /* disable address mask 1-3 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+                                  0x70, 0x00);
+               /* power down cec section */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_CLK_DIV + offset,
+                                  0x03, 0x00);
+               adv7511->cec_valid_addrs = 0;
+       }
+       adv7511->cec_enabled_adap = enable;
+       return 0;
+}
+
+static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+       struct adv7511 *adv7511 = cec_get_drvdata(adap);
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                       ADV7533_REG_CEC_OFFSET : 0;
+       unsigned int i, free_idx = ADV7511_MAX_ADDRS;
+
+       if (!adv7511->cec_enabled_adap)
+               return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
+
+       if (addr == CEC_LOG_ADDR_INVALID) {
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+                                  0x70, 0);
+               adv7511->cec_valid_addrs = 0;
+               return 0;
+       }
+
+       for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
+               bool is_valid = adv7511->cec_valid_addrs & (1 << i);
+
+               if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
+                       free_idx = i;
+               if (is_valid && adv7511->cec_addr[i] == addr)
+                       return 0;
+       }
+       if (i == ADV7511_MAX_ADDRS) {
+               i = free_idx;
+               if (i == ADV7511_MAX_ADDRS)
+                       return -ENXIO;
+       }
+       adv7511->cec_addr[i] = addr;
+       adv7511->cec_valid_addrs |= 1 << i;
+
+       switch (i) {
+       case 0:
+               /* enable address mask 0 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+                                  0x10, 0x10);
+               /* set address for mask 0 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
+                                  0x0f, addr);
+               break;
+       case 1:
+               /* enable address mask 1 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+                                  0x20, 0x20);
+               /* set address for mask 1 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
+                                  0xf0, addr << 4);
+               break;
+       case 2:
+               /* enable address mask 2 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+                                  0x40, 0x40);
+               /* set address for mask 1 */
+               regmap_update_bits(adv7511->regmap_cec,
+                                  ADV7511_REG_CEC_LOG_ADDR_2 + offset,
+                                  0x0f, addr);
+               break;
+       }
+       return 0;
+}
+
+static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                    u32 signal_free_time, struct cec_msg *msg)
+{
+       struct adv7511 *adv7511 = cec_get_drvdata(adap);
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                       ADV7533_REG_CEC_OFFSET : 0;
+       u8 len = msg->len;
+       unsigned int i;
+
+       /*
+        * The number of retries is the number of attempts - 1, but retry
+        * at least once. It's not clear if a value of 0 is allowed, so
+        * let's do at least one retry.
+        */
+       regmap_update_bits(adv7511->regmap_cec,
+                          ADV7511_REG_CEC_TX_RETRY + offset,
+                          0x70, max(1, attempts - 1) << 4);
+
+       /* blocking, clear cec tx irq status */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
+
+       /* write data */
+       for (i = 0; i < len; i++)
+               regmap_write(adv7511->regmap_cec,
+                            i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
+                            msg->msg[i]);
+
+       /* set length (data + header) */
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
+       /* start transmit, enable tx */
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
+       return 0;
+}
+
+static const struct cec_adap_ops adv7511_cec_adap_ops = {
+       .adap_enable = adv7511_cec_adap_enable,
+       .adap_log_addr = adv7511_cec_adap_log_addr,
+       .adap_transmit = adv7511_cec_adap_transmit,
+};
+
+static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
+{
+       adv7511->cec_clk = devm_clk_get(dev, "cec");
+       if (IS_ERR(adv7511->cec_clk)) {
+               int ret = PTR_ERR(adv7511->cec_clk);
+
+               adv7511->cec_clk = NULL;
+               return ret;
+       }
+       clk_prepare_enable(adv7511->cec_clk);
+       adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
+       return 0;
+}
+
+int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
+{
+       unsigned int offset = adv7511->type == ADV7533 ?
+                                               ADV7533_REG_CEC_OFFSET : 0;
+       int ret = adv7511_cec_parse_dt(dev, adv7511);
+
+       if (ret)
+               goto err_cec_parse_dt;
+
+       adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
+               adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
+       if (IS_ERR(adv7511->cec_adap)) {
+               ret = PTR_ERR(adv7511->cec_adap);
+               goto err_cec_alloc;
+       }
+
+       regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0);
+       /* cec soft reset */
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
+
+       /* legacy mode */
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00);
+
+       regmap_write(adv7511->regmap_cec,
+                    ADV7511_REG_CEC_CLK_DIV + offset,
+                    ((adv7511->cec_clk_freq / 750000) - 1) << 2);
+
+       ret = cec_register_adapter(adv7511->cec_adap, dev);
+       if (ret)
+               goto err_cec_register;
+       return 0;
+
+err_cec_register:
+       cec_delete_adapter(adv7511->cec_adap);
+       adv7511->cec_adap = NULL;
+err_cec_alloc:
+       dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",
+                ret);
+err_cec_parse_dt:
+       regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
+                    ADV7511_CEC_CTRL_POWER_DOWN);
+       return ret == -EPROBE_DEFER ? ret : 0;
+}
diff --git a/drivers/gpu/drm/verisilicon/adv7511/adv7511_drv.c b/drivers/gpu/drm/verisilicon/adv7511/adv7511_drv.c
new file mode 100755 (executable)
index 0000000..7585778
--- /dev/null
@@ -0,0 +1,1384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADV7511 HDMI transmitter driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <media/cec.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "adv7511.h"
+
+/* ADI recommended values for proper operation. */
+static const struct reg_sequence adv7511_fixed_registers[] = {
+       { 0x98, 0x03 },
+       { 0x9a, 0xe0 },
+       { 0x9c, 0x30 },
+       { 0x9d, 0x61 },
+       { 0xa2, 0xa4 },
+       { 0xa3, 0xa4 },
+       { 0xe0, 0xd0 },
+       { 0xf9, 0x00 },
+       { 0x55, 0x02 },
+};
+
+/* -----------------------------------------------------------------------------
+ * Register access
+ */
+
+static const uint8_t adv7511_register_defaults[] = {
+       0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00 */
+       0x00, 0x00, 0x01, 0x0e, 0xbc, 0x18, 0x01, 0x13,
+       0x25, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */
+       0x46, 0x62, 0x04, 0xa8, 0x00, 0x00, 0x1c, 0x84,
+       0x1c, 0xbf, 0x04, 0xa8, 0x1e, 0x70, 0x02, 0x1e, /* 20 */
+       0x00, 0x00, 0x04, 0xa8, 0x08, 0x12, 0x1b, 0xac,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */
+       0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb0,
+       0x00, 0x50, 0x90, 0x7e, 0x79, 0x70, 0x00, 0x00, /* 40 */
+       0x00, 0xa8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x00, /* 50 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 90 */
+       0x0b, 0x02, 0x00, 0x18, 0x5a, 0x60, 0x00, 0x00,
+       0x00, 0x00, 0x80, 0x80, 0x08, 0x04, 0x00, 0x00, /* a0 */
+       0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x14,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b0 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c0 */
+       0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x01, 0x04,
+       0x30, 0xff, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, /* d0 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01,
+       0x80, 0x75, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, /* e0 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x11, 0x00, /* f0 */
+       0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static bool adv7511_register_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ADV7511_REG_CHIP_REVISION:
+       case ADV7511_REG_SPDIF_FREQ:
+       case ADV7511_REG_CTS_AUTOMATIC1:
+       case ADV7511_REG_CTS_AUTOMATIC2:
+       case ADV7511_REG_VIC_DETECTED:
+       case ADV7511_REG_VIC_SEND:
+       case ADV7511_REG_AUX_VIC_DETECTED:
+       case ADV7511_REG_STATUS:
+       case ADV7511_REG_GC(1):
+       case ADV7511_REG_INT(0):
+       case ADV7511_REG_INT(1):
+       case ADV7511_REG_PLL_STATUS:
+       case ADV7511_REG_AN(0):
+       case ADV7511_REG_AN(1):
+       case ADV7511_REG_AN(2):
+       case ADV7511_REG_AN(3):
+       case ADV7511_REG_AN(4):
+       case ADV7511_REG_AN(5):
+       case ADV7511_REG_AN(6):
+       case ADV7511_REG_AN(7):
+       case ADV7511_REG_HDCP_STATUS:
+       case ADV7511_REG_BCAPS:
+       case ADV7511_REG_BKSV(0):
+       case ADV7511_REG_BKSV(1):
+       case ADV7511_REG_BKSV(2):
+       case ADV7511_REG_BKSV(3):
+       case ADV7511_REG_BKSV(4):
+       case ADV7511_REG_DDC_STATUS:
+       case ADV7511_REG_EDID_READ_CTRL:
+       case ADV7511_REG_BSTATUS(0):
+       case ADV7511_REG_BSTATUS(1):
+       case ADV7511_REG_CHIP_ID_HIGH:
+       case ADV7511_REG_CHIP_ID_LOW:
+               return true;
+       }
+
+       return false;
+}
+
+static const struct regmap_config adv7511_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = 0xff,
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults_raw = adv7511_register_defaults,
+       .num_reg_defaults_raw = ARRAY_SIZE(adv7511_register_defaults),
+
+       .volatile_reg = adv7511_register_volatile,
+};
+
+/* -----------------------------------------------------------------------------
+ * Hardware configuration
+ */
+
+static void adv7511_set_colormap(struct adv7511 *adv7511, bool enable,
+                                const uint16_t *coeff,
+                                unsigned int scaling_factor)
+{
+       unsigned int i;
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(1),
+                          ADV7511_CSC_UPDATE_MODE, ADV7511_CSC_UPDATE_MODE);
+
+       if (enable) {
+               for (i = 0; i < 12; ++i) {
+                       regmap_update_bits(adv7511->regmap,
+                                          ADV7511_REG_CSC_UPPER(i),
+                                          0x1f, coeff[i] >> 8);
+                       regmap_write(adv7511->regmap,
+                                    ADV7511_REG_CSC_LOWER(i),
+                                    coeff[i] & 0xff);
+               }
+       }
+
+       if (enable)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(0),
+                                  0xe0, 0x80 | (scaling_factor << 5));
+       else
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(0),
+                                  0x80, 0x00);
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_CSC_UPPER(1),
+                          ADV7511_CSC_UPDATE_MODE, 0);
+}
+
+static int adv7511_packet_enable(struct adv7511 *adv7511, unsigned int packet)
+{
+       if (packet & 0xff)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0,
+                                  packet, 0xff);
+
+       if (packet & 0xff00) {
+               packet >>= 8;
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+                                  packet, 0xff);
+       }
+
+       return 0;
+}
+
+static int adv7511_packet_disable(struct adv7511 *adv7511, unsigned int packet)
+{
+       if (packet & 0xff)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE0,
+                                  packet, 0x00);
+
+       if (packet & 0xff00) {
+               packet >>= 8;
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
+                                  packet, 0x00);
+       }
+
+       return 0;
+}
+
+/* Coefficients for adv7511 color space conversion */
+static const uint16_t adv7511_csc_ycbcr_to_rgb[] = {
+       0x0734, 0x04ad, 0x0000, 0x1c1b,
+       0x1ddc, 0x04ad, 0x1f24, 0x0135,
+       0x0000, 0x04ad, 0x087c, 0x1b77,
+};
+
+static void adv7511_set_config_csc(struct adv7511 *adv7511,
+                                  struct drm_connector *connector,
+                                  bool rgb, bool hdmi_mode)
+{
+       struct adv7511_video_config config;
+       bool output_format_422, output_format_ycbcr;
+       unsigned int mode;
+       uint8_t infoframe[17];
+
+       config.hdmi_mode = hdmi_mode;
+
+       hdmi_avi_infoframe_init(&config.avi_infoframe);
+
+       config.avi_infoframe.scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
+
+       if (rgb) {
+               config.csc_enable = false;
+               config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB;
+       } else {
+               config.csc_scaling_factor = ADV7511_CSC_SCALING_4;
+               config.csc_coefficents = adv7511_csc_ycbcr_to_rgb;
+
+               if ((connector->display_info.color_formats &
+                    DRM_COLOR_FORMAT_YCRCB422) &&
+                   config.hdmi_mode) {
+                       config.csc_enable = false;
+                       config.avi_infoframe.colorspace =
+                               HDMI_COLORSPACE_YUV422;
+               } else {
+                       config.csc_enable = true;
+                       config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB;
+               }
+       }
+
+       if (config.hdmi_mode) {
+               mode = ADV7511_HDMI_CFG_MODE_HDMI;
+
+               switch (config.avi_infoframe.colorspace) {
+               case HDMI_COLORSPACE_YUV444:
+                       output_format_422 = false;
+                       output_format_ycbcr = true;
+                       break;
+               case HDMI_COLORSPACE_YUV422:
+                       output_format_422 = true;
+                       output_format_ycbcr = true;
+                       break;
+               default:
+                       output_format_422 = false;
+                       output_format_ycbcr = false;
+                       break;
+               }
+       } else {
+               mode = ADV7511_HDMI_CFG_MODE_DVI;
+               output_format_422 = false;
+               output_format_ycbcr = false;
+       }
+
+       adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
+
+       adv7511_set_colormap(adv7511, config.csc_enable,
+                            config.csc_coefficents,
+                            config.csc_scaling_factor);
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG1, 0x81,
+                          (output_format_422 << 7) | output_format_ycbcr);
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_HDCP_HDMI_CFG,
+                          ADV7511_HDMI_CFG_MODE_MASK, mode);
+
+       hdmi_avi_infoframe_pack(&config.avi_infoframe, infoframe,
+                               sizeof(infoframe));
+
+       /* The AVI infoframe id is not configurable */
+       regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION,
+                         infoframe + 1, sizeof(infoframe) - 1);
+
+       adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
+}
+
+static void adv7511_set_link_config(struct adv7511 *adv7511,
+                                   const struct adv7511_link_config *config)
+{
+       /*
+        * The input style values documented in the datasheet don't match the
+        * hardware register field values :-(
+        */
+       static const unsigned int input_styles[4] = { 0, 2, 1, 3 };
+
+       unsigned int clock_delay;
+       unsigned int color_depth;
+       unsigned int input_id;
+
+       clock_delay = (config->clock_delay + 1200) / 400;
+       color_depth = config->input_color_depth == 8 ? 3
+                   : (config->input_color_depth == 10 ? 1 : 2);
+
+       /* TODO Support input ID 6 */
+       if (config->input_colorspace != HDMI_COLORSPACE_YUV422)
+               input_id = config->input_clock == ADV7511_INPUT_CLOCK_DDR
+                        ? 5 : 0;
+       else if (config->input_clock == ADV7511_INPUT_CLOCK_DDR)
+               input_id = config->embedded_sync ? 8 : 7;
+       else if (config->input_clock == ADV7511_INPUT_CLOCK_2X)
+               input_id = config->embedded_sync ? 4 : 3;
+       else
+               input_id = config->embedded_sync ? 2 : 1;
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, 0xf,
+                          input_id);
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG1, 0x7e,
+                          (color_depth << 4) |
+                          (input_styles[config->input_style] << 2));
+       regmap_write(adv7511->regmap, ADV7511_REG_VIDEO_INPUT_CFG2,
+                    config->input_justification << 3);
+       regmap_write(adv7511->regmap, ADV7511_REG_TIMING_GEN_SEQ,
+                    config->sync_pulse << 2);
+
+       regmap_write(adv7511->regmap, 0xba, clock_delay << 5);
+
+       adv7511->embedded_sync = config->embedded_sync;
+       adv7511->hsync_polarity = config->hsync_polarity;
+       adv7511->vsync_polarity = config->vsync_polarity;
+       adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
+}
+
+static void __adv7511_power_on(struct adv7511 *adv7511)
+{
+       adv7511->current_edid_segment = -1;
+
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+                          ADV7511_POWER_POWER_DOWN, 0);
+       if (adv7511->i2c_main->irq) {
+               /*
+                * Documentation says the INT_ENABLE registers are reset in
+                * POWER_DOWN mode. My 7511w preserved the bits, however.
+                * Still, let's be safe and stick to the documentation.
+                */
+               regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
+                            ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
+               regmap_update_bits(adv7511->regmap,
+                                  ADV7511_REG_INT_ENABLE(1),
+                                  ADV7511_INT1_DDC_ERROR,
+                                  ADV7511_INT1_DDC_ERROR);
+       }
+
+       /*
+        * Per spec it is allowed to pulse the HPD signal to indicate that the
+        * EDID information has changed. Some monitors do this when they wakeup
+        * from standby or are enabled. When the HPD goes low the adv7511 is
+        * reset and the outputs are disabled which might cause the monitor to
+        * go to standby again. To avoid this we ignore the HPD pin for the
+        * first few seconds after enabling the output.
+        */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                          ADV7511_REG_POWER2_HPD_SRC_MASK,
+                          ADV7511_REG_POWER2_HPD_SRC_NONE);
+}
+
+static void adv7511_power_on(struct adv7511 *adv7511)
+{
+       __adv7511_power_on(adv7511);
+
+       /*
+        * Most of the registers are reset during power down or when HPD is low.
+        */
+       regcache_sync(adv7511->regmap);
+
+       if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
+               adv7533_dsi_power_on(adv7511);
+       adv7511->powered = true;
+}
+
+static void __adv7511_power_off(struct adv7511 *adv7511)
+{
+       /* TODO: setup additional power down modes */
+       regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
+                          ADV7511_POWER_POWER_DOWN,
+                          ADV7511_POWER_POWER_DOWN);
+       regmap_update_bits(adv7511->regmap,
+                          ADV7511_REG_INT_ENABLE(1),
+                          ADV7511_INT1_DDC_ERROR, 0);
+       regcache_mark_dirty(adv7511->regmap);
+}
+
+static void adv7511_power_off(struct adv7511 *adv7511)
+{
+       __adv7511_power_off(adv7511);
+       if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
+               adv7533_dsi_power_off(adv7511);
+       adv7511->powered = false;
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt and hotplug detection
+ */
+
+static bool adv7511_hpd(struct adv7511 *adv7511)
+{
+       unsigned int irq0;
+       int ret;
+
+       ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
+       if (ret < 0)
+               return false;
+
+       if (irq0 & ADV7511_INT0_HPD) {
+               regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
+                            ADV7511_INT0_HPD);
+               return true;
+       }
+
+       return false;
+}
+
+static void adv7511_hpd_work(struct work_struct *work)
+{
+       struct adv7511 *adv7511 = container_of(work, struct adv7511, hpd_work);
+       enum drm_connector_status status;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val);
+       if (ret < 0)
+               status = connector_status_disconnected;
+       else if (val & ADV7511_STATUS_HPD)
+               status = connector_status_connected;
+       else
+               status = connector_status_disconnected;
+
+       /*
+        * The bridge resets its registers on unplug. So when we get a plug
+        * event and we're already supposed to be powered, cycle the bridge to
+        * restore its state.
+        */
+       if (status == connector_status_connected &&
+           adv7511->connector.status == connector_status_disconnected &&
+           adv7511->powered) {
+               regcache_mark_dirty(adv7511->regmap);
+               adv7511_power_on(adv7511);
+       }
+
+       if (adv7511->connector.status != status) {
+               adv7511->connector.status = status;
+
+               if (adv7511->connector.dev) {
+                       if (status == connector_status_disconnected)
+                               cec_phys_addr_invalidate(adv7511->cec_adap);
+                       drm_kms_helper_hotplug_event(adv7511->connector.dev);
+               } else {
+                       drm_bridge_hpd_notify(&adv7511->bridge, status);
+               }
+       }
+}
+
+static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
+{
+       unsigned int irq0, irq1;
+       int ret;
+
+       ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1);
+       if (ret < 0)
+               return ret;
+
+       regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
+       regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
+
+       if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder)
+               schedule_work(&adv7511->hpd_work);
+
+       if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
+               adv7511->edid_read = true;
+
+               if (adv7511->i2c_main->irq)
+                       wake_up_all(&adv7511->wq);
+       }
+
+#ifdef CONFIG_DRM_I2C_ADV7511_CEC
+       adv7511_cec_irq_process(adv7511, irq1);
+#endif
+
+       return 0;
+}
+
+static irqreturn_t adv7511_irq_handler(int irq, void *devid)
+{
+       struct adv7511 *adv7511 = devid;
+       int ret;
+
+       ret = adv7511_irq_process(adv7511, true);
+       return ret < 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * EDID retrieval
+ */
+
+static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout)
+{
+       int ret;
+
+       if (adv7511->i2c_main->irq) {
+               ret = wait_event_interruptible_timeout(adv7511->wq,
+                               adv7511->edid_read, msecs_to_jiffies(timeout));
+       } else {
+               for (; timeout > 0; timeout -= 25) {
+                       ret = adv7511_irq_process(adv7511, false);
+                       if (ret < 0)
+                               break;
+
+                       if (adv7511->edid_read)
+                               break;
+
+                       msleep(25);
+               }
+       }
+
+       return adv7511->edid_read ? 0 : -EIO;
+}
+
+static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
+                                 size_t len)
+{
+       struct adv7511 *adv7511 = data;
+       struct i2c_msg xfer[2];
+       uint8_t offset;
+       unsigned int i;
+       int ret;
+
+       if (len > 128)
+               return -EINVAL;
+
+       if (adv7511->current_edid_segment != block / 2) {
+               unsigned int status;
+
+               ret = regmap_read(adv7511->regmap, ADV7511_REG_DDC_STATUS,
+                                 &status);
+               if (ret < 0)
+                       return ret;
+
+               if (status != 2) {
+                       adv7511->edid_read = false;
+                       regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
+                                    block);
+                       ret = adv7511_wait_for_edid(adv7511, 200);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               /* Break this apart, hopefully more I2C controllers will
+                * support 64 byte transfers than 256 byte transfers
+                */
+
+               xfer[0].addr = adv7511->i2c_edid->addr;
+               xfer[0].flags = 0;
+               xfer[0].len = 1;
+               xfer[0].buf = &offset;
+               xfer[1].addr = adv7511->i2c_edid->addr;
+               xfer[1].flags = I2C_M_RD;
+               xfer[1].len = 64;
+               xfer[1].buf = adv7511->edid_buf;
+
+               offset = 0;
+
+               for (i = 0; i < 4; ++i) {
+                       ret = i2c_transfer(adv7511->i2c_edid->adapter, xfer,
+                                          ARRAY_SIZE(xfer));
+                       if (ret < 0)
+                               return ret;
+                       else if (ret != 2)
+                               return -EIO;
+
+                       xfer[1].buf += 64;
+                       offset += 64;
+               }
+
+               adv7511->current_edid_segment = block / 2;
+       }
+
+       if (block % 2 == 0)
+               memcpy(buf, adv7511->edid_buf, len);
+       else
+               memcpy(buf, adv7511->edid_buf + 128, len);
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * ADV75xx helpers
+ */
+
+static struct edid *adv7511_get_edid(struct adv7511 *adv7511,
+                                    struct drm_connector *connector)
+{
+       struct edid *edid;
+
+       /* Reading the EDID only works if the device is powered */
+       if (!adv7511->powered) {
+               unsigned int edid_i2c_addr =
+                                       (adv7511->i2c_edid->addr << 1);
+
+               __adv7511_power_on(adv7511);
+
+               /* Reset the EDID_I2C_ADDR register as it might be cleared */
+               regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR,
+                            edid_i2c_addr);
+       }
+
+       edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
+
+       if (!adv7511->powered)
+               __adv7511_power_off(adv7511);
+
+       adv7511_set_config_csc(adv7511, connector, adv7511->rgb,
+                              drm_detect_hdmi_monitor(edid));
+
+       cec_s_phys_addr_from_edid(adv7511->cec_adap, edid);
+
+       return edid;
+}
+
+static int adv7511_get_modes(struct adv7511 *adv7511,
+                            struct drm_connector *connector)
+{
+       struct edid *edid;
+       unsigned int count;
+
+       edid = adv7511_get_edid(adv7511, connector);
+
+       drm_connector_update_edid_property(connector, edid);
+       count = drm_add_edid_modes(connector, edid);
+
+       kfree(edid);
+
+       return count;
+}
+
+static enum drm_connector_status
+adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector)
+{
+       enum drm_connector_status status;
+       unsigned int val;
+       bool hpd;
+       int ret;
+
+       ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val);
+       if (ret < 0)
+               return connector_status_disconnected;
+
+       if (val & ADV7511_STATUS_HPD)
+               status = connector_status_connected;
+       else
+               status = connector_status_disconnected;
+
+       hpd = adv7511_hpd(adv7511);
+
+       /* The chip resets itself when the cable is disconnected, so in case
+        * there is a pending HPD interrupt and the cable is connected there was
+        * at least one transition from disconnected to connected and the chip
+        * has to be reinitialized. */
+       if (status == connector_status_connected && hpd && adv7511->powered) {
+               regcache_mark_dirty(adv7511->regmap);
+               adv7511_power_on(adv7511);
+               if (connector)
+                       adv7511_get_modes(adv7511, connector);
+               if (adv7511->status == connector_status_connected)
+                       status = connector_status_disconnected;
+       } else {
+               /* Renable HPD sensing */
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                                  ADV7511_REG_POWER2_HPD_SRC_MASK,
+                                  ADV7511_REG_POWER2_HPD_SRC_BOTH);
+       }
+
+       adv7511->status = status;
+       return status;
+}
+
+static enum drm_mode_status adv7511_mode_valid(struct adv7511 *adv7511,
+                             struct drm_display_mode *mode)
+{
+       if (mode->clock > 165000)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static void adv7511_mode_set(struct adv7511 *adv7511,
+                            const struct drm_display_mode *mode,
+                            const struct drm_display_mode *adj_mode)
+{
+       unsigned int low_refresh_rate;
+       unsigned int hsync_polarity = 0;
+       unsigned int vsync_polarity = 0;
+
+       if (adv7511->embedded_sync) {
+               unsigned int hsync_offset, hsync_len;
+               unsigned int vsync_offset, vsync_len;
+
+               hsync_offset = adj_mode->crtc_hsync_start -
+                              adj_mode->crtc_hdisplay;
+               vsync_offset = adj_mode->crtc_vsync_start -
+                              adj_mode->crtc_vdisplay;
+               hsync_len = adj_mode->crtc_hsync_end -
+                           adj_mode->crtc_hsync_start;
+               vsync_len = adj_mode->crtc_vsync_end -
+                           adj_mode->crtc_vsync_start;
+
+               /* The hardware vsync generator has a off-by-one bug */
+               vsync_offset += 1;
+
+               regmap_write(adv7511->regmap, ADV7511_REG_HSYNC_PLACEMENT_MSB,
+                            ((hsync_offset >> 10) & 0x7) << 5);
+               regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(0),
+                            (hsync_offset >> 2) & 0xff);
+               regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(1),
+                            ((hsync_offset & 0x3) << 6) |
+                            ((hsync_len >> 4) & 0x3f));
+               regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(2),
+                            ((hsync_len & 0xf) << 4) |
+                            ((vsync_offset >> 6) & 0xf));
+               regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(3),
+                            ((vsync_offset & 0x3f) << 2) |
+                            ((vsync_len >> 8) & 0x3));
+               regmap_write(adv7511->regmap, ADV7511_REG_SYNC_DECODER(4),
+                            vsync_len & 0xff);
+
+               hsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PHSYNC);
+               vsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PVSYNC);
+       } else {
+               enum adv7511_sync_polarity mode_hsync_polarity;
+               enum adv7511_sync_polarity mode_vsync_polarity;
+
+               /**
+                * If the input signal is always low or always high we want to
+                * invert or let it passthrough depending on the polarity of the
+                * current mode.
+                **/
+               if (adj_mode->flags & DRM_MODE_FLAG_NHSYNC)
+                       mode_hsync_polarity = ADV7511_SYNC_POLARITY_LOW;
+               else
+                       mode_hsync_polarity = ADV7511_SYNC_POLARITY_HIGH;
+
+               if (adj_mode->flags & DRM_MODE_FLAG_NVSYNC)
+                       mode_vsync_polarity = ADV7511_SYNC_POLARITY_LOW;
+               else
+                       mode_vsync_polarity = ADV7511_SYNC_POLARITY_HIGH;
+
+               if (adv7511->hsync_polarity != mode_hsync_polarity &&
+                   adv7511->hsync_polarity !=
+                   ADV7511_SYNC_POLARITY_PASSTHROUGH)
+                       hsync_polarity = 1;
+
+               if (adv7511->vsync_polarity != mode_vsync_polarity &&
+                   adv7511->vsync_polarity !=
+                   ADV7511_SYNC_POLARITY_PASSTHROUGH)
+                       vsync_polarity = 1;
+       }
+
+       if (drm_mode_vrefresh(mode) <= 24)
+               low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ;
+       else if (drm_mode_vrefresh(mode) <= 25)
+               low_refresh_rate = ADV7511_LOW_REFRESH_RATE_25HZ;
+       else if (drm_mode_vrefresh(mode) <= 30)
+               low_refresh_rate = ADV7511_LOW_REFRESH_RATE_30HZ;
+       else
+               low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE;
+
+       regmap_update_bits(adv7511->regmap, 0xfb,
+               0x6, low_refresh_rate << 1);
+       regmap_update_bits(adv7511->regmap, 0x17,
+               0x60, (vsync_polarity << 6) | (hsync_polarity << 5));
+
+       if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
+               adv7533_mode_set(adv7511, adj_mode);
+
+       drm_mode_copy(&adv7511->curr_mode, adj_mode);
+
+       /*
+        * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is
+        * supposed to give better results.
+        */
+
+       adv7511->f_tmds = mode->clock;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Connector Operations
+ */
+
+static struct adv7511 *connector_to_adv7511(struct drm_connector *connector)
+{
+       return container_of(connector, struct adv7511, connector);
+}
+
+static int adv7511_connector_get_modes(struct drm_connector *connector)
+{
+       struct adv7511 *adv = connector_to_adv7511(connector);
+
+       return adv7511_get_modes(adv, connector);
+}
+
+static enum drm_mode_status
+adv7511_connector_mode_valid(struct drm_connector *connector,
+                            struct drm_display_mode *mode)
+{
+       struct adv7511 *adv = connector_to_adv7511(connector);
+
+       return adv7511_mode_valid(adv, mode);
+}
+
+static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = {
+       .get_modes = adv7511_connector_get_modes,
+       .mode_valid = adv7511_connector_mode_valid,
+};
+
+static enum drm_connector_status
+adv7511_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct adv7511 *adv = connector_to_adv7511(connector);
+
+       return adv7511_detect(adv, connector);
+}
+
+static const struct drm_connector_funcs adv7511_connector_funcs = {
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = adv7511_connector_detect,
+       .destroy = drm_connector_cleanup,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int adv7511_connector_init(struct adv7511 *adv)
+{
+       struct drm_bridge *bridge = &adv->bridge;
+       int ret;
+
+       if (!bridge->encoder) {
+               DRM_ERROR("Parent encoder object not found");
+               return -ENODEV;
+       }
+
+       if (adv->i2c_main->irq)
+               adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
+       else
+               adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
+                               DRM_CONNECTOR_POLL_DISCONNECT;
+
+       ret = drm_connector_init(bridge->dev, &adv->connector,
+                                &adv7511_connector_funcs,
+                                DRM_MODE_CONNECTOR_HDMIA);
+       if (ret < 0) {
+               DRM_ERROR("Failed to initialize connector with drm\n");
+               return ret;
+       }
+       drm_connector_helper_add(&adv->connector,
+                                &adv7511_connector_helper_funcs);
+       drm_connector_attach_encoder(&adv->connector, bridge->encoder);
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
+static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge)
+{
+       return container_of(bridge, struct adv7511, bridge);
+}
+
+static void adv7511_bridge_enable(struct drm_bridge *bridge)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+       adv7511_power_on(adv);
+}
+
+static void adv7511_bridge_disable(struct drm_bridge *bridge)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+       adv7511_power_off(adv);
+}
+
+static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
+                                   const struct drm_display_mode *mode,
+                                   const struct drm_display_mode *adj_mode)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+       adv7511_mode_set(adv, mode, adj_mode);
+}
+
+static int adv7511_bridge_attach(struct drm_bridge *bridge,
+                                enum drm_bridge_attach_flags flags)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+       int ret = 0;
+
+       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+               ret = adv7511_connector_init(adv);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (adv->type == ADV7533 || adv->type == ADV7535)
+               ret = adv7533_attach_dsi(adv);
+
+       if (adv->i2c_main->irq)
+               regmap_write(adv->regmap, ADV7511_REG_INT_ENABLE(0),
+                            ADV7511_INT0_HPD);
+
+       return ret;
+}
+
+static enum drm_connector_status adv7511_bridge_detect(struct drm_bridge *bridge)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+       return adv7511_detect(adv, NULL);
+}
+
+static struct edid *adv7511_bridge_get_edid(struct drm_bridge *bridge,
+                                           struct drm_connector *connector)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+       return adv7511_get_edid(adv, connector);
+}
+
+static void adv7511_bridge_hpd_notify(struct drm_bridge *bridge,
+                                     enum drm_connector_status status)
+{
+       struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+       if (status == connector_status_disconnected)
+               cec_phys_addr_invalidate(adv->cec_adap);
+}
+
+static const struct drm_bridge_funcs adv7511_bridge_funcs = {
+       .enable = adv7511_bridge_enable,
+       .disable = adv7511_bridge_disable,
+       .mode_set = adv7511_bridge_mode_set,
+       .attach = adv7511_bridge_attach,
+       .detect = adv7511_bridge_detect,
+       .get_edid = adv7511_bridge_get_edid,
+       .hpd_notify = adv7511_bridge_hpd_notify,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & remove
+ */
+
+static const char * const adv7511_supply_names[] = {
+       "avdd",
+       "dvdd",
+       "pvdd",
+       "bgvdd",
+       "dvdd-3v",
+};
+
+static const char * const adv7533_supply_names[] = {
+       "avdd",
+       "dvdd",
+       "pvdd",
+       "a2vdd",
+       "v3p3",
+       "v1p2",
+};
+
+static int adv7511_init_regulators(struct adv7511 *adv)
+{
+       struct device *dev = &adv->i2c_main->dev;
+       const char * const *supply_names;
+       unsigned int i;
+       int ret;
+
+       if (adv->type == ADV7511) {
+               supply_names = adv7511_supply_names;
+               adv->num_supplies = ARRAY_SIZE(adv7511_supply_names);
+       } else {
+               supply_names = adv7533_supply_names;
+               adv->num_supplies = ARRAY_SIZE(adv7533_supply_names);
+       }
+
+       adv->supplies = devm_kcalloc(dev, adv->num_supplies,
+                                    sizeof(*adv->supplies), GFP_KERNEL);
+       if (!adv->supplies)
+               return -ENOMEM;
+
+       for (i = 0; i < adv->num_supplies; i++)
+               adv->supplies[i].supply = supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev, adv->num_supplies, adv->supplies);
+       if (ret)
+               return ret;
+
+       return regulator_bulk_enable(adv->num_supplies, adv->supplies);
+}
+
+static void adv7511_uninit_regulators(struct adv7511 *adv)
+{
+       regulator_bulk_disable(adv->num_supplies, adv->supplies);
+}
+
+static bool adv7511_cec_register_volatile(struct device *dev, unsigned int reg)
+{
+       struct i2c_client *i2c = to_i2c_client(dev);
+       struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+
+       if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
+               reg -= ADV7533_REG_CEC_OFFSET;
+
+       switch (reg) {
+       case ADV7511_REG_CEC_RX_FRAME_HDR:
+       case ADV7511_REG_CEC_RX_FRAME_DATA0...
+               ADV7511_REG_CEC_RX_FRAME_DATA0 + 14:
+       case ADV7511_REG_CEC_RX_FRAME_LEN:
+       case ADV7511_REG_CEC_RX_BUFFERS:
+       case ADV7511_REG_CEC_TX_LOW_DRV_CNT:
+               return true;
+       }
+
+       return false;
+}
+
+static const struct regmap_config adv7511_cec_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = 0xff,
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = adv7511_cec_register_volatile,
+};
+
+static int adv7511_init_cec_regmap(struct adv7511 *adv)
+{
+       int ret;
+
+       adv->i2c_cec = i2c_new_ancillary_device(adv->i2c_main, "cec",
+                                               ADV7511_CEC_I2C_ADDR_DEFAULT);
+       if (IS_ERR(adv->i2c_cec))
+               return PTR_ERR(adv->i2c_cec);
+       i2c_set_clientdata(adv->i2c_cec, adv);
+
+       adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
+                                       &adv7511_cec_regmap_config);
+       if (IS_ERR(adv->regmap_cec)) {
+               ret = PTR_ERR(adv->regmap_cec);
+               goto err;
+       }
+
+       if (adv->type == ADV7533 || adv->type == ADV7535) {
+               ret = adv7533_patch_cec_registers(adv);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+err:
+       i2c_unregister_device(adv->i2c_cec);
+       return ret;
+}
+
+static int adv7511_parse_dt(struct device_node *np,
+                           struct adv7511_link_config *config)
+{
+       const char *str;
+       int ret;
+
+       of_property_read_u32(np, "adi,input-depth", &config->input_color_depth);
+       if (config->input_color_depth != 8 && config->input_color_depth != 10 &&
+           config->input_color_depth != 12)
+               return -EINVAL;
+
+       ret = of_property_read_string(np, "adi,input-colorspace", &str);
+       if (ret < 0)
+               return ret;
+
+       if (!strcmp(str, "rgb"))
+               config->input_colorspace = HDMI_COLORSPACE_RGB;
+       else if (!strcmp(str, "yuv422"))
+               config->input_colorspace = HDMI_COLORSPACE_YUV422;
+       else if (!strcmp(str, "yuv444"))
+               config->input_colorspace = HDMI_COLORSPACE_YUV444;
+       else
+               return -EINVAL;
+
+       ret = of_property_read_string(np, "adi,input-clock", &str);
+       if (ret < 0)
+               return ret;
+
+       if (!strcmp(str, "1x"))
+               config->input_clock = ADV7511_INPUT_CLOCK_1X;
+       else if (!strcmp(str, "2x"))
+               config->input_clock = ADV7511_INPUT_CLOCK_2X;
+       else if (!strcmp(str, "ddr"))
+               config->input_clock = ADV7511_INPUT_CLOCK_DDR;
+       else
+               return -EINVAL;
+
+       if (config->input_colorspace == HDMI_COLORSPACE_YUV422 ||
+           config->input_clock != ADV7511_INPUT_CLOCK_1X) {
+               ret = of_property_read_u32(np, "adi,input-style",
+                                          &config->input_style);
+               if (ret)
+                       return ret;
+
+               if (config->input_style < 1 || config->input_style > 3)
+                       return -EINVAL;
+
+               ret = of_property_read_string(np, "adi,input-justification",
+                                             &str);
+               if (ret < 0)
+                       return ret;
+
+               if (!strcmp(str, "left"))
+                       config->input_justification =
+                               ADV7511_INPUT_JUSTIFICATION_LEFT;
+               else if (!strcmp(str, "evenly"))
+                       config->input_justification =
+                               ADV7511_INPUT_JUSTIFICATION_EVENLY;
+               else if (!strcmp(str, "right"))
+                       config->input_justification =
+                               ADV7511_INPUT_JUSTIFICATION_RIGHT;
+               else
+                       return -EINVAL;
+
+       } else {
+               config->input_style = 1;
+               config->input_justification = ADV7511_INPUT_JUSTIFICATION_LEFT;
+       }
+
+       of_property_read_u32(np, "adi,clock-delay", &config->clock_delay);
+       if (config->clock_delay < -1200 || config->clock_delay > 1600)
+               return -EINVAL;
+
+       config->embedded_sync = of_property_read_bool(np, "adi,embedded-sync");
+
+       /* Hardcode the sync pulse configurations for now. */
+       config->sync_pulse = ADV7511_INPUT_SYNC_PULSE_NONE;
+       config->vsync_polarity = ADV7511_SYNC_POLARITY_PASSTHROUGH;
+       config->hsync_polarity = ADV7511_SYNC_POLARITY_PASSTHROUGH;
+
+       return 0;
+}
+
+static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+       struct adv7511_link_config link_config;
+       struct adv7511 *adv7511;
+       struct device *dev = &i2c->dev;
+       unsigned int val;
+       int ret;
+       if (!dev->of_node)
+               return -EINVAL;
+
+       adv7511 = devm_kzalloc(dev, sizeof(*adv7511), GFP_KERNEL);
+       if (!adv7511)
+               return -ENOMEM;
+
+       adv7511->i2c_main = i2c;
+       adv7511->powered = false;
+       adv7511->status = connector_status_disconnected;
+
+       if (dev->of_node)
+               adv7511->type = (enum adv7511_type)of_device_get_match_data(dev);
+       else
+               adv7511->type = id->driver_data;
+
+       memset(&link_config, 0, sizeof(link_config));
+
+       if (adv7511->type == ADV7511)
+               ret = adv7511_parse_dt(dev->of_node, &link_config);
+       else
+               ret = adv7533_parse_dt(dev->of_node, adv7511);
+       if (ret)
+               return ret;
+
+       ret = adv7511_init_regulators(adv7511);
+       if (ret) {
+               dev_err(dev, "failed to init regulators\n");
+               return ret;
+       }
+
+       /*
+        * The power down GPIO is optional. If present, toggle it from active to
+        * inactive to wake up the encoder.
+        */
+       adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH);
+       if (IS_ERR(adv7511->gpio_pd)) {
+               ret = PTR_ERR(adv7511->gpio_pd);
+               goto uninit_regulators;
+       }
+
+       if (adv7511->gpio_pd) {
+               usleep_range(5000, 6000);
+               gpiod_set_value_cansleep(adv7511->gpio_pd, 0);
+       }
+
+       adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config);
+       if (IS_ERR(adv7511->regmap)) {
+               ret = PTR_ERR(adv7511->regmap);
+               goto uninit_regulators;
+       }
+
+       ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val);
+       if (ret)
+               goto uninit_regulators;
+       dev_dbg(dev, "Rev. %d\n", val);
+
+       if (adv7511->type == ADV7511)
+               ret = regmap_register_patch(adv7511->regmap,
+                                           adv7511_fixed_registers,
+                                           ARRAY_SIZE(adv7511_fixed_registers));
+       else
+               ret = adv7533_patch_registers(adv7511);
+       if (ret)
+               goto uninit_regulators;
+
+       adv7511_packet_disable(adv7511, 0xffff);
+
+       adv7511->i2c_edid = i2c_new_ancillary_device(i2c, "edid",
+                                       ADV7511_EDID_I2C_ADDR_DEFAULT);
+       if (IS_ERR(adv7511->i2c_edid)) {
+               ret = PTR_ERR(adv7511->i2c_edid);
+               goto uninit_regulators;
+       }
+
+       regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR,
+                    adv7511->i2c_edid->addr << 1);
+
+       adv7511->i2c_packet = i2c_new_ancillary_device(i2c, "packet",
+                                       ADV7511_PACKET_I2C_ADDR_DEFAULT);
+       if (IS_ERR(adv7511->i2c_packet)) {
+               ret = PTR_ERR(adv7511->i2c_packet);
+               goto err_i2c_unregister_edid;
+       }
+
+       regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
+                    adv7511->i2c_packet->addr << 1);
+
+       ret = adv7511_init_cec_regmap(adv7511);
+       if (ret)
+               goto err_i2c_unregister_packet;
+
+       regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR,
+                    adv7511->i2c_cec->addr << 1);
+
+       INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
+
+       if (i2c->irq) {
+               init_waitqueue_head(&adv7511->wq);
+
+               ret = devm_request_threaded_irq(dev, i2c->irq, NULL,
+                                               adv7511_irq_handler,
+                                               IRQF_ONESHOT, dev_name(dev),
+                                               adv7511);
+               if (ret)
+                       goto err_unregister_cec;
+       }
+
+       adv7511_power_off(adv7511);
+
+       i2c_set_clientdata(i2c, adv7511);
+
+       if (adv7511->type == ADV7511)
+               adv7511_set_link_config(adv7511, &link_config);
+
+       ret = adv7511_cec_init(dev, adv7511);
+       if (ret)
+               goto err_unregister_cec;
+
+       adv7511->bridge.funcs = &adv7511_bridge_funcs;
+       adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+                           | DRM_BRIDGE_OP_HPD;
+       adv7511->bridge.of_node = dev->of_node;
+       adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+
+       drm_bridge_add(&adv7511->bridge);
+
+       adv7511_audio_init(dev, adv7511);
+       return 0;
+
+err_unregister_cec:
+       i2c_unregister_device(adv7511->i2c_cec);
+       if (adv7511->cec_clk)
+               clk_disable_unprepare(adv7511->cec_clk);
+err_i2c_unregister_packet:
+       i2c_unregister_device(adv7511->i2c_packet);
+err_i2c_unregister_edid:
+       i2c_unregister_device(adv7511->i2c_edid);
+uninit_regulators:
+       adv7511_uninit_regulators(adv7511);
+
+       return ret;
+}
+
+static int adv7511_remove(struct i2c_client *i2c)
+{
+       struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+
+       if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
+               adv7533_detach_dsi(adv7511);
+       i2c_unregister_device(adv7511->i2c_cec);
+       if (adv7511->cec_clk)
+               clk_disable_unprepare(adv7511->cec_clk);
+
+       adv7511_uninit_regulators(adv7511);
+
+       drm_bridge_remove(&adv7511->bridge);
+
+       adv7511_audio_exit(adv7511);
+
+       cec_unregister_adapter(adv7511->cec_adap);
+
+       i2c_unregister_device(adv7511->i2c_packet);
+       i2c_unregister_device(adv7511->i2c_edid);
+
+       return 0;
+}
+
+static const struct i2c_device_id adv7511_i2c_ids[] = {
+       { "adv7511", ADV7511 },
+       { "adv7511w", ADV7511 },
+       { "adv7513", ADV7511 },
+       { "adv7533", ADV7533 },
+       { "adv7535", ADV7535 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
+
+static const struct of_device_id adv7511_of_ids[] = {
+       { .compatible = "adi,adv7511", .data = (void *)ADV7511 },
+       { .compatible = "adi,adv7511w", .data = (void *)ADV7511 },
+       { .compatible = "adi,adv7513", .data = (void *)ADV7511 },
+       { .compatible = "adi,adv7533", .data = (void *)ADV7533 },
+       { .compatible = "adi,adv7535", .data = (void *)ADV7535 },
+       { }
+};
+MODULE_DEVICE_TABLE(of, adv7511_of_ids);
+
+static struct mipi_dsi_driver adv7533_dsi_driver = {
+       .driver.name = "adv7533",
+};
+
+static struct i2c_driver adv7511_driver = {
+       .driver = {
+               .name = "adv7511",
+               .of_match_table = adv7511_of_ids,
+       },
+       .id_table = adv7511_i2c_ids,
+       .probe = adv7511_probe,
+       .remove = adv7511_remove,
+};
+
+static int __init adv7511_init(void)
+{
+       if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+               mipi_dsi_driver_register(&adv7533_dsi_driver);
+
+       return i2c_add_driver(&adv7511_driver);
+}
+fs_initcall(adv7511_init);
+
+static void __exit adv7511_exit(void)
+{
+       i2c_del_driver(&adv7511_driver);
+
+       if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+               mipi_dsi_driver_unregister(&adv7533_dsi_driver);
+}
+module_exit(adv7511_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADV7511 HDMI transmitter driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/adv7511/adv7533.c b/drivers/gpu/drm/verisilicon/adv7511/adv7533.c
new file mode 100755 (executable)
index 0000000..59d718b
--- /dev/null
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/of_graph.h>
+
+#include "adv7511.h"
+
+static const struct reg_sequence adv7533_fixed_registers[] = {
+       { 0x16, 0x20 },
+       { 0x9a, 0xe0 },
+       { 0xba, 0x70 },
+       { 0xde, 0x82 },
+       { 0xe4, 0x40 },
+       { 0xe5, 0x80 },
+};
+
+static const struct reg_sequence adv7533_cec_fixed_registers[] = {
+       { 0x15, 0xd0 },
+       { 0x17, 0xd0 },
+       { 0x24, 0x20 },
+       { 0x57, 0x11 },
+       { 0x05, 0xc8 },
+};
+
+static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
+{
+       struct mipi_dsi_device *dsi = adv->dsi;
+       struct drm_display_mode *mode = &adv->curr_mode;
+       unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
+       u8 clock_div_by_lanes[] = { 6, 4, 3 };  /* 2, 3, 4 lanes */
+
+       hsw = mode->hsync_end - mode->hsync_start;
+       hfp = mode->hsync_start - mode->hdisplay;
+       hbp = mode->htotal - mode->hsync_end;
+       vsw = mode->vsync_end - mode->vsync_start;
+       vfp = mode->vsync_start - mode->vdisplay;
+       vbp = mode->vtotal - mode->vsync_end;
+
+       /* set pixel clock divider mode */
+       regmap_write(adv->regmap_cec, 0x16,
+                    clock_div_by_lanes[dsi->lanes - 2] << 3);
+
+       /* horizontal porch params */
+       regmap_write(adv->regmap_cec, 0x28, mode->htotal >> 4);
+       regmap_write(adv->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
+       regmap_write(adv->regmap_cec, 0x2a, hsw >> 4);
+       regmap_write(adv->regmap_cec, 0x2b, (hsw << 4) & 0xff);
+       regmap_write(adv->regmap_cec, 0x2c, hfp >> 4);
+       regmap_write(adv->regmap_cec, 0x2d, (hfp << 4) & 0xff);
+       regmap_write(adv->regmap_cec, 0x2e, hbp >> 4);
+       regmap_write(adv->regmap_cec, 0x2f, (hbp << 4) & 0xff);
+
+       /* vertical porch params */
+       regmap_write(adv->regmap_cec, 0x30, mode->vtotal >> 4);
+       regmap_write(adv->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
+       regmap_write(adv->regmap_cec, 0x32, vsw >> 4);
+       regmap_write(adv->regmap_cec, 0x33, (vsw << 4) & 0xff);
+       regmap_write(adv->regmap_cec, 0x34, vfp >> 4);
+       regmap_write(adv->regmap_cec, 0x35, (vfp << 4) & 0xff);
+       regmap_write(adv->regmap_cec, 0x36, vbp >> 4);
+       regmap_write(adv->regmap_cec, 0x37, (vbp << 4) & 0xff);
+}
+
+void adv7533_dsi_power_on(struct adv7511 *adv)
+{
+       struct mipi_dsi_device *dsi = adv->dsi;
+
+       if (adv->use_timing_gen)
+               adv7511_dsi_config_timing_gen(adv);
+
+       /* set number of dsi lanes */
+       regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
+
+       if (adv->use_timing_gen) {
+               /* reset internal timing generator */
+               regmap_write(adv->regmap_cec, 0x27, 0xcb);
+               regmap_write(adv->regmap_cec, 0x27, 0x8b);
+               regmap_write(adv->regmap_cec, 0x27, 0xcb);
+       } else {
+               /* disable internal timing generator */
+               regmap_write(adv->regmap_cec, 0x27, 0x0b);
+       }
+
+       /* enable hdmi */
+       regmap_write(adv->regmap_cec, 0x03, 0x89);
+       /* disable test mode */
+       regmap_write(adv->regmap_cec, 0x55, 0x00);
+
+       regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers,
+                             ARRAY_SIZE(adv7533_cec_fixed_registers));
+}
+
+void adv7533_dsi_power_off(struct adv7511 *adv)
+{
+       /* disable hdmi */
+       regmap_write(adv->regmap_cec, 0x03, 0x0b);
+       /* disable internal timing generator */
+       regmap_write(adv->regmap_cec, 0x27, 0x0b);
+}
+
+void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode)
+{
+       struct mipi_dsi_device *dsi = adv->dsi;
+       int lanes, ret;
+
+       if (adv->num_dsi_lanes != 4)
+               return;
+
+       if (mode->clock > 80000)
+               lanes = 4;
+       else
+               lanes = 3;
+
+       if (lanes != dsi->lanes) {
+               mipi_dsi_detach(dsi);
+               dsi->lanes = lanes;
+               ret = mipi_dsi_attach(dsi);
+               if (ret)
+                       dev_err(&dsi->dev, "failed to change host lanes\n");
+       }
+}
+
+int adv7533_patch_registers(struct adv7511 *adv)
+{
+       return regmap_register_patch(adv->regmap,
+                                    adv7533_fixed_registers,
+                                    ARRAY_SIZE(adv7533_fixed_registers));
+}
+
+int adv7533_patch_cec_registers(struct adv7511 *adv)
+{
+       return regmap_register_patch(adv->regmap_cec,
+                                   adv7533_cec_fixed_registers,
+                                   ARRAY_SIZE(adv7533_cec_fixed_registers));
+}
+
+int adv7533_attach_dsi(struct adv7511 *adv)
+{
+       struct device *dev = &adv->i2c_main->dev;
+       struct mipi_dsi_host *host;
+       struct mipi_dsi_device *dsi;
+       int ret = 0;
+       const struct mipi_dsi_device_info info = { .type = "adv7533",
+                                                  .channel = 0,
+                                                  .node = NULL,
+                                                };
+
+       host = of_find_mipi_dsi_host_by_node(adv->host_node);
+       if (!host) {
+               dev_err(dev, "failed to find dsi host\n");
+               return -EPROBE_DEFER;
+       }
+
+       dsi = mipi_dsi_device_register_full(host, &info);
+       if (IS_ERR(dsi)) {
+               dev_err(dev, "failed to create dsi device\n");
+               ret = PTR_ERR(dsi);
+               goto err_dsi_device;
+       }
+
+       adv->dsi = dsi;
+
+       dsi->lanes = adv->num_dsi_lanes;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+                         MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0) {
+               dev_err(dev, "failed to attach dsi to host\n");
+               goto err_dsi_attach;
+       }
+
+       return 0;
+
+err_dsi_attach:
+       mipi_dsi_device_unregister(dsi);
+err_dsi_device:
+       return ret;
+}
+
+void adv7533_detach_dsi(struct adv7511 *adv)
+{
+       mipi_dsi_detach(adv->dsi);
+       mipi_dsi_device_unregister(adv->dsi);
+}
+
+int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
+{
+       u32 num_lanes;
+
+       of_property_read_u32(np, "adi,dsi-lanes", &num_lanes);
+
+       if (num_lanes < 1 || num_lanes > 4)
+               return -EINVAL;
+
+       adv->num_dsi_lanes = num_lanes;
+
+       adv->host_node = of_graph_get_remote_node(np, 0, 0);
+       if (!adv->host_node)
+               return -ENODEV;
+
+       of_node_put(adv->host_node);
+
+       adv->use_timing_gen = !of_property_read_bool(np,
+                                               "adi,disable-timing-generator");
+
+       /* TODO: Check if these need to be parsed by DT or not */
+       adv->rgb = true;
+       adv->embedded_sync = false;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/verisilicon/dw_mipi_dsi.c b/drivers/gpu/drm/verisilicon/dw_mipi_dsi.c
new file mode 100755 (executable)
index 0000000..673af7d
--- /dev/null
@@ -0,0 +1,1076 @@
+// 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/component.h>
+#include <linux/clk.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/delay.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 ENABLE_LOW_POWER        (0x3f << 8)
+#define ENABLE_LOW_POWER_MASK       (0x3f << 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;
+
+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;
+    void __iomem *base;
+    struct clk *pclk;
+    struct phy *dphy;
+
+    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 void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+    writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
+{
+    return readl(dsi->base + reg);
+}
+
+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_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;
+
+    dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS);
+    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 = readl_poll_timeout(dsi->base + 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 = readl_poll_timeout(dsi->base + 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 = readl_poll_timeout(dsi->base + 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 = readl_poll_timeout(dsi->base + 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 = readl_poll_timeout(dsi->base + 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;
+        }
+
+        val = dsi_read(dsi, DSI_GEN_PLD_DATA);
+        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_video_config(struct dw_mipi_dsi *dsi)
+{
+    dsi_write(dsi, DSI_VID_MODE_CFG,
+                ENABLE_LOW_POWER | VID_MODE_TYPE_BURST);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+                 unsigned long mode_flags)
+{
+    dsi_write(dsi, DSI_PWR_UP, RESET);
+
+    if (mode_flags & MIPI_DSI_MODE_VIDEO) {
+        dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
+        dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+    } else {
+        dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+    }
+
+    dsi_write(dsi, DSI_PWR_UP, POWERUP);
+}
+
+static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
+{
+    u8 esc_clk;
+    u32 esc_clk_division;
+
+    /* limit esc clk on FPGA */
+    if (!dsi->pclk)
+        esc_clk = 5; /* 5MHz */
+    else
+        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;
+
+    dsi_write(dsi, DSI_PWR_UP, RESET);
+
+    /*
+     * 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 val = 0, color = 0;
+
+    switch (dsi->format) {
+    case MIPI_DSI_FMT_RGB888:
+        color = DPI_COLOR_CODING_24BIT;
+        break;
+    case MIPI_DSI_FMT_RGB666:
+        color = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
+        break;
+    case MIPI_DSI_FMT_RGB666_PACKED:
+        color = DPI_COLOR_CODING_18BIT_1;
+        break;
+    case MIPI_DSI_FMT_RGB565:
+        color = DPI_COLOR_CODING_16BIT_1;
+        break;
+    }
+
+    if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+        val |= VSYNC_ACTIVE_LOW;
+    if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+        val |= HSYNC_ACTIVE_LOW;
+
+    dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
+    dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
+    dsi_write(dsi, DSI_DPI_CFG_POL, val);
+    /*
+     * 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 dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+    dsi_write(dsi, DSI_PCKHDL_CFG, CRC_RX_EN | ECC_RX_EN | BTA_EN);
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+
+    /*
+     * 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);
+
+}
+
+/* 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 dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
+{
+    /*
+     * TODO dw drv improvements
+     * data & clock lane timers should be computed according to panel
+     * blankings and to the automatic clock lane control mode...
+     * note: DSI_PHY_TMR_CFG.MAX_RD_TIME should be in line with
+     * DSI_CMD_MODE_CFG.MAX_RD_PKT_SIZE_LP (see CMD_MODE_ALL_LP)
+     */
+
+    dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) |
+            PHY_LP2HS_TIME(0x40));
+    dsi_write(dsi, DSI_PHY_TMR_RD_CFG, MAX_RD_TIME(10000));
+
+    dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
+          | PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void 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_init(struct dw_mipi_dsi *dsi)
+{
+    /* Clear PHY state */
+    dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
+          | PHY_RSTZ | PHY_SHUTDOWNZ);
+}
+
+static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
+{
+    u32 val;
+    int ret;
+
+    phy_power_on(dsi->dphy);
+    dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+          PHY_UNSHUTDOWNZ);
+    usleep_range(500, 600);
+    dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+          PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+
+    ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
+                 val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+    if (ret)
+        DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
+
+    ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
+                 val, val & PHY_STOP_STATE_CLK_LANE, 1000,
+                 PHY_STATUS_TIMEOUT_US);
+    if (ret)
+        DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void 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_mode_set(struct dw_mipi_dsi *dsi,
+                 const struct drm_display_mode *mode)
+{
+    struct phy_configure_opts_mipi_dphy dphy_cfg;
+    int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+
+    phy_init(dsi->dphy);
+    phy_mipi_dphy_get_default_config(mode->clock * 1000, bpp,
+                     dsi->lanes, &dphy_cfg);
+    phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0,
+            (union phy_configure_opts *)&dphy_cfg);
+    phy_configure(dsi->dphy, (union phy_configure_opts *)&dphy_cfg);
+
+    dsi->lane_link_rate = dphy_cfg.hs_clk_rate / 1000;
+
+    clk_prepare_enable(dsi->pclk);
+    pm_runtime_get_sync(dsi->dev);
+
+    dw_mipi_dsi_init(dsi);
+    dw_mipi_dsi_dpi_config(dsi, mode);
+    dw_mipi_dsi_packet_handler_config(dsi);
+    dw_mipi_dsi_video_config(dsi);
+    dw_mipi_dsi_command_mode_config(dsi);
+    dw_mipi_dsi_timing_config(dsi, mode);
+
+    dw_mipi_dsi_dphy_init(dsi);
+    dw_mipi_dsi_dphy_timing_config(dsi);
+    dw_mipi_dsi_dphy_interface_config(dsi);
+
+    dw_mipi_dsi_dphy_enable(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);
+}
+
+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);
+
+    dw_mipi_dsi_set_mode(&primary->dsi, MIPI_DSI_MODE_VIDEO);
+    if (primary->secondary_dsi)
+        dw_mipi_dsi_set_mode(primary->secondary_dsi,
+                     MIPI_DSI_MODE_VIDEO);
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+    phy_power_off(dsi->dphy);
+
+    dsi_write(dsi, DSI_PWR_UP, RESET);
+    dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
+
+    pm_runtime_put(dsi->dev);
+    clk_disable_unprepare(dsi->pclk);
+}
+
+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_RBG888_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_RBG888_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);
+}
+
+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;
+}
+
+static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
+    .mode_set   = bridge_mode_set,
+    .enable     = bridge_enable,
+    .post_disable   = bridge_post_disable,
+    .attach     = bridge_attach,
+    .mode_fixup = bridge_mode_fixup,
+};
+
+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-dsi"))
+        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_typed(panel,
+                            DRM_MODE_CONNECTOR_DSI);
+        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-dsi");
+    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;
+    const struct dw_mipi_dsi_funcs *funcs;
+    struct dw_mipi_dsi *dsi;
+    struct resource *res;
+    int ret;
+
+    funcs = of_device_get_match_data(dev);
+    dsi = funcs->get_dsi(dev);
+    if (!dsi)
+        return -ENOMEM;
+
+    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+    dsi->base = devm_ioremap_resource(dev, res);
+    if (IS_ERR(dsi->base))
+        return -ENODEV;
+
+    dsi->pclk = devm_clk_get_optional(dev, "pclk");
+    if (IS_ERR(dsi->pclk))
+        return PTR_ERR(dsi->pclk);
+
+    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);
+
+    return component_add(dev, &dsi_component_ops);
+}
+
+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;
+}
+
+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),
+    },
+};
+
+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 100755 (executable)
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/inno_hdmi.c b/drivers/gpu/drm/verisilicon/inno_hdmi.c
new file mode 100755 (executable)
index 0000000..d25c57d
--- /dev/null
@@ -0,0 +1,934 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang@rock-chips.com>
+ *    Yakir Yang <ykk@rock-chips.com>
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "vs_drv.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)        container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+       int vic;
+       bool sink_is_hdmi;
+       bool sink_has_audio;
+       unsigned int enc_in_format;
+       unsigned int enc_out_format;
+       unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+       struct i2c_adapter adap;
+
+       u8 ddc_addr;
+       u8 segment_addr;
+
+       struct mutex lock;
+       struct completion cmp;
+};
+
+struct inno_hdmi {
+       struct device *dev;
+       struct drm_device *drm_dev;
+
+       int irq;
+       struct clk *pclk;
+       void __iomem *regs;
+
+       struct drm_connector    connector;
+       struct drm_encoder      encoder;
+
+       struct inno_hdmi_i2c *i2c;
+       struct i2c_adapter *ddc;
+
+       unsigned int tmds_rate;
+
+       struct hdmi_data_info   hdmi_data;
+       struct drm_display_mode previous_mode;
+};
+
+enum {
+       CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+       CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+       CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+       CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+       CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+       CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+       /*
+        * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+        *   R = 1.164*Y + 1.596*V - 204
+        *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+        *   B = 1.164*Y + 2.018*U - 258
+        */
+       {
+               0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+               0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+               0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+       },
+       /*
+        * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+        *   R = Y + 1.402*V - 248
+        *   G = Y - 0.344*U - 0.714*V + 135
+        *   B = Y + 1.772*U - 227
+        */
+       {
+               0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+               0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+               0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+       },
+       /*
+        * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+        *   R = 1.164*Y + 1.793*V - 248
+        *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+        *   B = 1.164*Y + 2.115*U - 289
+        */
+       {
+               0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+               0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+               0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+       },
+
+       /*
+        * RGB2YUV:601 SD mode:
+        *   Cb = -0.291G - 0.148R + 0.439B + 128
+        *   Y  = 0.504G  + 0.257R + 0.098B + 16
+        *   Cr = -0.368G + 0.439R - 0.071B + 128
+        */
+       {
+               0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+               0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+               0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+       },
+       /*
+        * RGB2YUV:709 HD mode:
+        *   Cb = - 0.338G - 0.101R + 0.439B + 128
+        *   Y  = 0.614G   + 0.183R + 0.062B + 16
+        *   Cr = - 0.399G + 0.439R - 0.040B + 128
+        */
+       {
+               0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+               0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+               0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+       },
+       /*
+        * RGB[0:255]2RGB[16:235]:
+        *   R' = R x (235-16)/255 + 16;
+        *   G' = G x (235-16)/255 + 16;
+        *   B' = B x (235-16)/255 + 16;
+        */
+       {
+               0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+               0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+               0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+       },
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+       return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+       writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+                            u32 msk, u32 val)
+{
+       u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+       temp |= val & msk;
+       hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+       int ddc_bus_freq;
+
+       ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+       hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+       hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+       /* Clear the EDID interrupt flag and mute the interrupt */
+       hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+       hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+       if (enable)
+               hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+       else
+               hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+       switch (mode) {
+       case NORMAL:
+               inno_hdmi_sys_power(hdmi, false);
+
+               hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+               hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+               hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+               hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+               hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+               hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+               hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+               hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+               inno_hdmi_sys_power(hdmi, true);
+               break;
+
+       case LOWER_PWR:
+               inno_hdmi_sys_power(hdmi, false);
+               hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+               hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+               hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+               hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+               break;
+
+       default:
+               DRM_DEV_ERROR(hdmi->dev, "Unknown power mode %d\n", mode);
+       }
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+       u32 val;
+       u32 msk;
+
+       hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+       udelay(100);
+
+       hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+       udelay(100);
+
+       msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+       val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+       hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+       inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
+                                 union hdmi_infoframe *frame, u32 frame_index,
+                                 u32 mask, u32 disable, u32 enable)
+{
+       if (mask)
+               hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+
+       hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+       if (setup_rc >= 0) {
+               u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+               ssize_t rc, i;
+
+               rc = hdmi_infoframe_pack(frame, packed_frame,
+                                        sizeof(packed_frame));
+               if (rc < 0)
+                       return rc;
+
+               for (i = 0; i < rc; i++)
+                       hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+                                   packed_frame[i]);
+
+               if (mask)
+                       hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+       }
+
+       return setup_rc;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
+                                     struct drm_display_mode *mode)
+{
+       union hdmi_infoframe frame;
+       int rc;
+
+       rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+                                                        &hdmi->connector,
+                                                        mode);
+
+       return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
+               m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
+                                     struct drm_display_mode *mode)
+{
+       union hdmi_infoframe frame;
+       int rc;
+
+       rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
+                                                     &hdmi->connector,
+                                                     mode);
+
+       if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+               frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+       else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+               frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+       else
+               frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+       return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+       struct hdmi_data_info *data = &hdmi->hdmi_data;
+       int c0_c2_change = 0;
+       int csc_enable = 0;
+       int csc_mode = 0;
+       int auto_csc = 0;
+       int value;
+       int i;
+
+       /* Input video mode is SDR RGB24bit, data enable signal from external */
+       hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+                   v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+       /* Input color hardcode to RGB, and output color hardcode to RGB888 */
+       value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+               v_VIDEO_OUTPUT_COLOR(0) |
+               v_VIDEO_INPUT_CSP(0);
+       hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+       if (data->enc_in_format == data->enc_out_format) {
+               if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+                   (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+                       value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+                       hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+                       hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+                                 m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+                                 v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+                                 v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+                       return 0;
+               }
+       }
+
+       if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+               if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+                   (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+                       csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+                       auto_csc = AUTO_CSC_DISABLE;
+                       c0_c2_change = C0_C2_CHANGE_DISABLE;
+                       csc_enable = v_CSC_ENABLE;
+               } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+                          (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+                       csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+                       auto_csc = AUTO_CSC_ENABLE;
+                       c0_c2_change = C0_C2_CHANGE_DISABLE;
+                       csc_enable = v_CSC_DISABLE;
+               }
+       } else {
+               if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+                   (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+                       csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+                       auto_csc = AUTO_CSC_DISABLE;
+                       c0_c2_change = C0_C2_CHANGE_DISABLE;
+                       csc_enable = v_CSC_ENABLE;
+               } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+                          (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+                       csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+                       auto_csc = AUTO_CSC_ENABLE;
+                       c0_c2_change = C0_C2_CHANGE_DISABLE;
+                       csc_enable = v_CSC_DISABLE;
+               }
+       }
+
+       for (i = 0; i < 24; i++)
+               hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+                           coeff_csc[csc_mode][i]);
+
+       value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+       hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+       hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+                 m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+                 v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+       return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+                                        struct drm_display_mode *mode)
+{
+       int value;
+
+       /* Set detail external video timing polarity and interlace mode */
+       value = v_EXTERANL_VIDEO(1);
+       value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+                v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+       value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+                v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+       value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+                v_INETLACE(1) : v_INETLACE(0);
+       hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+       /* Set detail external video timing */
+       value = mode->htotal;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+       value = mode->htotal - mode->hdisplay;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+       value = mode->hsync_start - mode->hdisplay;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+       value = mode->hsync_end - mode->hsync_start;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+       value = mode->vtotal;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+       value = mode->vtotal - mode->vdisplay;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+       value = mode->vsync_start - mode->vdisplay;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+       value = mode->vsync_end - mode->vsync_start;
+       hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+       hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+       hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+       hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+       return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+                          struct drm_display_mode *mode)
+{
+       hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+       hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+       hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+       if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+           (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+           (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+           (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+               hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+       else
+               hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+       /* Mute video and audio output */
+       hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+                 v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+       /* Set HDMI Mode */
+       hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+                   v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+       inno_hdmi_config_video_timing(hdmi, mode);
+
+       inno_hdmi_config_video_csc(hdmi);
+
+       if (hdmi->hdmi_data.sink_is_hdmi) {
+               inno_hdmi_config_video_avi(hdmi, mode);
+               inno_hdmi_config_video_vsi(hdmi, mode);
+       }
+
+       /*
+        * When IP controller have configured to an accurate video
+        * timing, then the TMDS clock source would be switched to
+        * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+        * clock rate, and reconfigure the DDC clock.
+        */
+       hdmi->tmds_rate = mode->clock * 1000;
+       inno_hdmi_i2c_init(hdmi);
+
+       /* Unmute video and audio output */
+       hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+                 v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+       return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+                                      struct drm_display_mode *mode,
+                                      struct drm_display_mode *adj_mode)
+{
+       struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+       inno_hdmi_setup(hdmi, adj_mode);
+
+       /* Store the display mode for plugin/DPMS poweron events */
+       memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+       struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+       inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+       struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+       inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+                                        const struct drm_display_mode *mode,
+                                        struct drm_display_mode *adj_mode)
+{
+       return true;
+}
+
+static int
+inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+                              struct drm_crtc_state *crtc_state,
+                              struct drm_connector_state *conn_state)
+{
+       //struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+       //s->output_mode = ROCKCHIP_OUT_MODE_P888;
+       //s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+
+       return 0;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+       .enable     = inno_hdmi_encoder_enable,
+       .disable    = inno_hdmi_encoder_disable,
+       .mode_fixup = inno_hdmi_encoder_mode_fixup,
+       .mode_set   = inno_hdmi_encoder_mode_set,
+       .atomic_check = inno_hdmi_encoder_atomic_check,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+       return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+               connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+       struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+       struct edid *edid;
+       int ret = 0;
+
+       if (!hdmi->ddc)
+               return 0;
+
+       edid = drm_get_edid(connector, hdmi->ddc);
+       if (edid) {
+               hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+               hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+               drm_connector_update_edid_property(connector, edid);
+               ret = drm_add_edid_modes(connector, edid);
+               kfree(edid);
+       }
+
+       return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+                              struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+                                      uint32_t maxX, uint32_t maxY)
+{
+       return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
+       .fill_modes = inno_hdmi_probe_single_connector_modes,
+       .detect = inno_hdmi_connector_detect,
+       .destroy = inno_hdmi_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+       .get_modes = inno_hdmi_connector_get_modes,
+       .mode_valid = inno_hdmi_connector_mode_valid,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+       struct drm_encoder *encoder = &hdmi->encoder;
+       struct device *dev = hdmi->dev;
+
+       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+       /*
+        * If we failed to find the CRTC(s) which this encoder is
+        * supposed to be connected to, it's because the CRTC has
+        * not been registered yet.  Defer probing, and hope that
+        * the required CRTC is added later.
+        */
+       if (encoder->possible_crtcs == 0)
+               return -EPROBE_DEFER;
+
+       drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+       drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+       drm_connector_helper_add(&hdmi->connector,
+                                &inno_hdmi_connector_helper_funcs);
+       drm_connector_init_with_ddc(drm, &hdmi->connector,
+                                   &inno_hdmi_connector_funcs,
+                                   DRM_MODE_CONNECTOR_HDMIA,
+                                   hdmi->ddc);
+
+       drm_connector_attach_encoder(&hdmi->connector, encoder);
+
+       return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+       struct inno_hdmi_i2c *i2c = hdmi->i2c;
+       u8 stat;
+
+       stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+       if (!(stat & m_INT_EDID_READY))
+               return IRQ_NONE;
+
+       /* Clear HDMI EDID interrupt flag */
+       hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+       complete(&i2c->cmp);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+       struct inno_hdmi *hdmi = dev_id;
+       irqreturn_t ret = IRQ_NONE;
+       u8 interrupt;
+
+       if (hdmi->i2c)
+               ret = inno_hdmi_i2c_irq(hdmi);
+
+       interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+       if (interrupt & m_INT_HOTPLUG) {
+               hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+               ret = IRQ_WAKE_THREAD;
+       }
+
+       return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+       struct inno_hdmi *hdmi = dev_id;
+
+       drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+       return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+       int length = msgs->len;
+       u8 *buf = msgs->buf;
+       int ret;
+
+       ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+       if (!ret)
+               return -EAGAIN;
+
+       while (length--)
+               *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+       return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+       /*
+        * The DDC module only support read EDID message, so
+        * we assume that each word write to this i2c adapter
+        * should be the offset of EDID word address.
+        */
+       if ((msgs->len != 1) ||
+           ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+               return -EINVAL;
+
+       reinit_completion(&hdmi->i2c->cmp);
+
+       if (msgs->addr == DDC_SEGMENT_ADDR)
+               hdmi->i2c->segment_addr = msgs->buf[0];
+       if (msgs->addr == DDC_ADDR)
+               hdmi->i2c->ddc_addr = msgs->buf[0];
+
+       /* Set edid fifo first addr */
+       hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+       /* Set edid word address 0x00/0x80 */
+       hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+       /* Set edid segment pointer */
+       hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+       return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+                             struct i2c_msg *msgs, int num)
+{
+       struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+       struct inno_hdmi_i2c *i2c = hdmi->i2c;
+       int i, ret = 0;
+
+       mutex_lock(&i2c->lock);
+
+       /* Clear the EDID interrupt flag and unmute the interrupt */
+       hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+       hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+       for (i = 0; i < num; i++) {
+               DRM_DEV_DEBUG(hdmi->dev,
+                             "xfer: num: %d/%d, len: %d, flags: %#x\n",
+                             i + 1, num, msgs[i].len, msgs[i].flags);
+
+               if (msgs[i].flags & I2C_M_RD)
+                       ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+               else
+                       ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+               if (ret < 0)
+                       break;
+       }
+
+       if (!ret)
+               ret = num;
+
+       /* Mute HDMI EDID interrupt */
+       hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+       mutex_unlock(&i2c->lock);
+
+       return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+       .master_xfer    = inno_hdmi_i2c_xfer,
+       .functionality  = inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+       struct i2c_adapter *adap;
+       struct inno_hdmi_i2c *i2c;
+       int ret;
+
+       i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+       if (!i2c)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&i2c->lock);
+       init_completion(&i2c->cmp);
+
+       adap = &i2c->adap;
+       adap->class = I2C_CLASS_DDC;
+       adap->owner = THIS_MODULE;
+       adap->dev.parent = hdmi->dev;
+       adap->dev.of_node = hdmi->dev->of_node;
+       adap->algo = &inno_hdmi_algorithm;
+       strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+       i2c_set_adapdata(adap, hdmi);
+
+       ret = i2c_add_adapter(adap);
+       if (ret) {
+               dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+               devm_kfree(hdmi->dev, i2c);
+               return ERR_PTR(ret);
+       }
+
+       hdmi->i2c = i2c;
+
+       DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+       return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+                                void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm = data;
+       struct inno_hdmi *hdmi;
+       struct resource *iores;
+       int irq;
+       int ret;
+
+       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+       if (!hdmi)
+               return -ENOMEM;
+
+       hdmi->dev = dev;
+       hdmi->drm_dev = drm;
+
+       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       hdmi->regs = devm_ioremap_resource(dev, iores);
+       if (IS_ERR(hdmi->regs))
+               return PTR_ERR(hdmi->regs);
+
+       hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+       if (IS_ERR(hdmi->pclk)) {
+               DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI pclk clk\n");
+               return PTR_ERR(hdmi->pclk);
+       }
+
+       ret = clk_prepare_enable(hdmi->pclk);
+       if (ret) {
+               DRM_DEV_ERROR(hdmi->dev,
+                             "Cannot enable HDMI pclk clock: %d\n", ret);
+               return ret;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               ret = irq;
+               goto err_disable_clk;
+       }
+
+       inno_hdmi_reset(hdmi);
+
+       hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+       if (IS_ERR(hdmi->ddc)) {
+               ret = PTR_ERR(hdmi->ddc);
+               hdmi->ddc = NULL;
+               goto err_disable_clk;
+       }
+
+       /*
+        * When IP controller haven't configured to an accurate video
+        * timing, then the TMDS clock source would be switched to
+        * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+        * and reconfigure the DDC clock.
+        */
+       hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+       inno_hdmi_i2c_init(hdmi);
+
+       ret = inno_hdmi_register(drm, hdmi);
+       if (ret)
+               goto err_put_adapter;
+
+       dev_set_drvdata(dev, hdmi);
+
+       /* Unmute hotplug interrupt */
+       hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+       ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+                                       inno_hdmi_irq, IRQF_SHARED,
+                                       dev_name(dev), hdmi);
+       if (ret < 0)
+               goto err_cleanup_hdmi;
+
+       return 0;
+err_cleanup_hdmi:
+       hdmi->connector.funcs->destroy(&hdmi->connector);
+       hdmi->encoder.funcs->destroy(&hdmi->encoder);
+err_put_adapter:
+       i2c_put_adapter(hdmi->ddc);
+err_disable_clk:
+       clk_disable_unprepare(hdmi->pclk);
+       return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+                            void *data)
+{
+       struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+       hdmi->connector.funcs->destroy(&hdmi->connector);
+       hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+       i2c_put_adapter(hdmi->ddc);
+       clk_disable_unprepare(hdmi->pclk);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+       .bind   = inno_hdmi_bind,
+       .unbind = inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &inno_hdmi_ops);
+
+       return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+       { .compatible = "rockchip,rk3036-inno-hdmi",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+struct platform_driver inno_hdmi_driver = {
+       .probe  = inno_hdmi_probe,
+       .remove = inno_hdmi_remove,
+       .driver = {
+               .name = "innohdmi-rockchip",
+               .of_match_table = inno_hdmi_dt_ids,
+       },
+};
diff --git a/drivers/gpu/drm/verisilicon/inno_hdmi.h b/drivers/gpu/drm/verisilicon/inno_hdmi.h
new file mode 100755 (executable)
index 0000000..93245b5
--- /dev/null
@@ -0,0 +1,354 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang@rock-chips.com>
+ *    Yakir Yang <ykk@rock-chips.com>
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR               0x30
+
+enum PWR_MODE {
+       NORMAL,
+       LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE                  (100*1000)
+#define DDC_BUS_FREQ_L                 0x4b
+#define DDC_BUS_FREQ_H                 0x4c
+
+#define HDMI_SYS_CTRL                  0x00
+#define m_RST_ANALOG                   (1 << 6)
+#define v_RST_ANALOG                   (0 << 6)
+#define v_NOT_RST_ANALOG               (1 << 6)
+#define m_RST_DIGITAL                  (1 << 5)
+#define v_RST_DIGITAL                  (0 << 5)
+#define v_NOT_RST_DIGITAL              (1 << 5)
+#define m_REG_CLK_INV                  (1 << 4)
+#define v_REG_CLK_NOT_INV              (0 << 4)
+#define v_REG_CLK_INV                  (1 << 4)
+#define m_VCLK_INV                     (1 << 3)
+#define v_VCLK_NOT_INV                 (0 << 3)
+#define v_VCLK_INV                     (1 << 3)
+#define m_REG_CLK_SOURCE               (1 << 2)
+#define v_REG_CLK_SOURCE_TMDS          (0 << 2)
+#define v_REG_CLK_SOURCE_SYS           (1 << 2)
+#define m_POWER                                (1 << 1)
+#define v_PWR_ON                       (0 << 1)
+#define v_PWR_OFF                      (1 << 1)
+#define m_INT_POL                      (1 << 0)
+#define v_INT_POL_HIGH                 1
+#define v_INT_POL_LOW                  0
+
+#define HDMI_VIDEO_CONTRL1             0x01
+#define m_VIDEO_INPUT_FORMAT           (7 << 1)
+#define m_DE_SOURCE                    (1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)                (n << 1)
+#define v_DE_EXTERNAL                  1
+#define v_DE_INTERNAL                  0
+enum {
+       VIDEO_INPUT_SDR_RGB444 = 0,
+       VIDEO_INPUT_DDR_RGB444 = 5,
+       VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2             0x02
+#define m_VIDEO_OUTPUT_COLOR           (3 << 6)
+#define m_VIDEO_INPUT_BITS             (3 << 4)
+#define m_VIDEO_INPUT_CSP              (1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)                (((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)          (n << 4)
+#define v_VIDEO_INPUT_CSP(n)           (n << 0)
+enum {
+       VIDEO_INPUT_12BITS = 0,
+       VIDEO_INPUT_10BITS = 1,
+       VIDEO_INPUT_REVERT = 2,
+       VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL              0x03
+#define m_VIDEO_AUTO_CSC               (1 << 7)
+#define v_VIDEO_AUTO_CSC(n)            (n << 7)
+#define m_VIDEO_C0_C2_SWAP             (1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)          (n << 0)
+enum {
+       C0_C2_CHANGE_ENABLE = 0,
+       C0_C2_CHANGE_DISABLE = 1,
+       AUTO_CSC_DISABLE = 0,
+       AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3             0x04
+#define m_COLOR_DEPTH_NOT_INDICATED    (1 << 4)
+#define m_SOF                          (1 << 3)
+#define m_COLOR_RANGE                  (1 << 2)
+#define m_CSC                          (1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n) ((n) << 4)
+#define v_SOF_ENABLE                   (0 << 3)
+#define v_SOF_DISABLE                  (1 << 3)
+#define v_COLOR_RANGE_FULL             (1 << 2)
+#define v_COLOR_RANGE_LIMITED          (0 << 2)
+#define v_CSC_ENABLE                   1
+#define v_CSC_DISABLE                  0
+
+#define HDMI_AV_MUTE                   0x05
+#define m_AVMUTE_CLEAR                 (1 << 7)
+#define m_AVMUTE_ENABLE                        (1 << 6)
+#define m_AUDIO_MUTE                   (1 << 1)
+#define m_VIDEO_BLACK                  (1 << 0)
+#define v_AVMUTE_CLEAR(n)              (n << 7)
+#define v_AVMUTE_ENABLE(n)             (n << 6)
+#define v_AUDIO_MUTE(n)                        (n << 1)
+#define v_VIDEO_MUTE(n)                        (n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL          0x08
+#define v_HSYNC_POLARITY(n)            (n << 3)
+#define v_VSYNC_POLARITY(n)            (n << 2)
+#define v_INETLACE(n)                  (n << 1)
+#define v_EXTERANL_VIDEO(n)            (n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L                0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H                0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L                0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H                0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L                0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H                0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L     0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H     0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L                0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H                0x12
+#define HDMI_VIDEO_EXT_VBLANK          0x13
+#define HDMI_VIDEO_EXT_VDELAY          0x14
+#define HDMI_VIDEO_EXT_VDURATION       0x15
+
+#define HDMI_VIDEO_CSC_COEF            0x18
+
+#define HDMI_AUDIO_CTRL1               0x35
+enum {
+       CTS_SOURCE_INTERNAL = 0,
+       CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)                        (n << 7)
+
+enum {
+       DOWNSAMPLE_DISABLE = 0,
+       DOWNSAMPLE_1_2 = 1,
+       DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)               (n << 5)
+
+enum {
+       AUDIO_SOURCE_IIS = 0,
+       AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)              (n << 3)
+
+#define v_MCLK_ENABLE(n)               (n << 2)
+enum {
+       MCLK_128FS = 0,
+       MCLK_256FS = 1,
+       MCLK_384FS = 2,
+       MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)                        (n)
+
+#define AUDIO_SAMPLE_RATE              0x37
+enum {
+       AUDIO_32K = 0x3,
+       AUDIO_441K = 0x0,
+       AUDIO_48K = 0x2,
+       AUDIO_882K = 0x8,
+       AUDIO_96K = 0xa,
+       AUDIO_1764K = 0xc,
+       AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE                 0x38
+enum {
+       I2S_CHANNEL_1_2 = 1,
+       I2S_CHANNEL_3_4 = 3,
+       I2S_CHANNEL_5_6 = 7,
+       I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)               ((n) << 2)
+enum {
+       I2S_STANDARD = 0,
+       I2S_LEFT_JUSTIFIED = 1,
+       I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)                  (n)
+
+#define AUDIO_I2S_MAP                  0x39
+#define AUDIO_I2S_SWAPS_SPDIF          0x3a
+#define v_SPIDF_FREQ(n)                        (n)
+
+#define N_32K                          0x1000
+#define N_441K                         0x1880
+#define N_882K                         0x3100
+#define N_1764K                                0x6200
+#define N_48K                          0x1800
+#define N_96K                          0x3000
+#define N_192K                         0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS      0x3e
+#define m_AUDIO_STATUS_NLPCM           (1 << 7)
+#define m_AUDIO_STATUS_USE             (1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT       (1 << 5)
+#define m_AUDIO_STATUS_ADDITION                (3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY    (2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)                ((n & 1) << 7)
+#define AUDIO_N_H                      0x3f
+#define AUDIO_N_M                      0x40
+#define AUDIO_N_L                      0x41
+
+#define HDMI_AUDIO_CTS_H               0x45
+#define HDMI_AUDIO_CTS_M               0x46
+#define HDMI_AUDIO_CTS_L               0x47
+
+#define HDMI_DDC_CLK_L                 0x4b
+#define HDMI_DDC_CLK_H                 0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER      0x4d
+#define HDMI_EDID_WORD_ADDR            0x4e
+#define HDMI_EDID_FIFO_OFFSET          0x4f
+#define HDMI_EDID_FIFO_ADDR            0x50
+
+#define HDMI_PACKET_SEND_MANUAL                0x9c
+#define HDMI_PACKET_SEND_AUTO          0x9d
+#define m_PACKET_GCP_EN                        (1 << 7)
+#define m_PACKET_MSI_EN                        (1 << 6)
+#define m_PACKET_SDI_EN                        (1 << 5)
+#define m_PACKET_VSI_EN                        (1 << 4)
+#define v_PACKET_GCP_EN(n)             ((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)             ((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)             ((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)             ((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX  0x9f
+enum {
+       INFOFRAME_VSI = 0x05,
+       INFOFRAME_AVI = 0x06,
+       INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR       0xa0
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE   0x11
+enum {
+       AVI_COLOR_MODE_RGB = 0,
+       AVI_COLOR_MODE_YCBCR422 = 1,
+       AVI_COLOR_MODE_YCBCR444 = 2,
+       AVI_COLORIMETRY_NO_DATA = 0,
+
+       AVI_COLORIMETRY_SMPTE_170M = 1,
+       AVI_COLORIMETRY_ITU709 = 2,
+       AVI_COLORIMETRY_EXTENDED = 3,
+
+       AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+       AVI_CODED_FRAME_ASPECT_4_3 = 1,
+       AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+       ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+       ACTIVE_ASPECT_RATE_4_3 = 0x09,
+       ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+       ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL                 0x52
+#define m_HDMI_DVI                     (1 << 1)
+#define v_HDMI_DVI(n)                  (n << 1)
+
+#define HDMI_INTERRUPT_MASK1           0xc0
+#define HDMI_INTERRUPT_STATUS1         0xc1
+#define        m_INT_ACTIVE_VSYNC              (1 << 5)
+#define m_INT_EDID_READY               (1 << 2)
+
+#define HDMI_INTERRUPT_MASK2           0xc2
+#define HDMI_INTERRUPT_STATUS2         0xc3
+#define m_INT_HDCP_ERR                 (1 << 7)
+#define m_INT_BKSV_FLAG                        (1 << 6)
+#define m_INT_HDCP_OK                  (1 << 4)
+
+#define HDMI_STATUS                    0xc8
+#define m_HOTPLUG                      (1 << 7)
+#define m_MASK_INT_HOTPLUG             (1 << 5)
+#define m_INT_HOTPLUG                  (1 << 1)
+#define v_MASK_INT_HOTPLUG(n)          ((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC                  0xce
+#define HDMI_PHY_SYS_CTL               0xe0
+#define m_TMDS_CLK_SOURCE              (1 << 5)
+#define v_TMDS_FROM_PLL                        (0 << 5)
+#define v_TMDS_FROM_GEN                        (1 << 5)
+#define m_PHASE_CLK                    (1 << 4)
+#define v_DEFAULT_PHASE                        (0 << 4)
+#define v_SYNC_PHASE                   (1 << 4)
+#define m_TMDS_CURRENT_PWR             (1 << 3)
+#define v_TURN_ON_CURRENT              (0 << 3)
+#define v_CAT_OFF_CURRENT              (1 << 3)
+#define m_BANDGAP_PWR                  (1 << 2)
+#define v_BANDGAP_PWR_UP               (0 << 2)
+#define v_BANDGAP_PWR_DOWN             (1 << 2)
+#define m_PLL_PWR                      (1 << 1)
+#define v_PLL_PWR_UP                   (0 << 1)
+#define v_PLL_PWR_DOWN                 (1 << 1)
+#define m_TMDS_CHG_PWR                 (1 << 0)
+#define v_TMDS_CHG_PWR_UP              (0 << 0)
+#define v_TMDS_CHG_PWR_DOWN            (1 << 0)
+
+#define HDMI_PHY_CHG_PWR               0xe1
+#define v_CLK_CHG_PWR(n)               ((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)              ((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER                        0xe2
+#define v_CLK_MAIN_DRIVER(n)           (n << 4)
+#define v_DATA_MAIN_DRIVER(n)          (n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS          0xe3
+#define v_PRE_EMPHASIS(n)              ((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)            ((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)           ((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW                0xe7
+#define v_FEEDBACK_DIV_LOW(n)                  (n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH       0xe8
+#define v_FEEDBACK_DIV_HIGH(n)                 (n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO         0xed
+#define v_PRE_DIV_RATIO(n)             (n & 0x1f)
+
+#define HDMI_CEC_CTRL                  0xd0
+#define m_ADJUST_FOR_HISENSE           (1 << 6)
+#define m_REJECT_RX_BROADCAST          (1 << 5)
+#define m_BUSFREETIME_ENABLE           (1 << 2)
+#define m_REJECT_RX                    (1 << 1)
+#define m_START_TX                     (1 << 0)
+
+#define HDMI_CEC_DATA                  0xd1
+#define HDMI_CEC_TX_OFFSET             0xd2
+#define HDMI_CEC_RX_OFFSET             0xd3
+#define HDMI_CEC_CLK_H                 0xd4
+#define HDMI_CEC_CLK_L                 0xd5
+#define HDMI_CEC_TX_LENGTH             0xd6
+#define HDMI_CEC_RX_LENGTH             0xd7
+#define HDMI_CEC_TX_INT_MASK           0xd8
+#define m_TX_DONE                      (1 << 3)
+#define m_TX_NOACK                     (1 << 2)
+#define m_TX_BROADCAST_REJ             (1 << 1)
+#define m_TX_BUSNOTFREE                        (1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK           0xd9
+#define m_RX_LA_ERR                    (1 << 4)
+#define m_RX_GLITCH                    (1 << 3)
+#define m_RX_DONE                      (1 << 0)
+
+#define HDMI_CEC_TX_INT                        0xda
+#define HDMI_CEC_RX_INT                        0xdb
+#define HDMI_CEC_BUSFREETIME_L         0xdc
+#define HDMI_CEC_BUSFREETIME_H         0xdd
+#define HDMI_CEC_LOGICADDR             0xde
+
+#endif /* __INNO_HDMI_H__ */
diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c b/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c
new file mode 100755 (executable)
index 0000000..ac9b0a5
--- /dev/null
@@ -0,0 +1,1308 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright: 2017 Cadence Design Systems, Inc.
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+#include <video/mipi_display.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/component.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include "vs_drv.h"
+
+
+//sysrst registers
+#define SRST_ASSERT0           0x00
+#define SRST_STATUS0           0x04
+
+#define IP_CONF                                0x0
+#define SP_HS_FIFO_DEPTH(x)            (((x) & GENMASK(30, 26)) >> 26)
+#define SP_LP_FIFO_DEPTH(x)            (((x) & GENMASK(25, 21)) >> 21)
+#define VRS_FIFO_DEPTH(x)              (((x) & GENMASK(20, 16)) >> 16)
+#define DIRCMD_FIFO_DEPTH(x)           (((x) & GENMASK(15, 13)) >> 13)
+#define SDI_IFACE_32                   BIT(12)
+#define INTERNAL_DATAPATH_32           (0 << 10)
+#define INTERNAL_DATAPATH_16           (1 << 10)
+#define INTERNAL_DATAPATH_8            (3 << 10)
+#define INTERNAL_DATAPATH_SIZE         ((x) & GENMASK(11, 10))
+#define NUM_IFACE(x)                   ((((x) & GENMASK(9, 8)) >> 8) + 1)
+#define MAX_LANE_NB(x)                 (((x) & GENMASK(7, 6)) >> 6)
+#define RX_FIFO_DEPTH(x)               ((x) & GENMASK(5, 0))
+
+#define MCTL_MAIN_DATA_CTL             0x4
+#define TE_MIPI_POLLING_EN             BIT(25)
+#define TE_HW_POLLING_EN               BIT(24)
+#define DISP_EOT_GEN                   BIT(18)
+#define HOST_EOT_GEN                   BIT(17)
+#define DISP_GEN_CHECKSUM              BIT(16)
+#define DISP_GEN_ECC                   BIT(15)
+#define BTA_EN                         BIT(14)
+#define READ_EN                                BIT(13)
+#define REG_TE_EN                      BIT(12)
+#define IF_TE_EN(x)                    BIT(8 + (x))
+#define TVG_SEL                                BIT(6)
+#define VID_EN                         BIT(5)
+#define IF_VID_SELECT(x)               ((x) << 2)
+#define IF_VID_SELECT_MASK             GENMASK(3, 2)
+#define IF_VID_MODE                    BIT(1)
+#define LINK_EN                                BIT(0)
+
+#define MCTL_MAIN_PHY_CTL              0x8
+#define HS_INVERT_DAT(x)               BIT(19 + ((x) * 2))
+#define SWAP_PINS_DAT(x)               BIT(18 + ((x) * 2))
+#define HS_INVERT_CLK                  BIT(17)
+#define SWAP_PINS_CLK                  BIT(16)
+#define HS_SKEWCAL_EN                  BIT(15)
+#define WAIT_BURST_TIME(x)             ((x) << 10)
+#define DATA_ULPM_EN(x)                        BIT(6 + (x))
+#define CLK_ULPM_EN                    BIT(5)
+#define CLK_CONTINUOUS                 BIT(4)
+#define DATA_LANE_EN(x)                        BIT((x) - 1)
+
+#define MCTL_MAIN_EN                   0xc
+#define DATA_FORCE_STOP                        BIT(17)
+#define CLK_FORCE_STOP                 BIT(16)
+#define IF_EN(x)                       BIT(13 + (x))
+#define DATA_LANE_ULPM_REQ(l)          BIT(9 + (l))
+#define CLK_LANE_ULPM_REQ              BIT(8)
+#define DATA_LANE_START(x)             BIT(4 + (x))
+#define CLK_LANE_EN                    BIT(3)
+#define PLL_START                      BIT(0)
+
+#define MCTL_DPHY_CFG0                 0x10
+#define DPHY_C_RSTB                    BIT(20)
+#define DPHY_D_RSTB(x)                 GENMASK(15 + (x), 16)
+#define DPHY_PLL_PDN                   BIT(10)
+#define DPHY_CMN_PDN                   BIT(9)
+#define DPHY_C_PDN                     BIT(8)
+#define DPHY_D_PDN(x)                  GENMASK(3 + (x), 4)
+#define DPHY_ALL_D_PDN                 GENMASK(7, 4)
+#define DPHY_PLL_PSO                   BIT(1)
+#define DPHY_CMN_PSO                   BIT(0)
+
+#define MCTL_DPHY_TIMEOUT1             0x14
+#define HSTX_TIMEOUT(x)                        ((x) << 4)
+#define HSTX_TIMEOUT_MAX               GENMASK(17, 0)
+#define CLK_DIV(x)                     (x)
+#define CLK_DIV_MAX                    GENMASK(3, 0)
+
+#define MCTL_DPHY_TIMEOUT2             0x18
+#define LPRX_TIMEOUT(x)                        (x)
+
+#define MCTL_ULPOUT_TIME               0x1c
+#define DATA_LANE_ULPOUT_TIME(x)       ((x) << 9)
+#define CLK_LANE_ULPOUT_TIME(x)                (x)
+
+#define MCTL_3DVIDEO_CTL               0x20
+#define VID_VSYNC_3D_EN                        BIT(7)
+#define VID_VSYNC_3D_LR                        BIT(5)
+#define VID_VSYNC_3D_SECOND_EN         BIT(4)
+#define VID_VSYNC_3DFORMAT_LINE                (0 << 2)
+#define VID_VSYNC_3DFORMAT_FRAME       (1 << 2)
+#define VID_VSYNC_3DFORMAT_PIXEL       (2 << 2)
+#define VID_VSYNC_3DMODE_OFF           0
+#define VID_VSYNC_3DMODE_PORTRAIT      1
+#define VID_VSYNC_3DMODE_LANDSCAPE     2
+
+#define MCTL_MAIN_STS                  0x24
+#define MCTL_MAIN_STS_CTL              0x130
+#define MCTL_MAIN_STS_CLR              0x150
+#define MCTL_MAIN_STS_FLAG             0x170
+#define HS_SKEWCAL_DONE                        BIT(11)
+#define IF_UNTERM_PKT_ERR(x)           BIT(8 + (x))
+#define LPRX_TIMEOUT_ERR               BIT(7)
+#define HSTX_TIMEOUT_ERR               BIT(6)
+#define DATA_LANE_RDY(l)               BIT(2 + (l))
+#define CLK_LANE_RDY                   BIT(1)
+#define PLL_LOCKED                     BIT(0)
+
+#define MCTL_DPHY_ERR                  0x28
+#define MCTL_DPHY_ERR_CTL1             0x148
+#define MCTL_DPHY_ERR_CLR              0x168
+#define MCTL_DPHY_ERR_FLAG             0x188
+#define ERR_CONT_LP(x, l)              BIT(18 + ((x) * 4) + (l))
+#define ERR_CONTROL(l)                 BIT(14 + (l))
+#define ERR_SYNESC(l)                  BIT(10 + (l))
+#define ERR_ESC(l)                     BIT(6 + (l))
+
+#define MCTL_DPHY_ERR_CTL2             0x14c
+#define ERR_CONT_LP_EDGE(x, l)         BIT(12 + ((x) * 4) + (l))
+#define ERR_CONTROL_EDGE(l)            BIT(8 + (l))
+#define ERR_SYN_ESC_EDGE(l)            BIT(4 + (l))
+#define ERR_ESC_EDGE(l)                        BIT(0 + (l))
+
+#define MCTL_LANE_STS                  0x2c
+#define PPI_C_TX_READY_HS              BIT(18)
+#define DPHY_PLL_LOCK                  BIT(17)
+#define PPI_D_RX_ULPS_ESC(x)           (((x) & GENMASK(15, 12)) >> 12)
+#define LANE_STATE_START               0
+#define LANE_STATE_IDLE                        1
+#define LANE_STATE_WRITE               2
+#define LANE_STATE_ULPM                        3
+#define LANE_STATE_READ                        4
+#define DATA_LANE_STATE(l, val)                \
+       (((val) >> (2 + 2 * (l) + ((l) ? 1 : 0))) & GENMASK((l) ? 1 : 2, 0))
+#define CLK_LANE_STATE_HS              2
+#define CLK_LANE_STATE(val)            ((val) & GENMASK(1, 0))
+
+#define DSC_MODE_CTL                   0x30
+#define DSC_MODE_EN                    BIT(0)
+
+#define DSC_CMD_SEND                   0x34
+#define DSC_SEND_PPS                   BIT(0)
+#define DSC_EXECUTE_QUEUE              BIT(1)
+
+#define DSC_PPS_WRDAT                  0x38
+
+#define DSC_MODE_STS                   0x3c
+#define DSC_PPS_DONE                   BIT(1)
+#define DSC_EXEC_DONE                  BIT(2)
+
+#define CMD_MODE_CTL                   0x70
+#define IF_LP_EN(x)                    BIT(9 + (x))
+#define IF_VCHAN_ID(x, c)              ((c) << ((x) * 2))
+
+#define CMD_MODE_CTL2                  0x74
+#define TE_TIMEOUT(x)                  ((x) << 11)
+#define FILL_VALUE(x)                  ((x) << 3)
+#define ARB_IF_WITH_HIGHEST_PRIORITY(x)        ((x) << 1)
+#define ARB_ROUND_ROBIN_MODE           BIT(0)
+
+#define CMD_MODE_STS                   0x78
+#define CMD_MODE_STS_CTL               0x134
+#define CMD_MODE_STS_CLR               0x154
+#define CMD_MODE_STS_FLAG              0x174
+#define ERR_IF_UNDERRUN(x)             BIT(4 + (x))
+#define ERR_UNWANTED_READ              BIT(3)
+#define ERR_TE_MISS                    BIT(2)
+#define ERR_NO_TE                      BIT(1)
+#define CSM_RUNNING                    BIT(0)
+
+#define DIRECT_CMD_SEND                        0x80
+
+#define DIRECT_CMD_MAIN_SETTINGS       0x84
+#define TRIGGER_VAL(x)                 ((x) << 25)
+#define CMD_LP_EN                      BIT(24)
+#define CMD_SIZE(x)                    ((x) << 16)
+#define CMD_VCHAN_ID(x)                        ((x) << 14)
+#define CMD_DATATYPE(x)                        ((x) << 8)
+#define CMD_LONG                       BIT(3)
+#define WRITE_CMD                      0
+#define READ_CMD                       1
+#define TE_REQ                         4
+#define TRIGGER_REQ                    5
+#define BTA_REQ                                6
+
+#define DIRECT_CMD_STS                 0x88
+#define DIRECT_CMD_STS_CTL             0x138
+#define DIRECT_CMD_STS_CLR             0x158
+#define DIRECT_CMD_STS_FLAG            0x178
+#define RCVD_ACK_VAL(val)              ((val) >> 16)
+#define RCVD_TRIGGER_VAL(val)          (((val) & GENMASK(14, 11)) >> 11)
+#define READ_COMPLETED_WITH_ERR                BIT(10)
+#define BTA_FINISHED                   BIT(9)
+#define BTA_COMPLETED                  BIT(8)
+#define TE_RCVD                                BIT(7)
+#define TRIGGER_RCVD                   BIT(6)
+#define ACK_WITH_ERR_RCVD              BIT(5)
+#define ACK_RCVD                       BIT(4)
+#define READ_COMPLETED                 BIT(3)
+#define TRIGGER_COMPLETED              BIT(2)
+#define WRITE_COMPLETED                        BIT(1)
+
+#define SENDING_CMD                    BIT(0)
+
+#define DIRECT_CMD_STOP_READ           0x8c
+
+#define DIRECT_CMD_WRDATA              0x90
+
+#define DIRECT_CMD_FIFO_RST            0x94
+
+#define DIRECT_CMD_RDDATA              0xa0
+
+#define DIRECT_CMD_RD_PROPS            0xa4
+#define RD_DCS                         BIT(18)
+#define RD_VCHAN_ID(val)               (((val) >> 16) & GENMASK(1, 0))
+#define RD_SIZE(val)                   ((val) & GENMASK(15, 0))
+
+#define DIRECT_CMD_RD_STS              0xa8
+#define DIRECT_CMD_RD_STS_CTL          0x13c
+#define DIRECT_CMD_RD_STS_CLR          0x15c
+#define DIRECT_CMD_RD_STS_FLAG         0x17c
+#define ERR_EOT_WITH_ERR               BIT(8)
+#define ERR_MISSING_EOT                        BIT(7)
+#define ERR_WRONG_LENGTH               BIT(6)
+#define ERR_OVERSIZE                   BIT(5)
+#define ERR_RECEIVE                    BIT(4)
+#define ERR_UNDECODABLE                        BIT(3)
+#define ERR_CHECKSUM                   BIT(2)
+#define ERR_UNCORRECTABLE              BIT(1)
+#define ERR_FIXED                      BIT(0)
+
+#define VID_MAIN_CTL                   0xb0
+#define VID_IGNORE_MISS_VSYNC          BIT(31)
+#define VID_FIELD_SW                   BIT(28)
+#define VID_INTERLACED_EN              BIT(27)
+#define RECOVERY_MODE(x)               ((x) << 25)
+#define RECOVERY_MODE_NEXT_HSYNC       0
+#define RECOVERY_MODE_NEXT_STOP_POINT  2
+#define RECOVERY_MODE_NEXT_VSYNC       3
+#define REG_BLKEOL_MODE(x)             ((x) << 23)
+#define REG_BLKLINE_MODE(x)            ((x) << 21)
+#define REG_BLK_MODE_NULL_PKT          0
+#define REG_BLK_MODE_BLANKING_PKT      1
+#define REG_BLK_MODE_LP                        2
+#define SYNC_PULSE_HORIZONTAL          BIT(20)
+#define SYNC_PULSE_ACTIVE              BIT(19)
+#define BURST_MODE                     BIT(18)
+#define VID_PIXEL_MODE_MASK            GENMASK(17, 14)
+#define VID_PIXEL_MODE_RGB565          (0 << 14)
+#define VID_PIXEL_MODE_RGB666_PACKED   (1 << 14)
+#define VID_PIXEL_MODE_RGB666          (2 << 14)
+#define VID_PIXEL_MODE_RGB888          (3 << 14)
+#define VID_PIXEL_MODE_RGB101010       (4 << 14)
+#define VID_PIXEL_MODE_RGB121212       (5 << 14)
+#define VID_PIXEL_MODE_YUV420          (8 << 14)
+#define VID_PIXEL_MODE_YUV422_PACKED   (9 << 14)
+#define VID_PIXEL_MODE_YUV422          (10 << 14)
+#define VID_PIXEL_MODE_YUV422_24B      (11 << 14)
+#define VID_PIXEL_MODE_DSC_COMP                (12 << 14)
+#define VID_DATATYPE(x)                        ((x) << 8)
+#define VID_VIRTCHAN_ID(iface, x)      ((x) << (4 + (iface) * 2))
+#define STOP_MODE(x)                   ((x) << 2)
+#define START_MODE(x)                  (x)
+
+#define VID_VSIZE1                     0xb4
+#define VFP_LEN(x)                     ((x) << 12)
+#define VBP_LEN(x)                     ((x) << 6)
+#define VSA_LEN(x)                     (x)
+
+#define VID_VSIZE2                     0xb8
+#define VACT_LEN(x)                    (x)
+
+#define VID_HSIZE1                     0xc0
+#define HBP_LEN(x)                     ((x) << 16)
+#define HSA_LEN(x)                     (x)
+
+#define VID_HSIZE2                     0xc4
+#define HFP_LEN(x)                     ((x) << 16)
+#define HACT_LEN(x)                    (x)
+
+#define VID_BLKSIZE1                   0xcc
+#define BLK_EOL_PKT_LEN(x)             ((x) << 15)
+#define BLK_LINE_EVENT_PKT_LEN(x)      (x)
+
+#define VID_BLKSIZE2                   0xd0
+#define BLK_LINE_PULSE_PKT_LEN(x)      (x)
+
+#define VID_PKT_TIME                   0xd8
+#define BLK_EOL_DURATION(x)            (x)
+
+#define VID_DPHY_TIME                  0xdc
+#define REG_WAKEUP_TIME(x)             ((x) << 17)
+#define REG_LINE_DURATION(x)           (x)
+
+#define VID_ERR_COLOR1                 0xe0
+#define COL_GREEN(x)                   ((x) << 12)
+#define COL_RED(x)                     (x)
+
+#define VID_ERR_COLOR2                 0xe4
+#define PAD_VAL(x)                     ((x) << 12)
+#define COL_BLUE(x)                    (x)
+
+#define VID_VPOS                       0xe8
+#define LINE_VAL(val)                  (((val) & GENMASK(14, 2)) >> 2)
+#define LINE_POS(val)                  ((val) & GENMASK(1, 0))
+
+#define VID_HPOS                       0xec
+#define HORIZ_VAL(val)                 (((val) & GENMASK(17, 3)) >> 3)
+#define HORIZ_POS(val)                 ((val) & GENMASK(2, 0))
+
+#define VID_MODE_STS                   0xf0
+#define VID_MODE_STS_CTL               0x140
+#define VID_MODE_STS_CLR               0x160
+#define VID_MODE_STS_FLAG              0x180
+#define VSG_RECOVERY                   BIT(10)
+#define ERR_VRS_WRONG_LEN              BIT(9)
+#define ERR_LONG_READ                  BIT(8)
+#define ERR_LINE_WRITE                 BIT(7)
+#define ERR_BURST_WRITE                        BIT(6)
+#define ERR_SMALL_HEIGHT               BIT(5)
+#define ERR_SMALL_LEN                  BIT(4)
+#define ERR_MISSING_VSYNC              BIT(3)
+#define ERR_MISSING_HSYNC              BIT(2)
+#define ERR_MISSING_DATA               BIT(1)
+#define VSG_RUNNING                    BIT(0)
+
+#define VID_VCA_SETTING1               0xf4
+#define BURST_LP                       BIT(16)
+#define MAX_BURST_LIMIT(x)             (x)
+
+#define VID_VCA_SETTING2               0xf8
+#define MAX_LINE_LIMIT(x)              ((x) << 16)
+#define EXACT_BURST_LIMIT(x)           (x)
+
+#define TVG_CTL                                0xfc
+#define TVG_STRIPE_SIZE(x)             ((x) << 5)
+#define TVG_MODE_MASK                  GENMASK(4, 3)
+#define TVG_MODE_SINGLE_COLOR          (0 << 3)
+#define TVG_MODE_VSTRIPES              (2 << 3)
+#define TVG_MODE_HSTRIPES              (3 << 3)
+#define TVG_STOPMODE_MASK              GENMASK(2, 1)
+#define TVG_STOPMODE_EOF               (0 << 1)
+#define TVG_STOPMODE_EOL               (1 << 1)
+#define TVG_STOPMODE_NOW               (2 << 1)
+#define TVG_RUN                                BIT(0)
+
+#define TVG_IMG_SIZE                   0x100
+#define TVG_NBLINES(x)                 ((x) << 16)
+#define TVG_LINE_SIZE(x)               (x)
+
+#define TVG_COLOR1                     0x104
+#define TVG_COL1_GREEN(x)              ((x) << 12)
+#define TVG_COL1_RED(x)                        (x)
+
+#define TVG_COLOR1_BIS                 0x108
+#define TVG_COL1_BLUE(x)               (x)
+
+#define TVG_COLOR2                     0x10c
+#define TVG_COL2_GREEN(x)              ((x) << 12)
+#define TVG_COL2_RED(x)                        (x)
+
+#define TVG_COLOR2_BIS                 0x110
+#define TVG_COL2_BLUE(x)               (x)
+
+#define TVG_STS                                0x114
+#define TVG_STS_CTL                    0x144
+#define TVG_STS_CLR                    0x164
+#define TVG_STS_FLAG                   0x184
+#define TVG_STS_RUNNING                        BIT(0)
+
+#define STS_CTL_EDGE(e)                        ((e) << 16)
+
+#define DPHY_LANES_MAP                 0x198
+#define DAT_REMAP_CFG(b, l)            ((l) << ((b) * 8))
+
+#define DPI_IRQ_EN                     0x1a0
+#define DPI_IRQ_CLR                    0x1a4
+#define DPI_IRQ_STS                    0x1a8
+#define PIXEL_BUF_OVERFLOW             BIT(0)
+
+#define DPI_CFG                                0x1ac
+#define DPI_CFG_FIFO_DEPTH(x)          ((x) >> 16)
+#define DPI_CFG_FIFO_LEVEL(x)          ((x) & GENMASK(15, 0))
+
+#define TEST_GENERIC                   0x1f0
+#define TEST_STATUS(x)                 ((x) >> 16)
+#define TEST_CTRL(x)                   (x)
+
+#define ID_REG                         0x1fc
+#define REV_VENDOR_ID(x)               (((x) & GENMASK(31, 20)) >> 20)
+#define REV_PRODUCT_ID(x)              (((x) & GENMASK(19, 12)) >> 12)
+#define REV_HW(x)                      (((x) & GENMASK(11, 8)) >> 8)
+#define REV_MAJOR(x)                   (((x) & GENMASK(7, 4)) >> 4)
+#define REV_MINOR(x)                   ((x) & GENMASK(3, 0))
+
+#define DSI_OUTPUT_PORT                        0
+#define DSI_INPUT_PORT(inputid)                (1 + (inputid))
+
+#define DSI_HBP_FRAME_OVERHEAD         12
+#define DSI_HSA_FRAME_OVERHEAD         14
+#define DSI_HFP_FRAME_OVERHEAD         6
+#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4
+#define DSI_BLANKING_FRAME_OVERHEAD    6
+#define DSI_NULL_FRAME_OVERHEAD                6
+#define DSI_EOT_PKT_SIZE               4
+
+struct cdns_dsi_output {
+       struct mipi_dsi_device *dev;
+       struct drm_panel *panel;
+       struct drm_bridge *bridge;
+       union phy_configure_opts phy_opts;
+};
+
+enum cdns_dsi_input_id {
+       CDNS_SDI_INPUT,
+       CDNS_DPI_INPUT,
+       CDNS_DSC_INPUT,
+};
+
+struct cdns_dsi_cfg {
+       unsigned int hfp;
+       unsigned int hsa;
+       unsigned int hbp;
+       unsigned int hact;
+       unsigned int htotal;
+};
+
+struct cdns_dsi_input {
+       enum cdns_dsi_input_id id;
+       struct drm_bridge bridge;
+};
+
+struct cdns_dsi {
+       struct mipi_dsi_host base;
+       void __iomem *regs;
+       struct cdns_dsi_input input;
+       struct cdns_dsi_output output;
+       unsigned int direct_cmd_fifo_depth;
+       unsigned int rx_fifo_depth;
+       struct completion direct_cmd_comp;
+       struct clk *dsi_p_clk;
+       struct reset_control *dsi_p_rst;
+       struct clk *dsi_sys_clk;
+       bool link_initialized;
+       struct phy *dphy;
+};
+
+static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input)
+{
+       return container_of(input, struct cdns_dsi, input);
+}
+
+static inline struct cdns_dsi *to_cdns_dsi(struct mipi_dsi_host *host)
+{
+       return container_of(host, struct cdns_dsi, base);
+}
+
+static inline struct cdns_dsi_input *
+bridge_to_cdns_dsi_input(struct drm_bridge *bridge)
+{
+       return container_of(bridge, struct cdns_dsi_input, bridge);
+}
+
+static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode,
+                               bool mode_valid_check)
+{
+       if (mode_valid_check)
+               return mode->hsync_start - mode->hdisplay;
+
+       return mode->crtc_hsync_start - mode->crtc_hdisplay;
+}
+
+static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
+                               unsigned int dpi_bpp,
+                               unsigned int dsi_pkt_overhead)
+{
+       unsigned int dsi_timing = DIV_ROUND_UP(dpi_timing * dpi_bpp, 8);
+       //unsigned int dsi_timing = dpi_timing;
+
+       if (dsi_timing < dsi_pkt_overhead)
+               dsi_timing = 0;
+       else
+               dsi_timing -= dsi_pkt_overhead;
+
+       return dsi_timing;
+}
+
+static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
+                            const struct drm_display_mode *mode,
+                            struct cdns_dsi_cfg *dsi_cfg,
+                            bool mode_valid_check)
+{
+       struct cdns_dsi_output *output = &dsi->output;
+       unsigned int tmp;
+       bool sync_pulse = false;
+       int bpp;
+
+       memset(dsi_cfg, 0, sizeof(*dsi_cfg));
+
+       if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+               sync_pulse = true;
+       bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
+
+       if (mode_valid_check)
+               tmp = mode->htotal -
+                     (sync_pulse ? mode->hsync_end : mode->hsync_start);
+       else
+               tmp = mode->crtc_htotal -
+                     (sync_pulse ?
+                      mode->crtc_hsync_end : mode->crtc_hsync_start);
+
+       dsi_cfg->hbp = dpi_to_dsi_timing(tmp, bpp, DSI_HBP_FRAME_OVERHEAD);
+
+       if (sync_pulse) {
+               if (mode_valid_check)
+                       tmp = mode->hsync_end - mode->hsync_start;
+               else
+                       tmp = mode->crtc_hsync_end - mode->crtc_hsync_start;
+
+               dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp,
+                                                DSI_HSA_FRAME_OVERHEAD);
+       }
+
+       dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ?
+                                         mode->hdisplay : mode->crtc_hdisplay,
+                                         bpp, 0);
+       dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode, mode_valid_check),
+                                        bpp, DSI_HFP_FRAME_OVERHEAD);
+       //dpi to dsi transfer can not match , reconfig those parms
+       if (mode->hdisplay == 800) {
+               dsi_cfg->hsa = 16;      //30-14
+               dsi_cfg->hbp = 73;      //85-12
+               dsi_cfg->hfp = 146; //152-6
+       }
+
+       return 0;
+}
+
+static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi,
+                             struct cdns_dsi_cfg *dsi_cfg,
+                             struct phy_configure_opts_mipi_dphy *phy_cfg,
+                             const struct drm_display_mode *mode,
+                             bool mode_valid_check)
+{
+       struct cdns_dsi_output *output = &dsi->output;
+       unsigned long long dlane_bps;
+       unsigned long adj_dsi_htotal;
+       unsigned long dsi_htotal;
+       unsigned long dpi_htotal;
+       unsigned long dpi_hz;
+       unsigned int dsi_hfp_ext;
+       unsigned int lanes = output->dev->lanes;
+
+       dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
+
+       if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+               dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
+
+       dsi_htotal += dsi_cfg->hact;
+       dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
+
+       /*
+        * Make sure DSI htotal is aligned on a lane boundary when calculating
+        * the expected data rate. This is done by extending HFP in case of
+        * misalignment.
+        */
+       adj_dsi_htotal = dsi_htotal;
+       if (dsi_htotal % lanes)
+               adj_dsi_htotal += lanes - (dsi_htotal % lanes);
+
+       dpi_hz = (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000;
+       dlane_bps = (unsigned long long)dpi_hz * adj_dsi_htotal;
+
+       /* data rate in bytes/sec is not an integer, refuse the mode. */
+       dpi_htotal = mode_valid_check ? mode->htotal : mode->crtc_htotal;
+
+       if (do_div(dlane_bps, lanes * dpi_htotal))
+               return -EINVAL;
+
+       /* data rate was in bytes/sec, convert to bits/sec. */
+       phy_cfg->hs_clk_rate = dlane_bps * 8;
+
+       dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
+       dsi_cfg->hfp += dsi_hfp_ext;
+       dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
+
+       return 0;
+}
+
+static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
+                              const struct drm_display_mode *mode,
+                              struct cdns_dsi_cfg *dsi_cfg,
+                              bool mode_valid_check)
+{
+       struct cdns_dsi_output *output = &dsi->output;
+       struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
+       unsigned long dsi_hss_hsa_hse_hbp;
+       unsigned int nlanes = output->dev->lanes;
+       int ret;
+
+       ret = cdns_dsi_mode2cfg(dsi, mode, dsi_cfg, mode_valid_check);
+       if (ret)
+               return ret;
+
+       phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000,
+                                        mipi_dsi_pixel_format_to_bpp(output->dev->format),
+                                        nlanes, phy_cfg);
+
+       ret = cdns_dsi_adjust_phy_config(dsi, dsi_cfg, phy_cfg, mode, mode_valid_check);
+       if (ret)
+               return ret;
+
+       ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &output->phy_opts);
+       if (ret)
+               return ret;
+
+       dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
+       if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+               dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
+
+       /*
+        * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO
+        * is empty before we start a receiving a new line on the DPI
+        * interface.
+        */
+       if ((u64)phy_cfg->hs_clk_rate *
+           mode_to_dpi_hfp(mode, mode_valid_check) * nlanes <
+           (u64)dsi_hss_hsa_hse_hbp *
+           (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
+                                 enum drm_bridge_attach_flags flags)
+{
+       struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
+       struct cdns_dsi *dsi = input_to_dsi(input);
+       struct cdns_dsi_output *output = &dsi->output;
+
+       if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) {
+               dev_err(dsi->base.dev,
+                       "cdns-dsi driver is only compatible with DRM devices supporting atomic updates");
+               return -EOPNOTSUPP;
+       }
+
+       return drm_bridge_attach(bridge->encoder, output->bridge, bridge,
+                                flags);
+}
+
+static enum drm_mode_status
+cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+                          const struct drm_display_info *info,
+                          const struct drm_display_mode *mode)
+{
+       struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
+       struct cdns_dsi *dsi = input_to_dsi(input);
+       struct cdns_dsi_output *output = &dsi->output;
+       struct cdns_dsi_cfg dsi_cfg;
+       int bpp, ret;
+
+       /*
+        * VFP_DSI should be less than VFP_DPI and VFP_DSI should be at
+        * least 1.
+        */
+       if (mode->vtotal - mode->vsync_end < 2)
+               return MODE_V_ILLEGAL;
+
+       /* VSA_DSI = VSA_DPI and must be at least 2. */
+       if (mode->vsync_end - mode->vsync_start < 2)
+               return MODE_V_ILLEGAL;
+
+       /* HACT must be 32-bits aligned. */
+       bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
+       if ((mode->hdisplay * bpp) % 32)
+               return MODE_H_ILLEGAL;
+
+       ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true);
+       if (ret)
+               return MODE_BAD;
+
+       return MODE_OK;
+}
+
+static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
+{
+       struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
+       struct cdns_dsi *dsi = input_to_dsi(input);
+       u32 val;
+
+       dsi->link_initialized = false;
+       val = readl(dsi->regs + MCTL_MAIN_DATA_CTL);
+       val &= ~(IF_VID_SELECT_MASK | IF_VID_MODE | VID_EN | HOST_EOT_GEN |
+                DISP_EOT_GEN);
+       writel(val, dsi->regs + MCTL_MAIN_DATA_CTL);
+
+       val = readl(dsi->regs + MCTL_MAIN_EN) & ~IF_EN(input->id);
+       writel(val, dsi->regs + MCTL_MAIN_EN);
+       pm_runtime_put(dsi->base.dev);
+       phy_power_off(dsi->dphy);
+       phy_exit(dsi->dphy);
+}
+
+static void release_txbyte_rst(void)
+{
+       void __iomem *regs = ioremap(0x12250000, 0x10000);
+
+       u32 temp = readl(regs + SRST_ASSERT0);
+
+       temp &= ~(0x1 << 18);
+       temp |= (0x0 & 0x1) << 18;
+
+       writel(temp, regs + SRST_ASSERT0);
+
+       do {
+               temp = readl(regs + SRST_STATUS0) >> 18;
+               temp &= 0x1;
+       } while (temp != 0x1);
+       //udelay(1);
+}
+
+static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
+{
+       struct cdns_dsi_output *output = &dsi->output;
+       u32 dpi_fifo_int = 0;
+
+       /*
+        * Power all internal DPHY blocks down and maintain their reset line
+        * asserted before changing the DPHY config.
+        */
+       writel(DPHY_CMN_PSO | DPHY_PLL_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN |
+              DPHY_CMN_PDN | DPHY_PLL_PDN,
+              dsi->regs + MCTL_DPHY_CFG0);
+
+       phy_init(dsi->dphy);
+       phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
+       phy_configure(dsi->dphy, &output->phy_opts);
+       phy_power_on(dsi->dphy);
+       release_txbyte_rst();
+
+       writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR);
+       writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN,
+              dsi->regs + MCTL_DPHY_CFG0);
+       mdelay(100);
+       /* De-assert data and clock reset lines. */
+       writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
+              DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
+              dsi->regs + MCTL_DPHY_CFG0);
+
+       dpi_fifo_int = readl(dsi->regs + DPI_IRQ_CLR);
+       if (dpi_fifo_int)
+               writel(1, dsi->regs + DPI_IRQ_CLR);
+}
+
+static void cdns_dsi_init_link(struct cdns_dsi *dsi)
+{
+       struct cdns_dsi_output *output = &dsi->output;
+       unsigned long sysclk_period, ulpout;
+       u32 val;
+       int i;
+
+       if (dsi->link_initialized)
+               return;
+
+       val = 0;
+       for (i = 1; i < output->dev->lanes; i++)
+               val |= DATA_LANE_EN(i);
+
+       if (!(output->dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
+               val |= CLK_CONTINUOUS;
+
+       writel(val, dsi->regs + MCTL_MAIN_PHY_CTL);
+
+       /* ULPOUT should be set to 1ms and is expressed in sysclk cycles.*/
+       sysclk_period = NSEC_PER_SEC / clk_get_rate(dsi->dsi_sys_clk);
+       ulpout = DIV_ROUND_UP(NSEC_PER_MSEC, sysclk_period);
+       writel(CLK_LANE_ULPOUT_TIME(ulpout) | DATA_LANE_ULPOUT_TIME(ulpout),
+               dsi->regs + MCTL_ULPOUT_TIME);
+
+       writel(LINK_EN, dsi->regs + MCTL_MAIN_DATA_CTL);
+
+       val = CLK_LANE_EN | PLL_START;
+       for (i = 0; i < output->dev->lanes; i++)
+               val |= DATA_LANE_START(i);
+
+       writel(val, dsi->regs + MCTL_MAIN_EN);
+
+       dsi->link_initialized = true;
+}
+
+static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
+{
+       struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
+       struct cdns_dsi *dsi = input_to_dsi(input);
+       struct cdns_dsi_output *output = &dsi->output;
+       struct drm_display_mode *mode;
+       struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
+       unsigned long tx_byte_period;
+       struct cdns_dsi_cfg dsi_cfg;
+       u32 tmp, reg_wakeup;
+       int nlanes;
+
+       if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0))
+               return;
+
+       mode = &bridge->encoder->crtc->state->adjusted_mode;
+       nlanes = output->dev->lanes;
+
+       WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false));
+
+       cdns_dsi_hs_init(dsi);
+       cdns_dsi_init_link(dsi);
+
+       writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa),
+              dsi->regs + VID_HSIZE1);
+       writel(HFP_LEN(dsi_cfg.hfp) | HACT_LEN(dsi_cfg.hact),
+              dsi->regs + VID_HSIZE2);
+
+       writel(VBP_LEN(mode->crtc_vtotal - mode->crtc_vsync_end - 1) |
+              VFP_LEN(mode->crtc_vsync_start - mode->crtc_vdisplay) |
+              VSA_LEN(mode->crtc_vsync_end - mode->crtc_vsync_start + 1),
+              dsi->regs + VID_VSIZE1);
+       writel(mode->crtc_vdisplay, dsi->regs + VID_VSIZE2);
+
+       tmp = dsi_cfg.htotal -
+             (dsi_cfg.hsa + DSI_BLANKING_FRAME_OVERHEAD +
+              DSI_HSA_FRAME_OVERHEAD);
+       writel(BLK_LINE_PULSE_PKT_LEN(tmp), dsi->regs + VID_BLKSIZE2);
+       if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+               writel(MAX_LINE_LIMIT(tmp - DSI_NULL_FRAME_OVERHEAD),
+                      dsi->regs + VID_VCA_SETTING2);
+
+       tmp = dsi_cfg.htotal -
+             (DSI_HSS_VSS_VSE_FRAME_OVERHEAD + DSI_BLANKING_FRAME_OVERHEAD);
+       writel(BLK_LINE_EVENT_PKT_LEN(tmp), dsi->regs + VID_BLKSIZE1);
+       if (!(output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
+               writel(MAX_LINE_LIMIT(tmp - DSI_NULL_FRAME_OVERHEAD),
+                      dsi->regs + VID_VCA_SETTING2);
+
+       tmp = DIV_ROUND_UP(dsi_cfg.htotal, nlanes) -
+             DIV_ROUND_UP(dsi_cfg.hsa, nlanes);
+
+       if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET))
+               tmp -= DIV_ROUND_UP(DSI_EOT_PKT_SIZE, nlanes);
+
+       tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8,
+                                           phy_cfg->hs_clk_rate);
+       reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period;
+       writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp),
+              dsi->regs + VID_DPHY_TIME);
+
+       writel(0xafffb, dsi->regs + MCTL_DPHY_TIMEOUT1);
+       writel(0x3ffff, dsi->regs + MCTL_DPHY_TIMEOUT2);
+       writel(0x3ab05, dsi->regs + MCTL_ULPOUT_TIME);
+
+       if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) {
+               switch (output->dev->format) {
+               case MIPI_DSI_FMT_RGB888:
+                       tmp = VID_PIXEL_MODE_RGB888 |
+                                       VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_24);
+                       break;
+
+               case MIPI_DSI_FMT_RGB666:
+                       tmp = VID_PIXEL_MODE_RGB666 |
+                                       VID_DATATYPE(MIPI_DSI_PIXEL_STREAM_3BYTE_18);
+                       break;
+
+               case MIPI_DSI_FMT_RGB666_PACKED:
+                       tmp = VID_PIXEL_MODE_RGB666_PACKED |
+                                       VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_18);
+                       break;
+
+               case MIPI_DSI_FMT_RGB565:
+                       tmp = VID_PIXEL_MODE_RGB565 |
+                                       VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_16);
+                       break;
+
+               default:
+                  dev_err(dsi->base.dev, "Unsupported DSI format\n");
+                       return;
+               }
+
+               if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+                       tmp |= SYNC_PULSE_ACTIVE | SYNC_PULSE_HORIZONTAL;
+
+               tmp |= REG_BLKLINE_MODE(REG_BLK_MODE_BLANKING_PKT) |
+                      REG_BLKEOL_MODE(REG_BLK_MODE_BLANKING_PKT) |
+                      RECOVERY_MODE(RECOVERY_MODE_NEXT_HSYNC) |
+                      VID_IGNORE_MISS_VSYNC;
+
+               writel(tmp, dsi->regs + VID_MAIN_CTL);
+       }
+
+       tmp = readl(dsi->regs + MCTL_MAIN_DATA_CTL);
+       tmp &= ~(IF_VID_SELECT_MASK | HOST_EOT_GEN | IF_VID_MODE);
+
+       if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET))
+               tmp |= HOST_EOT_GEN;
+
+       if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO)
+               tmp |= IF_VID_MODE | IF_VID_SELECT(input->id) | VID_EN;
+
+       writel(tmp, dsi->regs + MCTL_MAIN_DATA_CTL);
+
+       tmp = readl(dsi->regs + MCTL_MAIN_EN) | IF_EN(input->id);
+       writel(tmp, dsi->regs + MCTL_MAIN_EN);
+}
+
+static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = {
+       .attach = cdns_dsi_bridge_attach,
+       .mode_valid = cdns_dsi_bridge_mode_valid,
+       .disable = cdns_dsi_bridge_disable,
+       .enable = cdns_dsi_bridge_enable,
+};
+
+static int cdns_dsi_attach(struct mipi_dsi_host *host,
+                          struct mipi_dsi_device *dev)
+{
+       struct cdns_dsi *dsi = to_cdns_dsi(host);
+       struct cdns_dsi_output *output = &dsi->output;
+       struct cdns_dsi_input *input = &dsi->input;
+       struct drm_bridge *bridge;
+       struct drm_panel *panel;
+       struct device_node *np;
+       int ret;
+
+       /*
+        * We currently do not support connecting several DSI devices to the
+        * same host. In order to support that we'd need the DRM bridge
+        * framework to allow dynamic reconfiguration of the bridge chain.
+        */
+       if (output->dev)
+               return -EBUSY;
+
+       /* We do not support burst mode yet. */
+       if (dev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+               return -EOPNOTSUPP;
+
+       /*
+        * The host <-> device link might be described using an OF-graph
+        * representation, in this case we extract the device of_node from
+        * this representation, otherwise we use dsidev->dev.of_node which
+        * should have been filled by the core.
+        */
+       np = of_graph_get_remote_node(dsi->base.dev->of_node, DSI_OUTPUT_PORT,
+                                     dev->channel);
+       if (!np)
+               np = of_node_get(dev->dev.of_node);
+
+       panel = of_drm_find_panel(np);
+       if (!IS_ERR(panel)) {
+               bridge = drm_panel_bridge_add_typed(panel,
+                                                   DRM_MODE_CONNECTOR_DSI);
+       } else {
+               bridge = of_drm_find_bridge(dev->dev.of_node);
+               if (!bridge)
+                       bridge = ERR_PTR(-EINVAL);
+       }
+
+       of_node_put(np);
+
+       if (IS_ERR(bridge)) {
+               ret = PTR_ERR(bridge);
+               dev_err(host->dev, "failed to add DSI device %s (err = %d)",
+                       dev->name, ret);
+               return ret;
+       }
+
+       output->dev = dev;
+       output->bridge = bridge;
+       output->panel = panel;
+
+       /*
+        * The DSI output has been properly configured, we can now safely
+        * register the input to the bridge framework so that it can take place
+        * in a display pipeline.
+        */
+       drm_bridge_add(&input->bridge);
+
+       return 0;
+}
+
+static int cdns_dsi_detach(struct mipi_dsi_host *host,
+                          struct mipi_dsi_device *dev)
+{
+       struct cdns_dsi *dsi = to_cdns_dsi(host);
+       struct cdns_dsi_output *output = &dsi->output;
+       struct cdns_dsi_input *input = &dsi->input;
+
+       drm_bridge_remove(&input->bridge);
+       if (output->panel)
+               drm_panel_bridge_remove(output->bridge);
+
+       return 0;
+}
+
+static irqreturn_t cdns_dsi_interrupt(int irq, void *data)
+{
+       struct cdns_dsi *dsi = data;
+       irqreturn_t ret = IRQ_NONE;
+       u32 flag, ctl;
+
+       flag = readl(dsi->regs + DIRECT_CMD_STS_FLAG);
+       if (flag) {
+               ctl = readl(dsi->regs + DIRECT_CMD_STS_CTL);
+               ctl &= ~flag;
+               writel(ctl, dsi->regs + DIRECT_CMD_STS_CTL);
+               complete(&dsi->direct_cmd_comp);
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
+static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host,
+                                const struct mipi_dsi_msg *msg)
+{
+       struct cdns_dsi *dsi = to_cdns_dsi(host);
+       u32 cmd, val, wait = WRITE_COMPLETED, ctl = 0;
+       struct mipi_dsi_packet packet;
+       int ret, i, tx_len, rx_len;
+       u32 stat = 0;
+       int timeout = 100;
+       int stat_88;
+       int stat_188;
+       int stat_88_ack_val;
+
+       ret = pm_runtime_get_sync(host->dev);
+       if (ret < 0)
+               return ret;
+
+       cdns_dsi_init_link(dsi);
+
+       ret = mipi_dsi_create_packet(&packet, msg);
+       if (ret)
+               goto out;
+
+       tx_len = msg->tx_buf ? msg->tx_len : 0;
+       rx_len = msg->rx_buf ? msg->rx_len : 0;
+
+       /* For read operations, the maximum TX len is 2. */
+       if (rx_len && tx_len > 2) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /* TX len is limited by the CMD FIFO depth. */
+       if (tx_len > dsi->direct_cmd_fifo_depth) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /* RX len is limited by the RX FIFO depth. */
+       if (rx_len > dsi->rx_fifo_depth) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       cmd = CMD_SIZE(tx_len) | CMD_VCHAN_ID(msg->channel) |
+                 CMD_DATATYPE(msg->type);
+
+       if (msg->flags & MIPI_DSI_MSG_USE_LPM)
+               cmd |= CMD_LP_EN;
+
+       if (mipi_dsi_packet_format_is_long(msg->type))
+               cmd |= CMD_LONG;
+
+       if (rx_len) {
+               cmd |= READ_CMD;
+               wait = READ_COMPLETED_WITH_ERR | READ_COMPLETED;
+               ctl = READ_EN | BTA_EN;
+       } else if (msg->flags & MIPI_DSI_MSG_REQ_ACK) {
+               cmd |= BTA_REQ;
+               wait = ACK_WITH_ERR_RCVD | ACK_RCVD;
+               ctl = BTA_EN;
+       }
+
+       /* Clear status flags before sending the command. */
+
+       iowrite32(wait, dsi->regs + DIRECT_CMD_STS_CLR);
+       iowrite32(wait, dsi->regs + DIRECT_CMD_STS_CTL);
+       iowrite32(cmd, dsi->regs + DIRECT_CMD_MAIN_SETTINGS);
+
+       for (i = 0; i < tx_len; i += 4) {
+               const u8 *buf = msg->tx_buf;
+               int j;
+
+               val = 0;
+               for (j = 0; j < 4 && j + i < tx_len; j++)
+                       val |= (u32)buf[i + j] << (8 * j);
+               iowrite32(val, dsi->regs + DIRECT_CMD_WRDATA);
+       }
+       iowrite32(0, dsi->regs + DIRECT_CMD_SEND);
+
+       do {
+               stat = readl(dsi->regs + DIRECT_CMD_STS);
+               if ((stat & 0x02) == 0x02)
+                       break;
+               mdelay(10);
+       } while (--timeout);
+       if (!timeout)
+               DRM_DEBUG("timeout!\n");
+
+       stat_88 = readl(dsi->regs + DIRECT_CMD_STS);
+       stat_188 = readl(dsi->regs + MCTL_DPHY_ERR_FLAG);
+       stat_88_ack_val = stat_88 >> 16;
+       if (stat_188 || stat_88_ack_val)
+               dev_dbg(host->dev, "stat: [188h] %08x, [88h] %08x\r\n", stat_188, stat_88);
+
+out:
+       pm_runtime_put(host->dev);
+       return ret;
+}
+
+static const struct mipi_dsi_host_ops cdns_dsi_ops = {
+       .attach = cdns_dsi_attach,
+       .detach = cdns_dsi_detach,
+       .transfer = cdns_dsi_transfer,
+};
+
+static int __maybe_unused cdns_dsi_resume(struct device *dev)
+{
+       struct cdns_dsi *dsi = dev_get_drvdata(dev);
+
+       reset_control_deassert(dsi->dsi_p_rst);
+       clk_prepare_enable(dsi->dsi_p_clk);
+       clk_prepare_enable(dsi->dsi_sys_clk);
+
+       return 0;
+}
+
+static int __maybe_unused cdns_dsi_suspend(struct device *dev)
+{
+       struct cdns_dsi *dsi = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(dsi->dsi_sys_clk);
+       clk_disable_unprepare(dsi->dsi_p_clk);
+       reset_control_assert(dsi->dsi_p_rst);
+       dsi->link_initialized = false;
+       return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume,
+                           NULL);
+
+static int cdns_dsi_drm_remove(struct platform_device *pdev)
+{
+       struct cdns_dsi *dsi = platform_get_drvdata(pdev);
+
+       mipi_dsi_host_unregister(&dsi->base);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id cdns_dsi_of_match[] = {
+       { .compatible = "cdns,dsi" },
+       { },
+};
+
+static int starfive_dsi_bind(struct device *dev, struct device *master, void *data)
+{
+       struct cdns_dsi *dsi;
+       struct cdns_dsi_input *input;
+       struct resource *res;
+       struct platform_device *pdev = to_platform_device(dev);
+       //struct drm_device *drm_dev = data;
+       //struct starfive_drm_private *private = drm_dev->dev_private;
+       int ret;
+       u32 val;
+
+       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+       if (!dsi)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, dsi);
+       input = &dsi->input;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dsi->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dsi->regs))
+               return PTR_ERR(dsi->regs);
+
+       dsi->dphy = devm_phy_get(&pdev->dev, "dphy");
+       if (IS_ERR(dsi->dphy))
+               return PTR_ERR(dsi->dphy);
+
+       val = readl(dsi->regs + ID_REG);
+
+       if (REV_VENDOR_ID(val) != 0xcad) {
+               dev_err(&pdev->dev, "invalid vendor id\n");
+               ret = -EINVAL;
+               goto err_disable_pclk;
+       }
+
+       val = readl(dsi->regs + IP_CONF);
+       dsi->direct_cmd_fifo_depth = 1 << (DIRCMD_FIFO_DEPTH(val) + 2);
+       dsi->rx_fifo_depth = RX_FIFO_DEPTH(val);
+       init_completion(&dsi->direct_cmd_comp);
+
+       writel(0, dsi->regs + MCTL_MAIN_DATA_CTL);
+       writel(0, dsi->regs + MCTL_MAIN_EN);
+       writel(0, dsi->regs + MCTL_MAIN_PHY_CTL);
+
+       input->id = CDNS_DPI_INPUT;
+       input->bridge.funcs = &cdns_dsi_bridge_funcs;
+       input->bridge.of_node = pdev->dev.of_node;
+
+       //drm_bridge_add(&input->bridge);
+
+       /* Mask all interrupts before registering the IRQ handler. */
+       writel(0, dsi->regs + MCTL_MAIN_STS_CTL);
+       writel(0, dsi->regs + MCTL_DPHY_ERR_CTL1);
+       writel(0, dsi->regs + CMD_MODE_STS_CTL);
+       writel(0, dsi->regs + DIRECT_CMD_STS_CTL);
+       writel(0, dsi->regs + DIRECT_CMD_RD_STS_CTL);
+       writel(0, dsi->regs + VID_MODE_STS_CTL);
+       writel(0, dsi->regs + TVG_STS_CTL);
+       writel(0, dsi->regs + DPI_IRQ_EN);
+
+       pm_runtime_enable(&pdev->dev);
+       dsi->base.dev = &pdev->dev;
+       dsi->base.ops = &cdns_dsi_ops;
+
+       ret = mipi_dsi_host_register(&dsi->base);
+       if (ret)
+               goto err_disable_runtime_pm;
+
+       init_seeed_panel();
+       return 0;
+
+err_disable_runtime_pm:
+       pm_runtime_disable(&pdev->dev);
+
+err_disable_pclk:
+       //clk_disable_unprepare(dsi->dsi_p_clk);
+
+       return ret;
+}
+
+static void starfive_dsi_unbind(struct device *dev, struct device *master, void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct cdns_dsi *dsi = platform_get_drvdata(pdev);
+
+       exit_seeed_panel();
+       mipi_dsi_host_unregister(&dsi->base);
+       pm_runtime_disable(dev);
+}
+
+static const struct component_ops starfive_dsi_component_ops = {
+       .bind   = starfive_dsi_bind,
+       .unbind = starfive_dsi_unbind,
+};
+
+static int starfive_dsi_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &starfive_dsi_component_ops);
+}
+
+static int starfive_dsi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &starfive_dsi_component_ops);
+       return 0;
+}
+
+struct platform_driver starfive_dsi_platform_driver = {
+       .probe  = starfive_dsi_probe,
+       .remove = starfive_dsi_remove,
+       .driver = {
+               .name   = "cdns-dsi",
+               .of_match_table = cdns_dsi_of_match,
+               .pm = &cdns_dsi_pm_ops,
+       },
+};
+
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>");
+MODULE_DESCRIPTION("Cadence DSI driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cdns-dsi");
+
diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_encoder.c b/drivers/gpu/drm/verisilicon/starfive_drm_encoder.c
new file mode 100755 (executable)
index 0000000..8b1407b
--- /dev/null
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only\r
+/*\r
+ * Copyright (c) 2021 StarFive Technology Co., Ltd\r
+ * Author: StarFive <StarFive@starfivetech.com>\r
+ */\r
+\r
+#include <linux/clk.h>\r
+#include <linux/component.h>\r
+#include <linux/of_device.h>\r
+\r
+#include <drm/drm_atomic.h>\r
+#include <drm/drm_atomic_helper.h>\r
+#include <drm/drm_atomic_uapi.h>\r
+#include <drm/drm_fb_cma_helper.h>\r
+#include <drm/drm_print.h>\r
+#include <drm/drm_probe_helper.h>\r
+#include <drm/drm_vblank.h>\r
+#include <drm/drm_of.h>\r
+\r
+//#include "starfive_drm_drv.h"\r
+#include "starfive_drm_encoder.h"\r
+\r
+static void starfive_encoder_destroy(struct drm_encoder *encoder)\r
+{
+       drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs starfive_encoder_funcs = {\r
+       .destroy = starfive_encoder_destroy,\r
+};\r
+\r
+\r
+static int starfive_encoder_bind(struct device *dev, struct device *master, void *data)\r
+{\r
+       struct drm_device *drm_dev = data;\r
+       struct device_node *np = dev->of_node;\r
+       struct starfive_encoder *encoderp;\r
+       int ret;\r
+\r
+       struct drm_panel *tmp_panel;\r
+       struct drm_bridge *tmp_bridge;\r
+\r
+       u32 crtcs = 0;\r
+\r
+       encoderp = devm_kzalloc(dev, sizeof(*encoderp), GFP_KERNEL);\r
+       if (!encoderp)\r
+               return -ENOMEM;\r
+\r
+       encoderp->dev = dev;\r
+       encoderp->drm_dev = drm_dev;\r
+       dev_set_drvdata(dev, encoderp);\r
+\r
+       if (dev->of_node) {\r
+               crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);\r
+\r
+               if (of_property_read_u32(np, "encoder-type", &encoderp->encoder_type)) {\r
+                       encoderp->encoder_type = DRM_MODE_ENCODER_TMDS;\r
+               }\r
+       }\r
+\r
+       /* If no CRTCs were found, fall back to our old behaviour */\r
+       if (crtcs == 0) {\r
+               dev_warn(dev, "Falling back to first CRTC\n");\r
+               crtcs = 1 << 0|1 << 1;\r
+       }\r
+\r
+       encoderp->encoder.possible_crtcs = 3;\r
+\r
+       ret = drm_encoder_init(drm_dev, &encoderp->encoder, &starfive_encoder_funcs,\r
+                                  encoderp->encoder_type, NULL);\r
+       if (ret)\r
+               goto err_encoder;\r
+\r
+\r
+       ret = drm_of_find_panel_or_bridge(dev->of_node,\r
+                                         0, 0,\r
+                                         &tmp_panel,\r
+                                         &tmp_bridge);\r
+       if (ret) {\r
+                       dev_err(dev,"endpoint returns %d\n", ret);\r
+       }\r
+\r
+       if (tmp_panel) {\r
+               dev_info(dev,"found panel on endpoint \n");\r
+       }\r
+       if (tmp_bridge) {\r
+               dev_info(dev,"found bridge on endpoint \n");\r
+       }\r
+\r
+       ret = drm_bridge_attach(&encoderp->encoder, tmp_bridge, NULL, 0);\r
+       if (ret)\r
+               goto err_bridge;\r
+\r
+       return 0;\r
+\r
+err_bridge:\r
+       drm_encoder_cleanup(&encoderp->encoder);\r
+err_encoder:\r
+       return ret;\r
+\r
+}\r
+\r
+static void starfive_encoder_unbind(struct device *dev, struct device *master, void *data)\r
+{\r
+       printk("===> %s , %d \n", __func__, __LINE__);\r
+}\r
+\r
+static const struct component_ops starfive_encoder_component_ops = {\r
+       .bind   = starfive_encoder_bind,\r
+       .unbind = starfive_encoder_unbind,\r
+};\r
+\r
+static const struct of_device_id starfive_encoder_driver_dt_match[] = {\r
+       { .compatible = "starfive,display-encoder",\r
+         /*.data = &7100-crtc*/ },\r
+       {},\r
+};\r
+MODULE_DEVICE_TABLE(of, starfive_encoder_driver_dt_match);\r
+\r
+static int starfive_encoder_probe(struct platform_device *pdev)\r
+{\r
+       return component_add(&pdev->dev, &starfive_encoder_component_ops);\r
+}\r
+\r
+static int starfive_encoder_remove(struct platform_device *pdev)\r
+{\r
+       component_del(&pdev->dev, &starfive_encoder_component_ops);\r
+       return 0;\r
+}\r
+\r
+struct platform_driver starfive_encoder_driver = {\r
+       .probe = starfive_encoder_probe,\r
+       .remove = starfive_encoder_remove,\r
+       .driver = {\r
+               .name = "display-encoder",\r
+               .of_match_table = of_match_ptr(starfive_encoder_driver_dt_match),\r
+       },\r
+};\r
diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_encoder.h b/drivers/gpu/drm/verisilicon/starfive_drm_encoder.h
new file mode 100755 (executable)
index 0000000..d9756d8
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */\r
+/*\r
+ * Copyright (c) 2021 StarFive Technology Co., Ltd\r
+ * Author: StarFive <StarFive@starfivetech.com>\r
+ */\r
+\r
+#ifndef _STARFIVE_DRM_ENCODER_H\r
+#define _STARFIVE_DRM_ENCODER_H\r
+\r
+struct starfive_encoder {\r
+       struct drm_encoder              encoder;\r
+       struct device                   *dev;\r
+       struct drm_device               *drm_dev;\r
+       bool is_enabled;\r
+       int encoder_type;\r
+};\r
+#define to_starfive_encoder(x) container_of(x, struct starfive_encoder, encoder)\r
+\r
+#endif /* _STARFIVE_DRM_CRTC_H */\r
diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c b/drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c
new file mode 100755 (executable)
index 0000000..9ccf8b4
--- /dev/null
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ *
+ * reference to seeed5inch.c
+ */
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include "vs_drv.h"
+#define RPI_DSI_DRIVER_NAME "cdns-dri-panel"
+
+/* I2C registers of the Atmel microcontroller. */
+enum REG_ADDR {
+       REG_ID = 0x80,
+       REG_PORTA, /* BIT(2) for horizontal flip, BIT(3) for vertical flip */
+       REG_PORTB,
+       REG_PORTC,
+       REG_PORTD,
+       REG_POWERON,
+       REG_PWM,
+       REG_DDRA,
+       REG_DDRB,
+       REG_DDRC,
+       REG_DDRD,
+       REG_TEST,
+       REG_WR_ADDRL,
+       REG_WR_ADDRH,
+       REG_READH,
+       REG_READL,
+       REG_WRITEH,
+       REG_WRITEL,
+       REG_ID2,
+};
+
+/* DSI D-PHY Layer Registers */
+#define D0W_DPHYCONTTX         0x0004
+#define CLW_DPHYCONTRX         0x0020
+#define D0W_DPHYCONTRX         0x0024
+#define D1W_DPHYCONTRX         0x0028
+#define COM_DPHYCONTRX         0x0038
+#define CLW_CNTRL              0x0040
+#define D0W_CNTRL              0x0044
+#define D1W_CNTRL              0x0048
+#define DFTMODE_CNTRL          0x0054
+
+/* DSI PPI Layer Registers */
+#define PPI_STARTPPI           0x0104
+#define PPI_BUSYPPI            0x0108
+#define PPI_LINEINITCNT                0x0110
+#define PPI_LPTXTIMECNT                0x0114
+#define PPI_CLS_ATMR           0x0140
+#define PPI_D0S_ATMR           0x0144
+#define PPI_D1S_ATMR           0x0148
+#define PPI_D0S_CLRSIPOCOUNT   0x0164
+#define PPI_D1S_CLRSIPOCOUNT   0x0168
+#define CLS_PRE                        0x0180
+#define D0S_PRE                        0x0184
+#define D1S_PRE                        0x0188
+#define CLS_PREP               0x01A0
+#define D0S_PREP               0x01A4
+#define D1S_PREP               0x01A8
+#define CLS_ZERO               0x01C0
+#define D0S_ZERO               0x01C4
+#define D1S_ZERO               0x01C8
+#define PPI_CLRFLG             0x01E0
+#define PPI_CLRSIPO            0x01E4
+#define HSTIMEOUT              0x01F0
+#define HSTIMEOUTENABLE                0x01F4
+
+/* DSI Protocol Layer Registers */
+#define DSI_STARTDSI           0x0204
+#define DSI_BUSYDSI            0x0208
+#define DSI_LANEENABLE         0x0210
+#define DSI_LANEENABLE_CLOCK           BIT(0)
+#define DSI_LANEENABLE_D0              BIT(1)
+#define DSI_LANEENABLE_D1              BIT(2)
+
+#define DSI_LANESTATUS0                0x0214
+#define DSI_LANESTATUS1                0x0218
+#define DSI_INTSTATUS          0x0220
+#define DSI_INTMASK            0x0224
+#define DSI_INTCLR             0x0228
+#define DSI_LPTXTO             0x0230
+#define DSI_MODE               0x0260
+#define DSI_PAYLOAD0           0x0268
+#define DSI_PAYLOAD1           0x026C
+#define DSI_SHORTPKTDAT                0x0270
+#define DSI_SHORTPKTREQ                0x0274
+#define DSI_BTASTA             0x0278
+#define DSI_BTACLR             0x027C
+
+/* DSI General Registers */
+#define DSIERRCNT              0x0300
+#define DSISIGMOD              0x0304
+
+/* DSI Application Layer Registers */
+#define APLCTRL                        0x0400
+#define APLSTAT                        0x0404
+#define APLERR                 0x0408
+#define PWRMOD                 0x040C
+#define RDPKTLN                        0x0410
+#define PXLFMT                 0x0414
+#define MEMWRCMD               0x0418
+
+/* LCDC/DPI Host Registers */
+#define LCDCTRL                        0x0420
+#define HSR                    0x0424
+#define HDISPR                 0x0428
+#define VSR                    0x042C
+#define VDISPR                 0x0430
+#define VFUEN                  0x0434
+
+/* DBI-B Host Registers */
+#define DBIBCTRL               0x0440
+
+/* SPI Master Registers */
+#define SPICMR                 0x0450
+#define SPITCR                 0x0454
+
+/* System Controller Registers */
+#define SYSSTAT                        0x0460
+#define SYSCTRL                        0x0464
+#define SYSPLL1                        0x0468
+#define SYSPLL2                        0x046C
+#define SYSPLL3                        0x0470
+#define SYSPMCTRL              0x047C
+
+/* GPIO Registers */
+#define GPIOC                  0x0480
+#define GPIOO                  0x0484
+#define GPIOI                  0x0488
+
+/* I2C Registers */
+#define I2CCLKCTRL             0x0490
+
+/* Chip/Rev Registers */
+#define IDREG                  0x04A0
+
+/* Debug Registers */
+#define WCMDQUEUE              0x0500
+#define RCMDQUEUE              0x0504
+
+struct seeed_panel_dev {
+       struct i2c_client *client;
+       struct drm_panel base;
+       struct mipi_dsi_device *dsi;
+
+       struct device   *dev;
+       int irq;
+
+};
+
+static int seeed_panel_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+       struct i2c_msg msg;
+       u8 buf[2];
+       int ret;
+
+       buf[0] = reg;
+       buf[1] = val;
+       msg.addr = client->addr;
+       msg.flags = 0;
+       msg.buf = buf;
+       msg.len = 2;
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret >= 0)
+               return 0;
+
+       return ret;
+}
+
+static int seeed_panel_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+       struct i2c_msg msg[2];
+       u8 buf[2];
+       int ret;
+
+       buf[0] = reg;
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = buf;
+       msg[0].len = 1;
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = val;
+       msg[1].len = 1;
+       ret = i2c_transfer(client->adapter, msg, 2);
+       if (ret >= 0)
+               return 0;
+
+       return ret;
+}
+
+enum dsi_rgb_pattern_t {
+       RGB_PAT_WHITE,
+       RGB_PAT_BLACK,
+       RGB_PAT_RED,
+       RGB_PAT_GREEN,
+       RGB_PAT_BLUE,
+       RGB_PAT_HORIZ_COLORBAR,
+       RGB_PAT_VERT_COLORBAR,
+       RGB_PAT_NUM
+};
+
+static struct seeed_panel_dev *panel_to_seeed(struct drm_panel *panel)
+{
+       return container_of(panel, struct seeed_panel_dev, base);
+}
+
+static const struct drm_display_mode seeed_panel_modes[] = {
+       {
+               .clock = 33000000 / 1000,
+               .hdisplay = 800,
+               .hsync_start = 800 + 50,
+               .hsync_end = 800 + 50 + 20,
+               .htotal = 800 + 50 + 20 + 10,
+               .vdisplay = 480,
+               .vsync_start = 480 + 135,
+               .vsync_end = 480 + 135 + 5,
+               .vtotal = 480 + 135 + 5 + 5,
+       },
+};
+
+static int seeed_panel_disable(struct drm_panel *panel)
+{
+       struct seeed_panel_dev *sp = panel_to_seeed(panel);
+
+       seeed_panel_i2c_write(sp->client, REG_PWM, 0);
+       seeed_panel_i2c_write(sp->client, REG_POWERON, 0);
+       udelay(1);
+
+       return 0;
+}
+
+static int seeed_panel_noop(struct drm_panel *panel)
+{
+       return 0;
+}
+
+static int seeed_dsi_write(struct drm_panel *panel, u16 reg, u32 val)
+{
+       struct seeed_panel_dev *sp = panel_to_seeed(panel);
+
+       u8 msg[] = {
+               reg,
+               reg >> 8,
+               val,
+               val >> 8,
+               val >> 16,
+               val >> 24,
+       };
+       mipi_dsi_generic_write(sp->dsi, msg, sizeof(msg));
+
+       return 0;
+}
+
+static int seeed_panel_enable(struct drm_panel *panel)
+{
+       struct seeed_panel_dev *sp = panel_to_seeed(panel);
+       int i;
+       u8 reg_value = 0;
+
+       seeed_panel_i2c_write(sp->client, REG_POWERON, 1);
+       /* Wait for nPWRDWN to go low to indicate poweron is done. */
+       for (i = 0; i < 100; i++) {
+               seeed_panel_i2c_read(sp->client, REG_PORTB, &reg_value);
+               if (reg_value & 1)
+                       break;
+       }
+
+       seeed_dsi_write(panel, DSI_LANEENABLE,
+                               DSI_LANEENABLE_CLOCK |
+                               DSI_LANEENABLE_D0);
+       seeed_dsi_write(panel, PPI_D0S_CLRSIPOCOUNT, 0x05);
+       seeed_dsi_write(panel, PPI_D1S_CLRSIPOCOUNT, 0x05);
+       seeed_dsi_write(panel, PPI_D0S_ATMR, 0x00);
+       seeed_dsi_write(panel, PPI_D1S_ATMR, 0x00);
+       seeed_dsi_write(panel, PPI_LPTXTIMECNT, 0x03);
+       seeed_dsi_write(panel, SPICMR, 0x00);
+       seeed_dsi_write(panel, LCDCTRL, 0x00100150);
+       seeed_dsi_write(panel, SYSCTRL, 0x040f);
+       msleep(100);
+
+       seeed_dsi_write(panel, PPI_STARTPPI, 0x01);
+       seeed_dsi_write(panel, DSI_STARTDSI, 0x01);
+       msleep(100);
+
+       /* Turn on the backlight. */
+       seeed_panel_i2c_write(sp->client, REG_PWM, 255);
+
+       /* Default to the same orientation as the closed source
+        * firmware used for the panel.  Runtime rotation
+        * configuration will be supported using VC4's plane
+        * orientation bits.
+        */
+       seeed_panel_i2c_write(sp->client, REG_PORTA, BIT(2));
+
+       return 0;
+}
+
+static int seeed_panel_get_modes(struct drm_panel *panel,
+                               struct drm_connector *connector)
+{
+       unsigned int i, num = 0;
+       static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+       for (i = 0; i < ARRAY_SIZE(seeed_panel_modes); i++) {
+               const struct drm_display_mode *m = &seeed_panel_modes[i];
+               struct drm_display_mode *mode;
+
+               mode = drm_mode_duplicate(connector->dev, m);
+               if (!mode) {
+                       dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
+                               m->hdisplay, m->vdisplay,
+                               drm_mode_vrefresh(m));
+                       continue;
+               }
+
+               mode->type |= DRM_MODE_TYPE_DRIVER;
+
+               if (i == 0)
+                       mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+               drm_mode_set_name(mode);
+               drm_mode_probed_add(connector, mode);
+               num++;
+       }
+
+       connector->display_info.bpc = 8;
+       connector->display_info.width_mm = 154;
+       connector->display_info.height_mm = 86;
+       drm_display_info_set_bus_formats(&connector->display_info,
+                               &bus_format, 1);
+
+       return num;
+}
+
+static const struct drm_panel_funcs seeed_panel_funcs = {
+       .disable = seeed_panel_disable,
+       .unprepare = seeed_panel_noop,
+       .prepare = seeed_panel_noop,
+       .enable = seeed_panel_enable,
+       .get_modes = seeed_panel_get_modes,
+};
+
+static int seeed_panel_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       u8 reg_value = 0;
+       struct seeed_panel_dev *seeed_panel;
+       struct device_node *endpoint, *dsi_host_node;
+       struct mipi_dsi_host *host;
+       struct device *dev = &client->dev;
+
+       struct mipi_dsi_device_info info = {
+               .type = RPI_DSI_DRIVER_NAME,
+               .channel = 0, //0,
+               .node = NULL,
+       };
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_warn(&client->dev,
+                        "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+               return -EIO;
+       }
+
+       seeed_panel = devm_kzalloc(&client->dev, sizeof(struct seeed_panel_dev), GFP_KERNEL);
+       if (!seeed_panel)
+               return -ENOMEM;
+
+       seeed_panel->client = client;
+       i2c_set_clientdata(client, seeed_panel);
+
+       seeed_panel_i2c_read(client, REG_ID, &reg_value);
+       switch (reg_value) {
+       case 0xde: /* ver 1 */
+       case 0xc3: /* ver 2 */
+       break;
+
+       default:
+               dev_err(&client->dev, "Unknown Atmel firmware revision: 0x%02x\n", reg_value);
+               return -ENODEV;
+       }
+
+       seeed_panel_i2c_write(client, REG_POWERON, 0);
+
+       endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+       if (!endpoint)
+               return -ENODEV;
+
+       dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+       if (!dsi_host_node)
+               goto error;
+
+       host = of_find_mipi_dsi_host_by_node(dsi_host_node);
+       of_node_put(dsi_host_node);
+       if (!host) {
+               of_node_put(endpoint);
+               return -EPROBE_DEFER;
+       }
+
+       drm_panel_init(&seeed_panel->base, dev, &seeed_panel_funcs,
+                       DRM_MODE_CONNECTOR_DSI);
+
+       /* This appears last, as it's what will unblock the DSI host
+        * driver's component bind function.
+        */
+       drm_panel_add(&seeed_panel->base);
+
+       info.node = of_node_get(of_graph_get_remote_port(endpoint));
+       if (!info.node)
+               goto error;
+
+       of_node_put(endpoint);
+
+       seeed_panel->dsi = mipi_dsi_device_register_full(host, &info);
+       if (IS_ERR(seeed_panel->dsi)) {
+               dev_err(dev, "DSI device registration failed: %ld\n",
+                       PTR_ERR(seeed_panel->dsi));
+               return PTR_ERR(seeed_panel->dsi);
+       }
+
+       return 0;
+error:
+       of_node_put(endpoint);
+       return -ENODEV;
+
+}
+
+static int seeed_panel_remove(struct i2c_client *client)
+{
+       struct seeed_panel_dev *seeed_panel = i2c_get_clientdata(client);
+
+       mipi_dsi_detach(seeed_panel->dsi);
+
+       drm_panel_remove(&seeed_panel->base);
+
+       mipi_dsi_device_unregister(seeed_panel->dsi);
+       // kfree(seeed_panel->dsi);
+       return 0;
+}
+
+static const struct i2c_device_id seeed_panel_id[] = {
+       { "seeed_panel", 0 },
+       { }
+};
+
+static const struct of_device_id seeed_panel_dt_ids[] = {
+       { .compatible = "seeed_panel", },
+       { /* sentinel */ }
+};
+
+static struct i2c_driver seeed_panel_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "seeed_panel",
+               .of_match_table = seeed_panel_dt_ids,
+       },
+       .probe          = seeed_panel_probe,
+       .remove         = seeed_panel_remove,
+       .id_table       = seeed_panel_id,
+};
+
+static int seeed_dsi_probe(struct mipi_dsi_device *dsi)
+{
+       int ret;
+
+       dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+                               MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+                               MIPI_DSI_MODE_LPM);
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->lanes = 1;
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret)
+               dev_err(&dsi->dev, "failed to attach dsi to host: %d\n", ret);
+
+       return ret;
+}
+
+static struct mipi_dsi_driver seeed_dsi_driver = {
+       .driver.name = RPI_DSI_DRIVER_NAME,
+       .probe = seeed_dsi_probe,
+};
+
+int init_seeed_panel(void)
+{
+       int err;
+
+       mipi_dsi_driver_register(&seeed_dsi_driver);
+       err = i2c_add_driver(&seeed_panel_driver);
+       return err;
+}
+EXPORT_SYMBOL(init_seeed_panel);
+
+void exit_seeed_panel(void)
+{
+       i2c_del_driver(&seeed_panel_driver);
+       mipi_dsi_driver_unregister(&seeed_dsi_driver);
+}
+EXPORT_SYMBOL(exit_seeed_panel);
+
+MODULE_DESCRIPTION("A driver for seeed_panel");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
new file mode 100755 (executable)
index 0000000..99eaf0e
--- /dev/null
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.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_RBG888_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, 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, 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,
+    //.gamma_set      = drm_atomic_helper_legacy_gamma_set,
+    .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, mode, adjusted_mode);
+}
+
+static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
+                                   struct drm_atomic_state *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);
+
+    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 *state)
+{
+    struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
+
+    drm_crtc_vblank_off(crtc);
+
+    vs_crtc->funcs->disable(vs_crtc->dev, crtc);
+
+    if (crtc->state->event && !crtc->state->active) {
+        spin_lock_irq(&crtc->dev->event_lock);
+        drm_crtc_send_vblank_event(crtc, crtc->state->event);
+        spin_unlock_irq(&crtc->dev->event_lock);
+
+        crtc->state->event = NULL;
+    }
+}
+
+static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
+                                 struct drm_atomic_state *state)
+{
+       struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+                                                                         crtc);
+       //struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,crtc);
+    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 *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 (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);
+    }
+
+    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 100755 (executable)
index 0000000..8283357
--- /dev/null
@@ -0,0 +1,74 @@
+/* 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,
+               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, 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 100755 (executable)
index 0000000..0072c61
--- /dev/null
@@ -0,0 +1,1194 @@
+// 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/media-bus-format.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"
+#include <soc/starfive/vic7100.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);
+
+    dc_hw_enable_interrupt(&dc->hw, 0);
+    dc_hw_deinit(&dc->hw);
+    //clk_disable_unprepare(dc->core_clk);
+    //clk_disable_unprepare(dc->pix_clk);
+    //clk_disable_unprepare(dc->axi_clk);
+}
+
+static int dc_init(struct device *dev)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    int ret;
+
+    dc->first_frame = true;
+
+#if 0
+    ret = clk_prepare_enable(dc->core_clk);
+    if (ret < 0) {
+        dev_err(dev, "failed to prepare/enable core_clk\n");
+        return ret;
+    }
+
+    ret = clk_prepare_enable(dc->pix_clk);
+    if (ret < 0) {
+        dev_err(dev, "failed to prepare/enable pix_clk\n");
+        goto err_unprepare_core_clk;
+    }
+
+    ret = clk_prepare_enable(dc->axi_clk);
+    if (ret < 0) {
+        dev_err(dev, "failed to prepare/enable axi_clk\n");
+        goto err_unprepare_pix_clk;
+    }
+
+    dc->pix_clk_rate = clk_get_rate(dc->pix_clk);
+#endif
+
+    ret = dc_hw_init(&dc->hw);
+    if (ret) {
+        dev_err(dev, "failed to init DC HW\n");
+        goto err_unprepare_axi_clk;
+    }
+
+    return 0;
+
+err_unprepare_axi_clk:
+    //clk_disable_unprepare(dc->axi_clk);
+err_unprepare_core_clk:
+    //clk_disable_unprepare(dc->core_clk);
+err_unprepare_pix_clk:
+    //clk_disable_unprepare(dc->pix_clk);
+    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 1
+       ;//printk("====> %s, %d--pix_clk.\n", __func__, __LINE__);
+#else
+    if (dc->pix_clk_rate != mode->clock) {
+        clk_set_rate(dc->pix_clk, mode->clock * 1000);
+        dc->pix_clk_rate = mode->clock;
+    }
+#endif
+
+    if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI)
+        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,
+                  const struct drm_display_mode *mode,
+                  struct drm_display_mode *adjusted_mode)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    long clk_rate;
+
+#if 1
+       ;//printk("====> %s, %d--pix_clk.\n", __func__, __LINE__);
+#else
+    if (dc->pix_clk) {
+        clk_rate = clk_round_rate(dc->pix_clk,
+                      adjusted_mode->clock * 1000);
+        adjusted_mode->clock = DIV_ROUND_UP(clk_rate, 1000);
+    }
+#endif
+
+    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, bool enable)
+{
+    struct vs_dc *dc = dev_get_drvdata(dev);
+
+    dc_hw_enable_interrupt(&dc->hw, 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)
+{
+    //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;
+
+    fb->display_id = display_id;
+    fb->y_address = plane->dma_addr[0];
+    fb->y_stride = drm_fb->pitches[0];
+    if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+        fb->u_address = plane->dma_addr[2];
+        fb->v_address = plane->dma_addr[1];
+        fb->u_stride = drm_fb->pitches[2];
+        fb->v_stride = drm_fb->pitches[1];
+    } else {
+        fb->u_address = plane->dma_addr[1];
+        fb->v_address = plane->dma_addr[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);
+       printk("update_fb tile_mode = %d\n",to_vs_tile_mode(drm_fb->modifier));
+       //fb->tile_mode = 0x04; 
+    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 drm_plane_state *state)
+{
+    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_plane_state *state)
+{
+    struct drm_vs_roi *data;
+    //struct drm_rect *src = &plane_state->base.src;
+    struct drm_rect *src = &state->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 drm_plane *drm_plane,
+                                               struct drm_atomic_state *drm_state)
+{
+    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 drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+                                                                          drm_plane);
+    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, state);
+#endif
+
+    display_id = to_vs_display_id(dc, state->crtc);
+    update_fb(plane, display_id, &fb, state);
+    fb.dec_enable = dec_enable;
+
+
+    update_roi(dc, plane->id, plane_state, &roi, state);
+
+    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 *drm_plane,
+                                               struct drm_atomic_state *drm_state)
+{
+    //struct drm_plane_state *state = plane->base.state;
+    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+                                                                          drm_plane);
+    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 *drm_plane,
+                                                                       struct drm_atomic_state *drm_state)
+{
+    //struct drm_plane_state *state = plane->base.state;
+    struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state,
+                                                                          drm_plane);
+    struct drm_framebuffer *drm_fb = state->fb;
+    struct dc_hw_cursor cursor;
+
+    cursor.address = plane->dma_addr[0];
+    cursor.x = state->crtc_x;
+    cursor.y = state->crtc_y;
+    cursor.hot_x = drm_fb->hot_x;
+    cursor.hot_y = drm_fb->hot_y;
+    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 drm_plane *drm_plane,
+                                                               struct drm_atomic_state *drm_state)
+{
+    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, drm_plane, drm_state);
+        update_qos(dc, plane, drm_plane, drm_state);
+        break;
+    case DRM_PLANE_TYPE_CURSOR:
+        update_cursor_plane(dc, plane, drm_plane, drm_state);
+        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 drm_plane *plane,
+                  struct drm_atomic_state *state)
+{
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                        plane);
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    struct drm_framebuffer *fb = new_plane_state->fb;
+    const struct vs_plane_info *plane_info;
+    struct drm_crtc *crtc = new_plane_state->crtc;
+    struct drm_crtc_state *crtc_state;
+       struct vs_plane *vs_plane = to_vs_plane(plane);
+
+    plane_info = &dc->hw.info->planes[vs_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",
+                 vs_plane->id);
+
+    if ((vs_plane->base.type != DRM_PLANE_TYPE_CURSOR) &&
+        (!vs_dc_mod_supported(plane_info, fb->modifier))) {
+        dev_err(dev, "unsupported modifier on plane%d.\n", vs_plane->id);
+        return -EINVAL;
+    }
+
+    crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+    if (IS_ERR(crtc_state))
+        return -EINVAL;
+
+    return drm_atomic_helper_check_plane_state(new_plane_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,ret;
+
+    ret = dc_hw_get_interrupt(&dc->hw);
+
+    for (i = 0; i < dc_info->panel_num; i ++) {
+        vs_crtc_handle_vblank(&dc->crtc[i]->base,dc_hw_check_underflow(&dc->hw));      
+    }
+       starfive_flush_dcache(dc->hw.plane[0].fb.y_address, dc->hw.plane[0].fb.width*dc->hw.plane[0].fb.height*2);
+       starfive_flush_dcache(dc->hw.plane[1].fb.y_address, dc->hw.plane[1].fb.width*dc->hw.plane[1].fb.height*2);
+       starfive_flush_dcache(dc->hw.plane[2].fb.y_address, dc->hw.plane[2].fb.width*dc->hw.plane[2].fb.height*2);
+
+    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;
+    }
+
+    port = of_get_child_by_name(dev->of_node, "port");
+    if (!port) {
+        dev_err(dev, "no port node found\n");
+        goto err_detach_dev;
+    }
+    of_node_put(port);
+
+    dc_info = dc->hw.info;
+
+    for(i = 0; i < dc_info->panel_num; i++) {
+        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 int dc_probe(struct platform_device *pdev)
+{
+    struct device *dev = &pdev->dev;
+    struct vs_dc *dc;
+    int irq, ret;
+
+    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
+
+    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;
+    }
+
+#if 0
+    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);
+    }
+
+    dc->pix_clk = devm_clk_get_optional(dev, "pix_clk");
+    if (IS_ERR(dc->core_clk)) {
+        dev_err(dev, "failed to get pix_clk source\n");
+        return PTR_ERR(dc->core_clk);
+    }
+
+    dc->axi_clk = devm_clk_get_optional(dev, "axi_clk");
+    if (IS_ERR(dc->core_clk)) {
+        dev_err(dev, "failed to get axi_clk source\n");
+        return PTR_ERR(dc->core_clk);
+    }
+#endif
+
+    dev_set_drvdata(dev, dc);
+
+    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);
+
+    dev_set_drvdata(dev, NULL);
+
+    return 0;
+}
+
+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),
+    },
+};
+
+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 100755 (executable)
index 0000000..e069a57
--- /dev/null
@@ -0,0 +1,55 @@
+/* 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;
+    struct clk      *axi_clk;
+    unsigned int     pix_clk_rate; /* in KHz */
+
+    bool            first_frame;
+
+    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 100755 (executable)
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 100755 (executable)
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 100755 (executable)
index 0000000..5a22755
--- /dev/null
@@ -0,0 +1,2210 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/io.h>
+#include <linux/bits.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/vs_drm.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_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_NV12,
+    DRM_FORMAT_NV21,
+    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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_HELPER_NO_SCALING,
+        .max_scale   = DRM_PLANE_HELPER_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_YCRCB444 |
+                          DRM_COLOR_FORMAT_YCRCB422 |
+                          DRM_COLOR_FORMAT_YCRCB420,
+        .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_YCRCB444 |
+                          DRM_COLOR_FORMAT_YCRCB422 |
+                          DRM_COLOR_FORMAT_YCRCB420,
+        .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_2],
+        .layer_num      = 6,
+        .max_bpc        = 10,
+        .color_formats  = DRM_COLOR_FORMAT_RGB444 |
+                          DRM_COLOR_FORMAT_YCRCB444 |
+                          DRM_COLOR_FORMAT_YCRCB422 |
+                          DRM_COLOR_FORMAT_YCRCB420,
+        .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);
+               
+    }
+
+    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);
+}
+
+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));
+                dc_write(hw, reg->water_mark + offset,
+                     plane->fb.water_mark);
+
+                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,
+                             BIT(6), BIT(8));
+                else
+                    dc_set_clear(hw,
+                             DC_OVERLAY_CONFIG + offset,
+                             BIT(29), 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 dpi_cfg, offset = id << 2;
+    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 {
+        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 100755 (executable)
index 0000000..cc33407
--- /dev/null
@@ -0,0 +1,581 @@
+/* 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,//0
+    FORMAT_A4R4G4B4,//1
+    FORMAT_X1R5G5B5,//2
+    FORMAT_A1R5G5B5,//3
+    FORMAT_R5G6B5,//4
+    FORMAT_X8R8G8B8,//5
+    FORMAT_A8R8G8B8,//6
+    FORMAT_YUY2,//7
+    FORMAT_UYVY,//8
+    FORMAT_INDEX8,//9
+    FORMAT_MONOCHROME,//10
+    FORMAT_YV12 = 0xf,
+    FORMAT_A8,//16
+    FORMAT_NV12,//17
+    FORMAT_NV16,//18
+    FORMAT_RG16,//19
+    FORMAT_R8,//20
+    FORMAT_NV12_10BIT,//21
+    FORMAT_A2R10G10B10,//22
+    FORMAT_NV16_10BIT,//23
+    FORMAT_INDEX1,//24
+    FORMAT_INDEX2,//25
+    FORMAT_INDEX4,//26
+    FORMAT_P010,//27
+    FORMAT_YUV444,//28
+    FORMAT_YUV444_10BIT,//29
+};
+
+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;
+};
+
+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);
+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 100755 (executable)
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 100755 (executable)
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 100755 (executable)
index 0000000..c0e723c
--- /dev/null
@@ -0,0 +1,488 @@
+// 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_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_fb_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 <linux/of_reserved_mem.h>
+#include <drm/drm_aperture.h>
+
+#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"
+
+extern struct platform_driver starfive_encoder_driver;
+
+#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 = %s\n", state->fb ?
+        //       plane_state->status.format_name.str : "(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,
+    .gem_prime_mmap     = vs_gem_prime_mmap,
+    .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)
+    static u64 dma_mask = DMA_BIT_MASK(40);
+#else
+    static u64 dma_mask = DMA_40BIT_MASK;
+#endif
+       printk("vs_drm_bind dma_mask = %08x\n",dma_mask);
+       printk("vs_drm_bind LINUX_VERSION_CODE = %08x\n",LINUX_VERSION_CODE);
+       printk("vs_drm_bind KERNEL_VERSION(2, 6, 24) = %08x\n",KERNEL_VERSION(2, 6, 24));
+       
+       /* Remove existing drivers that may own the framebuffer memory. */
+       ret = drm_aperture_remove_framebuffers(false, &vs_drm_driver);
+       if (ret) {
+               DRM_DEV_ERROR(dev,
+                             "Failed to remove existing framebuffers - %d.\n",
+                             ret);
+               return ret;
+       }
+
+    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;
+
+    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_STARFIVE_INNO_HDMI
+               &inno_hdmi_driver,
+#endif
+#ifdef CONFIG_STARFIVE_DSI
+       &starfive_dsi_platform_driver,
+#endif
+    /* encoder */
+    //&simple_encoder_driver,
+    &starfive_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;
+    printk("%s:%d\n",__FUNCTION__,__LINE__);
+    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 100755 (executable)
index 0000000..ad2de29
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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_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;
+}
+
+#ifdef CONFIG_STARFIVE_INNO_HDMI
+extern struct platform_driver inno_hdmi_driver;
+#endif
+
+#ifdef CONFIG_STARFIVE_DSI
+extern int init_seeed_panel(void);
+extern void exit_seeed_panel(void);
+extern struct platform_driver starfive_dsi_platform_driver;
+#endif
+
+#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 100755 (executable)
index 0000000..bfea105
--- /dev/null
@@ -0,0 +1,191 @@
+// 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_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,
+    .dirty      = drm_atomic_helper_dirtyfb,
+};
+
+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,
+    .output_poll_changed = drm_fb_helper_output_poll_changed,
+    .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.allow_fb_modifiers = true;
+
+    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 100755 (executable)
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 100755 (executable)
index 0000000..2b2f6dc
--- /dev/null
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/of_reserved_mem.h>
+#include <drm/drm_gem_cma_helper.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 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
+               | DMA_ATTR_NO_KERNEL_MAPPING;
+
+    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);
+       
+       printk("Allocated coherent memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)vs_obj->cookie, vs_obj->dma_addr);
+    if (!vs_obj->cookie) {
+       printk("====> %s, %d.\n", __func__, __LINE__);
+#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
+
+       printk("====> %s, %d.vs_obj->get_pages = %d\n", __func__, __LINE__,vs_obj->get_pages);
+
+    if (!vs_obj->get_pages) {
+               
+               printk("====> %s, %d.\n", __func__, __LINE__);
+        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;
+        }
+               printk("====> %s, %d.\n", __func__, __LINE__);
+
+               
+
+        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;
+        }
+               printk("====> %s, %d.\n", __func__, __LINE__);
+
+        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);
+    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) {
+        vma->vm_flags &= ~VM_PFNMAP;
+               DRM_INFO("vs_gem_mmap_obj 111111\n");
+
+        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;
+               DRM_INFO("vs_gem_mmap_obj 222222\n");
+
+        vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+        vma->vm_flags |= 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
+}
+
+int vs_gem_prime_vmap(struct drm_gem_object *obj, struct dma_buf_map *map)
+{
+    return 0;
+}
+
+void vs_gem_prime_vunmap(struct drm_gem_object *obj, struct dma_buf_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);
+    printk("vs_gem_dumb_create size = %08x\n",args->size);
+    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;
+    }
+       printk("====> %s, %d.\n", __func__, __LINE__);
+
+    //ret = drm_prime_sg_to_dma_addr_array(sgt, vs_obj->dma_addr,npages);
+       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 100755 (executable)
index 0000000..78646e7
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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_vmap(struct drm_gem_object *obj, struct dma_buf_map *map);
+void vs_gem_prime_vunmap(struct drm_gem_object *obj, struct dma_buf_map *map);
+
+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 100755 (executable)
index 0000000..95dc644
--- /dev/null
@@ -0,0 +1,416 @@
+// 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_plane_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_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 *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                plane);
+    struct vs_plane *vs_plane = to_vs_plane(plane);
+    struct drm_framebuffer *fb = new_plane_state->fb;
+    struct drm_crtc *crtc = new_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, new_plane_state);
+       return vs_plane->funcs->check(vs_crtc->dev, plane, state);
+}
+
+static void vs_plane_atomic_update(struct drm_plane *plane,
+               struct drm_atomic_state *state)
+{
+       struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
+                                                                          plane);
+    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(new_state->crtc);
+    struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
+    //struct drm_format_name_buf *name = &plane_state->status.format_name;
+
+    if (!new_state->fb || !new_state->crtc)
+        return;
+
+    fb = new_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(new_state);
+    plane_state->status.dest = drm_plane_state_dest(new_state);
+    //drm_get_format_name(fb->format->format, name);
+
+    vs_plane->funcs->update(vs_crtc->dev, vs_plane, plane, state);
+}
+
+static void vs_plane_atomic_disable(struct drm_plane *plane,
+                                    struct drm_atomic_state *state)
+{
+       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+                                                                                  plane);
+    struct vs_plane *vs_plane = to_vs_plane(plane);
+    struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
+       
+    vs_plane->funcs->disable(vs_crtc->dev, vs_plane, old_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;
+       printk("====> %s, %d.\n", __func__, __LINE__);
+
+    plane = kzalloc(sizeof(struct vs_plane), GFP_KERNEL);
+    if (!plane)
+        return NULL;
+       printk("====> %s, %d.\n", __func__, __LINE__);
+
+    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;
+       printk("====> %s, %d.\n", __func__, __LINE__);
+
+    drm_plane_helper_add(&plane->base, &vs_plane_helper_funcs);
+       printk("====> %s, %d.\n", __func__, __LINE__);
+
+    /* 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);
+    }
+       printk("====> %s, %d.\n", __func__, __LINE__);
+
+    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 100755 (executable)
index 0000000..9e11b79
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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, struct drm_plane *drm_plane,
+                                       struct drm_atomic_state *state);
+    void (*disable)(struct device *dev, struct vs_plane *plane,
+                    struct drm_plane_state *old_state);
+    int (*check)(struct device *dev, struct drm_plane *plane,
+                 struct drm_atomic_state *state);
+};
+
+struct vs_plane_status {
+    u32 tile_mode;
+    struct drm_rect src;
+    struct drm_rect dest;
+    //struct drm_format_name_buf format_name;
+};
+
+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 100755 (executable)
index 0000000..543fa64
--- /dev/null
@@ -0,0 +1,296 @@
+// 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>
+#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 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;
+    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, NULL, &bridge);
+    if (ret)
+        goto err;
+
+#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},
+    {},
+};
+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 100755 (executable)
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 100755 (executable)
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 100755 (executable)
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 100755 (executable)
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_ */
old mode 100644 (file)
new mode 100755 (executable)
index 82b63e6..edffeb2
@@ -94,5 +94,6 @@ source "drivers/phy/tegra/Kconfig"
 source "drivers/phy/ti/Kconfig"
 source "drivers/phy/intel/Kconfig"
 source "drivers/phy/xilinx/Kconfig"
+source "drivers/phy/m31/Kconfig"
 
 endmenu
old mode 100644 (file)
new mode 100755 (executable)
index 01e9eff..0bba6d8
@@ -33,4 +33,5 @@ obj-y                                 += allwinner/   \
                                           st/          \
                                           tegra/       \
                                           ti/          \
-                                          xilinx/
+                                          xilinx/      \
+                                          m31/
diff --git a/drivers/phy/m31/Kconfig b/drivers/phy/m31/Kconfig
new file mode 100755 (executable)
index 0000000..e6e4576
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Phy drivers for Starfive platforms
+#
+
+config PHY_M31_DPHY_RX0
+       tristate "Starfive M31 MIPI DPHY TX Driver"
+       select GENERIC_PHY_MIPI_DPHY
+       select GENERIC_PHY
+       help
+         Enable this to support the starfive MIPI  DPHY TX0
+
+         To compile this driver as a module, choose M here: the module
+         will be called phy-starfive-dphy-tx0.
+
diff --git a/drivers/phy/m31/Makefile b/drivers/phy/m31/Makefile
new file mode 100755 (executable)
index 0000000..7ce3391
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_M31_DPHY_RX0)     += phy-m31-dphy-tx0.o
+
diff --git a/drivers/phy/m31/phy-m31-dphy-tx0.c b/drivers/phy/m31/phy-m31-dphy-tx0.c
new file mode 100755 (executable)
index 0000000..e82e1da
--- /dev/null
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip MIPI Synopsys DPHY RX0 driver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on:
+ *
+ * drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
+ * in https://chromium.googlesource.com/chromiumos/third_party/kernel,
+ * chromeos-4.4 branch.
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *   Jacob Chen <jacob2.chen@rock-chips.com>
+ *   Shunqian Zheng <zhengsq@rock-chips.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+//syscfg registers
+#define SCFG_DSI_CSI_SEL               0x2c
+#define SCFG_PHY_RESETB                    0x30
+#define SCFG_REFCLK_SEL                    0x34
+#define SCFG_DBUS_PW_PLL_SSC_LD0       0x38
+#define SCFG_GRS_CDTX_PLL              0x3c
+
+#define SCFG_RG_CDTX_PLL_FBK_PRE       0x44
+#define SCFG_RG_CLANE_DLANE_TIME       0x58
+#define SCFG_RG_CLANE_HS_TIME          0x58
+
+#define SCFG_RG_EXTD_CYCLE_SEL         0x5c
+
+#define SCFG_L0N_L0P_HSTX              0x60
+#define SCFG_L1N_L1P_HSTX              0x64
+#define SCFG_L2N_L2P_HSTX              0x68
+#define SCFG_L3N_L3P_HSTX              0x6c
+#define SCFG_L4N_L4P_HSTX              0x70
+#define SCFG_LX_SWAP_SEL               0x78
+
+#define SCFG_HS_PRE_ZERO_T_D       0xc4
+#define SCFG_TXREADY_SRC_SEL_D     0xc8
+#define SCFG_HS_PRE_ZERO_T_C       0xd4
+#define SCFG_TXREADY_SRC_SEL_C     0xd8
+
+//reg SCFG_LX_SWAP_SEL
+#define        OFFSET_CFG_L0_SWAP_SEL  0
+#define        OFFSET_CFG_L1_SWAP_SEL  3
+#define        OFFSET_CFG_L2_SWAP_SEL  6
+#define        OFFSET_CFG_L3_SWAP_SEL  9
+#define OFFSET_CFG_L4_SWAP_SEL         12
+
+//reg SCFG_DBUS_PW_PLL_SSC_LD0
+#define OFFSET_SCFG_CFG_DATABUD16_SEL    0
+#define OFFSET_SCFG_PWRON_READY_N        1
+#define OFFSET_RG_CDTX_PLL_FM_EN         2
+#define OFFSET_SCFG_PLLSSC_EN            3
+#define OFFSET_RG_CDTX_PLL_LDO_STB_X2_EN 4
+
+//reg SCFG_RG_CLANE_DLANE_TIME
+#define OFFSET_DHS_PRE_TIME          8
+#define OFFSET_DHS_TRIAL_TIME        16
+#define OFFSET_DHS_ZERO_TIME         24
+
+//reg SCFG_RG_CLANE_HS_TIME
+#define OFFSET_CHS_PRE_TIME          8
+#define OFFSET_CHS_TRIAL_TIME        16
+#define OFFSET_CHS_ZERO_TIME         24
+
+//dsitx registers
+#define  VID_MCTL_MAIN_DATA_CTL                0x04
+#define  VID_MCTL_MAIN_PHY_CTL         0x08
+#define  VID_MCTL_MAIN_EN                  0x0c
+#define  VID_MAIN_CTRL_ADDR    0xb0
+#define  VID_VSIZE1_ADDR       0xb4
+#define  VID_VSIZE2_ADDR       0xb8
+#define  VID_HSIZE1_ADDR       0xc0
+#define  VID_HSIZE2_ADDR       0xc4
+#define  VID_BLKSIZE1_ADDR     0xCC
+#define  VID_BLKSIZE2_ADDR     0xd0
+#define  VID_PCK_TIME_ADDR     0xd8
+#define  VID_DPHY_TIME_ADDR    0xdc
+#define  VID_ERR_COLOR1_ADDR   0xe0
+#define  VID_ERR_COLOR2_ADDR   0xe4
+#define  VID_VPOS_ADDR         0xe8
+#define  VID_HPOS_ADDR         0xec
+#define  VID_MODE_STAT_ADDR    0xf0
+#define  VID_VCA_SET1_ADDR     0xf4
+#define  VID_VCA_SET2_ADDR     0xf8
+
+
+#define  VID_MODE_STAT_CLR_ADDR    0x160
+#define  VID_MODE_STAT_FLAG_ADDR   0x180
+
+#define  TVG_CTRL_ADDR      0x0fc
+#define  TVG_IMG_SIZE_ADDR  0x100
+#define  TVG_COLOR1_ADDR    0x104
+#define  TVG_COLOR1BIT_ADDR 0x108
+#define  TVG_COLOR2_ADDR    0x10c
+#define  TVG_COLOR2BIT_ADDR 0x110
+#define  TVG_STAT_ADDR      0x114
+#define  TVG_STAT_CTRL_ADDR 0x144
+#define  TVG_STAT_CLR_ADDR  0x164
+#define  TVG_STAT_FLAG_ADDR 0x184
+
+#define  DPI_IRQ_EN_ADDR   0x1a0
+#define  DPI_IRQ_CLR_ADDR  0x1a4
+#define  DPI_IRQ_STAT_ADDR 0x1a4
+#define  DPI_CFG_ADDR      0x1ac
+
+
+//sysrst registers
+#define SRST_ASSERT0       0x00
+#define SRST_STATUS0           0x04
+/* Definition controller bit for syd rst registers */
+#define BIT_RST_DSI_DPI_PIX            17
+
+struct sf_dphy {
+       struct device *dev;
+       void __iomem *topsys;
+
+       struct clk_bulk_data *clks;
+
+       struct phy_configure_opts_mipi_dphy config;
+
+       u8 hsfreq;
+
+       struct phy *phy;
+};
+
+static u32 top_sys_read32(struct sf_dphy *priv, u32 reg)
+{
+       return ioread32(priv->topsys + reg);
+}
+
+
+static inline void top_sys_write32(struct sf_dphy *priv, u32 reg, u32 val)
+{
+       iowrite32(val, priv->topsys + reg);
+}
+
+static void dsi_csi2tx_sel(struct sf_dphy *priv, int sel)
+{
+  u32 temp = 0;
+  temp = top_sys_read32(priv, SCFG_DSI_CSI_SEL);
+  temp &= ~(0x1);
+  temp |= (sel & 0x1);
+  top_sys_write32(priv, SCFG_DSI_CSI_SEL, temp);
+}
+
+static void dphy_clane_hs_txready_sel(struct sf_dphy *priv, u32 ready_sel)
+{
+       top_sys_write32(priv, SCFG_TXREADY_SRC_SEL_D, ready_sel);
+       top_sys_write32(priv, SCFG_TXREADY_SRC_SEL_C, ready_sel);
+       top_sys_write32(priv, SCFG_HS_PRE_ZERO_T_D, 0x30);
+       top_sys_write32(priv, SCFG_HS_PRE_ZERO_T_C, 0x30);
+}
+
+static void mipi_tx_lxn_set(struct sf_dphy *priv, u32 reg, u32 n_hstx, u32 p_hstx)
+{
+       u32 temp = 0;
+
+       temp = n_hstx;
+       temp |= p_hstx << 5;
+       top_sys_write32(priv, reg, temp);
+}
+
+static void dphy_config(struct sf_dphy *priv, int bit_rate)
+{
+       int pre_div,      fbk_int,       extd_cycle_sel;
+       int dhs_pre_time, dhs_zero_time, dhs_trial_time;
+       int chs_pre_time, chs_zero_time, chs_trial_time;
+       int chs_clk_pre_time, chs_clk_post_time;
+       u32 set_val = 0;
+
+       mipi_tx_lxn_set(priv, SCFG_L0N_L0P_HSTX, 0x10, 0x10);
+       mipi_tx_lxn_set(priv, SCFG_L1N_L1P_HSTX, 0x10, 0x10);
+       mipi_tx_lxn_set(priv, SCFG_L2N_L2P_HSTX, 0x10, 0x10);
+       mipi_tx_lxn_set(priv, SCFG_L3N_L3P_HSTX, 0x10, 0x10);
+       mipi_tx_lxn_set(priv, SCFG_L4N_L4P_HSTX, 0x10, 0x10);
+
+       if(bit_rate == 80) {
+               pre_div=0x1,            fbk_int=2*0x33,         extd_cycle_sel=0x4,
+               dhs_pre_time=0xe,       dhs_zero_time=0x1d,     dhs_trial_time=0x15,
+               chs_pre_time=0x5,       chs_zero_time=0x2b,     chs_trial_time=0xd,
+               chs_clk_pre_time=0xf,
+               chs_clk_post_time=0x71;
+       } else if (bit_rate == 100) {
+               pre_div=0x1,            fbk_int=2*0x40,         extd_cycle_sel=0x4,
+               dhs_pre_time=0x10,      dhs_zero_time=0x21,     dhs_trial_time=0x17,
+               chs_pre_time=0x7,       chs_zero_time=0x35,     chs_trial_time=0xf,
+               chs_clk_pre_time=0xf,
+               chs_clk_post_time=0x73;
+       } else if (bit_rate == 200) {
+               pre_div=0x1,            fbk_int=2*0x40,         extd_cycle_sel=0x3;
+               dhs_pre_time=0xc,       dhs_zero_time=0x1b,     dhs_trial_time=0x13;
+               chs_pre_time=0x7,       chs_zero_time=0x35,     chs_trial_time=0xf,
+               chs_clk_pre_time=0x7,
+               chs_clk_post_time=0x3f;
+       } else if(bit_rate == 300) {
+               pre_div=0x1,            fbk_int=2*0x60,         extd_cycle_sel=0x3,
+               dhs_pre_time=0x11,      dhs_zero_time=0x25, dhs_trial_time=0x19,
+               chs_pre_time=0xa,       chs_zero_time=0x50, chs_trial_time=0x15,
+               chs_clk_pre_time=0x7,
+               chs_clk_post_time=0x45;
+    } else if(bit_rate == 400) {
+               pre_div=0x1,            fbk_int=2*0x40,         extd_cycle_sel=0x2,
+               dhs_pre_time=0xa,       dhs_zero_time=0x18,     dhs_trial_time=0x11,
+               chs_pre_time=0x7,       chs_zero_time=0x35, chs_trial_time=0xf,
+               chs_clk_pre_time=0x3,
+               chs_clk_post_time=0x25;
+    } else if(bit_rate == 500 ) {
+               pre_div=0x1,      fbk_int=2*0x50,       extd_cycle_sel=0x2,
+               dhs_pre_time=0xc, dhs_zero_time=0x1d,   dhs_trial_time=0x14,
+               chs_pre_time=0x9, chs_zero_time=0x42,   chs_trial_time=0x12,
+               chs_clk_pre_time=0x3,
+               chs_clk_post_time=0x28;
+    } else if(bit_rate == 600 ) {
+               pre_div=0x1,      fbk_int=2*0x60,       extd_cycle_sel=0x2,
+               dhs_pre_time=0xe, dhs_zero_time=0x23,   dhs_trial_time=0x17,
+               chs_pre_time=0xa, chs_zero_time=0x50,   chs_trial_time=0x15,
+               chs_clk_pre_time=0x3,
+               chs_clk_post_time=0x2b;
+    } else if(bit_rate == 700) {
+               pre_div=0x1,      fbk_int=2*0x38,       extd_cycle_sel=0x1,
+               dhs_pre_time=0x8, dhs_zero_time=0x14,   dhs_trial_time=0xf,
+               chs_pre_time=0x6, chs_zero_time=0x2f,   chs_trial_time=0xe,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x16;
+    } else if(bit_rate == 800 ) {
+               pre_div=0x1,      fbk_int=2*0x40,       extd_cycle_sel=0x1,
+               dhs_pre_time=0x9, dhs_zero_time=0x17,   dhs_trial_time=0x10,
+               chs_pre_time=0x7, chs_zero_time=0x35,   chs_trial_time=0xf,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x18;
+    } else if(bit_rate == 900 ) {
+               pre_div=0x1,      fbk_int=2*0x48,       extd_cycle_sel=0x1,
+               dhs_pre_time=0xa, dhs_zero_time=0x19,   dhs_trial_time=0x12,
+               chs_pre_time=0x8, chs_zero_time=0x3c,   chs_trial_time=0x10,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x19;
+    } else if(bit_rate == 1000) {
+               pre_div=0x1,      fbk_int=2*0x50,       extd_cycle_sel=0x1,
+               dhs_pre_time=0xb, dhs_zero_time=0x1c,   dhs_trial_time=0x13,
+               chs_pre_time=0x9, chs_zero_time=0x42,   chs_trial_time=0x12,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x1b;
+    } else if(bit_rate == 1100) {
+               pre_div=0x1,      fbk_int=2*0x58,       extd_cycle_sel=0x1,
+               dhs_pre_time=0xc, dhs_zero_time=0x1e,   dhs_trial_time=0x15,
+               chs_pre_time=0x9, chs_zero_time=0x4a,   chs_trial_time=0x14,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x1d;
+    } else if(bit_rate == 1200) {
+               pre_div=0x1,      fbk_int=2*0x60,       extd_cycle_sel=0x1,
+               dhs_pre_time=0xe, dhs_zero_time=0x20,   dhs_trial_time=0x16,
+               chs_pre_time=0xa, chs_zero_time=0x50,   chs_trial_time=0x15,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x1e;
+    } else if(bit_rate == 1300) {
+               pre_div=0x1,      fbk_int=2*0x34,       extd_cycle_sel=0x0,
+               dhs_pre_time=0x7, dhs_zero_time=0x12,   dhs_trial_time=0xd,
+               chs_pre_time=0x5, chs_zero_time=0x2c,   chs_trial_time=0xd,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0xf;
+    } else if(bit_rate == 1400) {
+               pre_div=0x1,      fbk_int=2*0x38,       extd_cycle_sel=0x0,
+               dhs_pre_time=0x7, dhs_zero_time=0x14,   dhs_trial_time=0xe,
+               chs_pre_time=0x6, chs_zero_time=0x2f,   chs_trial_time=0xe,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x10;
+    } else if(bit_rate == 1500) {
+               pre_div=0x1,      fbk_int=2*0x3c,       extd_cycle_sel=0x0,
+               dhs_pre_time=0x8, dhs_zero_time=0x14,   dhs_trial_time=0xf,
+               chs_pre_time=0x6, chs_zero_time=0x32,   chs_trial_time=0xe,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x11;
+    } else if(bit_rate == 1600) {
+               pre_div=0x1,      fbk_int=2*0x40,       extd_cycle_sel=0x0,
+               dhs_pre_time=0x9, dhs_zero_time=0x15,   dhs_trial_time=0x10,
+               chs_pre_time=0x7, chs_zero_time=0x35,   chs_trial_time=0xf,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x12;
+    } else if(bit_rate == 1700) {
+               pre_div=0x1,      fbk_int=2*0x44,       extd_cycle_sel=0x0,
+               dhs_pre_time=0x9, dhs_zero_time=0x17,   dhs_trial_time=0x10,
+               chs_pre_time=0x7, chs_zero_time=0x39,   chs_trial_time=0x10,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x12;
+    } else if(bit_rate == 1800) {
+               pre_div=0x1,      fbk_int=2*0x48,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xa, dhs_zero_time=0x18,   dhs_trial_time=0x11,
+               chs_pre_time=0x8, chs_zero_time=0x3c,   chs_trial_time=0x10,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x13;
+    } else if(bit_rate == 1900) {
+               pre_div=0x1,      fbk_int=2*0x4c,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xa, dhs_zero_time=0x1a,   dhs_trial_time=0x12,
+               chs_pre_time=0x8, chs_zero_time=0x3f,   chs_trial_time=0x11,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x14;
+    } else if(bit_rate == 2000) {
+               pre_div=0x1,      fbk_int=2*0x50,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xb, dhs_zero_time=0x1b,   dhs_trial_time=0x13,
+               chs_pre_time=0x9, chs_zero_time=0x42,   chs_trial_time=0x12,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x15;
+    } else if(bit_rate == 2100) {
+               pre_div=0x1,      fbk_int=2*0x54,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xb, dhs_zero_time=0x1c,   dhs_trial_time=0x13,
+               chs_pre_time=0x9, chs_zero_time=0x46,   chs_trial_time=0x13,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x15;
+    } else if(bit_rate == 2200) {
+               pre_div=0x1,      fbk_int=2*0x5b,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xc, dhs_zero_time=0x1d,   dhs_trial_time=0x14,
+               chs_pre_time=0x9, chs_zero_time=0x4a,   chs_trial_time=0x14,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x16;
+    } else if(bit_rate == 2300) {
+               pre_div=0x1,      fbk_int=2*0x5c,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xc, dhs_zero_time=0x1f,   dhs_trial_time=0x15,
+               chs_pre_time=0xa, chs_zero_time=0x4c,   chs_trial_time=0x14,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x17;
+    } else if(bit_rate == 2400) {
+               pre_div=0x1,      fbk_int=2*0x60,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xd, dhs_zero_time=0x20,   dhs_trial_time=0x16,
+               chs_pre_time=0xa, chs_zero_time=0x50,   chs_trial_time=0x15,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x18;
+    } else if(bit_rate == 2500) {
+               pre_div=0x1,      fbk_int=2*0x64,       extd_cycle_sel=0x0,
+               dhs_pre_time=0xe, dhs_zero_time=0x21,   dhs_trial_time=0x16,
+               chs_pre_time=0xb, chs_zero_time=0x53,   chs_trial_time=0x16,
+               chs_clk_pre_time=0x0,
+               chs_clk_post_time=0x18;
+    } else {
+               //default bit_rate == 700
+               pre_div=0x1,      fbk_int=2*0x38,       extd_cycle_sel=0x1,
+               dhs_pre_time=0x8, dhs_zero_time=0x14,   dhs_trial_time=0xf,
+               chs_pre_time=0x6, chs_zero_time=0x2f,   chs_trial_time=0xe,
+               chs_clk_pre_time=0x1,
+               chs_clk_post_time=0x16;
+    }
+       top_sys_write32(priv, SCFG_REFCLK_SEL, 0x3);
+
+       set_val = 0
+                       | (1 << OFFSET_CFG_L1_SWAP_SEL)
+                       | (4 << OFFSET_CFG_L2_SWAP_SEL)
+                       | (2 << OFFSET_CFG_L3_SWAP_SEL)
+                       | (3 << OFFSET_CFG_L4_SWAP_SEL);
+       top_sys_write32(priv, SCFG_LX_SWAP_SEL, set_val);
+
+       set_val = 0
+                       | (0 << OFFSET_SCFG_PWRON_READY_N)
+                       | (1 << OFFSET_RG_CDTX_PLL_FM_EN)
+                       | (0 << OFFSET_SCFG_PLLSSC_EN)
+                       | (1 << OFFSET_RG_CDTX_PLL_LDO_STB_X2_EN);
+       top_sys_write32(priv, SCFG_DBUS_PW_PLL_SSC_LD0, set_val);
+
+       set_val = fbk_int
+                       | (pre_div << 9);
+       top_sys_write32(priv, SCFG_RG_CDTX_PLL_FBK_PRE, set_val);
+
+       top_sys_write32(priv, SCFG_RG_EXTD_CYCLE_SEL, extd_cycle_sel);
+
+       set_val = chs_zero_time
+                       | (dhs_pre_time << OFFSET_DHS_PRE_TIME)
+                       | (dhs_trial_time << OFFSET_DHS_TRIAL_TIME)
+                       | (dhs_zero_time << OFFSET_DHS_ZERO_TIME);
+       top_sys_write32(priv, SCFG_RG_CLANE_DLANE_TIME, set_val);
+
+       set_val = chs_clk_post_time
+                       | (chs_clk_pre_time << OFFSET_CHS_PRE_TIME)
+                       | (chs_pre_time << OFFSET_CHS_TRIAL_TIME)
+                       | (chs_trial_time << OFFSET_CHS_ZERO_TIME);
+       top_sys_write32(priv, SCFG_RG_CLANE_HS_TIME, set_val);
+
+}
+
+static void reset_dphy(struct sf_dphy *priv, int resetb)
+{
+       u32 cfg_dsc_enable = 0x01;//bit0
+
+       u32 precfg = top_sys_read32(priv, SCFG_PHY_RESETB);
+       precfg &= ~(cfg_dsc_enable);
+       precfg |= (resetb&cfg_dsc_enable);
+       top_sys_write32(priv, SCFG_PHY_RESETB, precfg);
+}
+
+static void polling_dphy_lock(struct sf_dphy *priv)
+{
+       int pll_unlock;
+
+       udelay(10);
+
+       do {
+               pll_unlock = top_sys_read32(priv, SCFG_GRS_CDTX_PLL) >> 3;
+               pll_unlock &= 0x1;
+       } while(pll_unlock == 0x1);
+}
+
+static int sf_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+       struct sf_dphy *dphy = phy_get_drvdata(phy);
+       uint32_t bit_rate = 800000000/1000000UL;//new mipi panel clock setting
+
+
+       dphy_config(dphy, bit_rate);
+       reset_dphy(dphy, 1);
+       mdelay(10);
+       polling_dphy_lock(dphy);
+
+       return 0;
+}
+
+static int sf_dphy_power_on(struct phy *phy)
+{
+       return 0;
+}
+
+static int sf_dphy_power_off(struct phy *phy)
+{
+       return 0;
+}
+
+static int sf_dphy_init(struct phy *phy)
+{
+       struct sf_dphy *dphy = phy_get_drvdata(phy);
+
+       dsi_csi2tx_sel(dphy, 0);
+       dphy_clane_hs_txready_sel(dphy, 0x1);
+
+       return 0;
+}
+
+static int sf_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
+                       union phy_configure_opts *opts)
+{
+       return 0;
+}
+
+static int sf_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+       return 0;
+}
+
+
+static int sf_dphy_exit(struct phy *phy)
+{
+       return 0;
+}
+
+static const struct phy_ops sf_dphy_ops = {
+       .power_on       = sf_dphy_power_on,
+       .power_off      = sf_dphy_power_off,
+       .init           = sf_dphy_init,
+       .exit           = sf_dphy_exit,
+       .configure      = sf_dphy_configure,
+       .validate  = sf_dphy_validate,
+       .set_mode  = sf_dphy_set_mode,
+       .owner          = THIS_MODULE,
+};
+
+static const struct of_device_id sf_dphy_dt_ids[] = {
+       {
+               .compatible = "starfive,jh7100-mipi-dphy-tx",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, sf_dphy_dt_ids);
+
+static int sf_dphy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct sf_dphy *dphy;
+       struct resource *res;
+       int ret;
+       dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
+       if (!dphy)
+               return -ENOMEM;
+       dev_set_drvdata(&pdev->dev, dphy);
+
+       dev_info(&pdev->dev,"===> %s enter, %d \n", __func__, __LINE__);
+
+       dphy->topsys = ioremap(0x12260000, 0x10000);
+
+       dphy->phy = devm_phy_create(&pdev->dev, NULL, &sf_dphy_ops);
+       if (IS_ERR(dphy->phy)) {
+               dev_err(&pdev->dev, "failed to create phy\n");
+               return PTR_ERR(dphy->phy);
+       }
+       phy_set_drvdata(dphy->phy, dphy);
+
+       phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver sf_dphy_driver = {
+       .probe = sf_dphy_probe,
+       .driver = {
+               .name   = "sf-mipi-dphy-tx",
+               .of_match_table = sf_dphy_dt_ids,
+       },
+};
+module_platform_driver(sf_dphy_driver);
+
+MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
+MODULE_DESCRIPTION("sf MIPI  DPHY TX0 driver");
+MODULE_LICENSE("Dual MIT/GPL");
old mode 100644 (file)
new mode 100755 (executable)
index 9f4bb4a..343bf53
@@ -357,6 +357,7 @@ extern "C" {
  */
 
 /* Vendor Ids: */
+#define DRM_FORMAT_MOD_NONE           0
 #define DRM_FORMAT_MOD_VENDOR_NONE    0
 #define DRM_FORMAT_MOD_VENDOR_INTEL   0x01
 #define DRM_FORMAT_MOD_VENDOR_AMD     0x02
@@ -368,6 +369,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 */
 
@@ -1358,6 +1360,88 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
 #define AMD_FMT_MOD_CLEAR(field) \
        (~((uint64_t)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
 
+
+#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