All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v6 0/6] Add Unisoc's drm kms module
@ 2020-07-28 10:07 ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

ChangeList:
v1:
1. only upstream modeset and atomic at first commit. 
2. remove some unused code;
3. use alpha and blend_mode properties;
3. add yaml support;
4. remove auto-adaptive panel driver;
5. bugfix

v2:
1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
2. remove gem drivers, use generic CMA handlers
3. remove redundant "module_init", all the sub modules loading by KMS

v3:
1. multi crtc&encoder design have problem, so rollback to v1

v4:
1. update to gcc-linaro-7.5.0
2. update to Linux 5.6-rc3
3. remove pm_runtime support
4. add COMPILE_TEST, remove unused kconfig
5. "drm_dev_put" on drm_unbind
6. fix some naming convention issue
7. remove semaphore lock for crtc flip
8. remove static variables

v5:
1. optimize encoder and connector code implementation
2. use "platform_get_irq" and "platform_get_resource"
3. drop useless function return type, drop unless debug log
4. custom properties should be separate, so drop it
5. use DRM_XXX replase pr_xxx
6. drop dsi&dphy hal callback ops
7. drop unless callback ops checking
8. add comments for sprd dpu structure

v6:
1. Access registers via readl/writel
2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
3. Remove always true checks for dpu core ops

Kevin Tang (6):
  dt-bindings: display: add Unisoc's drm master bindings
  drm/sprd: add Unisoc's drm kms master
  dt-bindings: display: add Unisoc's dpu bindings
  drm/sprd: add Unisoc's drm display controller driver
  dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
  drm/sprd: add Unisoc's drm mipi dsi&dphy driver

 .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
 .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
 .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
 .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
 drivers/gpu/drm/Kconfig                            |    2 +
 drivers/gpu/drm/Makefile                           |    1 +
 drivers/gpu/drm/sprd/Kconfig                       |   12 +
 drivers/gpu/drm/sprd/Makefile                      |   13 +
 drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
 drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
 drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
 drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
 drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
 drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
 drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
 drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
 drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
 drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
 drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
 drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
 drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
 33 files changed, 7074 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
 create mode 100644 drivers/gpu/drm/sprd/Kconfig
 create mode 100644 drivers/gpu/drm/sprd/Makefile
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
 create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
 create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 0/6] Add Unisoc's drm kms module
@ 2020-07-28 10:07 ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

ChangeList:
v1:
1. only upstream modeset and atomic at first commit. 
2. remove some unused code;
3. use alpha and blend_mode properties;
3. add yaml support;
4. remove auto-adaptive panel driver;
5. bugfix

v2:
1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
2. remove gem drivers, use generic CMA handlers
3. remove redundant "module_init", all the sub modules loading by KMS

v3:
1. multi crtc&encoder design have problem, so rollback to v1

v4:
1. update to gcc-linaro-7.5.0
2. update to Linux 5.6-rc3
3. remove pm_runtime support
4. add COMPILE_TEST, remove unused kconfig
5. "drm_dev_put" on drm_unbind
6. fix some naming convention issue
7. remove semaphore lock for crtc flip
8. remove static variables

v5:
1. optimize encoder and connector code implementation
2. use "platform_get_irq" and "platform_get_resource"
3. drop useless function return type, drop unless debug log
4. custom properties should be separate, so drop it
5. use DRM_XXX replase pr_xxx
6. drop dsi&dphy hal callback ops
7. drop unless callback ops checking
8. add comments for sprd dpu structure

v6:
1. Access registers via readl/writel
2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
3. Remove always true checks for dpu core ops

Kevin Tang (6):
  dt-bindings: display: add Unisoc's drm master bindings
  drm/sprd: add Unisoc's drm kms master
  dt-bindings: display: add Unisoc's dpu bindings
  drm/sprd: add Unisoc's drm display controller driver
  dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
  drm/sprd: add Unisoc's drm mipi dsi&dphy driver

 .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
 .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
 .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
 .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
 drivers/gpu/drm/Kconfig                            |    2 +
 drivers/gpu/drm/Makefile                           |    1 +
 drivers/gpu/drm/sprd/Kconfig                       |   12 +
 drivers/gpu/drm/sprd/Makefile                      |   13 +
 drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
 drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
 drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
 drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
 drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
 drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
 drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
 drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
 drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
 drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
 drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
 drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
 drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
 33 files changed, 7074 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
 create mode 100644 drivers/gpu/drm/sprd/Kconfig
 create mode 100644 drivers/gpu/drm/sprd/Makefile
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
 create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
 create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h

-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:07   ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

The Unisoc DRM master device is a virtual device needed to list all
DPU devices or other display interface nodes that comprise the
graphics subsystem

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml

diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
new file mode 100644
index 0000000..b5792c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc DRM master device
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+description: |
+  The Unisoc DRM master device is a virtual device needed to list all
+  DPU devices or other display interface nodes that comprise the
+  graphics subsystem.
+
+properties:
+  compatible:
+    const: sprd,display-subsystem
+
+  ports:
+    description:
+      Should contain a list of phandles pointing to display interface port
+      of DPU devices.
+
+required:
+  - compatible
+  - ports
+
+examples:
+  - |
+    display-subsystem {
+        compatible = "sprd,display-subsystem";
+        ports = <&dpu_out>;
+    };
+
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
@ 2020-07-28 10:07   ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

The Unisoc DRM master device is a virtual device needed to list all
DPU devices or other display interface nodes that comprise the
graphics subsystem

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml

diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
new file mode 100644
index 0000000..b5792c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc DRM master device
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+description: |
+  The Unisoc DRM master device is a virtual device needed to list all
+  DPU devices or other display interface nodes that comprise the
+  graphics subsystem.
+
+properties:
+  compatible:
+    const: sprd,display-subsystem
+
+  ports:
+    description:
+      Should contain a list of phandles pointing to display interface port
+      of DPU devices.
+
+required:
+  - compatible
+  - ports
+
+examples:
+  - |
+    display-subsystem {
+        compatible = "sprd,display-subsystem";
+        ports = <&dpu_out>;
+    };
+
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:07   ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

Adds drm support for the Unisoc's display subsystem.

This is drm kms driver, this driver provides support for the
application framework in Android, Yocto and more.

Application framework can access Unisoc's display internel
peripherals through libdrm or libkms, it's test ok by modetest
(DRM/KMS test tool) and Android HWComposer.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 drivers/gpu/drm/Kconfig         |   2 +
 drivers/gpu/drm/Makefile        |   1 +
 drivers/gpu/drm/sprd/Kconfig    |  12 +++
 drivers/gpu/drm/sprd/Makefile   |   5 +
 drivers/gpu/drm/sprd/sprd_drm.c | 226 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 262 insertions(+)
 create mode 100644 drivers/gpu/drm/sprd/Kconfig
 create mode 100644 drivers/gpu/drm/sprd/Makefile
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c4fd57d..7fe7ab91 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
 
 source "drivers/gpu/drm/tidss/Kconfig"
 
+source "drivers/gpu/drm/sprd/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2c0e5a7..ee2ccd9 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
 obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
 obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
+obj-$(CONFIG_DRM_SPRD) += sprd/
diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
new file mode 100644
index 0000000..b189a54
--- /dev/null
+++ b/drivers/gpu/drm/sprd/Kconfig
@@ -0,0 +1,12 @@
+config DRM_SPRD
+	tristate "DRM Support for Unisoc SoCs Platform"
+	depends on ARCH_SPRD || COMPILE_TEST
+	depends on DRM && OF
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_MIPI_DSI
+	help
+	  Choose this option if you have a Unisoc chipsets.
+	  If M is selected the module will be called sprd-drm.
+
diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
new file mode 100644
index 0000000..86d95d9
--- /dev/null
+++ b/drivers/gpu/drm/sprd/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
new file mode 100644
index 0000000..4706185
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_drm.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "sprd_drm.h"
+
+#define DRIVER_NAME	"sprd"
+#define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
+#define DRIVER_DATE	"20200201"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void sprd_drm_mode_config_init(struct drm_device *drm)
+{
+	drm_mode_config_init(drm);
+
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+	drm->mode_config.max_width = 8192;
+	drm->mode_config.max_height = 8192;
+	drm->mode_config.allow_fb_modifiers = true;
+
+	drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
+	drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
+
+static struct drm_driver sprd_drm_drv = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+	.fops			= &sprd_drm_fops,
+
+	/* GEM Operations */
+  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
+
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+	struct drm_device *drm;
+	struct sprd_drm *sprd;
+	int err;
+
+	drm = drm_dev_alloc(&sprd_drm_drv, dev);
+	if (IS_ERR(drm))
+		return PTR_ERR(drm);
+
+	dev_set_drvdata(dev, drm);
+
+	sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL);
+	if (!sprd)
+		err = -ENOMEM;
+
+	drm->dev_private = sprd;
+
+	sprd_drm_mode_config_init(drm);
+
+	/* bind and init sub drivers */
+	err = component_bind_all(drm->dev, drm);
+	if (err) {
+		DRM_ERROR("failed to bind all component.\n");
+		goto err_dc_cleanup;
+	}
+
+	/* vblank init */
+	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
+	if (err) {
+		DRM_ERROR("failed to initialize vblank.\n");
+		goto err_unbind_all;
+	}
+	/* with irq_enabled = true, we can use the vblank feature. */
+	drm->irq_enabled = true;
+
+	/* reset all the states of crtc/plane/encoder/connector */
+	drm_mode_config_reset(drm);
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(drm);
+
+	err = drm_dev_register(drm, 0);
+	if (err < 0)
+		goto err_kms_helper_poll_fini;
+
+	return 0;
+
+err_kms_helper_poll_fini:
+	drm_kms_helper_poll_fini(drm);
+err_unbind_all:
+	component_unbind_all(drm->dev, drm);
+err_dc_cleanup:
+	drm_mode_config_cleanup(drm);
+	return err;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm);
+
+	drm_kms_helper_poll_fini(drm);
+
+	drm_mode_config_cleanup(drm);
+
+	component_unbind_all(drm->dev, drm);
+	drm->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
+
+	drm_dev_put(drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+	.bind = sprd_drm_bind,
+	.unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
+	if (ret) {
+		DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
+		return ret;
+	}
+
+	return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops);
+}
+
+static int sprd_drm_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &drm_component_ops);
+	return 0;
+}
+
+static void sprd_drm_shutdown(struct platform_device *pdev)
+{
+	struct drm_device *drm = platform_get_drvdata(pdev);
+
+	if (!drm) {
+		DRM_WARN("drm device is not available, no shutdown\n");
+		return;
+	}
+
+	drm_atomic_helper_shutdown(drm);
+}
+
+static const struct of_device_id drm_match_table[] = {
+	{ .compatible = "sprd,display-subsystem", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, drm_match_table);
+
+static struct platform_driver sprd_drm_driver = {
+	.probe = sprd_drm_probe,
+	.remove = sprd_drm_remove,
+	.shutdown = sprd_drm_shutdown,
+	.driver = {
+		.name = "sprd-drm-drv",
+		.of_match_table = drm_match_table,
+	},
+};
+
+static struct platform_driver *sprd_drm_drivers[]  = {
+	&sprd_drm_driver,
+};
+
+static int __init sprd_drm_init(void)
+{
+	int ret;
+
+	ret = platform_register_drivers(sprd_drm_drivers,
+					ARRAY_SIZE(sprd_drm_drivers));
+	return ret;
+}
+
+static void __exit sprd_drm_exit(void)
+{
+	platform_unregister_drivers(sprd_drm_drivers,
+				    ARRAY_SIZE(sprd_drm_drivers));
+}
+
+module_init(sprd_drm_init);
+module_exit(sprd_drm_exit);
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
new file mode 100644
index 0000000..edf0881
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_drm.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+	struct drm_device *drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
@ 2020-07-28 10:07   ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

Adds drm support for the Unisoc's display subsystem.

This is drm kms driver, this driver provides support for the
application framework in Android, Yocto and more.

Application framework can access Unisoc's display internel
peripherals through libdrm or libkms, it's test ok by modetest
(DRM/KMS test tool) and Android HWComposer.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 drivers/gpu/drm/Kconfig         |   2 +
 drivers/gpu/drm/Makefile        |   1 +
 drivers/gpu/drm/sprd/Kconfig    |  12 +++
 drivers/gpu/drm/sprd/Makefile   |   5 +
 drivers/gpu/drm/sprd/sprd_drm.c | 226 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 262 insertions(+)
 create mode 100644 drivers/gpu/drm/sprd/Kconfig
 create mode 100644 drivers/gpu/drm/sprd/Makefile
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c4fd57d..7fe7ab91 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
 
 source "drivers/gpu/drm/tidss/Kconfig"
 
+source "drivers/gpu/drm/sprd/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2c0e5a7..ee2ccd9 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
 obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
 obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
+obj-$(CONFIG_DRM_SPRD) += sprd/
diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
new file mode 100644
index 0000000..b189a54
--- /dev/null
+++ b/drivers/gpu/drm/sprd/Kconfig
@@ -0,0 +1,12 @@
+config DRM_SPRD
+	tristate "DRM Support for Unisoc SoCs Platform"
+	depends on ARCH_SPRD || COMPILE_TEST
+	depends on DRM && OF
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_MIPI_DSI
+	help
+	  Choose this option if you have a Unisoc chipsets.
+	  If M is selected the module will be called sprd-drm.
+
diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
new file mode 100644
index 0000000..86d95d9
--- /dev/null
+++ b/drivers/gpu/drm/sprd/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
new file mode 100644
index 0000000..4706185
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_drm.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "sprd_drm.h"
+
+#define DRIVER_NAME	"sprd"
+#define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
+#define DRIVER_DATE	"20200201"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void sprd_drm_mode_config_init(struct drm_device *drm)
+{
+	drm_mode_config_init(drm);
+
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+	drm->mode_config.max_width = 8192;
+	drm->mode_config.max_height = 8192;
+	drm->mode_config.allow_fb_modifiers = true;
+
+	drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
+	drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
+
+static struct drm_driver sprd_drm_drv = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+	.fops			= &sprd_drm_fops,
+
+	/* GEM Operations */
+  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
+
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+	struct drm_device *drm;
+	struct sprd_drm *sprd;
+	int err;
+
+	drm = drm_dev_alloc(&sprd_drm_drv, dev);
+	if (IS_ERR(drm))
+		return PTR_ERR(drm);
+
+	dev_set_drvdata(dev, drm);
+
+	sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL);
+	if (!sprd)
+		err = -ENOMEM;
+
+	drm->dev_private = sprd;
+
+	sprd_drm_mode_config_init(drm);
+
+	/* bind and init sub drivers */
+	err = component_bind_all(drm->dev, drm);
+	if (err) {
+		DRM_ERROR("failed to bind all component.\n");
+		goto err_dc_cleanup;
+	}
+
+	/* vblank init */
+	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
+	if (err) {
+		DRM_ERROR("failed to initialize vblank.\n");
+		goto err_unbind_all;
+	}
+	/* with irq_enabled = true, we can use the vblank feature. */
+	drm->irq_enabled = true;
+
+	/* reset all the states of crtc/plane/encoder/connector */
+	drm_mode_config_reset(drm);
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(drm);
+
+	err = drm_dev_register(drm, 0);
+	if (err < 0)
+		goto err_kms_helper_poll_fini;
+
+	return 0;
+
+err_kms_helper_poll_fini:
+	drm_kms_helper_poll_fini(drm);
+err_unbind_all:
+	component_unbind_all(drm->dev, drm);
+err_dc_cleanup:
+	drm_mode_config_cleanup(drm);
+	return err;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm);
+
+	drm_kms_helper_poll_fini(drm);
+
+	drm_mode_config_cleanup(drm);
+
+	component_unbind_all(drm->dev, drm);
+	drm->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
+
+	drm_dev_put(drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+	.bind = sprd_drm_bind,
+	.unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
+	if (ret) {
+		DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
+		return ret;
+	}
+
+	return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops);
+}
+
+static int sprd_drm_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &drm_component_ops);
+	return 0;
+}
+
+static void sprd_drm_shutdown(struct platform_device *pdev)
+{
+	struct drm_device *drm = platform_get_drvdata(pdev);
+
+	if (!drm) {
+		DRM_WARN("drm device is not available, no shutdown\n");
+		return;
+	}
+
+	drm_atomic_helper_shutdown(drm);
+}
+
+static const struct of_device_id drm_match_table[] = {
+	{ .compatible = "sprd,display-subsystem", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, drm_match_table);
+
+static struct platform_driver sprd_drm_driver = {
+	.probe = sprd_drm_probe,
+	.remove = sprd_drm_remove,
+	.shutdown = sprd_drm_shutdown,
+	.driver = {
+		.name = "sprd-drm-drv",
+		.of_match_table = drm_match_table,
+	},
+};
+
+static struct platform_driver *sprd_drm_drivers[]  = {
+	&sprd_drm_driver,
+};
+
+static int __init sprd_drm_init(void)
+{
+	int ret;
+
+	ret = platform_register_drivers(sprd_drm_drivers,
+					ARRAY_SIZE(sprd_drm_drivers));
+	return ret;
+}
+
+static void __exit sprd_drm_exit(void)
+{
+	platform_unregister_drivers(sprd_drm_drivers,
+				    ARRAY_SIZE(sprd_drm_drivers));
+}
+
+module_init(sprd_drm_init);
+module_exit(sprd_drm_exit);
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
new file mode 100644
index 0000000..edf0881
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_drm.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+	struct drm_device *drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 3/6] dt-bindings: display: add Unisoc's dpu bindings
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:07   ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
which transfers the image data from a video memory buffer to an internal
LCD interface.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 .../devicetree/bindings/display/sprd/dpu.yaml      | 82 ++++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml

diff --git a/Documentation/devicetree/bindings/display/sprd/dpu.yaml b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
new file mode 100644
index 0000000..54ba9b6f
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/dpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc SoC Display Processor Unit (DPU)
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+description: |
+  DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
+  which transfers the image data from a video memory buffer to an internal
+  LCD interface.
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dpu
+
+  reg:
+    maxItems: 1
+    description:
+      Physical base address and length of the DPU registers set
+
+  interrupts:
+    maxItems: 1
+    description:
+      The interrupt signal from DPU
+
+  clocks:
+    minItems: 2
+
+  clock-names:
+    items:
+      - const: clk_src_128m
+      - const: clk_src_384m
+
+  power-domains:
+    description: A phandle to DPU power domain node.
+
+  iommus:
+    description: A phandle to DPU iommu node.
+
+  port:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the output endpoint, usually output to
+      the associated DSI.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sprd,sc9860-clk.h>
+    dpu: dpu@0x63000000 {
+          compatible = "sprd,sharkl3-dpu";
+          reg = <0x0 0x63000000 0x0 0x1000>;
+          interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+          clock-names = "clk_src_128m",
+      	        "clk_src_384m";
+
+          clocks = <&pll CLK_TWPLL_128M>,
+                <&pll CLK_TWPLL_384M>;
+
+          dpu_port: port {
+                  dpu_out: endpoint {
+                          remote-endpoint = <&dsi_in>;
+                  };
+          };
+    };
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 3/6] dt-bindings: display: add Unisoc's dpu bindings
@ 2020-07-28 10:07   ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
which transfers the image data from a video memory buffer to an internal
LCD interface.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 .../devicetree/bindings/display/sprd/dpu.yaml      | 82 ++++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml

diff --git a/Documentation/devicetree/bindings/display/sprd/dpu.yaml b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
new file mode 100644
index 0000000..54ba9b6f
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/dpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc SoC Display Processor Unit (DPU)
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+description: |
+  DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
+  which transfers the image data from a video memory buffer to an internal
+  LCD interface.
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dpu
+
+  reg:
+    maxItems: 1
+    description:
+      Physical base address and length of the DPU registers set
+
+  interrupts:
+    maxItems: 1
+    description:
+      The interrupt signal from DPU
+
+  clocks:
+    minItems: 2
+
+  clock-names:
+    items:
+      - const: clk_src_128m
+      - const: clk_src_384m
+
+  power-domains:
+    description: A phandle to DPU power domain node.
+
+  iommus:
+    description: A phandle to DPU iommu node.
+
+  port:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the output endpoint, usually output to
+      the associated DSI.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sprd,sc9860-clk.h>
+    dpu: dpu@0x63000000 {
+          compatible = "sprd,sharkl3-dpu";
+          reg = <0x0 0x63000000 0x0 0x1000>;
+          interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+          clock-names = "clk_src_128m",
+      	        "clk_src_384m";
+
+          clocks = <&pll CLK_TWPLL_128M>,
+                <&pll CLK_TWPLL_384M>;
+
+          dpu_port: port {
+                  dpu_out: endpoint {
+                          remote-endpoint = <&dsi_in>;
+                  };
+          };
+    };
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:07   ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.

RFC v6:
  - Access registers via readl/writel
  - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
  - Remove always true checks for dpu core ops

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 drivers/gpu/drm/sprd/Makefile       |   5 +-
 drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
 drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
 drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
 7 files changed, 1346 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h

diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
index 86d95d9..88ab32a 100644
--- a/drivers/gpu/drm/sprd/Makefile
+++ b/drivers/gpu/drm/sprd/Makefile
@@ -2,4 +2,7 @@
 
 subdir-ccflags-y += -I$(srctree)/$(src)
 
-obj-y := sprd_drm.o
+obj-y := sprd_drm.o \
+	sprd_dpu.o
+
+obj-y += dpu/
diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
new file mode 100644
index 0000000..40278b6
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dpu/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
new file mode 100644
index 0000000..4b9521d
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include "sprd_dpu.h"
+
+/* DPU registers size, 4 Bytes(32 Bits) */
+#define DPU_REG_SIZE	0x04
+
+/* Layer registers offset */
+#define DPU_LAY_REG_OFFSET	0x0C
+
+#define DPU_LAY_REG(reg, index) \
+	(reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
+
+#define DPU_REG_RD(reg) readl_relaxed(reg)
+
+#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
+
+#define DPU_REG_SET(reg, mask) \
+	writel_relaxed(readl_relaxed(reg) | mask, reg)
+
+#define DPU_REG_CLR(reg, mask) \
+	writel_relaxed(readl_relaxed(reg) & ~mask, reg)
+
+/* Global control registers */
+#define REG_DPU_CTRL	0x04
+#define REG_DPU_CFG0	0x08
+#define REG_DPU_CFG1	0x0C
+#define REG_DPU_CFG2	0x10
+#define REG_PANEL_SIZE	0x20
+#define REG_BLEND_SIZE	0x24
+#define REG_BG_COLOR	0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0	0x30
+#define REG_LAY_BASE_ADDR1	0x34
+#define REG_LAY_BASE_ADDR2	0x38
+#define REG_LAY_CTRL		0x40
+#define REG_LAY_SIZE		0x44
+#define REG_LAY_PITCH		0x48
+#define REG_LAY_POS		0x4C
+#define REG_LAY_ALPHA		0x50
+#define REG_LAY_PALLETE		0x58
+#define REG_LAY_CROP_START	0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN		0x1E0
+#define REG_DPU_INT_CLR		0x1E4
+#define REG_DPU_INT_STS		0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL		0x1F0
+#define REG_DPI_H_TIMING	0x1F4
+#define REG_DPI_V_TIMING	0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN			0x800
+#define REG_MMU_VPN_RANGE		0x80C
+#define REG_MMU_VAOR_ADDR_RD		0x818
+#define REG_MMU_VAOR_ADDR_WR		0x81C
+#define REG_MMU_INV_ADDR_RD		0x820
+#define REG_MMU_INV_ADDR_WR		0x824
+#define REG_MMU_PPN1			0x83C
+#define REG_MMU_RANGE1			0x840
+#define REG_MMU_PPN2			0x844
+#define REG_MMU_RANGE2			0x848
+
+/* Global control bits */
+#define BIT_DPU_RUN			BIT(0)
+#define BIT_DPU_STOP			BIT(1)
+#define BIT_DPU_REG_UPDATE		BIT(2)
+#define BIT_DPU_IF_EDPI			BIT(0)
+#define BIT_DPU_COEF_NARROW_RANGE		BIT(4)
+#define BIT_DPU_Y2R_COEF_ITU709_STANDARD	BIT(5)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN				BIT(0)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE		BIT(0)
+#define BIT_DPU_INT_TE			BIT(1)
+#define BIT_DPU_INT_ERR			BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
+#define BIT_DPU_INT_VSYNC		BIT(5)
+#define BIT_DPU_INT_FBC_PLD_ERR		BIT(8)
+#define BIT_DPU_INT_FBC_HDR_ERR		BIT(9)
+#define BIT_DPU_INT_MMU_VAOR_RD		BIT(16)
+#define BIT_DPU_INT_MMU_VAOR_WR		BIT(17)
+#define BIT_DPU_INT_MMU_INV_RD		BIT(18)
+#define BIT_DPU_INT_MMU_INV_WR		BIT(19)
+
+/* DPI control bits */
+#define BIT_DPU_EDPI_TE_EN		BIT(8)
+#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD	BIT(10)
+#define BIT_DPU_DPI_HALT_EN		BIT(16)
+
+
+static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
+{
+	u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
+			BIT_DPU_INT_MMU_VAOR_WR |
+			BIT_DPU_INT_MMU_INV_RD |
+			BIT_DPU_INT_MMU_INV_WR;
+	u32 val = reg_val & mmu_mask;
+	int i;
+
+	if (val) {
+		DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
+
+		if (val & BIT_DPU_INT_MMU_INV_RD)
+			DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_INV_WR)
+			DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
+		if (val & BIT_DPU_INT_MMU_VAOR_RD)
+			DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_VAOR_WR)
+			DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
+
+		for (i = 0; i < 8; i++) {
+			reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
+			if (reg_val & 0x1)
+				DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
+		}
+	}
+
+	return val;
+}
+
+static void dpu_clean_all(struct dpu_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
+}
+
+static u32 dpu_isr(struct dpu_context *ctx)
+{
+	u32 reg_val, int_mask = 0;
+
+	reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
+
+	/* disable err interrupt */
+	if (reg_val & BIT_DPU_INT_ERR)
+		int_mask |= BIT_DPU_INT_ERR;
+
+	/* dpu update done isr */
+	if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
+		ctx->evt_update = true;
+		wake_up_interruptible_all(&ctx->wait_queue);
+	}
+
+	/* dpu stop done isr */
+	if (reg_val & BIT_DPU_INT_DONE) {
+		ctx->evt_stop = true;
+		wake_up_interruptible_all(&ctx->wait_queue);
+	}
+
+	/* dpu ifbc payload error isr */
+	if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
+		int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
+		DRM_ERROR("dpu ifbc payload error\n");
+	}
+
+	/* dpu ifbc header error isr */
+	if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
+		int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
+		DRM_ERROR("dpu ifbc header error\n");
+	}
+
+	int_mask |= check_mmu_isr(ctx, reg_val);
+
+	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
+	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
+
+	return reg_val;
+}
+
+static int dpu_wait_stop_done(struct dpu_context *ctx)
+{
+	int rc;
+
+	if (ctx->stopped)
+		return 0;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
+					       msecs_to_jiffies(500));
+	ctx->evt_stop = false;
+
+	ctx->stopped = true;
+
+	if (!rc) {
+		DRM_ERROR("dpu wait for stop done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int dpu_wait_update_done(struct dpu_context *ctx)
+{
+	int rc;
+
+	ctx->evt_update = false;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
+					       msecs_to_jiffies(500));
+
+	if (!rc) {
+		DRM_ERROR("dpu wait for reg update done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void dpu_stop(struct dpu_context *ctx)
+{
+	if (ctx->if_type == SPRD_DPU_IF_DPI)
+		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
+
+	dpu_wait_stop_done(ctx);
+}
+
+static void dpu_run(struct dpu_context *ctx)
+{
+	DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
+
+	ctx->stopped = false;
+}
+
+static void dpu_init(struct dpu_context *ctx)
+{
+	u32 reg_val, size;
+
+	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
+
+	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+
+	DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
+	DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
+
+	reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
+	DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
+	DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
+	DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
+
+	if (ctx->stopped)
+		dpu_clean_all(ctx);
+
+	DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
+	DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
+	DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
+	DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
+	DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
+	DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
+
+	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
+}
+
+static void dpu_fini(struct dpu_context *ctx)
+{
+	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
+	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
+}
+
+static void dpu_layer(struct dpu_context *ctx,
+		    struct dpu_layer *hwlayer)
+{
+	const struct drm_format_info *info;
+	u32 size, offset, ctrl, pitch;
+	int i;
+
+	offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
+
+	if (hwlayer->src_w && hwlayer->src_h)
+		size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
+	else
+		size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
+
+	for (i = 0; i < hwlayer->planes; i++)
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
+				hwlayer->index), hwlayer->addr[i]);
+
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
+			hwlayer->index), offset);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
+			hwlayer->index), size);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
+			hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
+			hwlayer->index), hwlayer->alpha);
+
+	info = drm_format_info(hwlayer->format);
+	if (hwlayer->planes == 3) {
+		/* UV pitch is 1/2 of Y pitch*/
+		pitch = (hwlayer->pitch[0] / info->cpp[0]) |
+				(hwlayer->pitch[0] / info->cpp[0] << 15);
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
+				hwlayer->index), pitch);
+	} else {
+		pitch = hwlayer->pitch[0] / info->cpp[0];
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
+				hwlayer->index), pitch);
+	}
+
+	ctrl = hwlayer->format |
+		hwlayer->blending |
+		(hwlayer->rotation & 0x7) << 20;
+
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
+			hwlayer->index), ctrl);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
+			hwlayer->index), BIT_DPU_LAY_EN);
+
+	DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
+				hwlayer->dst_x, hwlayer->dst_y,
+				hwlayer->dst_w, hwlayer->dst_h);
+	DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
+				hwlayer->src_x, hwlayer->src_y,
+				hwlayer->src_w, hwlayer->src_h);
+}
+
+static void dpu_flip(struct dpu_context *ctx,
+		     struct dpu_layer layers[], u8 count)
+{
+	int i;
+	u32 reg_val;
+
+	/*
+	 * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
+	 * registers in EDPI mode. So the config registers can only be
+	 * updated in the rising edge of DPU_RUN bit.
+	 */
+	if (ctx->if_type == SPRD_DPU_IF_EDPI)
+		dpu_wait_stop_done(ctx);
+
+	/* reset the bgcolor to black */
+	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
+
+	/* disable all the layers */
+	dpu_clean_all(ctx);
+
+	/* start configure dpu layers */
+	for (i = 0; i < count; i++)
+		dpu_layer(ctx, &layers[i]);
+
+	/* update trigger and wait */
+	if (ctx->if_type == SPRD_DPU_IF_DPI) {
+		if (!ctx->stopped) {
+			DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+			dpu_wait_update_done(ctx);
+		}
+
+		DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
+
+		ctx->stopped = false;
+	}
+
+	/*
+	 * If the following interrupt was disabled in isr,
+	 * re-enable it.
+	 */
+	reg_val = BIT_DPU_INT_FBC_PLD_ERR |
+		  BIT_DPU_INT_FBC_HDR_ERR |
+		  BIT_DPU_INT_MMU_VAOR_RD |
+		  BIT_DPU_INT_MMU_VAOR_WR |
+		  BIT_DPU_INT_MMU_INV_RD |
+		  BIT_DPU_INT_MMU_INV_WR;
+	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
+
+}
+
+static void dpu_dpi_init(struct dpu_context *ctx)
+{
+	u32 int_mask = 0;
+	u32 reg_val;
+
+	if (ctx->if_type == SPRD_DPU_IF_DPI) {
+		/* use dpi as interface */
+		DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
+
+		/* disable Halt function for SPRD DSI */
+		DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
+
+		/* select te from external pad */
+		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
+
+		/* set dpi timing */
+		reg_val = ctx->vm.hsync_len << 0 |
+			  ctx->vm.hback_porch << 8 |
+			  ctx->vm.hfront_porch << 20;
+		DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
+
+		reg_val = ctx->vm.vsync_len << 0 |
+			  ctx->vm.vback_porch << 8 |
+			  ctx->vm.vfront_porch << 20;
+		DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
+
+		if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+			DRM_WARN("Warning: (vsync + vbp) < 32, "
+				"underflow risk!\n");
+
+		/* enable dpu update done INT */
+		int_mask |= BIT_DPU_INT_UPDATE_DONE;
+		/* enable dpu DONE  INT */
+		int_mask |= BIT_DPU_INT_DONE;
+		/* enable dpu dpi vsync */
+		int_mask |= BIT_DPU_INT_VSYNC;
+		/* enable dpu TE INT */
+		int_mask |= BIT_DPU_INT_TE;
+		/* enable underflow err INT */
+		int_mask |= BIT_DPU_INT_ERR;
+	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+		/* use edpi as interface */
+		DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
+
+		/* use external te */
+		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
+
+		/* enable te */
+		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
+
+		/* enable stop DONE INT */
+		int_mask |= BIT_DPU_INT_DONE;
+		/* enable TE INT */
+		int_mask |= BIT_DPU_INT_TE;
+	}
+
+	/* enable ifbc payload error INT */
+	int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
+	/* enable ifbc header error INT */
+	int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
+	/* enable iommu va out of range read error INT */
+	int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
+	/* enable iommu va out of range write error INT */
+	int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
+	/* enable iommu invalid read error INT */
+	int_mask |= BIT_DPU_INT_MMU_INV_RD;
+	/* enable iommu invalid write error INT */
+	int_mask |= BIT_DPU_INT_MMU_INV_WR;
+
+	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
+}
+
+static void enable_vsync(struct dpu_context *ctx)
+{
+	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static void disable_vsync(struct dpu_context *ctx)
+{
+	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const u32 primary_fmts[] = {
+	DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21, DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
+	DRM_FORMAT_YVU420,
+};
+
+static void dpu_capability(struct dpu_context *ctx,
+			struct dpu_capability *cap)
+{
+	cap->max_layers = 6;
+	cap->fmts_ptr = primary_fmts;
+	cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
+}
+
+const struct dpu_core_ops dpu_r2p0_core_ops = {
+	.init = dpu_init,
+	.fini = dpu_fini,
+	.run = dpu_run,
+	.stop = dpu_stop,
+	.isr = dpu_isr,
+	.ifconfig = dpu_dpi_init,
+	.capability = dpu_capability,
+	.flip = dpu_flip,
+	.enable_vsync = enable_vsync,
+	.disable_vsync = disable_vsync,
+};
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
new file mode 100644
index 0000000..5ec8e7c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dpu.c
@@ -0,0 +1,646 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+struct sprd_plane {
+	struct drm_plane plane;
+	u32 index;
+	u32 addr[4];
+	u32 pitch[4];
+	u32 format;
+	u32 rotation;
+	u32 blend_mode;
+};
+
+static void sprd_dpu_init(struct sprd_dpu *dpu);
+static void sprd_dpu_fini(struct sprd_dpu *dpu);
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct sprd_plane, plane);
+}
+
+static int sprd_plane_format_convert(u32 fourcc, u32 *format)
+{
+	switch (fourcc) {
+	case DRM_FORMAT_BGRA8888:
+		/* BGRA8888 -> ARGB8888 */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+		/* RGBA8888 -> ABGR8888 */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* FALLTHRU */
+	case DRM_FORMAT_ABGR8888:
+		/* RB switch */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* FALLTHRU */
+	case DRM_FORMAT_ARGB8888:
+		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_XBGR8888:
+		/* RB switch */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* FALLTHRU */
+	case DRM_FORMAT_XRGB8888:
+		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_BGR565:
+		/* RB switch */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* FALLTHRU */
+	case DRM_FORMAT_RGB565:
+		*format |= BIT_DPU_LAY_FORMAT_RGB565;
+		break;
+	case DRM_FORMAT_NV12:
+		/* 2-Lane: Yuv420 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_NV21:
+		/* 2-Lane: Yuv420 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV16:
+		/* 2-Lane: Yuv422 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV61:
+		/* 2-Lane: Yuv422 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YUV420:
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YVU420:
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
+{
+	switch (angle) {
+	case DRM_MODE_ROTATE_0:
+		*rotation = DPU_LAYER_ROTATION_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		*rotation = DPU_LAYER_ROTATION_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		*rotation = DPU_LAYER_ROTATION_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		*rotation = DPU_LAYER_ROTATION_270;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		*rotation = DPU_LAYER_ROTATION_180_M;
+		break;
+	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+		*rotation = DPU_LAYER_ROTATION_90_M;
+		break;
+	case DRM_MODE_REFLECT_X:
+		*rotation = DPU_LAYER_ROTATION_0_M;
+		break;
+	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+		*rotation = DPU_LAYER_ROTATION_270_M;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct sprd_plane *p = to_sprd_plane(plane);
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *cma_obj;
+	int i, ret;
+	u32 addr;
+
+	if (!state->fb || !state->crtc)
+		return 0;
+
+	ret = sprd_plane_format_convert(fb->format->format,
+					&p->format);
+	if (ret < 0) {
+		DRM_ERROR("Invalid plane format\n");
+		return ret;
+	}
+
+	ret = sprd_plane_rotation_convert(state->rotation,
+					&p->rotation);
+	if (ret < 0) {
+		DRM_ERROR("Invalid plane rotation\n");
+		return ret;
+	}
+
+	switch (state->pixel_blend_mode) {
+	case DRM_MODE_BLEND_COVERAGE:
+		/* alpha mode select - combo alpha */
+		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Normal mode */
+		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+		break;
+	case DRM_MODE_BLEND_PREMULTI:
+		/* alpha mode select - combo alpha */
+		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Pre-mult mode */
+		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+		break;
+	case DRM_MODE_BLEND_PIXEL_NONE:
+	default:
+		/* don't do blending, maybe RGBX */
+		/* alpha mode select - layer alpha */
+		p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
+		break;
+	}
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+		addr = cma_obj->paddr + fb->offsets[i];
+		if (addr % 16) {
+			DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
+				i, addr);
+			return -EFAULT;
+		}
+
+		p->addr[i] = addr;
+		p->pitch[i] = fb->pitches[i];
+	}
+
+	return 0;
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = plane->state->fb;
+	struct sprd_plane *p = to_sprd_plane(plane);
+	struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
+	struct dpu_layer *layer = &dpu->layers[p->index];
+	int i;
+
+	if (!state->crtc || !state->fb)
+		return;
+
+	layer->index = p->index;
+	layer->src_x = state->src_x >> 16;
+	layer->src_y = state->src_y >> 16;
+	layer->src_w = state->src_w >> 16;
+	layer->src_h = state->src_h >> 16;
+	layer->dst_x = state->crtc_x;
+	layer->dst_y = state->crtc_y;
+	layer->dst_w = state->crtc_w;
+	layer->dst_h = state->crtc_h;
+	layer->alpha = state->alpha;
+	layer->format = p->format;
+	layer->blending = p->blend_mode;
+	layer->rotation = p->rotation;
+	layer->planes = fb->format->num_planes;
+
+	for (i = 0; i < layer->planes; i++) {
+		layer->addr[i] = p->addr[i];
+		layer->pitch[i] = p->pitch[i];
+	}
+
+	dpu->pending_planes++;
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *p, int index)
+{
+	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				       BIT(DRM_MODE_BLEND_PREMULTI) |
+				       BIT(DRM_MODE_BLEND_COVERAGE);
+
+	/* create rotation property */
+	drm_plane_create_rotation_property(&p->plane,
+					   DRM_MODE_ROTATE_0,
+					   DRM_MODE_ROTATE_MASK |
+					   DRM_MODE_REFLECT_MASK);
+
+	/* create alpha property */
+	drm_plane_create_alpha_property(&p->plane);
+
+	/* create blend mode property */
+	drm_plane_create_blend_mode_property(&p->plane, supported_modes);
+
+	/* create zpos property */
+	drm_plane_create_zpos_immutable_property(&p->plane, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+	.atomic_check = sprd_plane_atomic_check,
+	.atomic_update = sprd_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs sprd_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct drm_plane *sprd_plane_init(struct drm_device *drm,
+					struct sprd_dpu *dpu)
+{
+	struct drm_plane *primary = NULL;
+	struct sprd_plane *p = NULL;
+	struct dpu_capability cap = {};
+	int ret, i;
+
+	dpu->core->capability(&dpu->ctx, &cap);
+
+	dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
+				  sizeof(struct dpu_layer), GFP_KERNEL);
+	if (!dpu->layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < cap.max_layers; i++) {
+
+		p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
+		if (!p)
+			return ERR_PTR(-ENOMEM);
+
+		ret = drm_universal_plane_init(drm, &p->plane, 1,
+					       &sprd_plane_funcs, cap.fmts_ptr,
+					       cap.fmts_cnt, NULL,
+					       DRM_PLANE_TYPE_PRIMARY, NULL);
+		if (ret) {
+			DRM_ERROR("fail to init primary plane\n");
+			return ERR_PTR(ret);
+		}
+
+		drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
+
+		sprd_plane_create_properties(p, i);
+
+		p->index = i;
+		if (i == 0)
+			primary = &p->plane;
+	}
+
+	return primary;
+}
+
+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
+
+	if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+		drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
+
+		if ((mode->hdisplay == mode->htotal) ||
+		    (mode->vdisplay == mode->vtotal))
+			dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
+		else
+			dpu->ctx.if_type = SPRD_DPU_IF_DPI;
+	}
+
+	return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+				   struct drm_crtc_state *old_state)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	sprd_dpu_init(dpu);
+
+	enable_irq(dpu->ctx.irq);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+				    struct drm_crtc_state *old_state)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+	struct drm_device *drm = dpu->crtc.dev;
+
+	disable_irq(dpu->ctx.irq);
+
+	sprd_dpu_fini(dpu);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_crtc_state *state)
+{
+	DRM_DEBUG("%s()\n", __func__);
+
+	return 0;
+}
+
+static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
+
+	dpu->pending_planes = 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+	struct drm_device *drm = dpu->crtc.dev;
+
+	if (dpu->pending_planes)
+		dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	dpu->core->enable_vsync(&dpu->ctx);
+
+	return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	dpu->core->disable_vsync(&dpu->ctx);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+	.mode_valid	= sprd_crtc_mode_valid,
+	.atomic_check	= sprd_crtc_atomic_check,
+	.atomic_begin	= sprd_crtc_atomic_begin,
+	.atomic_flush	= sprd_crtc_atomic_flush,
+	.atomic_enable	= sprd_crtc_atomic_enable,
+	.atomic_disable	= sprd_crtc_atomic_disable,
+};
+
+static const struct drm_crtc_funcs sprd_crtc_funcs = {
+	.destroy	= drm_crtc_cleanup,
+	.set_config	= drm_atomic_helper_set_config,
+	.page_flip	= drm_atomic_helper_page_flip,
+	.reset		= drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
+	.enable_vblank	= sprd_crtc_enable_vblank,
+	.disable_vblank	= sprd_crtc_disable_vblank,
+};
+
+static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
+			 struct drm_plane *primary)
+{
+	struct device_node *port;
+	int ret;
+
+	/*
+	 * set crtc port so that drm_of_find_possible_crtcs call works
+	 */
+	port = of_parse_phandle(drm->dev->of_node, "ports", 0);
+	if (!port) {
+		DRM_ERROR("find 'ports' phandle of %s failed\n",
+			  drm->dev->of_node->full_name);
+		return -EINVAL;
+	}
+	of_node_put(port);
+	crtc->port = port;
+
+	ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
+					&sprd_crtc_funcs, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
+	drm_mode_crtc_set_gamma_size(crtc, 256);
+
+	drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
+
+	return 0;
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	dpu->core->init(ctx);
+	dpu->core->ifconfig(ctx);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	dpu->core->fini(ctx);
+}
+
+static irqreturn_t sprd_dpu_isr(int irq, void *data)
+{
+	struct sprd_dpu *dpu = data;
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 int_mask = 0;
+
+	int_mask = dpu->core->isr(ctx);
+
+	if (int_mask & BIT_DPU_INT_ERR)
+		DRM_WARN("Warning: dpu underflow!\n");
+
+	if (int_mask & BIT_DPU_INT_VSYNC)
+		drm_crtc_handle_vblank(&dpu->crtc);
+
+	return IRQ_HANDLED;
+}
+
+static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct sprd_dpu *dpu = dev_get_drvdata(dev);
+	struct drm_plane *plane;
+	int ret;
+
+	plane = sprd_plane_init(drm, dpu);
+	if (IS_ERR_OR_NULL(plane)) {
+		ret = PTR_ERR(plane);
+		return ret;
+	}
+
+	ret = sprd_crtc_init(drm, &dpu->crtc, plane);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void sprd_dpu_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct sprd_dpu *dpu = dev_get_drvdata(dev);
+
+	drm_crtc_cleanup(&dpu->crtc);
+}
+
+static const struct component_ops dpu_component_ops = {
+	.bind = sprd_dpu_bind,
+	.unbind = sprd_dpu_unbind,
+};
+
+static int sprd_dpu_context_init(struct sprd_dpu *dpu,
+				struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dpu_context *ctx = &dpu->ctx;
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!ctx->base) {
+		DRM_ERROR("failed to map dpu registers\n");
+		return -EFAULT;
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		DRM_ERROR("failed to get dpu irq\n");
+		return ctx->irq;
+	}
+
+	irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
+					0, "DPU", dpu);
+	if (ret) {
+		DRM_ERROR("failed to register dpu irq handler\n");
+		return ret;
+	}
+
+	init_waitqueue_head(&ctx->wait_queue);
+
+	return 0;
+}
+
+static const struct sprd_dpu_ops sharkl3_dpu = {
+	.core = &dpu_r2p0_core_ops,
+};
+
+static const struct of_device_id dpu_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dpu",
+	  .data = &sharkl3_dpu },
+	{ /* sentinel */ },
+};
+
+static int sprd_dpu_probe(struct platform_device *pdev)
+{
+	const struct sprd_dpu_ops *pdata;
+	struct sprd_dpu *dpu;
+	int ret;
+
+	dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
+	if (!dpu)
+		return -ENOMEM;
+
+	pdata = of_device_get_match_data(&pdev->dev);
+	if (pdata) {
+		dpu->core = pdata->core;
+	} else {
+		DRM_ERROR("No matching driver data found\n");
+		return -EINVAL;
+	}
+
+	ret = sprd_dpu_context_init(dpu, &pdev->dev);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, dpu);
+
+	return component_add(&pdev->dev, &dpu_component_ops);
+}
+
+static int sprd_dpu_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dpu_component_ops);
+	return 0;
+}
+
+struct platform_driver sprd_dpu_driver = {
+	.probe = sprd_dpu_probe,
+	.remove = sprd_dpu_remove,
+	.driver = {
+		.name = "sprd-dpu-drv",
+		.of_match_table = dpu_match_table,
+	},
+};
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
new file mode 100644
index 0000000..7d3c5e4
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dpu.h
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef __SPRD_DPU_H__
+#define __SPRD_DPU_H__
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <uapi/drm/drm_mode.h>
+
+#define BIT_DPU_INT_DONE_		BIT(0)
+#define BIT_DPU_INT_TE			BIT(1)
+#define BIT_DPU_INT_ERR			BIT(2)
+#define BIT_DPU_INT_EDPI_TE		BIT(3)
+#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
+#define BIT_DPU_INT_VSYNC		BIT(5)
+#define BIT_DPU_INT_WB_DONE		BIT(6)
+#define BIT_DPU_INT_WB_ERR		BIT(7)
+
+#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
+
+enum {
+	SPRD_DPU_IF_DBI = 0,
+	SPRD_DPU_IF_DPI,
+	SPRD_DPU_IF_EDPI,
+	SPRD_DPU_IF_LIMIT
+};
+
+enum {
+	DPU_LAYER_ROTATION_0,
+	DPU_LAYER_ROTATION_90,
+	DPU_LAYER_ROTATION_180,
+	DPU_LAYER_ROTATION_270,
+	DPU_LAYER_ROTATION_0_M,
+	DPU_LAYER_ROTATION_90_M,
+	DPU_LAYER_ROTATION_180_M,
+	DPU_LAYER_ROTATION_270_M,
+};
+
+struct dpu_layer {
+	u8 index;
+	u8 planes;
+	u32 addr[4];
+	u32 pitch[4];
+	s16 src_x;
+	s16 src_y;
+	s16 src_w;
+	s16 src_h;
+	s16 dst_x;
+	s16 dst_y;
+	u16 dst_w;
+	u16 dst_h;
+	u32 format;
+	u32 alpha;
+	u32 blending;
+	u32 rotation;
+};
+
+/**
+ * Sprd DPU capability structure
+ *
+ * @max_layers: maximum number of layers available
+ * @fmts_ptr: A pointer to array of supported pixel formats
+ * @fmts_cnt: the number of format on @fmts_ptr
+ */
+struct dpu_capability {
+	u32 max_layers;
+	const u32 *fmts_ptr;
+	u32 fmts_cnt;
+};
+
+/**
+ * Sprd DPU core callback ops
+ *
+ * This structure decribes the display controller common
+ * callback ops
+ *
+ * @init: initial DPU core
+ * @fini: cleanup DPU core
+ * @run: enable DPU output
+ * @stop: disable DPU output
+ * @enable_vsync: enable vblank interrupt
+ * @disable_vsync: disable vblank interrupt
+ * @isr: function pointer to the isr
+ * @ifconfig: initial DPI interface
+ * @flip: commit CRTC planes to DPU
+ * @capability: callback for DPU capabilities
+ */
+struct dpu_context;
+struct dpu_core_ops {
+	void (*init)(struct dpu_context *ctx);
+	void (*fini)(struct dpu_context *ctx);
+	void (*run)(struct dpu_context *ctx);
+	void (*stop)(struct dpu_context *ctx);
+	void (*enable_vsync)(struct dpu_context *ctx);
+	void (*disable_vsync)(struct dpu_context *ctx);
+	u32 (*isr)(struct dpu_context *ctx);
+	void (*ifconfig)(struct dpu_context *ctx);
+	void (*flip)(struct dpu_context *ctx,
+		     struct dpu_layer layers[], u8 count);
+	void (*capability)(struct dpu_context *ctx,
+			struct dpu_capability *cap);
+};
+
+/**
+ * Sprd DPU context structure
+ *
+ * @base: DPU controller base address
+ * @irq: IRQ number to install the handler for
+ * @if_type: The type of DPI interface, default is DPI mode.
+ * @vm: videomode structure to use for DPU and DPI initialization
+ * @stopped: indicates whether DPU are stopped
+ * @wait_queue: wait queue, used to wait for DPU shadow register update done and
+ * DPU stop register done interrupt signal.
+ * @evt_update: wait queue condition for DPU shadow register
+ * @evt_stop: wait queue condition for DPU stop register
+ */
+struct dpu_context {
+	void __iomem *base;
+	int irq;
+	u8 if_type;
+	struct videomode vm;
+	bool stopped;
+	wait_queue_head_t wait_queue;
+	bool evt_update;
+	bool evt_stop;
+};
+
+/**
+ * Sprd DPU device structure
+ *
+ * @crtc: DRM crtc
+ * @ctx: A pointer to the DPU's implementation specific context
+ * @core: pointer to callbacks for DPU core functionality
+ * @layers: active DPU layers ready to commit
+ * @pending_planes: the number of layers on @layers
+ */
+struct sprd_dpu {
+	struct drm_crtc crtc;
+	struct dpu_context ctx;
+	const struct dpu_core_ops *core;
+	struct dpu_layer *layers;
+	u8 pending_planes;
+};
+
+/**
+ * Sprd DPU H/W callback ops match table structure
+ * The structure used for matching a specific device callback ops
+ *
+ * @core: pointer to callbacks for DPU core functionality
+ */
+struct sprd_dpu_ops {
+	const struct dpu_core_ops *core;
+};
+
+static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
+{
+	return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
+}
+
+extern const struct dpu_core_ops dpu_r2p0_core_ops;
+
+#endif
diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
index 4706185..200020f 100644
--- a/drivers/gpu/drm/sprd/sprd_drm.c
+++ b/drivers/gpu/drm/sprd/sprd_drm.c
@@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
 
 static struct platform_driver *sprd_drm_drivers[]  = {
 	&sprd_drm_driver,
+	&sprd_dpu_driver,
 };
 
 static int __init sprd_drm_init(void)
diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
index edf0881..3c32f3a 100644
--- a/drivers/gpu/drm/sprd/sprd_drm.h
+++ b/drivers/gpu/drm/sprd/sprd_drm.h
@@ -13,4 +13,6 @@ struct sprd_drm {
 	struct drm_device *drm;
 };
 
+extern struct platform_driver sprd_dpu_driver;
+
 #endif /* _SPRD_DRM_H_ */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
@ 2020-07-28 10:07   ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.

RFC v6:
  - Access registers via readl/writel
  - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
  - Remove always true checks for dpu core ops

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 drivers/gpu/drm/sprd/Makefile       |   5 +-
 drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
 drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
 drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
 7 files changed, 1346 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h

diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
index 86d95d9..88ab32a 100644
--- a/drivers/gpu/drm/sprd/Makefile
+++ b/drivers/gpu/drm/sprd/Makefile
@@ -2,4 +2,7 @@
 
 subdir-ccflags-y += -I$(srctree)/$(src)
 
-obj-y := sprd_drm.o
+obj-y := sprd_drm.o \
+	sprd_dpu.o
+
+obj-y += dpu/
diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
new file mode 100644
index 0000000..40278b6
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dpu/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
new file mode 100644
index 0000000..4b9521d
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include "sprd_dpu.h"
+
+/* DPU registers size, 4 Bytes(32 Bits) */
+#define DPU_REG_SIZE	0x04
+
+/* Layer registers offset */
+#define DPU_LAY_REG_OFFSET	0x0C
+
+#define DPU_LAY_REG(reg, index) \
+	(reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
+
+#define DPU_REG_RD(reg) readl_relaxed(reg)
+
+#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
+
+#define DPU_REG_SET(reg, mask) \
+	writel_relaxed(readl_relaxed(reg) | mask, reg)
+
+#define DPU_REG_CLR(reg, mask) \
+	writel_relaxed(readl_relaxed(reg) & ~mask, reg)
+
+/* Global control registers */
+#define REG_DPU_CTRL	0x04
+#define REG_DPU_CFG0	0x08
+#define REG_DPU_CFG1	0x0C
+#define REG_DPU_CFG2	0x10
+#define REG_PANEL_SIZE	0x20
+#define REG_BLEND_SIZE	0x24
+#define REG_BG_COLOR	0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0	0x30
+#define REG_LAY_BASE_ADDR1	0x34
+#define REG_LAY_BASE_ADDR2	0x38
+#define REG_LAY_CTRL		0x40
+#define REG_LAY_SIZE		0x44
+#define REG_LAY_PITCH		0x48
+#define REG_LAY_POS		0x4C
+#define REG_LAY_ALPHA		0x50
+#define REG_LAY_PALLETE		0x58
+#define REG_LAY_CROP_START	0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN		0x1E0
+#define REG_DPU_INT_CLR		0x1E4
+#define REG_DPU_INT_STS		0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL		0x1F0
+#define REG_DPI_H_TIMING	0x1F4
+#define REG_DPI_V_TIMING	0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN			0x800
+#define REG_MMU_VPN_RANGE		0x80C
+#define REG_MMU_VAOR_ADDR_RD		0x818
+#define REG_MMU_VAOR_ADDR_WR		0x81C
+#define REG_MMU_INV_ADDR_RD		0x820
+#define REG_MMU_INV_ADDR_WR		0x824
+#define REG_MMU_PPN1			0x83C
+#define REG_MMU_RANGE1			0x840
+#define REG_MMU_PPN2			0x844
+#define REG_MMU_RANGE2			0x848
+
+/* Global control bits */
+#define BIT_DPU_RUN			BIT(0)
+#define BIT_DPU_STOP			BIT(1)
+#define BIT_DPU_REG_UPDATE		BIT(2)
+#define BIT_DPU_IF_EDPI			BIT(0)
+#define BIT_DPU_COEF_NARROW_RANGE		BIT(4)
+#define BIT_DPU_Y2R_COEF_ITU709_STANDARD	BIT(5)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN				BIT(0)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE		BIT(0)
+#define BIT_DPU_INT_TE			BIT(1)
+#define BIT_DPU_INT_ERR			BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
+#define BIT_DPU_INT_VSYNC		BIT(5)
+#define BIT_DPU_INT_FBC_PLD_ERR		BIT(8)
+#define BIT_DPU_INT_FBC_HDR_ERR		BIT(9)
+#define BIT_DPU_INT_MMU_VAOR_RD		BIT(16)
+#define BIT_DPU_INT_MMU_VAOR_WR		BIT(17)
+#define BIT_DPU_INT_MMU_INV_RD		BIT(18)
+#define BIT_DPU_INT_MMU_INV_WR		BIT(19)
+
+/* DPI control bits */
+#define BIT_DPU_EDPI_TE_EN		BIT(8)
+#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD	BIT(10)
+#define BIT_DPU_DPI_HALT_EN		BIT(16)
+
+
+static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
+{
+	u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
+			BIT_DPU_INT_MMU_VAOR_WR |
+			BIT_DPU_INT_MMU_INV_RD |
+			BIT_DPU_INT_MMU_INV_WR;
+	u32 val = reg_val & mmu_mask;
+	int i;
+
+	if (val) {
+		DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
+
+		if (val & BIT_DPU_INT_MMU_INV_RD)
+			DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_INV_WR)
+			DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
+		if (val & BIT_DPU_INT_MMU_VAOR_RD)
+			DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_VAOR_WR)
+			DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
+				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
+
+		for (i = 0; i < 8; i++) {
+			reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
+			if (reg_val & 0x1)
+				DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
+					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
+		}
+	}
+
+	return val;
+}
+
+static void dpu_clean_all(struct dpu_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
+}
+
+static u32 dpu_isr(struct dpu_context *ctx)
+{
+	u32 reg_val, int_mask = 0;
+
+	reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
+
+	/* disable err interrupt */
+	if (reg_val & BIT_DPU_INT_ERR)
+		int_mask |= BIT_DPU_INT_ERR;
+
+	/* dpu update done isr */
+	if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
+		ctx->evt_update = true;
+		wake_up_interruptible_all(&ctx->wait_queue);
+	}
+
+	/* dpu stop done isr */
+	if (reg_val & BIT_DPU_INT_DONE) {
+		ctx->evt_stop = true;
+		wake_up_interruptible_all(&ctx->wait_queue);
+	}
+
+	/* dpu ifbc payload error isr */
+	if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
+		int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
+		DRM_ERROR("dpu ifbc payload error\n");
+	}
+
+	/* dpu ifbc header error isr */
+	if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
+		int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
+		DRM_ERROR("dpu ifbc header error\n");
+	}
+
+	int_mask |= check_mmu_isr(ctx, reg_val);
+
+	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
+	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
+
+	return reg_val;
+}
+
+static int dpu_wait_stop_done(struct dpu_context *ctx)
+{
+	int rc;
+
+	if (ctx->stopped)
+		return 0;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
+					       msecs_to_jiffies(500));
+	ctx->evt_stop = false;
+
+	ctx->stopped = true;
+
+	if (!rc) {
+		DRM_ERROR("dpu wait for stop done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int dpu_wait_update_done(struct dpu_context *ctx)
+{
+	int rc;
+
+	ctx->evt_update = false;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
+					       msecs_to_jiffies(500));
+
+	if (!rc) {
+		DRM_ERROR("dpu wait for reg update done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void dpu_stop(struct dpu_context *ctx)
+{
+	if (ctx->if_type == SPRD_DPU_IF_DPI)
+		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
+
+	dpu_wait_stop_done(ctx);
+}
+
+static void dpu_run(struct dpu_context *ctx)
+{
+	DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
+
+	ctx->stopped = false;
+}
+
+static void dpu_init(struct dpu_context *ctx)
+{
+	u32 reg_val, size;
+
+	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
+
+	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+
+	DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
+	DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
+
+	reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
+	DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
+	DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
+	DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
+
+	if (ctx->stopped)
+		dpu_clean_all(ctx);
+
+	DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
+	DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
+	DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
+	DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
+	DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
+	DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
+
+	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
+}
+
+static void dpu_fini(struct dpu_context *ctx)
+{
+	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
+	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
+}
+
+static void dpu_layer(struct dpu_context *ctx,
+		    struct dpu_layer *hwlayer)
+{
+	const struct drm_format_info *info;
+	u32 size, offset, ctrl, pitch;
+	int i;
+
+	offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
+
+	if (hwlayer->src_w && hwlayer->src_h)
+		size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
+	else
+		size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
+
+	for (i = 0; i < hwlayer->planes; i++)
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
+				hwlayer->index), hwlayer->addr[i]);
+
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
+			hwlayer->index), offset);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
+			hwlayer->index), size);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
+			hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
+			hwlayer->index), hwlayer->alpha);
+
+	info = drm_format_info(hwlayer->format);
+	if (hwlayer->planes == 3) {
+		/* UV pitch is 1/2 of Y pitch*/
+		pitch = (hwlayer->pitch[0] / info->cpp[0]) |
+				(hwlayer->pitch[0] / info->cpp[0] << 15);
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
+				hwlayer->index), pitch);
+	} else {
+		pitch = hwlayer->pitch[0] / info->cpp[0];
+		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
+				hwlayer->index), pitch);
+	}
+
+	ctrl = hwlayer->format |
+		hwlayer->blending |
+		(hwlayer->rotation & 0x7) << 20;
+
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
+			hwlayer->index), ctrl);
+	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
+			hwlayer->index), BIT_DPU_LAY_EN);
+
+	DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
+				hwlayer->dst_x, hwlayer->dst_y,
+				hwlayer->dst_w, hwlayer->dst_h);
+	DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
+				hwlayer->src_x, hwlayer->src_y,
+				hwlayer->src_w, hwlayer->src_h);
+}
+
+static void dpu_flip(struct dpu_context *ctx,
+		     struct dpu_layer layers[], u8 count)
+{
+	int i;
+	u32 reg_val;
+
+	/*
+	 * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
+	 * registers in EDPI mode. So the config registers can only be
+	 * updated in the rising edge of DPU_RUN bit.
+	 */
+	if (ctx->if_type == SPRD_DPU_IF_EDPI)
+		dpu_wait_stop_done(ctx);
+
+	/* reset the bgcolor to black */
+	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
+
+	/* disable all the layers */
+	dpu_clean_all(ctx);
+
+	/* start configure dpu layers */
+	for (i = 0; i < count; i++)
+		dpu_layer(ctx, &layers[i]);
+
+	/* update trigger and wait */
+	if (ctx->if_type == SPRD_DPU_IF_DPI) {
+		if (!ctx->stopped) {
+			DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+			dpu_wait_update_done(ctx);
+		}
+
+		DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
+
+		ctx->stopped = false;
+	}
+
+	/*
+	 * If the following interrupt was disabled in isr,
+	 * re-enable it.
+	 */
+	reg_val = BIT_DPU_INT_FBC_PLD_ERR |
+		  BIT_DPU_INT_FBC_HDR_ERR |
+		  BIT_DPU_INT_MMU_VAOR_RD |
+		  BIT_DPU_INT_MMU_VAOR_WR |
+		  BIT_DPU_INT_MMU_INV_RD |
+		  BIT_DPU_INT_MMU_INV_WR;
+	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
+
+}
+
+static void dpu_dpi_init(struct dpu_context *ctx)
+{
+	u32 int_mask = 0;
+	u32 reg_val;
+
+	if (ctx->if_type == SPRD_DPU_IF_DPI) {
+		/* use dpi as interface */
+		DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
+
+		/* disable Halt function for SPRD DSI */
+		DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
+
+		/* select te from external pad */
+		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
+
+		/* set dpi timing */
+		reg_val = ctx->vm.hsync_len << 0 |
+			  ctx->vm.hback_porch << 8 |
+			  ctx->vm.hfront_porch << 20;
+		DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
+
+		reg_val = ctx->vm.vsync_len << 0 |
+			  ctx->vm.vback_porch << 8 |
+			  ctx->vm.vfront_porch << 20;
+		DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
+
+		if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+			DRM_WARN("Warning: (vsync + vbp) < 32, "
+				"underflow risk!\n");
+
+		/* enable dpu update done INT */
+		int_mask |= BIT_DPU_INT_UPDATE_DONE;
+		/* enable dpu DONE  INT */
+		int_mask |= BIT_DPU_INT_DONE;
+		/* enable dpu dpi vsync */
+		int_mask |= BIT_DPU_INT_VSYNC;
+		/* enable dpu TE INT */
+		int_mask |= BIT_DPU_INT_TE;
+		/* enable underflow err INT */
+		int_mask |= BIT_DPU_INT_ERR;
+	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+		/* use edpi as interface */
+		DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
+
+		/* use external te */
+		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
+
+		/* enable te */
+		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
+
+		/* enable stop DONE INT */
+		int_mask |= BIT_DPU_INT_DONE;
+		/* enable TE INT */
+		int_mask |= BIT_DPU_INT_TE;
+	}
+
+	/* enable ifbc payload error INT */
+	int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
+	/* enable ifbc header error INT */
+	int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
+	/* enable iommu va out of range read error INT */
+	int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
+	/* enable iommu va out of range write error INT */
+	int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
+	/* enable iommu invalid read error INT */
+	int_mask |= BIT_DPU_INT_MMU_INV_RD;
+	/* enable iommu invalid write error INT */
+	int_mask |= BIT_DPU_INT_MMU_INV_WR;
+
+	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
+}
+
+static void enable_vsync(struct dpu_context *ctx)
+{
+	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static void disable_vsync(struct dpu_context *ctx)
+{
+	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const u32 primary_fmts[] = {
+	DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21, DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
+	DRM_FORMAT_YVU420,
+};
+
+static void dpu_capability(struct dpu_context *ctx,
+			struct dpu_capability *cap)
+{
+	cap->max_layers = 6;
+	cap->fmts_ptr = primary_fmts;
+	cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
+}
+
+const struct dpu_core_ops dpu_r2p0_core_ops = {
+	.init = dpu_init,
+	.fini = dpu_fini,
+	.run = dpu_run,
+	.stop = dpu_stop,
+	.isr = dpu_isr,
+	.ifconfig = dpu_dpi_init,
+	.capability = dpu_capability,
+	.flip = dpu_flip,
+	.enable_vsync = enable_vsync,
+	.disable_vsync = disable_vsync,
+};
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
new file mode 100644
index 0000000..5ec8e7c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dpu.c
@@ -0,0 +1,646 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+struct sprd_plane {
+	struct drm_plane plane;
+	u32 index;
+	u32 addr[4];
+	u32 pitch[4];
+	u32 format;
+	u32 rotation;
+	u32 blend_mode;
+};
+
+static void sprd_dpu_init(struct sprd_dpu *dpu);
+static void sprd_dpu_fini(struct sprd_dpu *dpu);
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct sprd_plane, plane);
+}
+
+static int sprd_plane_format_convert(u32 fourcc, u32 *format)
+{
+	switch (fourcc) {
+	case DRM_FORMAT_BGRA8888:
+		/* BGRA8888 -> ARGB8888 */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+		/* RGBA8888 -> ABGR8888 */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* FALLTHRU */
+	case DRM_FORMAT_ABGR8888:
+		/* RB switch */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* FALLTHRU */
+	case DRM_FORMAT_ARGB8888:
+		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_XBGR8888:
+		/* RB switch */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* FALLTHRU */
+	case DRM_FORMAT_XRGB8888:
+		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_BGR565:
+		/* RB switch */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* FALLTHRU */
+	case DRM_FORMAT_RGB565:
+		*format |= BIT_DPU_LAY_FORMAT_RGB565;
+		break;
+	case DRM_FORMAT_NV12:
+		/* 2-Lane: Yuv420 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_NV21:
+		/* 2-Lane: Yuv420 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV16:
+		/* 2-Lane: Yuv422 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV61:
+		/* 2-Lane: Yuv422 */
+		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YUV420:
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YVU420:
+		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
+{
+	switch (angle) {
+	case DRM_MODE_ROTATE_0:
+		*rotation = DPU_LAYER_ROTATION_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		*rotation = DPU_LAYER_ROTATION_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		*rotation = DPU_LAYER_ROTATION_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		*rotation = DPU_LAYER_ROTATION_270;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		*rotation = DPU_LAYER_ROTATION_180_M;
+		break;
+	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+		*rotation = DPU_LAYER_ROTATION_90_M;
+		break;
+	case DRM_MODE_REFLECT_X:
+		*rotation = DPU_LAYER_ROTATION_0_M;
+		break;
+	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+		*rotation = DPU_LAYER_ROTATION_270_M;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct sprd_plane *p = to_sprd_plane(plane);
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *cma_obj;
+	int i, ret;
+	u32 addr;
+
+	if (!state->fb || !state->crtc)
+		return 0;
+
+	ret = sprd_plane_format_convert(fb->format->format,
+					&p->format);
+	if (ret < 0) {
+		DRM_ERROR("Invalid plane format\n");
+		return ret;
+	}
+
+	ret = sprd_plane_rotation_convert(state->rotation,
+					&p->rotation);
+	if (ret < 0) {
+		DRM_ERROR("Invalid plane rotation\n");
+		return ret;
+	}
+
+	switch (state->pixel_blend_mode) {
+	case DRM_MODE_BLEND_COVERAGE:
+		/* alpha mode select - combo alpha */
+		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Normal mode */
+		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+		break;
+	case DRM_MODE_BLEND_PREMULTI:
+		/* alpha mode select - combo alpha */
+		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Pre-mult mode */
+		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+		break;
+	case DRM_MODE_BLEND_PIXEL_NONE:
+	default:
+		/* don't do blending, maybe RGBX */
+		/* alpha mode select - layer alpha */
+		p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
+		break;
+	}
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+		addr = cma_obj->paddr + fb->offsets[i];
+		if (addr % 16) {
+			DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
+				i, addr);
+			return -EFAULT;
+		}
+
+		p->addr[i] = addr;
+		p->pitch[i] = fb->pitches[i];
+	}
+
+	return 0;
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = plane->state->fb;
+	struct sprd_plane *p = to_sprd_plane(plane);
+	struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
+	struct dpu_layer *layer = &dpu->layers[p->index];
+	int i;
+
+	if (!state->crtc || !state->fb)
+		return;
+
+	layer->index = p->index;
+	layer->src_x = state->src_x >> 16;
+	layer->src_y = state->src_y >> 16;
+	layer->src_w = state->src_w >> 16;
+	layer->src_h = state->src_h >> 16;
+	layer->dst_x = state->crtc_x;
+	layer->dst_y = state->crtc_y;
+	layer->dst_w = state->crtc_w;
+	layer->dst_h = state->crtc_h;
+	layer->alpha = state->alpha;
+	layer->format = p->format;
+	layer->blending = p->blend_mode;
+	layer->rotation = p->rotation;
+	layer->planes = fb->format->num_planes;
+
+	for (i = 0; i < layer->planes; i++) {
+		layer->addr[i] = p->addr[i];
+		layer->pitch[i] = p->pitch[i];
+	}
+
+	dpu->pending_planes++;
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *p, int index)
+{
+	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				       BIT(DRM_MODE_BLEND_PREMULTI) |
+				       BIT(DRM_MODE_BLEND_COVERAGE);
+
+	/* create rotation property */
+	drm_plane_create_rotation_property(&p->plane,
+					   DRM_MODE_ROTATE_0,
+					   DRM_MODE_ROTATE_MASK |
+					   DRM_MODE_REFLECT_MASK);
+
+	/* create alpha property */
+	drm_plane_create_alpha_property(&p->plane);
+
+	/* create blend mode property */
+	drm_plane_create_blend_mode_property(&p->plane, supported_modes);
+
+	/* create zpos property */
+	drm_plane_create_zpos_immutable_property(&p->plane, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+	.atomic_check = sprd_plane_atomic_check,
+	.atomic_update = sprd_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs sprd_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct drm_plane *sprd_plane_init(struct drm_device *drm,
+					struct sprd_dpu *dpu)
+{
+	struct drm_plane *primary = NULL;
+	struct sprd_plane *p = NULL;
+	struct dpu_capability cap = {};
+	int ret, i;
+
+	dpu->core->capability(&dpu->ctx, &cap);
+
+	dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
+				  sizeof(struct dpu_layer), GFP_KERNEL);
+	if (!dpu->layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < cap.max_layers; i++) {
+
+		p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
+		if (!p)
+			return ERR_PTR(-ENOMEM);
+
+		ret = drm_universal_plane_init(drm, &p->plane, 1,
+					       &sprd_plane_funcs, cap.fmts_ptr,
+					       cap.fmts_cnt, NULL,
+					       DRM_PLANE_TYPE_PRIMARY, NULL);
+		if (ret) {
+			DRM_ERROR("fail to init primary plane\n");
+			return ERR_PTR(ret);
+		}
+
+		drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
+
+		sprd_plane_create_properties(p, i);
+
+		p->index = i;
+		if (i == 0)
+			primary = &p->plane;
+	}
+
+	return primary;
+}
+
+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
+
+	if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+		drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
+
+		if ((mode->hdisplay == mode->htotal) ||
+		    (mode->vdisplay == mode->vtotal))
+			dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
+		else
+			dpu->ctx.if_type = SPRD_DPU_IF_DPI;
+	}
+
+	return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+				   struct drm_crtc_state *old_state)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	sprd_dpu_init(dpu);
+
+	enable_irq(dpu->ctx.irq);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+				    struct drm_crtc_state *old_state)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+	struct drm_device *drm = dpu->crtc.dev;
+
+	disable_irq(dpu->ctx.irq);
+
+	sprd_dpu_fini(dpu);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_crtc_state *state)
+{
+	DRM_DEBUG("%s()\n", __func__);
+
+	return 0;
+}
+
+static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
+
+	dpu->pending_planes = 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+	struct drm_device *drm = dpu->crtc.dev;
+
+	if (dpu->pending_planes)
+		dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	dpu->core->enable_vsync(&dpu->ctx);
+
+	return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+	dpu->core->disable_vsync(&dpu->ctx);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+	.mode_valid	= sprd_crtc_mode_valid,
+	.atomic_check	= sprd_crtc_atomic_check,
+	.atomic_begin	= sprd_crtc_atomic_begin,
+	.atomic_flush	= sprd_crtc_atomic_flush,
+	.atomic_enable	= sprd_crtc_atomic_enable,
+	.atomic_disable	= sprd_crtc_atomic_disable,
+};
+
+static const struct drm_crtc_funcs sprd_crtc_funcs = {
+	.destroy	= drm_crtc_cleanup,
+	.set_config	= drm_atomic_helper_set_config,
+	.page_flip	= drm_atomic_helper_page_flip,
+	.reset		= drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
+	.enable_vblank	= sprd_crtc_enable_vblank,
+	.disable_vblank	= sprd_crtc_disable_vblank,
+};
+
+static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
+			 struct drm_plane *primary)
+{
+	struct device_node *port;
+	int ret;
+
+	/*
+	 * set crtc port so that drm_of_find_possible_crtcs call works
+	 */
+	port = of_parse_phandle(drm->dev->of_node, "ports", 0);
+	if (!port) {
+		DRM_ERROR("find 'ports' phandle of %s failed\n",
+			  drm->dev->of_node->full_name);
+		return -EINVAL;
+	}
+	of_node_put(port);
+	crtc->port = port;
+
+	ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
+					&sprd_crtc_funcs, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
+	drm_mode_crtc_set_gamma_size(crtc, 256);
+
+	drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
+
+	return 0;
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	dpu->core->init(ctx);
+	dpu->core->ifconfig(ctx);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	dpu->core->fini(ctx);
+}
+
+static irqreturn_t sprd_dpu_isr(int irq, void *data)
+{
+	struct sprd_dpu *dpu = data;
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 int_mask = 0;
+
+	int_mask = dpu->core->isr(ctx);
+
+	if (int_mask & BIT_DPU_INT_ERR)
+		DRM_WARN("Warning: dpu underflow!\n");
+
+	if (int_mask & BIT_DPU_INT_VSYNC)
+		drm_crtc_handle_vblank(&dpu->crtc);
+
+	return IRQ_HANDLED;
+}
+
+static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct sprd_dpu *dpu = dev_get_drvdata(dev);
+	struct drm_plane *plane;
+	int ret;
+
+	plane = sprd_plane_init(drm, dpu);
+	if (IS_ERR_OR_NULL(plane)) {
+		ret = PTR_ERR(plane);
+		return ret;
+	}
+
+	ret = sprd_crtc_init(drm, &dpu->crtc, plane);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void sprd_dpu_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct sprd_dpu *dpu = dev_get_drvdata(dev);
+
+	drm_crtc_cleanup(&dpu->crtc);
+}
+
+static const struct component_ops dpu_component_ops = {
+	.bind = sprd_dpu_bind,
+	.unbind = sprd_dpu_unbind,
+};
+
+static int sprd_dpu_context_init(struct sprd_dpu *dpu,
+				struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dpu_context *ctx = &dpu->ctx;
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!ctx->base) {
+		DRM_ERROR("failed to map dpu registers\n");
+		return -EFAULT;
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		DRM_ERROR("failed to get dpu irq\n");
+		return ctx->irq;
+	}
+
+	irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
+					0, "DPU", dpu);
+	if (ret) {
+		DRM_ERROR("failed to register dpu irq handler\n");
+		return ret;
+	}
+
+	init_waitqueue_head(&ctx->wait_queue);
+
+	return 0;
+}
+
+static const struct sprd_dpu_ops sharkl3_dpu = {
+	.core = &dpu_r2p0_core_ops,
+};
+
+static const struct of_device_id dpu_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dpu",
+	  .data = &sharkl3_dpu },
+	{ /* sentinel */ },
+};
+
+static int sprd_dpu_probe(struct platform_device *pdev)
+{
+	const struct sprd_dpu_ops *pdata;
+	struct sprd_dpu *dpu;
+	int ret;
+
+	dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
+	if (!dpu)
+		return -ENOMEM;
+
+	pdata = of_device_get_match_data(&pdev->dev);
+	if (pdata) {
+		dpu->core = pdata->core;
+	} else {
+		DRM_ERROR("No matching driver data found\n");
+		return -EINVAL;
+	}
+
+	ret = sprd_dpu_context_init(dpu, &pdev->dev);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, dpu);
+
+	return component_add(&pdev->dev, &dpu_component_ops);
+}
+
+static int sprd_dpu_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dpu_component_ops);
+	return 0;
+}
+
+struct platform_driver sprd_dpu_driver = {
+	.probe = sprd_dpu_probe,
+	.remove = sprd_dpu_remove,
+	.driver = {
+		.name = "sprd-dpu-drv",
+		.of_match_table = dpu_match_table,
+	},
+};
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
new file mode 100644
index 0000000..7d3c5e4
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dpu.h
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef __SPRD_DPU_H__
+#define __SPRD_DPU_H__
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <uapi/drm/drm_mode.h>
+
+#define BIT_DPU_INT_DONE_		BIT(0)
+#define BIT_DPU_INT_TE			BIT(1)
+#define BIT_DPU_INT_ERR			BIT(2)
+#define BIT_DPU_INT_EDPI_TE		BIT(3)
+#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
+#define BIT_DPU_INT_VSYNC		BIT(5)
+#define BIT_DPU_INT_WB_DONE		BIT(6)
+#define BIT_DPU_INT_WB_ERR		BIT(7)
+
+#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
+
+enum {
+	SPRD_DPU_IF_DBI = 0,
+	SPRD_DPU_IF_DPI,
+	SPRD_DPU_IF_EDPI,
+	SPRD_DPU_IF_LIMIT
+};
+
+enum {
+	DPU_LAYER_ROTATION_0,
+	DPU_LAYER_ROTATION_90,
+	DPU_LAYER_ROTATION_180,
+	DPU_LAYER_ROTATION_270,
+	DPU_LAYER_ROTATION_0_M,
+	DPU_LAYER_ROTATION_90_M,
+	DPU_LAYER_ROTATION_180_M,
+	DPU_LAYER_ROTATION_270_M,
+};
+
+struct dpu_layer {
+	u8 index;
+	u8 planes;
+	u32 addr[4];
+	u32 pitch[4];
+	s16 src_x;
+	s16 src_y;
+	s16 src_w;
+	s16 src_h;
+	s16 dst_x;
+	s16 dst_y;
+	u16 dst_w;
+	u16 dst_h;
+	u32 format;
+	u32 alpha;
+	u32 blending;
+	u32 rotation;
+};
+
+/**
+ * Sprd DPU capability structure
+ *
+ * @max_layers: maximum number of layers available
+ * @fmts_ptr: A pointer to array of supported pixel formats
+ * @fmts_cnt: the number of format on @fmts_ptr
+ */
+struct dpu_capability {
+	u32 max_layers;
+	const u32 *fmts_ptr;
+	u32 fmts_cnt;
+};
+
+/**
+ * Sprd DPU core callback ops
+ *
+ * This structure decribes the display controller common
+ * callback ops
+ *
+ * @init: initial DPU core
+ * @fini: cleanup DPU core
+ * @run: enable DPU output
+ * @stop: disable DPU output
+ * @enable_vsync: enable vblank interrupt
+ * @disable_vsync: disable vblank interrupt
+ * @isr: function pointer to the isr
+ * @ifconfig: initial DPI interface
+ * @flip: commit CRTC planes to DPU
+ * @capability: callback for DPU capabilities
+ */
+struct dpu_context;
+struct dpu_core_ops {
+	void (*init)(struct dpu_context *ctx);
+	void (*fini)(struct dpu_context *ctx);
+	void (*run)(struct dpu_context *ctx);
+	void (*stop)(struct dpu_context *ctx);
+	void (*enable_vsync)(struct dpu_context *ctx);
+	void (*disable_vsync)(struct dpu_context *ctx);
+	u32 (*isr)(struct dpu_context *ctx);
+	void (*ifconfig)(struct dpu_context *ctx);
+	void (*flip)(struct dpu_context *ctx,
+		     struct dpu_layer layers[], u8 count);
+	void (*capability)(struct dpu_context *ctx,
+			struct dpu_capability *cap);
+};
+
+/**
+ * Sprd DPU context structure
+ *
+ * @base: DPU controller base address
+ * @irq: IRQ number to install the handler for
+ * @if_type: The type of DPI interface, default is DPI mode.
+ * @vm: videomode structure to use for DPU and DPI initialization
+ * @stopped: indicates whether DPU are stopped
+ * @wait_queue: wait queue, used to wait for DPU shadow register update done and
+ * DPU stop register done interrupt signal.
+ * @evt_update: wait queue condition for DPU shadow register
+ * @evt_stop: wait queue condition for DPU stop register
+ */
+struct dpu_context {
+	void __iomem *base;
+	int irq;
+	u8 if_type;
+	struct videomode vm;
+	bool stopped;
+	wait_queue_head_t wait_queue;
+	bool evt_update;
+	bool evt_stop;
+};
+
+/**
+ * Sprd DPU device structure
+ *
+ * @crtc: DRM crtc
+ * @ctx: A pointer to the DPU's implementation specific context
+ * @core: pointer to callbacks for DPU core functionality
+ * @layers: active DPU layers ready to commit
+ * @pending_planes: the number of layers on @layers
+ */
+struct sprd_dpu {
+	struct drm_crtc crtc;
+	struct dpu_context ctx;
+	const struct dpu_core_ops *core;
+	struct dpu_layer *layers;
+	u8 pending_planes;
+};
+
+/**
+ * Sprd DPU H/W callback ops match table structure
+ * The structure used for matching a specific device callback ops
+ *
+ * @core: pointer to callbacks for DPU core functionality
+ */
+struct sprd_dpu_ops {
+	const struct dpu_core_ops *core;
+};
+
+static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
+{
+	return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
+}
+
+extern const struct dpu_core_ops dpu_r2p0_core_ops;
+
+#endif
diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
index 4706185..200020f 100644
--- a/drivers/gpu/drm/sprd/sprd_drm.c
+++ b/drivers/gpu/drm/sprd/sprd_drm.c
@@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
 
 static struct platform_driver *sprd_drm_drivers[]  = {
 	&sprd_drm_driver,
+	&sprd_dpu_driver,
 };
 
 static int __init sprd_drm_init(void)
diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
index edf0881..3c32f3a 100644
--- a/drivers/gpu/drm/sprd/sprd_drm.h
+++ b/drivers/gpu/drm/sprd/sprd_drm.h
@@ -13,4 +13,6 @@ struct sprd_drm {
 	struct drm_device *drm;
 };
 
+extern struct platform_driver sprd_dpu_driver;
+
 #endif /* _SPRD_DRM_H_ */
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 5/6] dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:07   ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

Adds MIPI DSI Master and MIPI DSI-PHY (D-PHY)
support for Unisoc's display subsystem.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 .../devicetree/bindings/display/sprd/dphy.yaml     | 75 +++++++++++++++++
 .../devicetree/bindings/display/sprd/dsi.yaml      | 98 ++++++++++++++++++++++
 2 files changed, 173 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml

diff --git a/Documentation/devicetree/bindings/display/sprd/dphy.yaml b/Documentation/devicetree/bindings/display/sprd/dphy.yaml
new file mode 100644
index 0000000..1b83260
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/dphy.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/dphy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI-PHY (D-PHY)
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-phy
+
+  reg:
+    maxItems: 1
+    description:
+      Must be the dsi controller base address.
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  port@0:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the output endpoint, usually output to
+      the associated panel.
+  port@1:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the input endpoint, usually coming from
+      the associated DSI controller.
+
+required:
+  - compatible
+  - reg
+  - port@0
+  - port@1
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    dphy: dphy {
+        compatible = "sprd,sharkl3-dsi-phy";
+        reg = <0x0 0x63100000 0x0 0x1000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        /* input port*/
+        port@1 {
+            reg = <1>;
+            dphy_in: endpoint {
+    	          remote-endpoint = <&dsi_out>;
+            };
+        };
+
+        /* output port */
+        port@0 {
+    	      reg = <0>;
+    	      dphy_out: endpoint {
+    		        remote-endpoint = <&panel_in>;
+    	      };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/sprd/dsi.yaml b/Documentation/devicetree/bindings/display/sprd/dsi.yaml
new file mode 100644
index 0000000..d89d957
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/dsi.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/dsi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Master
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+    description:
+      Physical base address and length of the registers set for the device.
+
+  interrupts:
+    maxItems: 2
+    description:
+      Should contain DSI interrupt.
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+    description: A phandle to DSIM power domain node
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  port@0:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the input endpoint, usually coming from
+      the associated DPU.
+  port@1:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the output endpoint, usually output to
+      the associated DPHY.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - port@0
+  - port@1
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sprd,sc9860-clk.h>
+    dsi: dsi@0x63100000 {
+        compatible = "sprd,sharkl3-dsi-host";
+        reg = <0 0x63100000 0 0x1000>;
+        interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>,
+          <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
+        clock-names = "clk_src_96m";
+        clocks = <&pll CLK_TWPLL_96M>;
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+        port@0 {
+            reg = <0>;
+            dsi_in: endpoint {
+                remote-endpoint = <&dpu_out>;
+            };
+        };
+
+        port@1 {
+            reg = <1>;
+            dsi_out: endpoint@1 {
+                remote-endpoint = <&dphy_in>;
+            };
+        };
+    };
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 5/6] dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
@ 2020-07-28 10:07   ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

Adds MIPI DSI Master and MIPI DSI-PHY (D-PHY)
support for Unisoc's display subsystem.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
---
 .../devicetree/bindings/display/sprd/dphy.yaml     | 75 +++++++++++++++++
 .../devicetree/bindings/display/sprd/dsi.yaml      | 98 ++++++++++++++++++++++
 2 files changed, 173 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml

diff --git a/Documentation/devicetree/bindings/display/sprd/dphy.yaml b/Documentation/devicetree/bindings/display/sprd/dphy.yaml
new file mode 100644
index 0000000..1b83260
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/dphy.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/dphy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI-PHY (D-PHY)
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-phy
+
+  reg:
+    maxItems: 1
+    description:
+      Must be the dsi controller base address.
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  port@0:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the output endpoint, usually output to
+      the associated panel.
+  port@1:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the input endpoint, usually coming from
+      the associated DSI controller.
+
+required:
+  - compatible
+  - reg
+  - port@0
+  - port@1
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    dphy: dphy {
+        compatible = "sprd,sharkl3-dsi-phy";
+        reg = <0x0 0x63100000 0x0 0x1000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        /* input port*/
+        port@1 {
+            reg = <1>;
+            dphy_in: endpoint {
+    	          remote-endpoint = <&dsi_out>;
+            };
+        };
+
+        /* output port */
+        port@0 {
+    	      reg = <0>;
+    	      dphy_out: endpoint {
+    		        remote-endpoint = <&panel_in>;
+    	      };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/sprd/dsi.yaml b/Documentation/devicetree/bindings/display/sprd/dsi.yaml
new file mode 100644
index 0000000..d89d957
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sprd/dsi.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/dsi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Master
+
+maintainers:
+  - Mark Rutland <mark.rutland@arm.com>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+    description:
+      Physical base address and length of the registers set for the device.
+
+  interrupts:
+    maxItems: 2
+    description:
+      Should contain DSI interrupt.
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+    description: A phandle to DSIM power domain node
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  port@0:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the input endpoint, usually coming from
+      the associated DPU.
+  port@1:
+    type: object
+    description:
+      A port node with endpoint definitions as defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+      That port should be the output endpoint, usually output to
+      the associated DPHY.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - port@0
+  - port@1
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sprd,sc9860-clk.h>
+    dsi: dsi@0x63100000 {
+        compatible = "sprd,sharkl3-dsi-host";
+        reg = <0 0x63100000 0 0x1000>;
+        interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>,
+          <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
+        clock-names = "clk_src_96m";
+        clocks = <&pll CLK_TWPLL_96M>;
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+        port@0 {
+            reg = <0>;
+            dsi_in: endpoint {
+                remote-endpoint = <&dpu_out>;
+            };
+        };
+
+        port@1 {
+            reg = <1>;
+            dsi_out: endpoint@1 {
+                remote-endpoint = <&dphy_in>;
+            };
+        };
+    };
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:07   ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

From: Kevin Tang <kevin.tang@unisoc.com>

Adds dsi host controller support for the Unisoc's display subsystem.
Adds dsi phy support for the Unisoc's display subsystem.
Only MIPI DSI Displays supported, DP/TV/HMDI will be support
in the feature.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin3.tang@gmail.com>
---
 drivers/gpu/drm/sprd/Makefile                     |    7 +-
 drivers/gpu/drm/sprd/disp_lib.c                   |   57 +
 drivers/gpu/drm/sprd/disp_lib.h                   |   16 +
 drivers/gpu/drm/sprd/dphy/Makefile                |    7 +
 drivers/gpu/drm/sprd/dphy/pll/Makefile            |    3 +
 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c |  473 +++++++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c         |  201 +++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h         |   22 +
 drivers/gpu/drm/sprd/dsi/Makefile                 |    8 +
 drivers/gpu/drm/sprd/dsi/core/Makefile            |    4 +
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c     |  964 ++++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h     | 1477 +++++++++++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c |  328 +++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h |   32 +
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c           |  590 ++++++++
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h           |   26 +
 drivers/gpu/drm/sprd/sprd_dphy.c                  |  209 +++
 drivers/gpu/drm/sprd/sprd_dphy.h                  |   50 +
 drivers/gpu/drm/sprd/sprd_dpu.c                   |   22 +
 drivers/gpu/drm/sprd/sprd_dpu.h                   |    3 +
 drivers/gpu/drm/sprd/sprd_dsi.c                   |  571 ++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h                   |  108 ++
 22 files changed, 5177 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
 create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h

diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
index 88ab32a..c78565d 100644
--- a/drivers/gpu/drm/sprd/Makefile
+++ b/drivers/gpu/drm/sprd/Makefile
@@ -3,6 +3,11 @@
 subdir-ccflags-y += -I$(srctree)/$(src)
 
 obj-y := sprd_drm.o \
-	sprd_dpu.o
+	sprd_dpu.o \
+	sprd_dsi.o \
+	sprd_dphy.o
 
+obj-y += disp_lib.o
 obj-y += dpu/
+obj-y += dsi/
+obj-y += dphy/
diff --git a/drivers/gpu/drm/sprd/disp_lib.c b/drivers/gpu/drm/sprd/disp_lib.c
new file mode 100644
index 0000000..b2f8202
--- /dev/null
+++ b/drivers/gpu/drm/sprd/disp_lib.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include "disp_lib.h"
+
+struct device *sprd_disp_pipe_get_by_port(struct device *dev, int port)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *endpoint;
+	struct device_node *remote_node;
+	struct platform_device *remote_pdev;
+
+	endpoint = of_graph_get_endpoint_by_regs(np, port, 0);
+	if (!endpoint) {
+		DRM_ERROR("%s/port%d/endpoint0 was not found\n",
+			  np->full_name, port);
+		return NULL;
+	}
+
+	remote_node = of_graph_get_remote_port_parent(endpoint);
+	if (!remote_node) {
+		DRM_ERROR("device node was not found by endpoint0\n");
+		return NULL;
+	}
+
+	remote_pdev = of_find_device_by_node(remote_node);
+	if (remote_pdev == NULL) {
+		DRM_ERROR("find %s platform device failed\n",
+			  remote_node->full_name);
+		return NULL;
+	}
+
+	return &remote_pdev->dev;
+}
+
+struct device *sprd_disp_pipe_get_input(struct device *dev)
+{
+	return sprd_disp_pipe_get_by_port(dev, 1);
+}
+
+struct device *sprd_disp_pipe_get_output(struct device *dev)
+{
+	return sprd_disp_pipe_get_by_port(dev, 0);
+}
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc display common API library");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/disp_lib.h b/drivers/gpu/drm/sprd/disp_lib.h
new file mode 100644
index 0000000..adbd239
--- /dev/null
+++ b/drivers/gpu/drm/sprd/disp_lib.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DISP_LIB_H_
+#define _DISP_LIB_H_
+
+#include <linux/list.h>
+#include <drm/drm_print.h>
+
+struct device *sprd_disp_pipe_get_by_port(struct device *dev, int port);
+struct device *sprd_disp_pipe_get_input(struct device *dev);
+struct device *sprd_disp_pipe_get_output(struct device *dev);
+
+#endif /* _DISP_LIB_H_ */
diff --git a/drivers/gpu/drm/sprd/dphy/Makefile b/drivers/gpu/drm/sprd/dphy/Makefile
new file mode 100644
index 0000000..6637c27
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_dphy_api.o
+
+obj-y += pll/
diff --git a/drivers/gpu/drm/sprd/dphy/pll/Makefile b/drivers/gpu/drm/sprd/dphy/pll/Makefile
new file mode 100644
index 0000000..59e74ec
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/pll/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += megacores_sharkle.o
diff --git a/drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c b/drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
new file mode 100644
index 0000000..a6869cc0
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <asm/div64.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+
+#include "sprd_dphy.h"
+
+#define L						0
+#define H						1
+#define CLK						0
+#define DATA					1
+#define INFINITY				0xffffffff
+#define MIN_OUTPUT_FREQ			(100)
+
+#define AVERAGE(a, b) (min(a, b) + abs((b) - (a)) / 2)
+
+enum TIMING {
+	NONE,
+	REQUEST_TIME,
+	PREPARE_TIME,
+	SETTLE_TIME,
+	ZERO_TIME,
+	TRAIL_TIME,
+	EXIT_TIME,
+	CLKPOST_TIME,
+	TA_GET,
+	TA_GO,
+	TA_SURE,
+	TA_WAIT,
+};
+
+struct pll_regs {
+	union __reg_03__ {
+		struct __03 {
+			u8 prbs_bist: 1;
+			u8 en_lp_treot: 1;
+			u8 lpf_sel: 4;
+			u8 txfifo_bypass: 1;
+			u8 freq_hopping: 1;
+		} bits;
+		u8 val;
+	} _03;
+	union __reg_04__ {
+		struct __04 {
+			u8 div: 3;
+			u8 masterof8lane: 1;
+			u8 hop_trig: 1;
+			u8 cp_s: 2;
+			u8 fdk_s: 1;
+		} bits;
+		u8 val;
+	} _04;
+	union __reg_06__ {
+		struct __06 {
+			u8 nint: 7;
+			u8 mod_en: 1;
+		} bits;
+		u8 val;
+	} _06;
+	union __reg_07__ {
+		struct __07 {
+			u8 kdelta_h: 8;
+		} bits;
+		u8 val;
+	} _07;
+	union __reg_08__ {
+		struct __08 {
+			u8 vco_band: 1;
+			u8 sdm_en: 1;
+			u8 refin: 2;
+			u8 kdelta_l: 4;
+		} bits;
+		u8 val;
+	} _08;
+	union __reg_09__ {
+		struct __09 {
+			u8 kint_h: 8;
+		} bits;
+		u8 val;
+	} _09;
+	union __reg_0a__ {
+		struct __0a {
+			u8 kint_m: 8;
+		} bits;
+		u8 val;
+	} _0a;
+	union __reg_0b__ {
+		struct __0b {
+			u8 out_sel: 4;
+			u8 kint_l: 4;
+		} bits;
+		u8 val;
+	} _0b;
+	union __reg_0c__ {
+		struct __0c {
+			u8 kstep_h: 8;
+		} bits;
+		u8 val;
+	} _0c;
+	union __reg_0d__ {
+		struct __0d {
+			u8 kstep_m: 8;
+		} bits;
+		u8 val;
+	} _0d;
+	union __reg_0e__ {
+		struct __0e {
+			u8 pll_pu_byp: 1;
+			u8 pll_pu: 1;
+			u8 hsbist_len: 2;
+			u8 stopstate_sel: 1;
+			u8 kstep_l: 3;
+		} bits;
+		u8 val;
+	} _0e;
+
+	union __reg_0f__ {
+		struct __0f {
+			u8 det_delay:2;
+			u8 kdelta: 4;
+			u8 ldo0p4:2;
+		} bits;
+		u8 val;
+	} _0f;
+
+};
+
+struct dphy_pll {
+	u8 refin; /* Pre-divider control signal */
+	u8 cp_s; /* 00: SDM_EN=1, 10: SDM_EN=0 */
+	u8 fdk_s; /* PLL mode control: integer or fraction */
+	u8 sdm_en;
+	u8 div;
+	u8 int_n; /* integer N PLL */
+	u32 ref_clk; /* dphy reference clock, unit: MHz */
+	u32 freq; /* panel config, unit: KHz */
+	u32 fvco; /* MHz */
+	u32 potential_fvco; /* MHz */
+	u32 nint; /* sigma delta modulator NINT control */
+	u32 kint; /* sigma delta modulator KINT control */
+	u8 lpf_sel; /* low pass filter control */
+	u8 out_sel; /* post divider control */
+	u8 vco_band; /* vco range */
+	u8 det_delay;
+};
+
+static struct pll_regs regs;
+static struct dphy_pll pll;
+
+/* sharkle */
+#define VCO_BAND_LOW	750
+#define VCO_BAND_MID	1100
+#define VCO_BAND_HIGH	1500
+#define PHY_REF_CLK	26000
+
+static int dphy_calc_pll_param(struct dphy_pll *pll)
+{
+	int i;
+	const u32 khz = 1000;
+	const u32 mhz = 1000000;
+	const unsigned long long factor = 100;
+	unsigned long long tmp;
+
+	pll->potential_fvco = pll->freq / khz; /* MHz */
+	pll->ref_clk = PHY_REF_CLK / khz; /* MHz */
+
+	for (i = 0; i < 4; ++i) {
+		if (pll->potential_fvco >= VCO_BAND_LOW &&
+			pll->potential_fvco <= VCO_BAND_HIGH) {
+			pll->fvco = pll->potential_fvco;
+			pll->out_sel = BIT(i);
+			break;
+		}
+		pll->potential_fvco <<= 1;
+	}
+	if (pll->fvco == 0)
+		return -EINVAL;
+
+	if (pll->fvco >= VCO_BAND_LOW && pll->fvco <= VCO_BAND_MID) {
+		/* vco band control */
+		pll->vco_band = 0x0;
+		/* low pass filter control */
+		pll->lpf_sel = 1;
+	} else if (pll->fvco > VCO_BAND_MID && pll->fvco <= VCO_BAND_HIGH) {
+		pll->vco_band = 0x1;
+		pll->lpf_sel = 0;
+	} else
+		return -EINVAL;
+
+	pll->nint = pll->fvco / pll->ref_clk;
+	tmp = pll->fvco * factor * mhz;
+	do_div(tmp, pll->ref_clk);
+	tmp = tmp - pll->nint * factor * mhz;
+	tmp *= BIT(20);
+	do_div(tmp, 100000000);
+	pll->kint = (u32)tmp;
+	pll->refin = 3; /* pre-divider bypass */
+	pll->sdm_en = true; /* use fraction N PLL */
+	pll->fdk_s = 0x1; /* fraction */
+	pll->cp_s = 0x0;
+	pll->det_delay = 0x1;
+
+	return 0;
+}
+
+static void dphy_set_pll_reg(struct regmap *regmap, struct dphy_pll *pll)
+{
+	int i;
+	u8 *val;
+
+	u8 regs_addr[] = {
+		0x03, 0x04, 0x06, 0x08, 0x09,
+		0x0a, 0x0b, 0x0e, 0x0f
+	};
+
+	regs._03.bits.prbs_bist = 1;
+	regs._03.bits.en_lp_treot = true;
+	regs._03.bits.lpf_sel = pll->lpf_sel;
+	regs._03.bits.txfifo_bypass = 0;
+	regs._04.bits.div = pll->div;
+	regs._04.bits.masterof8lane = 1;
+	regs._04.bits.cp_s = pll->cp_s;
+	regs._04.bits.fdk_s = pll->fdk_s;
+	regs._06.bits.nint = pll->nint;
+	regs._08.bits.vco_band = pll->vco_band;
+	regs._08.bits.sdm_en = pll->sdm_en;
+	regs._08.bits.refin = pll->refin;
+	regs._09.bits.kint_h = pll->kint >> 12;
+	regs._0a.bits.kint_m = (pll->kint >> 4) & 0xff;
+	regs._0b.bits.out_sel = pll->out_sel;
+	regs._0b.bits.kint_l = pll->kint & 0xf;
+	regs._0e.bits.pll_pu_byp = 0;
+	regs._0e.bits.pll_pu = 0;
+	regs._0e.bits.stopstate_sel = 1;
+	regs._0f.bits.det_delay = pll->det_delay;
+
+	val = (u8 *)&regs;
+
+	for (i = 0; i < sizeof(regs_addr); ++i) {
+		regmap_write(regmap, regs_addr[i], val[i]);
+		DRM_DEBUG("%02x: %02x\n", regs_addr[i], val[i]);
+	}
+}
+
+static int dphy_pll_config(struct dphy_context *ctx)
+{
+	int ret;
+	struct regmap *regmap = ctx->regmap;
+
+	pll.freq = ctx->freq;
+
+	/* FREQ = 26M * (NINT + KINT / 2^20) / out_sel */
+	ret = dphy_calc_pll_param(&pll);
+	if (ret) {
+		DRM_ERROR("failed to calculate dphy pll parameters\n");
+		return ret;
+	}
+	dphy_set_pll_reg(regmap, &pll);
+
+	return 0;
+}
+
+static void dphy_set_timing_regs(struct regmap *regmap, int type, u8 val[])
+{
+	switch (type) {
+	case REQUEST_TIME:
+		regmap_write(regmap, 0x31, val[CLK]);
+		regmap_write(regmap, 0x41, val[DATA]);
+		regmap_write(regmap, 0x51, val[DATA]);
+		regmap_write(regmap, 0x61, val[DATA]);
+		regmap_write(regmap, 0x71, val[DATA]);
+
+		regmap_write(regmap, 0x90, val[CLK]);
+		regmap_write(regmap, 0xa0, val[DATA]);
+		regmap_write(regmap, 0xb0, val[DATA]);
+		regmap_write(regmap, 0xc0, val[DATA]);
+		regmap_write(regmap, 0xd0, val[DATA]);
+		break;
+	case PREPARE_TIME:
+		regmap_write(regmap, 0x32, val[CLK]);
+		regmap_write(regmap, 0x42, val[DATA]);
+		regmap_write(regmap, 0x52, val[DATA]);
+		regmap_write(regmap, 0x62, val[DATA]);
+		regmap_write(regmap, 0x72, val[DATA]);
+
+		regmap_write(regmap, 0x91, val[CLK]);
+		regmap_write(regmap, 0xa1, val[DATA]);
+		regmap_write(regmap, 0xb1, val[DATA]);
+		regmap_write(regmap, 0xc1, val[DATA]);
+		regmap_write(regmap, 0xd1, val[DATA]);
+		break;
+	case ZERO_TIME:
+		regmap_write(regmap, 0x33, val[CLK]);
+		regmap_write(regmap, 0x43, val[DATA]);
+		regmap_write(regmap, 0x53, val[DATA]);
+		regmap_write(regmap, 0x63, val[DATA]);
+		regmap_write(regmap, 0x73, val[DATA]);
+
+		regmap_write(regmap, 0x92, val[CLK]);
+		regmap_write(regmap, 0xa2, val[DATA]);
+		regmap_write(regmap, 0xb2, val[DATA]);
+		regmap_write(regmap, 0xc2, val[DATA]);
+		regmap_write(regmap, 0xd2, val[DATA]);
+		break;
+	case TRAIL_TIME:
+		regmap_write(regmap, 0x34, val[CLK]);
+		regmap_write(regmap, 0x44, val[DATA]);
+		regmap_write(regmap, 0x54, val[DATA]);
+		regmap_write(regmap, 0x64, val[DATA]);
+		regmap_write(regmap, 0x74, val[DATA]);
+
+		regmap_write(regmap, 0x93, val[CLK]);
+		regmap_write(regmap, 0xa3, val[DATA]);
+		regmap_write(regmap, 0xb3, val[DATA]);
+		regmap_write(regmap, 0xc3, val[DATA]);
+		regmap_write(regmap, 0xd3, val[DATA]);
+		break;
+	case EXIT_TIME:
+		regmap_write(regmap, 0x36, val[CLK]);
+		regmap_write(regmap, 0x46, val[DATA]);
+		regmap_write(regmap, 0x56, val[DATA]);
+		regmap_write(regmap, 0x66, val[DATA]);
+		regmap_write(regmap, 0x76, val[DATA]);
+
+		regmap_write(regmap, 0x95, val[CLK]);
+		regmap_write(regmap, 0xA5, val[DATA]);
+		regmap_write(regmap, 0xB5, val[DATA]);
+		regmap_write(regmap, 0xc5, val[DATA]);
+		regmap_write(regmap, 0xd5, val[DATA]);
+		break;
+	case CLKPOST_TIME:
+		regmap_write(regmap, 0x35, val[CLK]);
+		regmap_write(regmap, 0x94, val[CLK]);
+		break;
+
+	/* the following just use default value */
+	case SETTLE_TIME:
+	case TA_GET:
+	case TA_GO:
+	case TA_SURE:
+		break;
+	default:
+		break;
+	}
+}
+
+static void dphy_timing_config(struct dphy_context *ctx)
+{
+	u8 val[2];
+	u32 tmp = 0;
+	u32 range[2], constant;
+	u32 t_ui, t_byteck, t_half_byteck;
+	const u32 factor = 2;
+	const u32 scale = 100;
+	struct regmap *regmap = ctx->regmap;
+
+	/* t_ui: 1 ui, byteck: 8 ui, half byteck: 4 ui */
+	t_ui = 1000 * scale / (ctx->freq / 1000);
+	t_byteck = t_ui << 3;
+	t_half_byteck = t_ui << 2;
+	constant = t_ui << 1;
+
+	/* REQUEST_TIME: HS T-LPX: LP-01
+	 * For T-LPX, mipi spec defined min value is 50ns,
+	 * but maybe it shouldn't be too small, because BTA,
+	 * LP-10, LP-00, LP-01, all of this is related to T-LPX.
+	 */
+	range[L] = 50 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * (factor << 1), t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_regs(regmap, REQUEST_TIME, val);
+
+	/* PREPARE_TIME: HS sequence: LP-00 */
+	range[L] = 38 * scale;
+	range[H] = 95 * scale;
+	tmp = AVERAGE(range[L], range[H]);
+	val[CLK] = DIV_ROUND_UP(AVERAGE(range[L], range[H]),
+			t_half_byteck) - 1;
+	range[L] = 40 * scale + 4 * t_ui;
+	range[H] = 85 * scale + 6 * t_ui;
+	tmp |= AVERAGE(range[L], range[H]) << 16;
+	val[DATA] = DIV_ROUND_UP(AVERAGE(range[L], range[H]),
+			t_half_byteck) - 1;
+	dphy_set_timing_regs(regmap, PREPARE_TIME, val);
+
+	/* ZERO_TIME: HS-ZERO */
+	range[L] = 300 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor + (tmp & 0xffff)
+			- 525 * t_byteck / 100, t_byteck) - 2;
+	range[L] = 145 * scale + 10 * t_ui;
+	val[DATA] = DIV_ROUND_UP(range[L] * factor
+			+ ((tmp >> 16) & 0xffff) - 525 * t_byteck / 100,
+			t_byteck) - 2;
+	dphy_set_timing_regs(regmap, ZERO_TIME, val);
+
+	/* TRAIL_TIME: HS-TRAIL */
+	range[L] = 60 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor - constant, t_half_byteck);
+	range[L] = max(8 * t_ui, 60 * scale + 4 * t_ui);
+	val[DATA] = DIV_ROUND_UP(range[L] * 3 / 2 - constant, t_half_byteck) - 2;
+	dphy_set_timing_regs(regmap, TRAIL_TIME, val);
+
+	/* EXIT_TIME: */
+	range[L] = 100 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_regs(regmap, EXIT_TIME, val);
+
+	/* CLKPOST_TIME: */
+	range[L] = 60 * scale + 52 * t_ui;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_regs(regmap, CLKPOST_TIME, val);
+
+	/* SETTLE_TIME:
+	 * This time is used for receiver. So for transmitter,
+	 * it can be ignored.
+	 */
+
+	/* TA_GO:
+	 * transmitter drives bridge state(LP-00) before releasing control,
+	 * reg 0x1f default value: 0x04, which is good.
+	 */
+
+	/* TA_SURE:
+	 * After LP-10 state and before bridge state(LP-00),
+	 * reg 0x20 default value: 0x01, which is good.
+	 */
+
+	/* TA_GET:
+	 * receiver drives Bridge state(LP-00) before releasing control
+	 * reg 0x21 default value: 0x03, which is good.
+	 */
+}
+
+/**
+ * Force D-PHY PLL to stay on while in ULPS
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param force (1) disable (0)
+ * @note To follow the programming model, use wakeup_pll function
+ */
+static void dphy_force_pll(struct dphy_context *ctx, int force)
+{
+	u8 data;
+	struct regmap *regmap = ctx->regmap;
+
+	if (force)
+		data = 0x03;
+	else
+		data = 0x0;
+
+	/* for megocores, to force pll, dphy register should be set */
+	regmap_write(regmap, 0x0e, data);
+}
+
+const struct dphy_pll_ops sharkle_dphy_pll_ops = {
+	.pll_config = dphy_pll_config,
+	.timing_config = dphy_timing_config,
+	.force_pll = dphy_force_pll,
+};
diff --git a/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
new file mode 100644
index 0000000..e90d1f7
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "sprd_dphy_api.h"
+#include "dsi/core/dsi_ctrl_r1p0_ppi.h"
+
+static int dphy_wait_pll_locked(struct dphy_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 50000; i++) {
+		if (dsi_phy_is_pll_locked(ctx))
+			return 0;
+		udelay(3);
+	}
+
+	DRM_ERROR("error: dphy pll can not be locked\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_datalane_stop_state(struct dphy_context *ctx, u8 mask)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_stop_state_datalane(ctx) == mask)
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait datalane stop-state time out\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_datalane_ulps_active(struct dphy_context *ctx, u8 mask)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_ulps_active_datalane(ctx) == mask)
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait datalane ulps-active time out\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_clklane_stop_state(struct dphy_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_stop_state_clklane(ctx))
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait clklane stop-state time out\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_clklane_ulps_active(struct dphy_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_ulps_active_clklane(ctx))
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait clklane ulps-active time out\n");
+	return -ETIMEDOUT;
+}
+
+int sprd_dphy_power_on(struct sprd_dphy *dphy)
+{
+	const struct dphy_pll_ops *pll = dphy->pll;
+	struct dphy_context *ctx = &dphy->ctx;
+	int ret;
+
+	dsi_phy_rstz(ctx, 0);
+	dsi_phy_shutdownz(ctx, 0);
+	dsi_phy_clklane_en(ctx, 0);
+
+	dsi_phy_test_clr(ctx, 0);
+	dsi_phy_test_clr(ctx, 1);
+	dsi_phy_test_clr(ctx, 0);
+
+	pll->pll_config(ctx);
+	pll->timing_config(ctx);
+
+	dsi_phy_shutdownz(ctx, 1);
+	dsi_phy_rstz(ctx, 1);
+	dsi_phy_stop_wait_time(ctx, 0x1C);
+	dsi_phy_clklane_en(ctx, 1);
+	dsi_phy_datalane_en(ctx);
+
+	ret = dphy_wait_pll_locked(ctx);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void sprd_dphy_power_off(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_rstz(ctx, 0);
+	dsi_phy_shutdownz(ctx, 0);
+	dsi_phy_rstz(ctx, 1);
+}
+
+void sprd_dphy_data_ulps_enter(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+	u8 lane_mask = (1 << ctx->lanes) - 1;
+
+	dsi_phy_datalane_ulps_rqst(ctx, 1);
+	dphy_wait_datalane_ulps_active(ctx, lane_mask);
+	dsi_phy_datalane_ulps_rqst(ctx, 0);
+}
+
+void sprd_dphy_data_ulps_exit(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+	u8 lane_mask = (1 << ctx->lanes) - 1;
+
+	dsi_phy_datalane_ulps_exit(ctx, 1);
+	dphy_wait_datalane_stop_state(ctx, lane_mask);
+	dsi_phy_datalane_ulps_exit(ctx, 0);
+}
+
+void sprd_dphy_clk_ulps_enter(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_clklane_ulps_rqst(ctx, 1);
+	dphy_wait_clklane_ulps_active(ctx);
+	dsi_phy_clklane_ulps_rqst(ctx, 0);
+}
+
+void sprd_dphy_clk_ulps_exit(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_clklane_ulps_exit(ctx, 1);
+	dphy_wait_clklane_stop_state(ctx);
+	dsi_phy_clklane_ulps_exit(ctx, 0);
+}
+
+void sprd_dphy_force_pll(struct sprd_dphy *dphy, bool enable)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_force_pll(ctx, enable);
+}
+
+void sprd_dphy_hs_clk_en(struct sprd_dphy *dphy, bool enable)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_clk_hs_rqst(ctx, enable);
+	dphy_wait_pll_locked(ctx);
+}
+
+void sprd_dphy_test_write(struct sprd_dphy *dphy, u8 address, u8 data)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_test_en(ctx, 1);
+	dsi_phy_test_din(ctx, address);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+	dsi_phy_test_en(ctx, 0);
+	dsi_phy_test_din(ctx, data);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+}
+
+u8 sprd_dphy_test_read(struct sprd_dphy *dphy, u8 address)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_test_en(ctx, 1);
+	dsi_phy_test_din(ctx, address);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+	dsi_phy_test_en(ctx, 0);
+
+	udelay(1);
+
+	return dsi_phy_test_dout(ctx);
+}
diff --git a/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
new file mode 100644
index 0000000..5def380
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DPHY_API_H_
+#define _SPRD_DPHY_API_H_
+
+#include "sprd_dphy.h"
+
+int sprd_dphy_power_on(struct sprd_dphy *dphy);
+void sprd_dphy_power_off(struct sprd_dphy *dphy);
+void sprd_dphy_data_ulps_enter(struct sprd_dphy *dphy);
+void sprd_dphy_data_ulps_exit(struct sprd_dphy *dphy);
+void sprd_dphy_clk_ulps_enter(struct sprd_dphy *dphy);
+void sprd_dphy_clk_ulps_exit(struct sprd_dphy *dphy);
+void sprd_dphy_force_pll(struct sprd_dphy *dphy, bool enable);
+void sprd_dphy_hs_clk_en(struct sprd_dphy *dphy, bool enable);
+void sprd_dphy_test_write(struct sprd_dphy *dphy, u8 address, u8 data);
+u8 sprd_dphy_test_read(struct sprd_dphy *dphy, u8 address);
+
+#endif /* _SPRD_DPHY_API_H_ */
\ No newline at end of file
diff --git a/drivers/gpu/drm/sprd/dsi/Makefile b/drivers/gpu/drm/sprd/dsi/Makefile
new file mode 100644
index 0000000..d95189c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y += sprd_dsi_api.o
+
+obj-y += core/
+
diff --git a/drivers/gpu/drm/sprd/dsi/core/Makefile b/drivers/gpu/drm/sprd/dsi/core/Makefile
new file mode 100644
index 0000000..41f6477
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dsi_ctrl_r1p0.o dsi_ctrl_r1p0_ppi.o
+
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
new file mode 100644
index 0000000..83bc37c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include "dsi_ctrl_r1p0.h"
+
+/**
+ * Get DSI Host core version
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return ascii number of the version
+ */
+bool dsi_check_version(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	u32 version = readl(&reg->DSI_VERSION);
+
+	if (version == 0x100)
+		return true;
+	else if (version == 0x200)
+		return true;
+	else if (version == 0x300)
+		return true;
+	else
+		return false;
+}
+/**
+ * Modify power status of DSI Host core
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param on (1) or off (0)
+ */
+void dsi_power_enable(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(enable, &reg->SOFT_RESET);
+}
+/**
+ * Enable/disable DPI video mode
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_video_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(0, &reg->DSI_MODE_CFG);
+}
+/**
+ * Enable command mode (Generic interface)
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_cmd_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(1, &reg->DSI_MODE_CFG);
+}
+
+bool dsi_is_cmd_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	return readl(&reg->DSI_MODE_CFG);
+}
+/**
+ * Configure the read back virtual channel for the generic interface
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param vc to listen to on the line
+ */
+void dsi_rx_vcid(struct dsi_context *ctx, u8 vc)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x1C virtual_channel_id;
+
+	virtual_channel_id.val = readl(&reg->VIRTUAL_CHANNEL_ID);
+	virtual_channel_id.bits.gen_rx_vcid = vc;
+
+	writel(virtual_channel_id.val, &reg->VIRTUAL_CHANNEL_ID);
+}
+/**
+ * Write the DPI video virtual channel destination
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param vc virtual channel
+ */
+void dsi_video_vcid(struct dsi_context *ctx, u8 vc)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x1C virtual_channel_id;
+
+	virtual_channel_id.val = readl(&reg->VIRTUAL_CHANNEL_ID);
+	virtual_channel_id.bits.video_pkt_vcid = vc;
+
+	writel(virtual_channel_id.val, &reg->VIRTUAL_CHANNEL_ID);
+}
+/**
+ * Set DPI video mode type (burst/non-burst - with sync pulses or events)
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param type
+ * @return error code
+ */
+void dsi_dpi_video_burst_mode(struct dsi_context *ctx, int mode)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.vid_mode_type = mode;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Set DPI video color coding
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param color_coding enum (configuration and color depth)
+ * @return error code
+ */
+void dsi_dpi_color_coding(struct dsi_context *ctx, int coding)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x20 dpi_video_format;
+
+	dpi_video_format.val = readl(&reg->DPI_VIDEO_FORMAT);
+	dpi_video_format.bits.dpi_video_mode_format = coding;
+
+	writel(dpi_video_format.val, &reg->DPI_VIDEO_FORMAT);
+}
+/**
+ * Configure the Horizontal Line time
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle taken to transmit the total of the horizontal line
+ */
+void dsi_dpi_hline_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x2C video_line_time;
+
+	video_line_time.val = readl(&reg->VIDEO_LINE_TIME);
+	video_line_time.bits.video_line_time = byte_cycle;
+
+	writel(video_line_time.val, &reg->VIDEO_LINE_TIME);
+}
+/**
+ * Configure the Horizontal back porch time
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle taken to transmit the horizontal back porch
+ */
+void dsi_dpi_hbp_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x28 video_line_hblk_time;
+
+	video_line_hblk_time.val = readl(&reg->VIDEO_LINE_HBLK_TIME);
+	video_line_hblk_time.bits.video_line_hbp_time = byte_cycle;
+
+	writel(video_line_hblk_time.val, &reg->VIDEO_LINE_HBLK_TIME);
+}
+/**
+ * Configure the Horizontal sync time
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle taken to transmit the horizontal sync
+ */
+void dsi_dpi_hsync_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x28 video_line_hblk_time;
+
+	video_line_hblk_time.val = readl(&reg->VIDEO_LINE_HBLK_TIME);
+	video_line_hblk_time.bits.video_line_hsa_time = byte_cycle;
+
+	writel(video_line_hblk_time.val, &reg->VIDEO_LINE_HBLK_TIME);
+}
+/**
+ * Configure the vertical active lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vact(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x34 video_active_lines;
+
+	video_active_lines.val = readl(&reg->VIDEO_VACTIVE_LINES);
+	video_active_lines.bits.vactive_lines = lines;
+
+	writel(video_active_lines.val, &reg->VIDEO_VACTIVE_LINES);
+}
+/**
+ * Configure the vertical front porch lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vfp(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vfp_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+/**
+ * Configure the vertical back porch lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vbp(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vbp_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+/**
+ * Configure the vertical sync lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vsync(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vsa_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+/**
+ * Enable return to low power mode inside horizontal front porch periods when
+ *  timing allows
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_dpi_hporch_lp_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+
+	vid_mode_cfg.bits.lp_hfp_en = enable;
+	vid_mode_cfg.bits.lp_hbp_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Enable return to low power mode inside vertical active lines periods when
+ *  timing allows
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_dpi_vporch_lp_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+
+	vid_mode_cfg.bits.lp_vact_en = enable;
+	vid_mode_cfg.bits.lp_vfp_en = enable;
+	vid_mode_cfg.bits.lp_vbp_en = enable;
+	vid_mode_cfg.bits.lp_vsa_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Enable FRAME BTA ACK
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_dpi_frame_ack_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.frame_bta_ack_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Write no of chunks to core - taken into consideration only when multi packet
+ * is enabled
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param no of chunks
+ */
+void dsi_dpi_chunk_num(struct dsi_context *ctx, u16 num)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x24 video_pkt_config;
+
+	video_pkt_config.val = readl(&reg->VIDEO_PKT_CONFIG);
+	video_pkt_config.bits.video_line_chunk_num = num;
+
+	writel(video_pkt_config.val, &reg->VIDEO_PKT_CONFIG);
+}
+/**
+ * Write the null packet size - will only be taken into account when null
+ * packets are enabled.
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param size of null packet
+ * @return error code
+ */
+void dsi_dpi_null_packet_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xC0 video_nullpkt_size;
+
+	video_nullpkt_size.val = readl(&reg->VIDEO_NULLPKT_SIZE);
+	video_nullpkt_size.bits.video_nullpkt_size = size;
+
+	writel(video_nullpkt_size.val, &reg->VIDEO_NULLPKT_SIZE);
+}
+/**
+ * Write video packet size. obligatory for sending video
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param size of video packet - containing information
+ * @return error code
+ */
+void dsi_dpi_video_packet_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x24 video_pkt_config;
+
+	video_pkt_config.val = readl(&reg->VIDEO_PKT_CONFIG);
+	video_pkt_config.bits.video_pkt_size = size;
+
+	writel(video_pkt_config.val, &reg->VIDEO_PKT_CONFIG);
+}
+/**
+ * Specifiy the size of the packet memory write start/continue
+ * @param instance pointer to structure holding the DSI Host core information
+ * @ size of the packet
+ * @note when different than zero (0) eDPI is enabled
+ */
+void dsi_edpi_max_pkt_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xC4 dcs_wm_pkt_size;
+
+	dcs_wm_pkt_size.val = readl(&reg->DCS_WM_PKT_SIZE);
+	dcs_wm_pkt_size.bits.dcs_wm_pkt_size = size;
+
+	writel(dcs_wm_pkt_size.val, &reg->DCS_WM_PKT_SIZE);
+}
+/**
+ * Enable tear effect acknowledge
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_tear_effect_ack_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x68 cmd_mode_cfg;
+
+	cmd_mode_cfg.val = readl(&reg->CMD_MODE_CFG);
+	cmd_mode_cfg.bits.tear_fx_en = enable;
+
+	writel(cmd_mode_cfg.val, &reg->CMD_MODE_CFG);
+}
+/**
+ * Set DCS command packet transmission to transmission type
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param no_of_param of command
+ * @param lp transmit in low power
+ * @return error code
+ */
+void dsi_cmd_mode_lp_cmd_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x68 cmd_mode_cfg;
+
+	cmd_mode_cfg.val = readl(&reg->CMD_MODE_CFG);
+
+	cmd_mode_cfg.bits.gen_sw_0p_tx = enable;
+	cmd_mode_cfg.bits.gen_sw_1p_tx = enable;
+	cmd_mode_cfg.bits.gen_sw_2p_tx = enable;
+	cmd_mode_cfg.bits.gen_lw_tx = enable;
+	cmd_mode_cfg.bits.dcs_sw_0p_tx = enable;
+	cmd_mode_cfg.bits.dcs_sw_1p_tx = enable;
+	cmd_mode_cfg.bits.dcs_lw_tx = enable;
+	cmd_mode_cfg.bits.max_rd_pkt_size = enable;
+
+	cmd_mode_cfg.bits.gen_sr_0p_tx = enable;
+	cmd_mode_cfg.bits.gen_sr_1p_tx = enable;
+	cmd_mode_cfg.bits.gen_sr_2p_tx = enable;
+	cmd_mode_cfg.bits.dcs_sr_0p_tx = enable;
+
+	writel(cmd_mode_cfg.val, &reg->CMD_MODE_CFG);
+}
+/**
+ * Set DCS read command packet transmission to transmission type
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param no_of_param of command
+ * @param lp transmit in low power
+ * @return error code
+ */
+void dsi_video_mode_lp_cmd_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.lp_cmd_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+
+/**
+ * Write command header in the generic interface
+ * (which also sends DCS commands) as a subset
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param vc of destination
+ * @param packet_type (or type of DCS command)
+ * @param ls_byte (if DCS, it is the DCS command)
+ * @param ms_byte (only parameter of short DCS packet)
+ * @return error code
+ */
+void dsi_set_packet_header(struct dsi_context *ctx,
+				   u8 vc,
+				   u8 type,
+				   u8 wc_lsb,
+				   u8 wc_msb)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x6C gen_hdr;
+
+	gen_hdr.bits.gen_dt = type;
+	gen_hdr.bits.gen_vc = vc;
+	gen_hdr.bits.gen_wc_lsbyte = wc_lsb;
+	gen_hdr.bits.gen_wc_msbyte = wc_msb;
+
+	writel(gen_hdr.val, &reg->GEN_HDR);
+}
+/**
+ * Write the payload of the long packet commands
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param payload array of bytes of payload
+ * @return error code
+ */
+void dsi_set_packet_payload(struct dsi_context *ctx, u32 payload)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(payload, &reg->GEN_PLD_DATA);
+}
+/**
+ * Write the payload of the long packet commands
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param payload pointer to 32-bit array to hold read information
+ * @return error code
+ */
+u32 dsi_get_rx_payload(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	return readl(&reg->GEN_PLD_DATA);
+}
+
+/**
+ * Enable Bus Turn-around request
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_bta_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(enable, &reg->TA_EN);
+}
+/**
+ * Enable EOTp reception
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_eotp_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xBC eotp_en;
+
+	eotp_en.val = readl(&reg->EOTP_EN);
+	eotp_en.bits.rx_eotp_en = enable;
+
+	writel(eotp_en.val, &reg->EOTP_EN);
+}
+/**
+ * Enable EOTp transmission
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_eotp_tx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xBC eotp_en;
+
+	eotp_en.val = readl(&reg->EOTP_EN);
+	eotp_en.bits.tx_eotp_en = enable;
+
+	writel(eotp_en.val, &reg->EOTP_EN);
+}
+/**
+ * Enable ECC reception, error correction and reporting
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_ecc_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xB4 rx_pkt_check_config;
+
+	rx_pkt_check_config.val = readl(&reg->RX_PKT_CHECK_CONFIG);
+	rx_pkt_check_config.bits.rx_pkt_ecc_en = enable;
+
+	writel(rx_pkt_check_config.val, &reg->RX_PKT_CHECK_CONFIG);
+}
+/**
+ * Enable CRC reception, error reporting
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_crc_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xB4 rx_pkt_check_config;
+
+	rx_pkt_check_config.val = readl(&reg->RX_PKT_CHECK_CONFIG);
+	rx_pkt_check_config.bits.rx_pkt_crc_en = enable;
+
+	writel(rx_pkt_check_config.val, &reg->RX_PKT_CHECK_CONFIG);
+}
+/**
+ * NOTE: dsi-ctrl-r1p0 only
+ *
+ * Get status of read command
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if busy
+ */
+bool dsi_is_bta_returned(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdcmd_done;
+}
+/**
+ * Get the FULL status of generic read payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo full
+ */
+bool dsi_is_rx_payload_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdata_fifo_full;
+}
+/**
+ * Get the EMPTY status of generic read payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo empty
+ */
+bool dsi_is_rx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdata_fifo_empty;
+}
+/**
+ * Get the FULL status of generic write payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo full
+ */
+bool dsi_is_tx_payload_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_wdata_fifo_full;
+}
+/**
+ * Get the EMPTY status of generic write payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo empty
+ */
+bool dsi_is_tx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_wdata_fifo_empty;
+}
+/**
+ * Get the FULL status of generic command fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo full
+ */
+bool dsi_is_tx_cmd_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_cmd_fifo_full;
+}
+/**
+ * Get the EMPTY status of generic command fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo empty
+ */
+bool dsi_is_tx_cmd_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_cmd_fifo_empty;
+}
+
+/* only if DPI */
+/**
+ * DPI interface signal delay config
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle period for waiting after controller receiving HSYNC from
+ *       DPI interface to start read pixel data from memory.
+ */
+void dsi_dpi_sig_delay(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xD0 video_sig_delay_config;
+
+	video_sig_delay_config.val = readl(&reg->VIDEO_SIG_DELAY_CONFIG);
+	video_sig_delay_config.bits.video_sig_delay = byte_cycle;
+
+	writel(video_sig_delay_config.val, &reg->VIDEO_SIG_DELAY_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch data lane from high speed to low power
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_datalane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xAC phy_datalane_time_config;
+
+	phy_datalane_time_config.val = readl(&reg->PHY_DATALANE_TIME_CONFIG);
+	phy_datalane_time_config.bits.phy_datalane_hs_to_lp_time = byte_cycle;
+
+	writel(phy_datalane_time_config.val, &reg->PHY_DATALANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch the data lane from to low power high speed
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_datalane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xAC phy_datalane_time_config;
+
+	phy_datalane_time_config.val = readl(&reg->PHY_DATALANE_TIME_CONFIG);
+	phy_datalane_time_config.bits.phy_datalane_lp_to_hs_time = byte_cycle;
+
+	writel(phy_datalane_time_config.val, &reg->PHY_DATALANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch clock lane from high speed to low power
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_clklane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xA8 phy_clklane_time_config;
+
+	phy_clklane_time_config.val = readl(&reg->PHY_CLKLANE_TIME_CONFIG);
+	phy_clklane_time_config.bits.phy_clklane_hs_to_lp_time = byte_cycle;
+
+	writel(phy_clklane_time_config.val, &reg->PHY_CLKLANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch clock lane from to low power high speed
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_clklane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xA8 phy_clklane_time_config;
+
+	phy_clklane_time_config.val = readl(&reg->PHY_CLKLANE_TIME_CONFIG);
+	phy_clklane_time_config.bits.phy_clklane_lp_to_hs_time = byte_cycle;
+
+	writel(phy_clklane_time_config.val, &reg->PHY_CLKLANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to turn the bus around to start receiving
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_max_read_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->MAX_READ_TIME);
+}
+/**
+ * Enable the automatic mechanism to stop providing clock in the clock
+ * lane when time allows
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ * @return error code
+ */
+void dsi_nc_clk_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x74 phy_clk_lane_lp_ctrl;
+
+	phy_clk_lane_lp_ctrl.val = readl(&reg->PHY_CLK_LANE_LP_CTRL);
+	phy_clk_lane_lp_ctrl.bits.auto_clklane_ctrl_en = enable;
+
+	writel(phy_clk_lane_lp_ctrl.val, &reg->PHY_CLK_LANE_LP_CTRL);
+}
+/**
+ * Write transmission escape timeout
+ * a safe guard so that the state machine would reset if transmission
+ * takes too long
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param div
+ */
+void dsi_tx_escape_division(struct dsi_context *ctx, u8 div)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(div, &reg->TX_ESC_CLK_CONFIG);
+}
+/* PRESP Time outs */
+/**
+ * configure timeout divisions (so they would have more clock ticks)
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param div no of hs cycles before transiting back to LP in
+ *  (lane_clk / div)
+ */
+void dsi_timeout_clock_division(struct dsi_context *ctx, u8 div)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(div, &reg->TIMEOUT_CNT_CLK_CONFIG);
+}
+/**
+ * Configure the Low power receive time out
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle (of byte cycles)
+ */
+void dsi_lp_rx_timeout(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->LRX_H_TO_CONFIG);
+}
+/**
+ * Configure a high speed transmission time out
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle (byte cycles)
+ */
+void dsi_hs_tx_timeout(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->HTX_TO_CONFIG);
+}
+/**
+ * Get the error 0 interrupt register status
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask the mask to be read from the register
+ * @return error status 0 value
+ */
+u32 dsi_int0_status(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x08 protocol_int_sts;
+
+	protocol_int_sts.val = readl(&reg->PROTOCOL_INT_STS);
+	writel(protocol_int_sts.val, &reg->PROTOCOL_INT_CLR);
+
+	if (protocol_int_sts.bits.dphy_errors_0)
+		DRM_ERROR("dphy_err: escape entry error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_1)
+		DRM_ERROR("dphy_err: lp data transmission sync error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_2)
+		DRM_ERROR("dphy_err: control error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_3)
+		DRM_ERROR("dphy_err: LP0 contention error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_4)
+		DRM_ERROR("dphy_err: LP1 contention error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_0)
+		DRM_ERROR("ack_err: SoT error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_1)
+		DRM_ERROR("ack_err: SoT Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_2)
+		DRM_ERROR("ack_err: EoT Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_3)
+		DRM_ERROR("ack_err: Escape Mode Entry Command error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_4)
+		DRM_ERROR("ack_err: LP Transmit Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_5)
+		DRM_ERROR("ack_err: Peripheral Timeout error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_6)
+		DRM_ERROR("ack_err: False Control error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_7)
+		DRM_ERROR("ack_err: reserved (specific to device)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_8)
+		DRM_ERROR("ack_err: ECC error, single-bit (corrected)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_9)
+		DRM_ERROR("ack_err: ECC error, multi-bit (not corrected)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_10)
+		DRM_ERROR("ack_err: checksum error (long packet only)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_11)
+		DRM_ERROR("ack_err: not recognized DSI data type\n");
+
+	if (protocol_int_sts.bits.ack_with_err_12)
+		DRM_ERROR("ack_err: DSI VC ID Invalid\n");
+
+	if (protocol_int_sts.bits.ack_with_err_13)
+		DRM_ERROR("ack_err: invalid transmission length\n");
+
+	if (protocol_int_sts.bits.ack_with_err_14)
+		DRM_ERROR("ack_err: reserved (specific to device)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_15)
+		DRM_ERROR("ack_err: DSI protocol violation\n");
+
+	return 0;
+}
+/**
+ * Get the error 1 interrupt register status
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask the mask to be read from the register
+ * @return error status 1 value
+ */
+u32 dsi_int1_status(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x10 internal_int_sts;
+	u32 status = 0;
+
+	internal_int_sts.val = readl(&reg->INTERNAL_INT_STS);
+	writel(internal_int_sts.val, &reg->INTERNAL_INT_CLR);
+
+	if (internal_int_sts.bits.receive_pkt_size_err)
+		DRM_ERROR("receive packet size error\n");
+
+	if (internal_int_sts.bits.eotp_not_receive_err)
+		DRM_ERROR("EoTp packet is not received\n");
+
+	if (internal_int_sts.bits.gen_cmd_cmd_fifo_wr_err)
+		DRM_ERROR("cmd header-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_rdata_fifo_rd_err)
+		DRM_ERROR("cmd read-payload-fifo is empty\n");
+
+	if (internal_int_sts.bits.gen_cmd_rdata_fifo_wr_err)
+		DRM_ERROR("cmd read-payload-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_wdata_fifo_wr_err)
+		DRM_ERROR("cmd write-payload-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_wdata_fifo_rd_err)
+		DRM_ERROR("cmd write-payload-fifo is empty\n");
+
+	if (internal_int_sts.bits.dpi_pix_fifo_wr_err) {
+		DRM_ERROR("DPI pixel-fifo is full\n");
+		status |= DSI_INT_STS_NEED_SOFT_RESET;
+	}
+
+	if (internal_int_sts.bits.ecc_single_err)
+		DRM_ERROR("ECC single error in a received packet\n");
+
+	if (internal_int_sts.bits.ecc_multi_err)
+		DRM_ERROR("ECC multiple error in a received packet\n");
+
+	if (internal_int_sts.bits.crc_err)
+		DRM_ERROR("CRC error in the received packet payload\n");
+
+	if (internal_int_sts.bits.hs_tx_timeout)
+		DRM_ERROR("high-speed transmission timeout\n");
+
+	if (internal_int_sts.bits.lp_rx_timeout)
+		DRM_ERROR("low-power reception timeout\n");
+
+	return status;
+}
+/**
+ * Configure MASK (hiding) of interrupts coming from error 0 source
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask to be written to the register
+ */
+void dsi_int0_mask(struct dsi_context *ctx, u32 mask)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(mask, &reg->MASK_PROTOCOL_INT);
+}
+/**
+ * Configure MASK (hiding) of interrupts coming from error 1 source
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask the mask to be written to the register
+ */
+void dsi_int1_mask(struct dsi_context *ctx, u32 mask)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(mask, &reg->MASK_INTERNAL_INT);
+}
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
new file mode 100644
index 0000000..08b9958
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
@@ -0,0 +1,1477 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DSI_CTRL_R1P0_H_
+#define _DSI_CTRL_R1P0_H_
+
+#include <asm/types.h>
+
+#include "sprd_dsi.h"
+
+struct dsi_reg {
+	union _0x00 {
+		u32 val;
+		struct _DSI_VERSION {
+		u32 dsi_version: 16;
+		u32 reserved: 16;
+		} bits;
+	} DSI_VERSION;
+
+	union _0x04 {
+		u32 val;
+		struct _SOFT_RESET {
+		/*
+		 * This bit configures the core either to work normal or to
+		 * reset. It's default value is 0. After the core configur-
+		 * ation, to enable the mipi_dsi_host, set this register to 1.
+		 * 1: power up     0: reset core
+		 */
+		u32 dsi_soft_reset: 1;
+
+		u32 reserved: 31;
+		} bits;
+	} SOFT_RESET;
+
+	union _0x08 {
+		u32 val;
+		struct _PROTOCOL_INT_STS {
+		/* ErrEsc escape entry error from Lane 0 */
+		u32 dphy_errors_0: 1;
+
+		/* ErrSyncEsc low-power data transmission synchronization
+		 * error from Lane 0
+		 */
+		u32 dphy_errors_1: 1;
+
+		/* ErrControl error from Lane 0 */
+		u32 dphy_errors_2: 1;
+
+		/* ErrContentionLP0 LP0 contention error from Lane 0 */
+		u32 dphy_errors_3: 1;
+
+		/* ErrContentionLP1 LP1 contention error from Lane 0 */
+		u32 dphy_errors_4: 1;
+
+		/* debug mode protocol errors */
+		u32 protocol_debug_err: 11;
+
+		/* SoT error from the Acknowledge error report */
+		u32 ack_with_err_0: 1;
+
+		/* SoT Sync error from the Acknowledge error report */
+		u32 ack_with_err_1: 1;
+
+		/* EoT Sync error from the Acknowledge error report */
+		u32 ack_with_err_2: 1;
+
+		/* Escape Mode Entry Command error from the Acknowledge
+		 * error report
+		 */
+		u32 ack_with_err_3: 1;
+
+		/* LP Transmit Sync error from the Acknowledge error report */
+		u32 ack_with_err_4: 1;
+
+		/* Peripheral Timeout error from the Acknowledge error report */
+		u32 ack_with_err_5: 1;
+
+		/* False Control error from the Acknowledge error report */
+		u32 ack_with_err_6: 1;
+
+		/* reserved (specific to device) from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_7: 1;
+
+		/* ECC error, single-bit (detected and corrected) from the
+		 * Acknowledge error report
+		 */
+		u32 ack_with_err_8: 1;
+
+		/* ECC error, multi-bit (detected, not corrected) from the
+		 * Acknowledge error report
+		 */
+		u32 ack_with_err_9: 1;
+
+		/* checksum error (long packet only) from the Acknowledge
+		 * error report
+		 */
+		u32 ack_with_err_10: 1;
+
+		/* not recognized DSI data type from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_11: 1;
+
+		/* DSI VC ID Invalid from the Acknowledge error report */
+		u32 ack_with_err_12: 1;
+
+		/* invalid transmission length from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_13: 1;
+
+		/* reserved (specific to device) from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_14: 1;
+
+		/* DSI protocol violation from the Acknowledge error report */
+		u32 ack_with_err_15: 1;
+
+		} bits;
+	} PROTOCOL_INT_STS;
+
+	union _0x0C {
+		u32 val;
+		struct _MASK_PROTOCOL_INT {
+		u32 mask_dphy_errors_0: 1;
+		u32 mask_dphy_errors_1: 1;
+		u32 mask_dphy_errors_2: 1;
+		u32 mask_dphy_errors_3: 1;
+		u32 mask_dphy_errors_4: 1;
+		u32 mask_protocol_debug_err: 11;
+		u32 mask_ack_with_err_0: 1;
+		u32 mask_ack_with_err_1: 1;
+		u32 mask_ack_with_err_2: 1;
+		u32 mask_ack_with_err_3: 1;
+		u32 mask_ack_with_err_4: 1;
+		u32 mask_ack_with_err_5: 1;
+		u32 mask_ack_with_err_6: 1;
+		u32 mask_ack_with_err_7: 1;
+		u32 mask_ack_with_err_8: 1;
+		u32 mask_ack_with_err_9: 1;
+		u32 mask_ack_with_err_10: 1;
+		u32 mask_ack_with_err_11: 1;
+		u32 mask_ack_with_err_12: 1;
+		u32 mask_ack_with_err_13: 1;
+		u32 mask_ack_with_err_14: 1;
+		u32 mask_ack_with_err_15: 1;
+		} bits;
+	} MASK_PROTOCOL_INT;
+
+	union _0x10 {
+		u32 val;
+		struct _INTERNAL_INT_STS {
+		/* This bit indicates that the packet size error is detected
+		 * during the packet reception.
+		 */
+		u32 receive_pkt_size_err: 1;
+
+		/* This bit indicates that the EoTp packet is not received at
+		 * the end of the incoming peripheral transmission
+		 */
+		u32 eotp_not_receive_err: 1;
+
+		/* This bit indicates that the system tried to write a command
+		 * through the Generic interface and the FIFO is full. There-
+		 * fore, the command is not written.
+		 */
+		u32 gen_cmd_cmd_fifo_wr_err: 1;
+
+		/* This bit indicates that during a DCS read data, the payload
+		 * FIFO becomes	empty and the data sent to the interface is
+		 * corrupted.
+		 */
+		u32 gen_cmd_rdata_fifo_rd_err: 1;
+
+		/* This bit indicates that during a generic interface packet
+		 * read back, the payload FIFO becomes full and the received
+		 * data is corrupted.
+		 */
+		u32 gen_cmd_rdata_fifo_wr_err: 1;
+
+		/* This bit indicates that the system tried to write a payload
+		 * data through the Generic interface and the FIFO is full.
+		 * Therefore, the payload is not written.
+		 */
+		u32 gen_cmd_wdata_fifo_wr_err: 1;
+
+		/* This bit indicates that during a Generic interface packet
+		 * build, the payload FIFO becomes empty and corrupt data is
+		 * sent.
+		 */
+		u32 gen_cmd_wdata_fifo_rd_err: 1;
+
+		/* This bit indicates that during a DPI pixel line storage,
+		 * the payload FIFO becomes full and the data stored is
+		 * corrupted.
+		 */
+		u32 dpi_pix_fifo_wr_err: 1;
+
+		/* internal debug error	*/
+		u32 internal_debug_err: 19;
+
+		/* This bit indicates that the ECC single error is detected
+		 * and corrected in a received packet.
+		 */
+		u32 ecc_single_err: 1;
+
+		/* This bit indicates that the ECC multiple error is detected
+		 * in a received packet.
+		 */
+		u32 ecc_multi_err: 1;
+
+		/* This bit indicates that the CRC error is detected in the
+		 * received packet payload.
+		 */
+		u32 crc_err: 1;
+
+		/* This bit indicates that the high-speed transmission timeout
+		 * counter reached the end and contention is detected.
+		 */
+		u32 hs_tx_timeout: 1;
+
+		/* This bit indicates that the low-power reception timeout
+		 * counter reached the end and contention is detected.
+		 */
+		u32 lp_rx_timeout: 1;
+
+		} bits;
+	} INTERNAL_INT_STS;
+
+	union _0x14 {
+		u32 val;
+		struct _MASK_INTERNAL_INT {
+		u32 mask_receive_pkt_size_err: 1;
+		u32 mask_eopt_not_receive_err: 1;
+		u32 mask_gen_cmd_cmd_fifo_wr_err: 1;
+		u32 mask_gen_cmd_rdata_fifo_rd_err: 1;
+		u32 mask_gen_cmd_rdata_fifo_wr_err: 1;
+		u32 mask_gen_cmd_wdata_fifo_wr_err: 1;
+		u32 mask_gen_cmd_wdata_fifo_rd_err: 1;
+		u32 mask_dpi_pix_fifo_wr_err: 1;
+		u32 mask_internal_debug_err: 19;
+		u32 mask_ecc_single_err: 1;
+		u32 mask_ecc_multi_err: 1;
+		u32 mask_crc_err: 1;
+		u32 mask_hs_tx_timeout: 1;
+		u32 mask_lp_rx_timeout: 1;
+		} bits;
+	} MASK_INTERNAL_INT;
+
+	union _0x18 {
+		u32 val;
+		struct _DSI_MODE_CFG {
+		/* This bit configures the operation mode
+		 * 0: Video mode ;   1: Command mode
+		 */
+		u32 cmd_video_mode: 1;
+
+		u32 reserved: 31;
+
+		} bits;
+	} DSI_MODE_CFG;
+
+	union _0x1C {
+		u32 val;
+		struct _VIRTUAL_CHANNEL_ID {
+		/* This field indicates the Generic interface read-back
+		 * virtual channel identification
+		 */
+		u32 gen_rx_vcid: 2;
+
+		/* This field configures the DPI virtual channel id that
+		 * is indexed to the VIDEO mode packets
+		 */
+		u32 video_pkt_vcid: 2;
+
+		u32 reserved: 28;
+
+		} bits;
+	} VIRTUAL_CHANNEL_ID;
+
+	union _0x20 {
+		u32 val;
+		struct _DPI_VIDEO_FORMAT {
+		/*
+		 * This field configures the DPI color coding as follows:
+		 * 0000: 16-bit configuration 1
+		 * 0001: 16-bit configuration 2
+		 * 0010: 16-bit configuration 3
+		 * 0011: 18-bit configuration 1
+		 * 0100: 18-bit configuration 2
+		 * 0101: 24-bit
+		 * 0110: 20-bit YCbCr 4:2:2 loosely packed
+		 * 0111: 24-bit YCbCr 4:2:2
+		 * 1000: 16-bit YCbCr 4:2:2
+		 * 1001: 30-bit
+		 * 1010: 36-bit
+		 * 1011: 12-bit YCbCr 4:2:0
+		 * 1100: Compression Display Stream
+		 * 1101-1111: 12-bit YCbCr 4:2:0
+		 */
+		u32 dpi_video_mode_format: 6;
+
+		/* When set to 1, this bit activates loosely packed
+		 * variant to 18-bit configurations
+		 */
+		u32 loosely18_en: 1;
+
+		u32 reserved: 25;
+
+		} bits;
+	} DPI_VIDEO_FORMAT;
+
+	union _0x24 {
+		u32 val;
+		struct _VIDEO_PKT_CONFIG {
+		/*
+		 * This field configures the number of pixels in a single
+		 * video packet. For 18-bit not loosely packed data types,
+		 * this number must be a multiple of 4. For YCbCr data
+		 * types, it must be a multiple of 2, as described in the
+		 * DSI specification.
+		 */
+		u32 video_pkt_size: 16;
+
+		/*
+		 * This register configures the number of chunks to be
+		 * transmitted during a Line period (a chunk consists of
+		 * a video packet and a null packet). If set to 0 or 1,
+		 * the video line is transmitted in a single packet. If
+		 * set to 1, the packet is part of a chunk, so a null packet
+		 * follows it if vid_null_size > 0. Otherwise, multiple chunks
+		 * are used to transmit each video line.
+		 */
+		u32 video_line_chunk_num: 16;
+
+		} bits;
+	} VIDEO_PKT_CONFIG;
+
+	union _0x28 {
+		u32 val;
+		struct _VIDEO_LINE_HBLK_TIME {
+		/* This field configures the Horizontal Back Porch period
+		 * in lane byte clock cycles
+		 */
+		u32 video_line_hbp_time: 16;
+
+		/* This field configures the Horizontal Synchronism Active
+		 * period in lane byte clock cycles
+		 */
+		u32 video_line_hsa_time: 16;
+
+		} bits;
+	} VIDEO_LINE_HBLK_TIME;
+
+	union _0x2C {
+		u32 val;
+		struct _VIDEO_LINE_TIME {
+		/* This field configures the size of the total line time
+		 * (HSA+HBP+HACT+HFP) counted in lane byte clock cycles
+		 */
+		u32 video_line_time: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} VIDEO_LINE_TIME;
+
+	union _0x30 {
+		u32 val;
+		struct _VIDEO_VBLK_LINES {
+		/* This field configures the Vertical Front Porch period
+		 * measured in number of horizontal lines
+		 */
+		u32 vfp_lines: 10;
+
+		/* This field configures the Vertical Back Porch period
+		 * measured in number of horizontal lines
+		 */
+		u32 vbp_lines: 10;
+
+		/* This field configures the Vertical Synchronism Active
+		 * period measured in number of horizontal lines
+		 */
+		u32 vsa_lines: 10;
+
+		u32 reserved: 2;
+
+		} bits;
+	} VIDEO_VBLK_LINES;
+
+	union _0x34 {
+		u32 val;
+		struct _VIDEO_VACTIVE_LINES {
+		/* This field configures the Vertical Active period measured
+		 * in number of horizontal lines
+		 */
+		u32 vactive_lines: 14;
+
+		u32 reserved: 18;
+
+		} bits;
+	} VIDEO_VACTIVE_LINES;
+
+	union _0x38 {
+		u32 val;
+		struct _VID_MODE_CFG {
+		/*
+		 * This field indicates the video mode transmission type as
+		 * follows:
+		 * 00: Non-burst with sync pulses
+		 * 01: Non-burst with sync events
+		 * 10 and 11: Burst mode
+		 */
+		u32 vid_mode_type: 2;
+
+		u32 reserved_0: 6;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VSA period when timing allows.
+		 */
+		u32 lp_vsa_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VBP period when timing allows.
+		 */
+		u32 lp_vbp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VFP period when timing allows.
+		 */
+		u32 lp_vfp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VACT period when timing allows.
+		 */
+		u32 lp_vact_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the HBP period when timing allows.
+		 */
+		u32 lp_hbp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the HFP period when timing allows.
+		 */
+		u32 lp_hfp_en: 1;
+
+		/* When set to 1, this bit enables the request for an ack-
+		 * nowledge response at the end of a frame.
+		 */
+		u32 frame_bta_ack_en: 1;
+
+		/* When set to 1, this bit enables the command transmission
+		 * only in low-power mode.
+		 */
+		u32 lp_cmd_en: 1;
+
+		u32 reserved_1: 16;
+
+		} bits;
+	} VID_MODE_CFG;
+
+	union _0x3C {
+		u32 val;
+		struct _SDF_MODE_CONFIG {
+		/*
+		 * This field defines the 3D mode on/off & display orientation:
+		 * 00: 3D mode off (2D mode on)
+		 * 01: 3D mode on, portrait orientation
+		 * 10: 3D mode on, landscape orientation
+		 * 11: Reserved
+		 */
+		u32 rf_3d_mode: 2;
+
+		/*
+		 * This field defines the 3D image format:
+		 * 00: Line (alternating lines of left and right data)
+		 * 01: Frame (alternating frames of left and right data)
+		 * 10: Pixel (alternating pixels of left and right data)
+		 * 11: Reserved
+		 */
+		u32 rf_3d_format: 2;
+
+		/*
+		 * This field defines whether there is a second VSYNC pulse
+		 * between Left and Right Images, when 3D Image Format is
+		 * Frame-based:
+		 * 0: No sync pulses between left and right data
+		 * 1: Sync pulse (HSYNC, VSYNC, blanking) between left and
+		 *    right data
+		 */
+		u32 second_vsync_en: 1;
+
+		/*
+		 * This bit defines the left or right order:
+		 * 0: Left eye data is sent first, and then the right eye data
+		 *    is sent.
+		 * 1: Right eye data is sent first, and then the left eye data
+		 *    is sent.
+		 */
+		u32 left_right_order: 1;
+
+		u32 reserved_0: 2;
+
+		/*
+		 * When set, causes the next VSS packet to include 3D control
+		 * payload in every VSS packet.
+		 */
+		u32 rf_3d_payload_en: 1;
+
+		u32 reserved_1: 23;
+
+		} bits;
+	} SDF_MODE_CONFIG;
+
+	union _0x40 {
+		u32 val;
+		struct _TIMEOUT_CNT_CLK_CONFIG {
+		/*
+		 * This field indicates the division factor for the Time Out
+		 * clock used as the timing unit in the configuration of HS to
+		 * LP and LP to HS transition error.
+		 */
+		u32 timeout_cnt_clk_config: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} TIMEOUT_CNT_CLK_CONFIG;
+
+	union _0x44 {
+		u32 val;
+		struct _HTX_TO_CONFIG {
+		/*
+		 * This field configures the timeout counter that triggers
+		 * a high speed transmission timeout contention detection
+		 * (measured in TO_CLK_DIVISION cycles).
+		 *
+		 * If using the non-burst mode and there is no sufficient
+		 * time to switch from HS to LP and back in the period which
+		 * is from one line data finishing to the next line sync
+		 * start, the DSI link returns the LP state once per frame,
+		 * then you should configure the TO_CLK_DIVISION and
+		 * hstx_to_cnt to be in accordance with:
+		 * hstx_to_cnt * lanebyteclkperiod * TO_CLK_DIVISION >= the
+		 * time of one FRAME data transmission * (1 + 10%)
+		 *
+		 * In burst mode, RGB pixel packets are time-compressed,
+		 * leaving more time during a scan line. Therefore, if in
+		 * burst mode and there is sufficient time to switch from HS
+		 * to LP and back in the period of time from one line data
+		 * finishing to the next line sync start, the DSI link can
+		 * return LP mode and back in this time interval to save power.
+		 * For this, configure the TO_CLK_DIVISION and hstx_to_cnt
+		 * to be in accordance with:
+		 * hstx_to_cnt * lanebyteclkperiod * TO_CLK_DIVISION >= the
+		 * time of one LINE data transmission * (1 + 10%)
+		 */
+		u32 htx_to_cnt_limit: 32;
+		} bits;
+	} HTX_TO_CONFIG;
+
+	union _0x48 {
+		u32 val;
+		struct _LRX_H_TO_CONFIG {
+		/*
+		 * This field configures the timeout counter that triggers
+		 * a low-power reception timeout contention detection (measured
+		 * in TO_CLK_DIVISION cycles).
+		 */
+		u32 lrx_h_to_cnt_limit: 32;
+		} bits;
+	} LRX_H_TO_CONFIG;
+
+	union _0x4C {
+		u32 val;
+		struct _RD_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a low-power read oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 lprd_presp_to_cnt_limit: 16;
+
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a high-speed read oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 hsrd_presp_to_cnt_limit: 16;
+
+		} bits;
+	} RD_PRESP_TO_CONFIG;
+
+	union _0x50 {
+		u32 val;
+		struct _HSWR_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link inactive after sending a high-speed write
+		 * operation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 hswr_presp_to_cnt_limit: 16;
+
+		u32 reserved_0: 8;
+
+		/*
+		 * When set to 1, this bit ensures that the peripheral response
+		 * timeout caused by hs_wr_to_cnt is used only once per eDPI
+		 * frame, when both the following conditions are met:
+		 * dpivsync_edpiwms has risen and fallen.
+		 * Packets originated from eDPI have been transmitted and its
+		 * FIFO is empty again In this scenario no non-eDPI requests
+		 * are sent to the D-PHY, even if there is traffic from generic
+		 * or DBI ready to be sent, making it return to stop state.
+		 * When it does so, PRESP_TO counter is activated and only when
+		 * it finishes does the controller send any other traffic that
+		 * is ready.
+		 */
+		u32 hswr_presp_to_mode: 1;
+
+		u32 reserved_1: 7;
+
+		} bits;
+	} HSWR_PRESP_TO_CONFIG;
+
+	union _0x54 {
+		u32 val;
+		struct _LPWR_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a low-power write oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 lpwr_presp_to_cnt_limit: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} LPWR_PRESP_TO_CONFIG;
+
+	union _0x58 {
+		u32 val;
+		struct _BTA_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after completing a Bus Turn-Around.
+		 * This period is measured in cycles of lanebyteclk. The
+		 * counting starts when the D-PHY enters the Stop state and
+		 * causes no interrupts.
+		 */
+		u32 bta_presp_to_cnt_limit: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} BTA_PRESP_TO_CONFIG;
+
+	union _0x5C {
+		u32 val;
+		struct _TX_ESC_CLK_CONFIG {
+		/*
+		 * This field indicates the division factor for the TX Escape
+		 * clock source (lanebyteclk). The values 0 and 1 stop the
+		 * TX_ESC clock generation.
+		 */
+		u32 tx_esc_clk_config: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} TX_ESC_CLK_CONFIG;
+
+	union _0x60 {
+		u32 val;
+		struct _VACT_CMD_TRANS_LIMIT {
+		/*
+		 * This field is used for the transmission of commands in
+		 * low-power mode. It defines the size, in bytes, of the
+		 * largest packet that can fit in a line during the VACT
+		 * region.
+		 */
+		u32 vact_cmd_trans_limit: 8;
+
+		u32 reserved: 24;
+
+		} bits;
+	} VACT_CMD_TRANS_LIMIT;
+
+	union _0x64 {
+		u32 val;
+		struct _VBLK_CMD_TRANS_LIMIT {
+		/*
+		 * This field is used for the transmission of commands in
+		 * low-power mode. It defines the size, in bytes, of the
+		 * largest packet that can fit in a line during the VSA, VBP,
+		 * and VFP regions.
+		 */
+		u32 vblk_cmd_trans_limit: 8;
+
+		u32 reserved: 24;
+
+		} bits;
+	} VBLK_CMD_TRANS_LIMIT;
+
+	union _0x68 {
+		u32 val;
+		struct _CMD_MODE_CFG {
+		/*
+		 * When set to 1, this bit enables the tearing effect
+		 * acknowledge request.
+		 */
+		u32 tear_fx_en: 1;
+
+		/*
+		 * When set to 1, this bit enables the acknowledge request
+		 * after each packet transmission.
+		 */
+		u32 ack_rqst_en: 1;
+
+		u32 reserved_0: 3;
+
+		u32 pps_tx: 1;
+		u32 exq_tx: 1;
+		u32 cmc_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * zero parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_0p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * one parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_1p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * two parameters command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_2p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * zero parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_0p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * one parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_1p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * two parameters command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_2p_tx: 1;
+
+		/*
+		 * This bit configures the Generic long write packet command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_lw_tx: 1;
+
+		u32 reserved_1: 1;
+
+		/*
+		 * This bit configures the DCS short write packet with zero
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sw_0p_tx: 1;
+
+		/*
+		 * This bit configures the DCS short write packet with one
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sw_1p_tx: 1;
+
+		/*
+		 * This bit configures the DCS short read packet with zero
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sr_0p_tx: 1;
+
+		/*
+		 * This bit configures the DCS long write packet command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_lw_tx: 1;
+
+		u32 reserved_2: 4;
+
+		/*
+		 * This bit configures the maximum read packet size command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 max_rd_pkt_size: 1;
+
+		u32 reserved_3: 7;
+
+		} bits;
+	} CMD_MODE_CFG;
+
+	union _0x6C {
+		u32 val;
+		struct _GEN_HDR {
+		/*
+		 * This field configures the packet data type of the header
+		 * packet.
+		 */
+		u32 gen_dt: 6;
+
+		/*
+		 * This field configures the virtual channel id of the header
+		 * packet.
+		 */
+		u32 gen_vc: 2;
+
+		/*
+		 * This field configures the least significant byte of the
+		 * header packet's Word count for long packets or data 0 for
+		 * short packets.
+		 */
+		u32 gen_wc_lsbyte: 8;
+
+		/*
+		 * This field configures the most significant byte of the
+		 * header packet's word count for long packets or data 1 for
+		 * short packets.
+		 */
+		u32 gen_wc_msbyte: 8;
+
+		u32 reserved: 8;
+
+		} bits;
+	} GEN_HDR;
+
+	union _0x70 {
+		u32 val;
+		struct _GEN_PLD_DATA {
+		/* This field indicates byte 1 of the packet payload. */
+		u32 gen_pld_b1: 8;
+
+		/* This field indicates byte 2 of the packet payload. */
+		u32 gen_pld_b2: 8;
+
+		/* This field indicates byte 3 of the packet payload. */
+		u32 gen_pld_b3: 8;
+
+		/* This field indicates byte 4 of the packet payload. */
+		u32 gen_pld_b4: 8;
+
+		} bits;
+	} GEN_PLD_DATA;
+
+	union _0x74 {
+		u32 val;
+		struct _PHY_CLK_LANE_LP_CTRL {
+		/* This bit controls the D-PHY PPI txrequestclkhs signal */
+		u32 phy_clklane_tx_req_hs: 1;
+
+		/* This bit enables the automatic mechanism to stop providing
+		 * clock in the clock lane when time allows.
+		 */
+		u32 auto_clklane_ctrl_en: 1;
+
+		u32 reserved: 30;
+		} bits;
+	} PHY_CLK_LANE_LP_CTRL;
+
+	union _0x78 {
+		u32 val;
+		struct _PHY_INTERFACE_CTRL {
+		/* When set to 0, this bit places the D-PHY macro in power-
+		 * down state.
+		 */
+		u32 rf_phy_shutdown: 1;
+
+		/* When set to 0, this bit places the digital section of the
+		 * D-PHY in the reset state.
+		 */
+		u32 rf_phy_reset_n: 1;
+
+		/* When set to 1, this bit enables the D-PHY Clock Lane
+		 * module.
+		 */
+		u32 rf_phy_clk_en: 1;
+
+		/* When the D-PHY is in ULPS, this bit enables the D-PHY PLL. */
+		u32 rf_phy_force_pll: 1;
+
+		/* ULPS mode Request on clock lane */
+		u32 rf_phy_clk_txrequlps: 1;
+
+		/* ULPS mode Exit on clock lane */
+		u32 rf_phy_clk_txexitulps: 1;
+
+		/* ULPS mode Request on all active data lanes */
+		u32 rf_phy_data_txrequlps: 1;
+
+		/* ULPS mode Exit on all active data lanes */
+		u32 rf_phy_data_txexitulps: 1;
+
+		u32 reserved: 24;
+		} bits;
+	} PHY_INTERFACE_CTRL;
+
+	union _0x7C {
+		u32 val;
+		struct _PHY_TX_TRIGGERS {
+		/* This field controls the trigger transmissions. */
+		u32 phy_tx_triggers: 4;
+
+		u32 reserved: 28;
+		} bits;
+	} PHY_TX_TRIGGERS;
+
+	union _0x80 {
+		u32 val;
+		struct _DESKEW_START {
+		u32 deskew_start: 1;
+		u32 reserved: 31;
+		} bits;
+	} DESKEW_START;
+
+	union _0x84 {
+		u32 val;
+		struct _DESKEW_MODE {
+		u32 deskew_mode: 2;
+		u32 reserved: 30;
+		} bits;
+	} DESKEW_MODE;
+
+	union _0x88 {
+		u32 val;
+		struct _DESKEW_TIME {
+		u32 deskew_time: 32;
+		} bits;
+	} DESKEW_TIME;
+
+	union _0x8C {
+		u32 val;
+		struct _DESKEW_PERIOD {
+		u32 deskew_period: 32;
+		} bits;
+	} DESKEW_PERIOD;
+
+	union _0x90 {
+		u32 val;
+		struct _DESKEW_BUSY {
+		u32 deskew_busy: 1;
+		u32 reserved: 31;
+		} bits;
+	} DESKEW_BUSY;
+
+	union _0x94 {
+		u32 val;
+		struct _DESKEW_LANE_MASK {
+		u32 deskew_lane0_mask: 1;
+		u32 deskew_lane1_mask: 1;
+		u32 deskew_lane2_mask: 1;
+		u32 deskew_lane3_mask: 1;
+		u32 reserved: 28;
+		} bits;
+	} DESKEW_LANE_MASK;
+
+	union _0x98 {
+		u32 val;
+		struct _CMD_MODE_STATUS {
+		/*
+		 * This bit is set when a read command is issued and cleared
+		 * when the entire response is stored in the FIFO.
+		 * Value after reset: 0x0
+		 *
+		 * NOTE:
+		 * For mipi-dsi-r1p0 IP, this bit is set immediately when
+		 *     the read cmd is set to the GEN_HDR register.
+		 *
+		 * For dsi-ctrl-r1p0 IP, this bit is set only after the read
+		 *     cmd was actually sent out from the controller.
+		 */
+		u32 gen_cmd_rdcmd_ongoing: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic read
+		 * payload FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_rdata_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic read
+		 * payload FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_rdata_fifo_full: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic write
+		 * payload FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_wdata_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic write
+		 * payload FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_wdata_fifo_full: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic
+		 * command FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_cmd_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic
+		 * command FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_cmd_fifo_full: 1;
+
+		/*
+		 * This bit is set when the entire response of read is
+		 * stored in the rx payload FIFO. And it will be cleared
+		 * automaticlly after read this bit each time.
+		 * Value after reset: 0x0
+		 *
+		 * NOTE: this bit is just supported for dsi-ctrl-r1p0 IP
+		 */
+		u32 gen_cmd_rdcmd_done: 1;
+
+		u32 reserved : 24;
+
+		} bits;
+	} CMD_MODE_STATUS;
+
+	union _0x9C {
+		u32 val;
+		struct _PHY_STATUS {
+		/* the status of phydirection D-PHY signal */
+		u32 phy_direction: 1;
+
+		/* the status of phylock D-PHY signal */
+		u32 phy_lock: 1;
+
+		/* the status of rxulpsesc0lane D-PHY signal */
+		u32 phy_rxulpsesc0lane: 1;
+
+		/* the status of phystopstateclklane D-PHY signal */
+		u32 phy_stopstateclklane: 1;
+
+		/* the status of phystopstate0lane D-PHY signal */
+		u32 phy_stopstate0lane: 1;
+
+		/* the status of phystopstate1lane D-PHY signal */
+		u32 phy_stopstate1lane: 1;
+
+		/* the status of phystopstate2lane D-PHY signal */
+		u32 phy_stopstate2lane: 1;
+
+		/* the status of phystopstate3lane D-PHY signal */
+		u32 phy_stopstate3lane: 1;
+
+		/* the status of phyulpsactivenotclk D-PHY signal */
+		u32 phy_ulpsactivenotclk: 1;
+
+		/* the status of ulpsactivenot0lane D-PHY signal */
+		u32 phy_ulpsactivenot0lane: 1;
+
+		/* the status of ulpsactivenot1lane D-PHY signal */
+		u32 phy_ulpsactivenot1lane: 1;
+
+		/* the status of ulpsactivenot2lane D-PHY signal */
+		u32 phy_ulpsactivenot2lane: 1;
+
+		/* the status of ulpsactivenot3lane D-PHY signal */
+		u32 phy_ulpsactivenot3lane: 1;
+
+		u32 reserved: 19;
+
+		} bits;
+	} PHY_STATUS;
+
+	union _0xA0 {
+		u32 val;
+		struct _PHY_MIN_STOP_TIME {
+		/* This field configures the minimum wait period to request
+		 * a high-speed transmission after the Stop state.
+		 */
+		u32 phy_min_stop_time: 8;
+
+		u32 reserved: 24;
+		} bits;
+	} PHY_MIN_STOP_TIME;
+
+	union _0xA4 {
+		u32 val;
+		struct _PHY_LANE_NUM_CONFIG {
+		/*
+		 * This field configures the number of active data lanes:
+		 * 00: One data lane (lane 0)
+		 * 01: Two data lanes (lanes 0 and 1)
+		 * 10: Three data lanes (lanes 0, 1, and 2)
+		 * 11: Four data lanes (lanes 0, 1, 2, and 3)
+		 */
+		u32 phy_lane_num: 2;
+
+		u32 reserved: 30;
+
+		} bits;
+	} PHY_LANE_NUM_CONFIG;
+
+	union _0xA8 {
+		u32 val;
+		struct _PHY_CLKLANE_TIME_CONFIG {
+		/*
+		 * This field configures the maximum time that the D-PHY
+		 * clock lane takes to go from low-power to high-speed
+		 * transmission measured in lane byte clock cycles.
+		 */
+		u32 phy_clklane_lp_to_hs_time: 16;
+
+		/*
+		 * This field configures the maximum time that the D-PHY
+		 * clock lane takes to go from high-speed to low-power
+		 * transmission measured in lane byte clock cycles.
+		 */
+		u32 phy_clklane_hs_to_lp_time: 16;
+
+		} bits;
+	} PHY_CLKLANE_TIME_CONFIG;
+
+	union _0xAC {
+		u32 val;
+		struct _PHY_DATALANE_TIME_CONFIG {
+		/*
+		 * This field configures the maximum time that the D-PHY data
+		 * lanes take to go from low-power to high-speed transmission
+		 * measured in lane byte clock cycles.
+		 */
+		u32 phy_datalane_lp_to_hs_time: 16;
+
+		/*
+		 * This field configures the maximum time that the D-PHY data
+		 * lanes take to go from high-speed to low-power transmission
+		 * measured in lane byte clock cycles.
+		 */
+		u32 phy_datalane_hs_to_lp_time: 16;
+
+		} bits;
+	} PHY_DATALANE_TIME_CONFIG;
+
+	union _0xB0 {
+		u32 val;
+		struct _MAX_READ_TIME {
+		/*
+		 * This field configures the maximum time required to perform
+		 * a read command in lane byte clock cycles. This register can
+		 * only be modified when no read command is in progress.
+		 */
+		u32 max_rd_time: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} MAX_READ_TIME;
+
+	union _0xB4 {
+		u32 val;
+		struct _RX_PKT_CHECK_CONFIG {
+		/* When set to 1, this bit enables the ECC reception, error
+		 * correction, and reporting.
+		 */
+		u32 rx_pkt_ecc_en: 1;
+
+		/* When set to 1, this bit enables the CRC reception and error
+		 * reporting.
+		 */
+		u32 rx_pkt_crc_en: 1;
+
+		u32 reserved: 30;
+
+		} bits;
+	} RX_PKT_CHECK_CONFIG;
+
+	union _0xB8 {
+		u32 val;
+		struct _TA_EN {
+		/* When set to 1, this bit enables the Bus Turn-Around (BTA)
+		 * request.
+		 */
+		u32 ta_en: 1;
+
+		u32 reserved: 31;
+
+		} bits;
+	} TA_EN;
+
+	union _0xBC {
+		u32 val;
+		struct _EOTP_EN {
+		/* When set to 1, this bit enables the EoTp transmission */
+		u32 tx_eotp_en: 1;
+
+		/* When set to 1, this bit enables the EoTp reception. */
+		u32 rx_eotp_en: 1;
+
+		u32 reserved: 30;
+
+		} bits;
+	} EOTP_EN;
+
+	union _0xC0 {
+		u32 val;
+		struct _VIDEO_NULLPKT_SIZE {
+		/*
+		 * This register configures the number of bytes inside a null
+		 * packet. Setting it to 0 disables the null packets.
+		 */
+		u32 video_nullpkt_size: 13;
+
+		u32 reserved: 19;
+
+		} bits;
+	} VIDEO_NULLPKT_SIZE;
+
+	union _0xC4 {
+		u32 val;
+		struct _DCS_WM_PKT_SIZE {
+		/*
+		 * This field configures the maximum allowed size for an eDPI
+		 * write memory command, measured in pixels. Automatic parti-
+		 * tioning of data obtained from eDPI is permanently enabled.
+		 */
+		u32 dcs_wm_pkt_size: 16;
+
+		u32 reserved: 16;
+		} bits;
+	} DCS_WM_PKT_SIZE;
+
+	union _0xC8 {
+		u32 val;
+		struct _PROTOCOL_INT_CLR {
+		u32 clr_dphy_errors_0: 1;
+		u32 clr_dphy_errors_1: 1;
+		u32 clr_dphy_errors_2: 1;
+		u32 clr_dphy_errors_3: 1;
+		u32 clr_dphy_errors_4: 1;
+		u32 clr_protocol_debug_err: 11;
+		u32 clr_ack_with_err_0: 1;
+		u32 clr_ack_with_err_1: 1;
+		u32 clr_ack_with_err_2: 1;
+		u32 clr_ack_with_err_3: 1;
+		u32 clr_ack_with_err_4: 1;
+		u32 clr_ack_with_err_5: 1;
+		u32 clr_ack_with_err_6: 1;
+		u32 clr_ack_with_err_7: 1;
+		u32 clr_ack_with_err_8: 1;
+		u32 clr_ack_with_err_9: 1;
+		u32 clr_ack_with_err_10: 1;
+		u32 clr_ack_with_err_11: 1;
+		u32 clr_ack_with_err_12: 1;
+		u32 clr_ack_with_err_13: 1;
+		u32 clr_ack_with_err_14: 1;
+		u32 clr_ack_with_err_15: 1;
+		} bits;
+	} PROTOCOL_INT_CLR;
+
+	union _0xCC {
+		u32 val;
+		struct _INTERNAL_INT_CLR {
+		u32 clr_receive_pkt_size_err: 1;
+		u32 clr_eopt_not_receive_err: 1;
+		u32 clr_gen_cmd_cmd_fifo_wr_err: 1;
+		u32 clr_gen_cmd_rdata_fifo_rd_err: 1;
+		u32 clr_gen_cmd_rdata_fifo_wr_err: 1;
+		u32 clr_gen_cmd_wdata_fifo_wr_err: 1;
+		u32 clr_gen_cmd_wdata_fifo_rd_err: 1;
+		u32 clr_dpi_pix_fifo_wr_err: 1;
+		u32 clr_internal_debug_err: 19;
+		u32 clr_ecc_single_err: 1;
+		u32 clr_ecc_multi_err: 1;
+		u32 clr_crc_err: 1;
+		u32 clr_hs_tx_timeout: 1;
+		u32 clr_lp_rx_timeout: 1;
+		} bits;
+	} INTERNAL_INT_CLR;
+
+	union _0xD0 {
+		u32 val;
+		struct _VIDEO_SIG_DELAY_CONFIG {
+
+		/*
+		 * DPI interface signal delay to be used in clk lanebyte
+		 * domain for control logic to read video data from pixel
+		 * memory in mannal mode, measured in clk_lanebyte cycles
+		 */
+		u32 video_sig_delay: 24;
+
+		/*
+		 * 1'b1: mannal mode
+		 *       dsi controller will use video_sig_delay value as
+		 *       the delay for the packet handle logic to read video
+		 *       data from pixel memory.
+		 *
+		 * 1'b0: auto mode
+		 *       dsi controller will auto calculate the delay for
+		 *       the packet handle logic to read video data from
+		 *       pixel memory.
+		 */
+		u32 video_sig_delay_mode: 1;
+
+		u32 reserved: 7;
+		} bits;
+	} VIDEO_SIG_DELAY_CONFIG;
+
+	u32 reservedD4_EC[7];
+
+	union _0xF0 {
+		u32 val;
+		struct _PHY_TST_CTRL0 {
+		/* PHY test interface clear (active high) */
+		u32 phy_testclr: 1;
+
+		/* This bit is used to clock the TESTDIN bus into the D-PHY */
+		u32 phy_testclk: 1;
+
+		u32 reserved: 30;
+		} bits;
+	} PHY_TST_CTRL0;
+
+	union _0xF4 {
+		u32 val;
+		struct _PHY_TST_CTRL1 {
+		/* PHY test interface input 8-bit data bus for internal
+		 * register programming and test functionalities access.
+		 */
+		u32 phy_testdin: 8;
+
+		/* PHY output 8-bit data bus for read-back and internal
+		 * probing functionalities.
+		 */
+		u32 phy_testdout: 8;
+
+		/*
+		 * PHY test interface operation selector:
+		 * 1: The address write operation is set on the falling edge
+		 *    of the testclk signal.
+		 * 0: The data write operation is set on the rising edge of
+		 *    the testclk signal.
+		 */
+		u32 phy_testen: 1;
+
+		u32 reserved: 15;
+		} bits;
+	} PHY_TST_CTRL1;
+
+	u32 reservedF8_1FC[66];
+
+	union _0x200 {
+		u32 val;
+		struct _INT_PLL_STS {
+		u32 int_pll_sts: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_STS;
+
+	union _0x204 {
+		u32 val;
+		struct _INT_PLL_MSK {
+		u32 int_pll_msk: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_MSK;
+
+	union _0x208 {
+		u32 val;
+		struct _INT_PLL_CLR {
+		u32 int_pll_clr: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_CLR;
+
+};
+
+bool dsi_check_version(struct dsi_context *ctx);
+void dsi_power_enable(struct dsi_context *ctx, int enable);
+void dsi_video_mode(struct dsi_context *ctx);
+void dsi_cmd_mode(struct dsi_context *ctx);
+bool dsi_is_cmd_mode(struct dsi_context *ctx);
+void dsi_rx_vcid(struct dsi_context *ctx, u8 vc);
+void dsi_video_vcid(struct dsi_context *ctx, u8 vc);
+void dsi_dpi_video_burst_mode(struct dsi_context *ctx, int mode);
+void dsi_dpi_color_coding(struct dsi_context *ctx, int coding);
+void dsi_dpi_sig_delay(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hline_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hsync_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hbp_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_vact(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vfp(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vbp(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vsync(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_hporch_lp_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_vporch_lp_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_frame_ack_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_chunk_num(struct dsi_context *ctx, u16 no);
+void dsi_dpi_null_packet_size(struct dsi_context *ctx, u16 size);
+void dsi_dpi_video_packet_size(struct dsi_context *ctx, u16 size);
+void dsi_edpi_max_pkt_size(struct dsi_context *ctx, u16 size);
+void dsi_tear_effect_ack_en(struct dsi_context *ctx, int enable);
+void dsi_cmd_mode_lp_cmd_en(struct dsi_context *ctx, int enable);
+void dsi_video_mode_lp_cmd_en(struct dsi_context *ctx, int enable);
+void dsi_set_packet_header(struct dsi_context *ctx, u8 vc, u8 type,
+						u8 wc_lsb, u8 wc_msb);
+void dsi_set_packet_payload(struct dsi_context *ctx, u32 payload);
+u32 dsi_get_rx_payload(struct dsi_context *ctx);
+void dsi_bta_en(struct dsi_context *ctx, int enable);
+void dsi_eotp_rx_en(struct dsi_context *ctx, int enable);
+void dsi_eotp_tx_en(struct dsi_context *ctx, int enable);
+void dsi_ecc_rx_en(struct dsi_context *ctx, int enable);
+void dsi_crc_rx_en(struct dsi_context *ctx, int enable);
+bool dsi_is_bta_returned(struct dsi_context *ctx);
+bool dsi_is_rx_payload_fifo_full(struct dsi_context *ctx);
+bool dsi_is_rx_payload_fifo_empty(struct dsi_context *ctx);
+bool dsi_is_tx_payload_fifo_full(struct dsi_context *ctx);
+bool dsi_is_tx_payload_fifo_empty(struct dsi_context *ctx);
+bool dsi_is_tx_cmd_fifo_full(struct dsi_context *ctx);
+bool dsi_is_tx_cmd_fifo_empty(struct dsi_context *ctx);
+void dsi_datalane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_datalane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_clklane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_clklane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_max_read_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_nc_clk_en(struct dsi_context *ctx, int enable);
+void dsi_tx_escape_division(struct dsi_context *ctx, u8 div);
+void dsi_timeout_clock_division(struct dsi_context *ctx, u8 div);
+void dsi_lp_rx_timeout(struct dsi_context *ctx, u16 count);
+void dsi_hs_tx_timeout(struct dsi_context *ctx, u16 count);
+u32 dsi_int0_status(struct dsi_context *ctx);
+u32 dsi_int1_status(struct dsi_context *ctx);
+void dsi_int0_mask(struct dsi_context *ctx, u32 mask);
+void dsi_int1_mask(struct dsi_context *ctx, u32 mask);
+
+#endif /* _DSI_CTRL_R1P0_H_ */
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
new file mode 100644
index 0000000..2102d7c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "dsi_ctrl_r1p0.h"
+#include "dsi_ctrl_r1p0_ppi.h"
+#include "sprd_dphy.h"
+
+/**
+ * Reset D-PHY module
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param reset
+ */
+void dsi_phy_rstz(struct dphy_context *ctx, int level)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_reset_n = level;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Power up/down D-PHY module
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param enable (1: shutdown)
+ */
+void dsi_phy_shutdownz(struct dphy_context *ctx, int level)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_shutdown = level;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Force D-PHY PLL to stay on while in ULPS
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param force (1) disable (0)
+ * @note To follow the programming model, use wakeup_pll function
+ */
+void dsi_phy_force_pll(struct dphy_context *ctx, int force)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_force_pll = force;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_clklane_ulps_rqst(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_txrequlps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_clklane_ulps_exit(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_txexitulps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_datalane_ulps_rqst(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_data_txrequlps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_datalane_ulps_exit(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_data_txexitulps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Configure minimum wait period for HS transmission request after a stop state
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param no_of_byte_cycles [in byte (lane) clock cycles]
+ */
+void dsi_phy_stop_wait_time(struct dphy_context *ctx, u8 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+
+	writel(byte_cycle, &reg->PHY_MIN_STOP_TIME);
+}
+
+/**
+ * Set number of active lanes
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param no_of_lanes
+ */
+void dsi_phy_datalane_en(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+
+	writel(ctx->lanes - 1, &reg->PHY_LANE_NUM_CONFIG);
+}
+
+/**
+ * Enable clock lane module
+ * @param phy pointer to structure
+ *  which holds information about the d-phy module
+ * @param en
+ */
+void dsi_phy_clklane_en(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_en = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Request the PHY module to start transmission of high speed clock.
+ * This causes the clock lane to start transmitting DDR clock on the
+ * lane interconnect.
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param enable
+ * @note this function should be called explicitly by user always except for
+ * transmitting
+ */
+void dsi_phy_clk_hs_rqst(struct dphy_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x74 phy_clk_lane_lp_ctrl;
+
+	phy_clk_lane_lp_ctrl.val = readl(&reg->PHY_CLK_LANE_LP_CTRL);
+	phy_clk_lane_lp_ctrl.bits.auto_clklane_ctrl_en = 0;
+	phy_clk_lane_lp_ctrl.bits.phy_clklane_tx_req_hs = enable;
+
+	writel(phy_clk_lane_lp_ctrl.val, &reg->PHY_CLK_LANE_LP_CTRL);
+}
+
+/**
+ * Get D-PHY PPI status
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param mask
+ * @return status
+ */
+u8 dsi_phy_is_pll_locked(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return phy_status.bits.phy_lock;
+}
+
+u8 dsi_phy_is_stop_state_datalane(struct dphy_context *ctx)
+{
+	u8 state = 0;
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	if (phy_status.bits.phy_stopstate0lane)
+		state |= BIT(0);
+	if (phy_status.bits.phy_stopstate1lane)
+		state |= BIT(1);
+	if (phy_status.bits.phy_stopstate2lane)
+		state |= BIT(2);
+	if (phy_status.bits.phy_stopstate3lane)
+		state |= BIT(3);
+
+	return state;
+}
+
+u8 dsi_phy_is_stop_state_clklane(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return phy_status.bits.phy_stopstateclklane;
+}
+
+u8 dsi_phy_is_ulps_active_datalane(struct dphy_context *ctx)
+{
+	u8 state = 0;
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	if (!phy_status.bits.phy_ulpsactivenot0lane)
+		state |= BIT(0);
+	if (!phy_status.bits.phy_ulpsactivenot1lane)
+		state |= BIT(1);
+	if (!phy_status.bits.phy_ulpsactivenot2lane)
+		state |= BIT(2);
+	if (!phy_status.bits.phy_ulpsactivenot3lane)
+		state |= BIT(3);
+
+	return state;
+}
+
+u8 dsi_phy_is_ulps_active_clklane(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return !phy_status.bits.phy_ulpsactivenotclk;
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param value
+ */
+void dsi_phy_test_clk(struct dphy_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF0 phy_tst_ctrl0;
+
+	phy_tst_ctrl0.val = readl(&reg->PHY_TST_CTRL0);
+	phy_tst_ctrl0.bits.phy_testclk = value;
+
+	writel(phy_tst_ctrl0.val, &reg->PHY_TST_CTRL0);
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param value
+ */
+void dsi_phy_test_clr(struct dphy_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF0 phy_tst_ctrl0;
+
+	phy_tst_ctrl0.val = readl(&reg->PHY_TST_CTRL0);
+	phy_tst_ctrl0.bits.phy_testclr = value;
+
+	writel(phy_tst_ctrl0.val, &reg->PHY_TST_CTRL0);
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param on_falling_edge
+ */
+void dsi_phy_test_en(struct dphy_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+	phy_tst_ctrl1.bits.phy_testen = value;
+
+	writel(phy_tst_ctrl1.val, &reg->PHY_TST_CTRL1);
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ */
+u8 dsi_phy_test_dout(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+
+	return phy_tst_ctrl1.bits.phy_testdout;
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param test_data
+ */
+void dsi_phy_test_din(struct dphy_context *ctx, u8 data)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+	phy_tst_ctrl1.bits.phy_testdin = data;
+
+	writel(phy_tst_ctrl1.val, &reg->PHY_TST_CTRL1);
+}
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
new file mode 100644
index 0000000..7477604
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DSI_CTRL_R1P0_PPI_H_
+#define _DSI_CTRL_R1P0_PPI_H_
+
+void dsi_phy_rstz(struct dphy_context *ctx, int level);
+void dsi_phy_shutdownz(struct dphy_context *ctx, int level);
+void dsi_phy_force_pll(struct dphy_context *ctx, int force);
+void dsi_phy_clklane_ulps_rqst(struct dphy_context *ctx, int en);
+void dsi_phy_clklane_ulps_exit(struct dphy_context *ctx, int en);
+void dsi_phy_datalane_ulps_rqst(struct dphy_context *ctx, int en);
+void dsi_phy_datalane_ulps_exit(struct dphy_context *ctx, int en);
+void dsi_phy_stop_wait_time(struct dphy_context *ctx, u8 byte_clk);
+void dsi_phy_datalane_en(struct dphy_context *ctx);
+void dsi_phy_clklane_en(struct dphy_context *ctx, int en);
+void dsi_phy_clk_hs_rqst(struct dphy_context *ctx, int en);
+u8 dsi_phy_is_pll_locked(struct dphy_context *ctx);
+u8 dsi_phy_is_stop_state_clklane(struct dphy_context *ctx);
+u8 dsi_phy_is_stop_state_datalane(struct dphy_context *ctx);
+u8 dsi_phy_is_ulps_active_datalane(struct dphy_context *ctx);
+u8 dsi_phy_is_ulps_active_clklane(struct dphy_context *ctx);
+void dsi_phy_test_clk(struct dphy_context *ctx, u8 level);
+void dsi_phy_test_clr(struct dphy_context *ctx, u8 level);
+void dsi_phy_test_en(struct dphy_context *ctx, u8 level);
+u8 dsi_phy_test_dout(struct dphy_context *ctx);
+void dsi_phy_test_din(struct dphy_context *ctx, u8 data);
+void dsi_phy_bist_en(struct dphy_context *ctx, int en);
+
+#endif /* _DSI_CTRL_R1P0_PPI_H_ */
\ No newline at end of file
diff --git a/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
new file mode 100644
index 0000000..98487ba
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/delay.h>
+
+#include "core/dsi_ctrl_r1p0.h"
+#include "sprd_dsi.h"
+
+static int dsi_wait_tx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_is_tx_payload_fifo_empty(ctx))
+			return 0;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int dsi_wait_tx_cmd_fifo_empty(struct dsi_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_is_tx_cmd_fifo_empty(ctx))
+			return 0;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int dsi_wait_rd_resp_completed(struct dsi_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 10000; i++) {
+		if (dsi_is_bta_returned(ctx))
+			return 0;
+		udelay(10);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static u16 calc_bytes_per_pixel_x100(int coding)
+{
+	u16 Bpp_x100;
+
+	switch (coding) {
+	case COLOR_CODE_16BIT_CONFIG1:
+	case COLOR_CODE_16BIT_CONFIG2:
+	case COLOR_CODE_16BIT_CONFIG3:
+		Bpp_x100 = 200;
+		break;
+	case COLOR_CODE_18BIT_CONFIG1:
+	case COLOR_CODE_18BIT_CONFIG2:
+		Bpp_x100 = 225;
+		break;
+	case COLOR_CODE_24BIT:
+		Bpp_x100 = 300;
+		break;
+	case COLOR_CODE_COMPRESSTION:
+		Bpp_x100 = 100;
+		break;
+	case COLOR_CODE_20BIT_YCC422_LOOSELY:
+		Bpp_x100 = 250;
+		break;
+	case COLOR_CODE_24BIT_YCC422:
+		Bpp_x100 = 300;
+		break;
+	case COLOR_CODE_16BIT_YCC422:
+		Bpp_x100 = 200;
+		break;
+	case COLOR_CODE_30BIT:
+		Bpp_x100 = 375;
+		break;
+	case COLOR_CODE_36BIT:
+		Bpp_x100 = 450;
+		break;
+	case COLOR_CODE_12BIT_YCC420:
+		Bpp_x100 = 150;
+		break;
+	default:
+		DRM_ERROR("invalid color coding");
+		Bpp_x100 = -EINVAL;
+		break;
+	}
+
+	return Bpp_x100;
+}
+
+static u8 calc_video_size_step(int coding)
+{
+	u8 video_size_step;
+
+	switch (coding) {
+	case COLOR_CODE_16BIT_CONFIG1:
+	case COLOR_CODE_16BIT_CONFIG2:
+	case COLOR_CODE_16BIT_CONFIG3:
+	case COLOR_CODE_18BIT_CONFIG1:
+	case COLOR_CODE_18BIT_CONFIG2:
+	case COLOR_CODE_24BIT:
+	case COLOR_CODE_COMPRESSTION:
+		return video_size_step = 1;
+	case COLOR_CODE_20BIT_YCC422_LOOSELY:
+	case COLOR_CODE_24BIT_YCC422:
+	case COLOR_CODE_16BIT_YCC422:
+	case COLOR_CODE_30BIT:
+	case COLOR_CODE_36BIT:
+	case COLOR_CODE_12BIT_YCC420:
+		return video_size_step = 2;
+	default:
+		DRM_ERROR("invalid color coding");
+		return -EINVAL;
+	}
+}
+
+static u16 round_video_size(int coding, u16 video_size)
+{
+	switch (coding) {
+	case COLOR_CODE_16BIT_YCC422:
+	case COLOR_CODE_24BIT_YCC422:
+	case COLOR_CODE_20BIT_YCC422_LOOSELY:
+	case COLOR_CODE_12BIT_YCC420:
+		/* round up active H pixels to a multiple of 2 */
+		if ((video_size % 2) != 0)
+			video_size += 1;
+		break;
+	default:
+		break;
+	}
+
+	return video_size;
+}
+
+#define SPRD_MIPI_DSI_FMT_DSC 0xff
+static u32 fmt_to_coding(u32 fmt)
+{
+	switch (fmt) {
+	case MIPI_DSI_FMT_RGB565:
+		return COLOR_CODE_16BIT_CONFIG1;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return COLOR_CODE_18BIT_CONFIG1;
+	case MIPI_DSI_FMT_RGB666:
+	case MIPI_DSI_FMT_RGB888:
+		return COLOR_CODE_24BIT;
+	case SPRD_MIPI_DSI_FMT_DSC:
+		return COLOR_CODE_COMPRESSTION;
+	default:
+		DRM_ERROR("Unsupported format (%d)\n", fmt);
+		return COLOR_CODE_24BIT;
+	}
+}
+
+#define ns_to_cycle(ns, byte_clk) \
+	DIV_ROUND_UP((ns) * (byte_clk), 1000000)
+
+void sprd_dsi_power_on(struct sprd_dsi *dsi)
+{
+	int div;
+	struct dsi_context *ctx = &dsi->ctx;
+	u16 max_rd_time;
+	u16 data_hs2lp, data_lp2hs, clk_hs2lp, clk_lp2hs;
+
+	dsi_power_enable(ctx, 0);
+	dsi_int0_mask(ctx, 0xffffffff);
+	dsi_int1_mask(ctx, 0xffffffff);
+	dsi_cmd_mode(ctx);
+	dsi_eotp_rx_en(ctx, 0);
+	dsi_eotp_tx_en(ctx, 0);
+	dsi_ecc_rx_en(ctx, 1);
+	dsi_crc_rx_en(ctx, 1);
+	dsi_bta_en(ctx, 1);
+	dsi_video_vcid(ctx, 0);
+	dsi_rx_vcid(ctx, 0);
+
+	div = DIV_ROUND_UP(ctx->byte_clk, ctx->esc_clk);
+	dsi_tx_escape_division(ctx, div);
+
+	max_rd_time = ns_to_cycle(ctx->max_rd_time, ctx->byte_clk);
+	dsi_max_read_time(ctx, max_rd_time);
+
+	data_hs2lp = ns_to_cycle(ctx->data_hs2lp, ctx->byte_clk);
+	data_lp2hs = ns_to_cycle(ctx->data_lp2hs, ctx->byte_clk);
+	clk_hs2lp = ns_to_cycle(ctx->clk_hs2lp, ctx->byte_clk);
+	clk_lp2hs = ns_to_cycle(ctx->clk_lp2hs, ctx->byte_clk);
+	dsi_datalane_hs2lp_config(ctx, data_hs2lp);
+	dsi_datalane_lp2hs_config(ctx, data_lp2hs);
+	dsi_clklane_hs2lp_config(ctx, clk_hs2lp);
+	dsi_clklane_lp2hs_config(ctx, clk_lp2hs);
+
+	dsi_power_enable(ctx, 1);
+}
+
+/**
+ * Close DSI Host driver
+ * - Free up resources and shutdown host controller and PHY
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @return
+ */
+void sprd_dsi_power_off(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	dsi_int0_mask(ctx, 0xffffffff);
+	dsi_int1_mask(ctx, 0xffffffff);
+	dsi_power_enable(ctx, 0);
+}
+
+/**
+ * Configure DPI video interface
+ * - If not in burst mode, it will compute the video and null packet sizes
+ * according to necessity
+ * - Configure timers for data lanes and/or clock lane to return to LP when
+ * bandwidth is not filled by data
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @param param pointer to video stream-to-send information
+ * @return error code
+ */
+int sprd_dsi_dpi_video(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	struct videomode *vm = &dsi->ctx.vm;
+	u16 Bpp_x100;
+	u16 video_size;
+	u32 ratio_x1000;
+	u16 null_pkt_size = 0;
+	u8 video_size_step;
+	u32 hs_to;
+	u32 total_bytes;
+	u32 bytes_per_chunk;
+	u32 chunks = 0;
+	u32 bytes_left = 0;
+	u32 chunk_overhead;
+	const u8 pkt_header = 6;
+	u8 coding;
+	int div;
+	u16 hline;
+
+	coding = fmt_to_coding(ctx->format);
+	video_size = round_video_size(coding, vm->hactive);
+	Bpp_x100 = calc_bytes_per_pixel_x100(coding);
+	video_size_step = calc_video_size_step(coding);
+	ratio_x1000 = ctx->byte_clk * 1000 / (vm->pixelclock / 1000);
+	hline = vm->hactive + vm->hsync_len + vm->hfront_porch +
+		vm->hback_porch;
+
+	dsi_power_enable(ctx, 0);
+	dsi_dpi_frame_ack_en(ctx, ctx->frame_ack_en);
+	dsi_dpi_color_coding(ctx, coding);
+	dsi_dpi_video_burst_mode(ctx, ctx->burst_mode);
+	dsi_dpi_sig_delay(ctx, 95 * hline * ratio_x1000 / 100000);
+	dsi_dpi_hline_time(ctx, hline * ratio_x1000 / 1000);
+	dsi_dpi_hsync_time(ctx, vm->hsync_len * ratio_x1000 / 1000);
+	dsi_dpi_hbp_time(ctx, vm->hback_porch * ratio_x1000 / 1000);
+	dsi_dpi_vact(ctx, vm->vactive);
+	dsi_dpi_vfp(ctx, vm->vfront_porch);
+	dsi_dpi_vbp(ctx, vm->vback_porch);
+	dsi_dpi_vsync(ctx, vm->vsync_len);
+	dsi_dpi_hporch_lp_en(ctx, 1);
+	dsi_dpi_vporch_lp_en(ctx, 1);
+
+	hs_to = (hline * vm->vactive) + (2 * Bpp_x100) / 100;
+	for (div = 0x80; (div < hs_to) && (div > 2); div--) {
+		if ((hs_to % div) == 0) {
+			dsi_timeout_clock_division(ctx, div);
+			dsi_lp_rx_timeout(ctx, hs_to / div);
+			dsi_hs_tx_timeout(ctx, hs_to / div);
+			break;
+		}
+	}
+
+	if (ctx->burst_mode == VIDEO_BURST_WITH_SYNC_PULSES) {
+		dsi_dpi_video_packet_size(ctx, video_size);
+		dsi_dpi_null_packet_size(ctx, 0);
+		dsi_dpi_chunk_num(ctx, 0);
+	} else {
+		/* non burst transmission */
+		null_pkt_size = 0;
+
+		/* bytes to be sent - first as one chunk */
+		bytes_per_chunk = vm->hactive * Bpp_x100 / 100 + pkt_header;
+
+		/* hline total bytes from the DPI interface */
+		total_bytes = (vm->hactive + vm->hfront_porch) *
+				ratio_x1000 / ctx->lanes / 1000;
+
+		/* check if the pixels actually fit on the DSI link */
+		if (total_bytes < bytes_per_chunk) {
+			DRM_ERROR("current resolution can not be set\n");
+			return -EINVAL;
+		}
+
+		chunk_overhead = total_bytes - bytes_per_chunk;
+
+		/* overhead higher than 1 -> enable multi packets */
+		if (chunk_overhead > 1) {
+
+			/* multi packets */
+			for (video_size = video_size_step;
+			     video_size < vm->hactive;
+			     video_size += video_size_step) {
+
+				if (vm->hactive * 1000 / video_size % 1000)
+					continue;
+
+				chunks = vm->hactive / video_size;
+				bytes_per_chunk = Bpp_x100 * video_size / 100
+						  + pkt_header;
+				if (total_bytes >= (bytes_per_chunk * chunks)) {
+					bytes_left = total_bytes -
+						     bytes_per_chunk * chunks;
+					break;
+				}
+			}
+
+			/* prevent overflow (unsigned - unsigned) */
+			if (bytes_left > (pkt_header * chunks)) {
+				null_pkt_size = (bytes_left -
+						pkt_header * chunks) / chunks;
+				/* avoid register overflow */
+				if (null_pkt_size > 1023)
+					null_pkt_size = 1023;
+			}
+
+		} else {
+
+			/* single packet */
+			chunks = 1;
+
+			/* must be a multiple of 4 except 18 loosely */
+			for (video_size = vm->hactive;
+			    (video_size % video_size_step) != 0;
+			     video_size++)
+				;
+		}
+
+		dsi_dpi_video_packet_size(ctx, video_size);
+		dsi_dpi_null_packet_size(ctx, null_pkt_size);
+		dsi_dpi_chunk_num(ctx, chunks);
+	}
+
+	dsi_int0_mask(ctx, ctx->int0_mask);
+	dsi_int1_mask(ctx, ctx->int1_mask);
+	dsi_power_enable(ctx, 1);
+
+	return 0;
+}
+
+int sprd_dsi_edpi_video(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	const u32 fifo_depth = 1096;
+	const u32 word_length = 4;
+	u32 hactive = ctx->vm.hactive;
+	u32 Bpp_x100;
+	u32 max_fifo_len;
+	u8 coding;
+
+	coding = fmt_to_coding(ctx->format);
+	Bpp_x100 = calc_bytes_per_pixel_x100(coding);
+	max_fifo_len = word_length * fifo_depth * 100 / Bpp_x100;
+
+	dsi_power_enable(ctx, 0);
+	dsi_dpi_color_coding(ctx, coding);
+	dsi_tear_effect_ack_en(ctx, ctx->te_ack_en);
+
+	if (max_fifo_len > hactive)
+		dsi_edpi_max_pkt_size(ctx, hactive);
+	else
+		dsi_edpi_max_pkt_size(ctx, max_fifo_len);
+
+	dsi_int0_mask(ctx, ctx->int0_mask);
+	dsi_int1_mask(ctx, ctx->int1_mask);
+	dsi_power_enable(ctx, 1);
+
+	return 0;
+}
+
+/**
+ * Send a packet on the generic interface
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @param vc destination virtual channel
+ * @param data_type type of command, inserted in first byte of header
+ * @param params byte array of command parameters
+ * @param param_length length of the above array
+ * @return error code
+ * @note this function has an active delay to wait for the buffer to clear.
+ * The delay is limited to:
+ * (param_length / 4) x DSIH_FIFO_ACTIVE_WAIT x register access time
+ * @note the controller restricts the sending of .
+ * This function will not be able to send Null and Blanking packets due to
+ *  controller restriction
+ */
+int sprd_dsi_wr_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			const u8 *param, u16 len)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	u8 wc_lsbyte, wc_msbyte;
+	u32 payload;
+	int i, j, ret;
+
+	if (vc > 3)
+		return -EINVAL;
+
+
+	/* 1st: for long packet, must config payload first */
+	ret = dsi_wait_tx_payload_fifo_empty(ctx);
+	if (ret) {
+		DRM_ERROR("tx payload fifo is not empty\n");
+		return ret;
+	}
+
+	if (len > 2) {
+		for (i = 0, j = 0; i < len; i += j) {
+			payload = 0;
+			for (j = 0; (j < 4) && ((j + i) < (len)); j++)
+				payload |= param[i + j] << (j * 8);
+
+			dsi_set_packet_payload(ctx, payload);
+		}
+		wc_lsbyte = len & 0xff;
+		wc_msbyte = len >> 8;
+	} else {
+		wc_lsbyte = (len > 0) ? param[0] : 0;
+		wc_msbyte = (len > 1) ? param[1] : 0;
+	}
+
+	/* 2nd: then set packet header */
+	ret = dsi_wait_tx_cmd_fifo_empty(ctx);
+	if (ret) {
+		DRM_ERROR("tx cmd fifo is not empty\n");
+		return ret;
+	}
+
+	dsi_set_packet_header(ctx, vc, type, wc_lsbyte, wc_msbyte);
+
+	return 0;
+}
+
+
+/**
+ * Send READ packet to peripheral using the generic interface
+ * This will force command mode and stop video mode (because of BTA)
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @param vc destination virtual channel
+ * @param data_type generic command type
+ * @param lsb_byte first command parameter
+ * @param msb_byte second command parameter
+ * @param bytes_to_read no of bytes to read (expected to arrive at buffer)
+ * @param read_buffer pointer to 8-bit array to hold the read buffer words
+ * return read_buffer_length
+ * @note this function has an active delay to wait for the buffer to clear.
+ * The delay is limited to 2 x DSIH_FIFO_ACTIVE_WAIT
+ * (waiting for command buffer, and waiting for receiving)
+ * @note this function will enable BTA
+ */
+int sprd_dsi_rd_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			u8 msb_byte, u8 lsb_byte,
+			u8 *buffer, u8 bytes_to_read)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	int i, ret;
+	int count = 0;
+	u32 temp;
+
+	if (vc > 3)
+		return -EINVAL;
+
+	/* 1st: send read command to peripheral */
+	if (!dsi_is_tx_cmd_fifo_empty(ctx))
+		return -EIO;
+
+	dsi_set_packet_header(ctx, vc, type, lsb_byte, msb_byte);
+
+	/* 2nd: wait peripheral response completed */
+	ret = dsi_wait_rd_resp_completed(ctx);
+	if (ret) {
+		DRM_ERROR("wait read response time out\n");
+		return ret;
+	}
+
+	/* 3rd: get data from rx payload fifo */
+	if (dsi_is_rx_payload_fifo_empty(ctx)) {
+		DRM_ERROR("rx payload fifo empty\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < 100; i++) {
+		temp = dsi_get_rx_payload(ctx);
+
+		if (count < bytes_to_read)
+			buffer[count++] = temp & 0xff;
+		if (count < bytes_to_read)
+			buffer[count++] = (temp >> 8) & 0xff;
+		if (count < bytes_to_read)
+			buffer[count++] = (temp >> 16) & 0xff;
+		if (count < bytes_to_read)
+			buffer[count++] = (temp >> 24) & 0xff;
+
+		if (dsi_is_rx_payload_fifo_empty(ctx))
+			return count;
+		else {
+			DRM_ERROR("read too many buffers\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+void sprd_dsi_set_work_mode(struct sprd_dsi *dsi, u8 mode)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (mode == DSI_MODE_CMD)
+		dsi_cmd_mode(ctx);
+	else
+		dsi_video_mode(ctx);
+}
+
+int sprd_dsi_get_work_mode(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (dsi_is_cmd_mode(ctx))
+		return DSI_MODE_CMD;
+	else
+		return DSI_MODE_VIDEO;
+}
+
+void sprd_dsi_lp_cmd_enable(struct sprd_dsi *dsi, bool enable)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (dsi_is_cmd_mode(ctx))
+		dsi_cmd_mode_lp_cmd_en(ctx, enable);
+	else
+		dsi_video_mode_lp_cmd_en(ctx, enable);
+}
+
+void sprd_dsi_nc_clk_en(struct sprd_dsi *dsi, bool enable)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	dsi_nc_clk_en(ctx, enable);
+}
+
+void sprd_dsi_state_reset(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	dsi_power_enable(ctx, 0);
+	udelay(100);
+	dsi_power_enable(ctx, 1);
+}
+
+u32 sprd_dsi_int_status(struct sprd_dsi *dsi, int index)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	u32 status;
+
+	if (0 == index)
+		status = dsi_int0_status(ctx);
+	else if (1 == index)
+		status = dsi_int1_status(ctx);
+	else {
+		DRM_ERROR("invalid dsi IRQ index %d\n", index);
+		status = -EINVAL;
+	}
+
+	return status;
+}
+
+void sprd_dsi_int_mask(struct sprd_dsi *dsi, int index)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (0 == index)
+		dsi_int0_mask(ctx, dsi->ctx.int0_mask);
+	else if (1 == index)
+		dsi_int1_mask(ctx, dsi->ctx.int1_mask);
+	else
+		DRM_ERROR("invalid dsi IRQ index %d\n", index);
+}
diff --git a/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
new file mode 100644
index 0000000..91eef86
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DSI_API_H_
+#define _SPRD_DSI_API_H_
+
+void sprd_dsi_power_on(struct sprd_dsi *dsi);
+void sprd_dsi_power_off(struct sprd_dsi *dsi);
+int sprd_dsi_dpi_video(struct sprd_dsi *dsi);
+int sprd_dsi_edpi_video(struct sprd_dsi *dsi);
+int sprd_dsi_wr_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			const u8 *param, u16 len);
+int sprd_dsi_rd_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			u8 msb_byte, u8 lsb_byte,
+			u8 *buffer, u8 bytes_to_read);
+void sprd_dsi_set_work_mode(struct sprd_dsi *dsi, u8 mode);
+int sprd_dsi_get_work_mode(struct sprd_dsi *dsi);
+void sprd_dsi_lp_cmd_enable(struct sprd_dsi *dsi, bool enable);
+void sprd_dsi_nc_clk_en(struct sprd_dsi *dsi, bool enable);
+void sprd_dsi_state_reset(struct sprd_dsi *dsi);
+u32 sprd_dsi_int_status(struct sprd_dsi *dsi, int index);
+void sprd_dsi_int_mask(struct sprd_dsi *dsi, int index);
+
+#endif /* _SPRD_DSI_API_H_ */
diff --git a/drivers/gpu/drm/sprd/sprd_dphy.c b/drivers/gpu/drm/sprd/sprd_dphy.c
new file mode 100644
index 0000000..3dcd270
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dphy.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "dphy/sprd_dphy_api.h"
+#include "sprd_dphy.h"
+
+static int regmap_tst_io_write(void *context, u32 reg, u32 val)
+{
+	struct sprd_dphy *dphy = context;
+
+	if (val > 0xff || reg > 0xff)
+		return -EINVAL;
+
+	DRM_DEBUG("reg = 0x%02x, val = 0x%02x\n", reg, val);
+
+	sprd_dphy_test_write(dphy, reg, val);
+
+	return 0;
+}
+
+static int regmap_tst_io_read(void *context, u32 reg, u32 *val)
+{
+	struct sprd_dphy *dphy = context;
+	int ret;
+
+	if (reg > 0xff)
+		return -EINVAL;
+
+	ret = sprd_dphy_test_read(dphy, reg);
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+
+	DRM_DEBUG("reg = 0x%02x, val = 0x%02x\n", reg, *val);
+	return 0;
+}
+
+static struct regmap_bus regmap_tst_io = {
+	.reg_write = regmap_tst_io_write,
+	.reg_read = regmap_tst_io_read,
+};
+
+static const struct regmap_config byte_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static const struct regmap_config word_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int sprd_dphy_regmap_init(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+	struct regmap *regmap;
+
+	if (ctx->apbbase)
+		regmap = devm_regmap_init_mmio(&dphy->dev,
+			(void __iomem *)ctx->apbbase, &word_config);
+	else
+		regmap = devm_regmap_init(&dphy->dev, &regmap_tst_io,
+					  dphy, &byte_config);
+
+	if (IS_ERR(regmap)) {
+		DRM_ERROR("dphy regmap init failed\n");
+		return PTR_ERR(regmap);
+	}
+
+	ctx->regmap = regmap;
+
+	return 0;
+}
+
+void sprd_dphy_ulps_enter(struct sprd_dphy *dphy)
+{
+	sprd_dphy_hs_clk_en(dphy, false);
+	sprd_dphy_data_ulps_enter(dphy);
+	sprd_dphy_clk_ulps_enter(dphy);
+}
+
+void sprd_dphy_ulps_exit(struct sprd_dphy *dphy)
+{
+	sprd_dphy_force_pll(dphy, true);
+	sprd_dphy_clk_ulps_exit(dphy);
+	sprd_dphy_data_ulps_exit(dphy);
+	sprd_dphy_force_pll(dphy, false);
+}
+
+int sprd_dphy_init(struct sprd_dphy *dphy)
+{
+	int ret;
+
+	ret = sprd_dphy_power_on(dphy);
+	if (ret) {
+		DRM_ERROR("sprd dphy initial failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+void sprd_dphy_fini(struct sprd_dphy *dphy)
+{
+	sprd_dphy_power_off(dphy);
+}
+
+static int sprd_dphy_context_init(struct sprd_dphy *dphy,
+				  struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dphy_context *ctx = &dphy->ctx;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->ctrlbase = (unsigned long)devm_ioremap(dev, res->start,
+					resource_size(res));
+	if (ctx->ctrlbase == 0) {
+		DRM_ERROR("failed to map dphy ctrl registers\n");
+		return -ENXIO;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		ctx->ctrlbase = (unsigned long)devm_ioremap(dev, res->start,
+					resource_size(res));
+		if (ctx->apbbase == 0) {
+			DRM_INFO("failed to map dphy apb registers\n");
+			return -ENXIO;
+		}
+	}
+
+	return 0;
+}
+
+static const struct sprd_dphy_ops sharkl3_dphy = {
+	.pll = &sharkle_dphy_pll_ops,
+};
+
+static const struct of_device_id dphy_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dsi-phy",
+	  .data = &sharkl3_dphy },
+	{ /* sentinel */ },
+};
+
+static int sprd_dphy_probe(struct platform_device *pdev)
+{
+	const struct sprd_dphy_ops *pdata;
+	struct sprd_dphy *dphy;
+	struct device *dsi_dev;
+	int ret;
+
+	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
+	if (!dphy)
+		return -ENOMEM;
+
+	dsi_dev = sprd_disp_pipe_get_input(&pdev->dev);
+	if (!dsi_dev)
+		return -ENODEV;
+
+	pdata = of_device_get_match_data(&pdev->dev);
+	if (pdata) {
+		dphy->pll = pdata->pll;
+	} else {
+		DRM_ERROR("No matching driver data found\n");
+		return -EINVAL;
+	}
+
+	ret = sprd_dphy_context_init(dphy, &pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = sprd_dphy_regmap_init(dphy);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, dphy);
+
+	return 0;
+}
+
+struct platform_driver sprd_dphy_driver = {
+	.probe	= sprd_dphy_probe,
+	.driver = {
+		.name  = "sprd-dphy-drv",
+		.of_match_table	= dphy_match_table,
+	}
+};
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc SoC MIPI DSI PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_dphy.h b/drivers/gpu/drm/sprd/sprd_dphy.h
new file mode 100644
index 0000000..ecf2d36
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dphy.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DPHY_H_
+#define _SPRD_DPHY_H_
+
+#include <asm/types.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "disp_lib.h"
+
+struct dphy_context {
+	struct regmap *regmap;
+	unsigned long ctrlbase;
+	unsigned long apbbase;
+	u32 freq;
+	u8 lanes;
+};
+
+struct dphy_pll_ops {
+	int (*pll_config)(struct dphy_context *ctx);
+	void (*timing_config)(struct dphy_context *ctx);
+	void (*force_pll)(struct dphy_context *ctx, int force);
+};
+
+struct sprd_dphy_ops {
+	const struct dphy_pll_ops *pll;
+};
+
+struct sprd_dphy {
+	struct device dev;
+	struct dphy_context ctx;
+	const struct dphy_pll_ops *pll;
+};
+
+int sprd_dphy_init(struct sprd_dphy *dphy);
+void sprd_dphy_fini(struct sprd_dphy *dphy);
+void sprd_dphy_ulps_enter(struct sprd_dphy *dphy);
+void sprd_dphy_ulps_exit(struct sprd_dphy *dphy);
+
+extern const struct dphy_pll_ops sharkle_dphy_pll_ops;
+
+#endif /* _SPRD_DPHY_H_ */
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
index 5ec8e7c..ebea25f 100644
--- a/drivers/gpu/drm/sprd/sprd_dpu.c
+++ b/drivers/gpu/drm/sprd/sprd_dpu.c
@@ -489,6 +489,28 @@ static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
 	return 0;
 }
 
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	if (dpu->core->run)
+		dpu->core->run(ctx);
+
+	drm_crtc_vblank_on(&dpu->crtc);
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	if (dpu->core->stop)
+		dpu->core->stop(ctx);
+
+	drm_crtc_handle_vblank(&dpu->crtc);
+	drm_crtc_vblank_off(&dpu->crtc);
+}
+
+
 static void sprd_dpu_init(struct sprd_dpu *dpu)
 {
 	struct dpu_context *ctx = &dpu->ctx;
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
index 7d3c5e4..83a2a86 100644
--- a/drivers/gpu/drm/sprd/sprd_dpu.h
+++ b/drivers/gpu/drm/sprd/sprd_dpu.h
@@ -182,6 +182,9 @@ static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
 	return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
 }
 
+void sprd_dpu_run(struct sprd_dpu *dpu);
+void sprd_dpu_stop(struct sprd_dpu *dpu);
+
 extern const struct dpu_core_ops dpu_r2p0_core_ops;
 
 #endif
diff --git a/drivers/gpu/drm/sprd/sprd_dsi.c b/drivers/gpu/drm/sprd/sprd_dsi.c
new file mode 100644
index 0000000..452c43a
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dsi.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+#include <video/mipi_display.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+
+#include "disp_lib.h"
+#include "sprd_dpu.h"
+#include "sprd_dsi.h"
+#include "dsi/sprd_dsi_api.h"
+#include "dphy/sprd_dphy_api.h"
+
+#define encoder_to_dsi(encoder) \
+	container_of(encoder, struct sprd_dsi, encoder)
+#define host_to_dsi(host) \
+	container_of(host, struct sprd_dsi, host)
+#define connector_to_dsi(connector) \
+	container_of(connector, struct sprd_dsi, connector)
+
+static int sprd_dsi_init(struct sprd_dsi *dsi)
+{
+	sprd_dsi_power_on(dsi);
+
+	if (dsi->ctx.work_mode == DSI_MODE_VIDEO)
+		sprd_dsi_dpi_video(dsi);
+	else
+		sprd_dsi_edpi_video(dsi);
+
+	return 0;
+}
+
+static void sprd_dsi_fini(struct sprd_dsi *dsi)
+{
+	sprd_dsi_power_off(dsi);
+}
+
+static void sprd_dsi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct sprd_dsi *dsi = encoder_to_dsi(encoder);
+	struct sprd_dpu *dpu = crtc_to_dpu(encoder->crtc);
+
+	if (dsi->ctx.enabled) {
+		DRM_ERROR("dsi is initialized\n");
+		return;
+	}
+
+	sprd_dsi_init(dsi);
+	sprd_dphy_init(dsi->phy);
+
+	sprd_dsi_lp_cmd_enable(dsi, true);
+
+	if (dsi->panel) {
+		drm_panel_prepare(dsi->panel);
+		drm_panel_enable(dsi->panel);
+	}
+
+	sprd_dsi_set_work_mode(dsi, dsi->ctx.work_mode);
+	sprd_dsi_state_reset(dsi);
+
+	if (dsi->ctx.nc_clk_en)
+		sprd_dsi_nc_clk_en(dsi, true);
+	else
+		sprd_dphy_hs_clk_en(dsi->phy, true);
+
+	sprd_dpu_run(dpu);
+
+	dsi->ctx.enabled = true;
+}
+
+static void sprd_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct sprd_dsi *dsi = encoder_to_dsi(encoder);
+	struct sprd_dpu *dpu = crtc_to_dpu(encoder->crtc);
+
+	if (!dsi->ctx.enabled) {
+		DRM_ERROR("dsi isn't initialized\n");
+		return;
+	}
+
+	sprd_dpu_stop(dpu);
+	sprd_dsi_set_work_mode(dsi, DSI_MODE_CMD);
+	sprd_dsi_lp_cmd_enable(dsi, true);
+
+	if (dsi->panel) {
+		drm_panel_disable(dsi->panel);
+		sprd_dphy_ulps_enter(dsi->phy);
+		drm_panel_unprepare(dsi->panel);
+	}
+
+	sprd_dphy_fini(dsi->phy);
+	sprd_dsi_fini(dsi);
+
+	dsi->ctx.enabled = false;
+}
+
+static void sprd_dsi_encoder_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adj_mode)
+{
+	struct sprd_dsi *dsi = encoder_to_dsi(encoder);
+
+	DRM_DEBUG("%s() set mode: %s\n", __func__, dsi->mode->name);
+}
+
+static int sprd_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+				    struct drm_crtc_state *crtc_state,
+				    struct drm_connector_state *conn_state)
+{
+	return 0;
+}
+
+static const struct drm_encoder_helper_funcs sprd_encoder_helper_funcs = {
+	.atomic_check	= sprd_dsi_encoder_atomic_check,
+	.mode_set	= sprd_dsi_encoder_mode_set,
+	.enable		= sprd_dsi_encoder_enable,
+	.disable	= sprd_dsi_encoder_disable
+};
+
+static const struct drm_encoder_funcs sprd_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int sprd_dsi_encoder_init(struct drm_device *drm,
+			       struct sprd_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct device *dev = dsi->host.dev;
+	u32 crtc_mask;
+	int ret;
+
+	crtc_mask = drm_of_find_possible_crtcs(drm, dev->of_node);
+	if (!crtc_mask) {
+		DRM_ERROR("failed to find crtc mask\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("find possible crtcs: 0x%08x\n", crtc_mask);
+
+	encoder->possible_crtcs = crtc_mask;
+	ret = drm_encoder_init(drm, encoder, &sprd_encoder_funcs,
+			       DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init dsi encoder\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &sprd_encoder_helper_funcs);
+
+	return 0;
+}
+
+static int sprd_dsi_find_panel(struct sprd_dsi *dsi)
+{
+	struct device *dev = dsi->host.dev;
+	struct device_node *child, *lcds_node;
+	struct drm_panel *panel;
+
+	/* search /lcds child node first */
+	lcds_node = of_find_node_by_path("/lcds");
+	for_each_child_of_node(lcds_node, child) {
+		panel = of_drm_find_panel(child);
+		if (panel) {
+			dsi->panel = panel;
+			return 0;
+		}
+	}
+
+	/*
+	 * If /lcds child node search failed, we search
+	 * the child of dsi host node.
+	 */
+	for_each_child_of_node(dev->of_node, child) {
+		panel = of_drm_find_panel(child);
+		if (panel) {
+			dsi->panel = panel;
+			return 0;
+		}
+	}
+
+	DRM_ERROR("of_drm_find_panel() failed\n");
+	return -ENODEV;
+}
+
+static int sprd_dsi_phy_attach(struct sprd_dsi *dsi)
+{
+	struct device *dev;
+
+	dev = sprd_disp_pipe_get_output(&dsi->dev);
+	if (!dev)
+		return -ENODEV;
+
+	dsi->phy = dev_get_drvdata(dev);
+	if (!dsi->phy) {
+		DRM_ERROR("dsi attach phy failed\n");
+		return -EINVAL;
+	}
+
+	dsi->phy->ctx.lanes = dsi->ctx.lanes;
+	dsi->phy->ctx.freq = dsi->ctx.byte_clk * 8;
+
+	return 0;
+}
+
+static int sprd_dsi_host_attach(struct mipi_dsi_host *host,
+			   struct mipi_dsi_device *slave)
+{
+	struct sprd_dsi *dsi = host_to_dsi(host);
+	struct dsi_context *ctx = &dsi->ctx;
+	int ret;
+
+	dsi->slave = slave;
+	ctx->lanes = slave->lanes;
+	ctx->format = slave->format;
+	ctx->byte_clk = slave->hs_rate / 8;
+	ctx->esc_clk = slave->lp_rate;
+
+	if (slave->mode_flags & MIPI_DSI_MODE_VIDEO)
+		ctx->work_mode = DSI_MODE_VIDEO;
+	else
+		ctx->work_mode = DSI_MODE_CMD;
+
+	if (slave->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+		ctx->burst_mode = VIDEO_BURST_WITH_SYNC_PULSES;
+	else if (slave->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		ctx->burst_mode = VIDEO_NON_BURST_WITH_SYNC_PULSES;
+	else
+		ctx->burst_mode = VIDEO_NON_BURST_WITH_SYNC_EVENTS;
+
+	if (slave->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+		ctx->nc_clk_en = true;
+
+	ret = sprd_dsi_phy_attach(dsi);
+	if (ret)
+		return ret;
+
+	ret = sprd_dsi_find_panel(dsi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int sprd_dsi_host_detach(struct mipi_dsi_host *host,
+			   struct mipi_dsi_device *slave)
+{
+	/* do nothing */
+	return 0;
+}
+
+static ssize_t sprd_dsi_host_transfer(struct mipi_dsi_host *host,
+				const struct mipi_dsi_msg *msg)
+{
+	struct sprd_dsi *dsi = host_to_dsi(host);
+	const u8 *tx_buf = msg->tx_buf;
+
+	if (msg->rx_buf && msg->rx_len) {
+		u8 lsb = (msg->tx_len > 0) ? tx_buf[0] : 0;
+		u8 msb = (msg->tx_len > 1) ? tx_buf[1] : 0;
+
+		return sprd_dsi_rd_pkt(dsi, msg->channel, msg->type,
+				msb, lsb, msg->rx_buf, msg->rx_len);
+	}
+
+	if (msg->tx_buf && msg->tx_len)
+		return sprd_dsi_wr_pkt(dsi, msg->channel, msg->type,
+					tx_buf, msg->tx_len);
+
+	return 0;
+}
+
+static const struct mipi_dsi_host_ops sprd_dsi_host_ops = {
+	.attach = sprd_dsi_host_attach,
+	.detach = sprd_dsi_host_detach,
+	.transfer = sprd_dsi_host_transfer,
+};
+
+static int sprd_dsi_host_init(struct sprd_dsi *dsi, struct device *dev)
+{
+	int ret;
+
+	dsi->host.dev = dev;
+	dsi->host.ops = &sprd_dsi_host_ops;
+
+	ret = mipi_dsi_host_register(&dsi->host);
+	if (ret)
+		DRM_ERROR("failed to register dsi host\n");
+
+	return ret;
+}
+
+static int sprd_dsi_connector_get_modes(struct drm_connector *connector)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	return drm_panel_get_modes(dsi->panel, connector);
+}
+
+static enum drm_mode_status
+sprd_dsi_connector_mode_valid(struct drm_connector *connector,
+			 struct drm_display_mode *mode)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
+
+	if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+		dsi->mode = mode;
+		drm_display_mode_to_videomode(dsi->mode, &dsi->ctx.vm);
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+sprd_dsi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs sprd_dsi_connector_helper_funcs = {
+	.get_modes = sprd_dsi_connector_get_modes,
+	.mode_valid = sprd_dsi_connector_mode_valid,
+	.best_encoder = sprd_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+sprd_dsi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	if (dsi->panel) {
+		drm_panel_attach(dsi->panel, connector);
+		return connector_status_connected;
+	}
+
+	return connector_status_disconnected;
+}
+
+static void sprd_dsi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs sprd_dsi_atomic_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = sprd_dsi_connector_detect,
+	.destroy = sprd_dsi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int sprd_dsi_connector_init(struct drm_device *drm, struct sprd_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_connector *connector = &dsi->connector;
+	int ret;
+
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	ret = drm_connector_init(drm, connector,
+				 &sprd_dsi_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DSI);
+	if (ret) {
+		DRM_ERROR("drm_connector_init() failed\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector,
+				 &sprd_dsi_connector_helper_funcs);
+
+	drm_connector_attach_encoder(connector, encoder);
+
+	return 0;
+}
+
+static int sprd_dsi_bridge_attach(struct sprd_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_bridge *bridge = dsi->bridge;
+	struct device *dev = dsi->host.dev;
+	struct device_node *bridge_node;
+	int ret;
+
+	bridge_node = of_graph_get_remote_node(dev->of_node, 2, 0);
+	if (!bridge_node)
+		return 0;
+
+	bridge = of_drm_find_bridge(bridge_node);
+	if (!bridge) {
+		DRM_ERROR("of_drm_find_bridge() failed\n");
+		return -ENODEV;
+	}
+	dsi->bridge = bridge;
+
+	ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+	if (ret) {
+		DRM_ERROR("failed to attach external bridge\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static irqreturn_t sprd_dsi_isr(int irq, void *data)
+{
+	u32 status = 0;
+	struct sprd_dsi *dsi = data;
+
+	if (dsi->ctx.irq0 == irq)
+		status = sprd_dsi_int_status(dsi, 0);
+	else if (dsi->ctx.irq1 == irq)
+		status = sprd_dsi_int_status(dsi, 1);
+
+	if (status & DSI_INT_STS_NEED_SOFT_RESET)
+		sprd_dsi_state_reset(dsi);
+
+	return IRQ_HANDLED;
+}
+
+static int sprd_dsi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct sprd_dsi *dsi = dev_get_drvdata(dev);
+	int ret;
+
+	ret = sprd_dsi_encoder_init(drm, dsi);
+	if (ret)
+		goto cleanup_host;
+
+	ret = sprd_dsi_connector_init(drm, dsi);
+	if (ret)
+		goto cleanup_encoder;
+
+	ret = sprd_dsi_bridge_attach(dsi);
+	if (ret)
+		goto cleanup_connector;
+
+	return 0;
+
+cleanup_connector:
+	drm_connector_cleanup(&dsi->connector);
+cleanup_encoder:
+	drm_encoder_cleanup(&dsi->encoder);
+cleanup_host:
+	mipi_dsi_host_unregister(&dsi->host);
+	return ret;
+}
+
+static void sprd_dsi_unbind(struct device *dev,
+			struct device *master, void *data)
+{
+	/* do nothing */
+	DRM_DEBUG("%s()\n", __func__);
+
+}
+
+static const struct component_ops dsi_component_ops = {
+	.bind	= sprd_dsi_bind,
+	.unbind	= sprd_dsi_unbind,
+};
+
+static int sprd_dsi_context_init(struct sprd_dsi *dsi,
+			struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dsi_context *ctx = &dsi->ctx;
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = (unsigned long)devm_ioremap(dev, res->start,
+					resource_size(res));
+	if (ctx->base == 0) {
+		DRM_ERROR("failed to map dsi host registers\n");
+		return -ENXIO;
+	}
+
+	ctx->irq0 = platform_get_irq(pdev, 0);
+	if (ctx->irq0 > 0) {
+		ret = request_irq(ctx->irq0, sprd_dsi_isr, 0, "DSI_INT0", dsi);
+		if (ret) {
+			DRM_ERROR("failed to request dsi irq int0!\n");
+			return -EINVAL;
+		}
+	}
+
+	ctx->irq1 = platform_get_irq(pdev, 1);
+	if (ctx->irq1 > 0) {
+		ret = request_irq(ctx->irq1, sprd_dsi_isr, 0, "DSI_INT1", dsi);
+		if (ret) {
+			DRM_ERROR("failed to request dsi irq int1!\n");
+			return -EINVAL;
+		}
+	}
+
+	ctx->data_hs2lp = 120;
+	ctx->data_lp2hs = 500;
+	ctx->clk_hs2lp = 4;
+	ctx->clk_lp2hs = 15;
+	ctx->max_rd_time = 6000;
+	ctx->int0_mask = 0xffffffff;
+	ctx->int1_mask = 0xffffffff;
+
+	return 0;
+}
+
+static const struct of_device_id dsi_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dsi-host" },
+	{ /* sentinel */ },
+};
+
+static int sprd_dsi_probe(struct platform_device *pdev)
+{
+	struct sprd_dsi *dsi;
+	int ret;
+
+	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi) {
+		DRM_ERROR("failed to allocate dsi data.\n");
+		return -ENOMEM;
+	}
+
+	ret = sprd_dsi_context_init(dsi, &pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = sprd_dsi_host_init(dsi, &pdev->dev);
+	if (ret)
+		return ret;
+	
+	platform_set_drvdata(pdev, dsi);
+
+	return component_add(&pdev->dev, &dsi_component_ops);
+}
+
+static int sprd_dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dsi_component_ops);
+
+	return 0;
+}
+
+struct platform_driver sprd_dsi_driver = {
+	.probe = sprd_dsi_probe,
+	.remove = sprd_dsi_remove,
+	.driver = {
+		.name = "sprd-dsi-drv",
+		.of_match_table = dsi_match_table,
+	},
+};
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc MIPI DSI HOST Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_dsi.h b/drivers/gpu/drm/sprd/sprd_dsi.h
new file mode 100644
index 0000000..7f57f46
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dsi.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef __SPRD_DSI_H__
+#define __SPRD_DSI_H__
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <video/videomode.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_print.h>
+#include <drm/drm_panel.h>
+
+#include "disp_lib.h"
+#include "sprd_dphy.h"
+
+#define DSI_INT_STS_NEED_SOFT_RESET	BIT(0)
+#define DSI_INT_STS_NEED_HARD_RESET	BIT(1)
+
+enum dsi_work_mode {
+	DSI_MODE_CMD = 0,
+	DSI_MODE_VIDEO
+};
+
+enum video_burst_mode {
+	VIDEO_NON_BURST_WITH_SYNC_PULSES = 0,
+	VIDEO_NON_BURST_WITH_SYNC_EVENTS,
+	VIDEO_BURST_WITH_SYNC_PULSES
+};
+
+enum dsi_color_coding {
+	COLOR_CODE_16BIT_CONFIG1 = 0,
+	COLOR_CODE_16BIT_CONFIG2,
+	COLOR_CODE_16BIT_CONFIG3,
+	COLOR_CODE_18BIT_CONFIG1,
+	COLOR_CODE_18BIT_CONFIG2,
+	COLOR_CODE_24BIT,
+	COLOR_CODE_20BIT_YCC422_LOOSELY,
+	COLOR_CODE_24BIT_YCC422,
+	COLOR_CODE_16BIT_YCC422,
+	COLOR_CODE_30BIT,
+	COLOR_CODE_36BIT,
+	COLOR_CODE_12BIT_YCC420,
+	COLOR_CODE_COMPRESSTION,
+	COLOR_CODE_MAX
+};
+
+struct dsi_context {
+	unsigned long base;
+	struct videomode vm;
+	bool enabled;
+
+	u8 lanes;
+	u32 format;
+	u8 work_mode;
+	u8 burst_mode;
+
+	int irq0;
+	int irq1;
+	u32 int0_mask;
+	u32 int1_mask;
+
+	/* byte clock [KHz] */
+	u32 byte_clk;
+	/* escape clock [KHz] */
+	u32 esc_clk;
+
+	/* maximum time (ns) for data lanes from HS to LP */
+	u16 data_hs2lp;
+	/* maximum time (ns) for data lanes from LP to HS */
+	u16 data_lp2hs;
+	/* maximum time (ns) for clk lanes from HS to LP */
+	u16 clk_hs2lp;
+	/* maximum time (ns) for clk lanes from LP to HS */
+	u16 clk_lp2hs;
+	/* maximum time (ns) for BTA operation - REQUIRED */
+	u16 max_rd_time;
+
+	/* is 18-bit loosely packets (valid only when BPP == 18) */
+	bool is_18_loosely;
+	/* enable receiving frame ack packets - for video mode */
+	bool frame_ack_en;
+	/* enable receiving tear effect ack packets - for cmd mode */
+	bool te_ack_en;
+	/* enable non coninuous clock for energy saving */
+	bool nc_clk_en;
+};
+
+struct sprd_dsi {
+	struct device dev;
+	struct mipi_dsi_host host;
+	struct mipi_dsi_device *slave;
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
+	struct drm_display_mode *mode;
+	struct sprd_dphy *phy;
+	struct dsi_context ctx;
+};
+
+#endif /* __SPRD_DSI_H__ */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH RFC v6 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver
@ 2020-07-28 10:07   ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-07-28 10:07 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, kevin3.tang
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

From: Kevin Tang <kevin.tang@unisoc.com>

Adds dsi host controller support for the Unisoc's display subsystem.
Adds dsi phy support for the Unisoc's display subsystem.
Only MIPI DSI Displays supported, DP/TV/HMDI will be support
in the feature.

Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Signed-off-by: Kevin Tang <kevin3.tang@gmail.com>
---
 drivers/gpu/drm/sprd/Makefile                     |    7 +-
 drivers/gpu/drm/sprd/disp_lib.c                   |   57 +
 drivers/gpu/drm/sprd/disp_lib.h                   |   16 +
 drivers/gpu/drm/sprd/dphy/Makefile                |    7 +
 drivers/gpu/drm/sprd/dphy/pll/Makefile            |    3 +
 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c |  473 +++++++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c         |  201 +++
 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h         |   22 +
 drivers/gpu/drm/sprd/dsi/Makefile                 |    8 +
 drivers/gpu/drm/sprd/dsi/core/Makefile            |    4 +
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c     |  964 ++++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h     | 1477 +++++++++++++++++++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c |  328 +++++
 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h |   32 +
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c           |  590 ++++++++
 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h           |   26 +
 drivers/gpu/drm/sprd/sprd_dphy.c                  |  209 +++
 drivers/gpu/drm/sprd/sprd_dphy.h                  |   50 +
 drivers/gpu/drm/sprd/sprd_dpu.c                   |   22 +
 drivers/gpu/drm/sprd/sprd_dpu.h                   |    3 +
 drivers/gpu/drm/sprd/sprd_dsi.c                   |  571 ++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h                   |  108 ++
 22 files changed, 5177 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
 create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
 create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
 create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
 create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h

diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
index 88ab32a..c78565d 100644
--- a/drivers/gpu/drm/sprd/Makefile
+++ b/drivers/gpu/drm/sprd/Makefile
@@ -3,6 +3,11 @@
 subdir-ccflags-y += -I$(srctree)/$(src)
 
 obj-y := sprd_drm.o \
-	sprd_dpu.o
+	sprd_dpu.o \
+	sprd_dsi.o \
+	sprd_dphy.o
 
+obj-y += disp_lib.o
 obj-y += dpu/
+obj-y += dsi/
+obj-y += dphy/
diff --git a/drivers/gpu/drm/sprd/disp_lib.c b/drivers/gpu/drm/sprd/disp_lib.c
new file mode 100644
index 0000000..b2f8202
--- /dev/null
+++ b/drivers/gpu/drm/sprd/disp_lib.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include "disp_lib.h"
+
+struct device *sprd_disp_pipe_get_by_port(struct device *dev, int port)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *endpoint;
+	struct device_node *remote_node;
+	struct platform_device *remote_pdev;
+
+	endpoint = of_graph_get_endpoint_by_regs(np, port, 0);
+	if (!endpoint) {
+		DRM_ERROR("%s/port%d/endpoint0 was not found\n",
+			  np->full_name, port);
+		return NULL;
+	}
+
+	remote_node = of_graph_get_remote_port_parent(endpoint);
+	if (!remote_node) {
+		DRM_ERROR("device node was not found by endpoint0\n");
+		return NULL;
+	}
+
+	remote_pdev = of_find_device_by_node(remote_node);
+	if (remote_pdev == NULL) {
+		DRM_ERROR("find %s platform device failed\n",
+			  remote_node->full_name);
+		return NULL;
+	}
+
+	return &remote_pdev->dev;
+}
+
+struct device *sprd_disp_pipe_get_input(struct device *dev)
+{
+	return sprd_disp_pipe_get_by_port(dev, 1);
+}
+
+struct device *sprd_disp_pipe_get_output(struct device *dev)
+{
+	return sprd_disp_pipe_get_by_port(dev, 0);
+}
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc display common API library");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/disp_lib.h b/drivers/gpu/drm/sprd/disp_lib.h
new file mode 100644
index 0000000..adbd239
--- /dev/null
+++ b/drivers/gpu/drm/sprd/disp_lib.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DISP_LIB_H_
+#define _DISP_LIB_H_
+
+#include <linux/list.h>
+#include <drm/drm_print.h>
+
+struct device *sprd_disp_pipe_get_by_port(struct device *dev, int port);
+struct device *sprd_disp_pipe_get_input(struct device *dev);
+struct device *sprd_disp_pipe_get_output(struct device *dev);
+
+#endif /* _DISP_LIB_H_ */
diff --git a/drivers/gpu/drm/sprd/dphy/Makefile b/drivers/gpu/drm/sprd/dphy/Makefile
new file mode 100644
index 0000000..6637c27
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_dphy_api.o
+
+obj-y += pll/
diff --git a/drivers/gpu/drm/sprd/dphy/pll/Makefile b/drivers/gpu/drm/sprd/dphy/pll/Makefile
new file mode 100644
index 0000000..59e74ec
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/pll/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += megacores_sharkle.o
diff --git a/drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c b/drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
new file mode 100644
index 0000000..a6869cc0
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <asm/div64.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+
+#include "sprd_dphy.h"
+
+#define L						0
+#define H						1
+#define CLK						0
+#define DATA					1
+#define INFINITY				0xffffffff
+#define MIN_OUTPUT_FREQ			(100)
+
+#define AVERAGE(a, b) (min(a, b) + abs((b) - (a)) / 2)
+
+enum TIMING {
+	NONE,
+	REQUEST_TIME,
+	PREPARE_TIME,
+	SETTLE_TIME,
+	ZERO_TIME,
+	TRAIL_TIME,
+	EXIT_TIME,
+	CLKPOST_TIME,
+	TA_GET,
+	TA_GO,
+	TA_SURE,
+	TA_WAIT,
+};
+
+struct pll_regs {
+	union __reg_03__ {
+		struct __03 {
+			u8 prbs_bist: 1;
+			u8 en_lp_treot: 1;
+			u8 lpf_sel: 4;
+			u8 txfifo_bypass: 1;
+			u8 freq_hopping: 1;
+		} bits;
+		u8 val;
+	} _03;
+	union __reg_04__ {
+		struct __04 {
+			u8 div: 3;
+			u8 masterof8lane: 1;
+			u8 hop_trig: 1;
+			u8 cp_s: 2;
+			u8 fdk_s: 1;
+		} bits;
+		u8 val;
+	} _04;
+	union __reg_06__ {
+		struct __06 {
+			u8 nint: 7;
+			u8 mod_en: 1;
+		} bits;
+		u8 val;
+	} _06;
+	union __reg_07__ {
+		struct __07 {
+			u8 kdelta_h: 8;
+		} bits;
+		u8 val;
+	} _07;
+	union __reg_08__ {
+		struct __08 {
+			u8 vco_band: 1;
+			u8 sdm_en: 1;
+			u8 refin: 2;
+			u8 kdelta_l: 4;
+		} bits;
+		u8 val;
+	} _08;
+	union __reg_09__ {
+		struct __09 {
+			u8 kint_h: 8;
+		} bits;
+		u8 val;
+	} _09;
+	union __reg_0a__ {
+		struct __0a {
+			u8 kint_m: 8;
+		} bits;
+		u8 val;
+	} _0a;
+	union __reg_0b__ {
+		struct __0b {
+			u8 out_sel: 4;
+			u8 kint_l: 4;
+		} bits;
+		u8 val;
+	} _0b;
+	union __reg_0c__ {
+		struct __0c {
+			u8 kstep_h: 8;
+		} bits;
+		u8 val;
+	} _0c;
+	union __reg_0d__ {
+		struct __0d {
+			u8 kstep_m: 8;
+		} bits;
+		u8 val;
+	} _0d;
+	union __reg_0e__ {
+		struct __0e {
+			u8 pll_pu_byp: 1;
+			u8 pll_pu: 1;
+			u8 hsbist_len: 2;
+			u8 stopstate_sel: 1;
+			u8 kstep_l: 3;
+		} bits;
+		u8 val;
+	} _0e;
+
+	union __reg_0f__ {
+		struct __0f {
+			u8 det_delay:2;
+			u8 kdelta: 4;
+			u8 ldo0p4:2;
+		} bits;
+		u8 val;
+	} _0f;
+
+};
+
+struct dphy_pll {
+	u8 refin; /* Pre-divider control signal */
+	u8 cp_s; /* 00: SDM_EN=1, 10: SDM_EN=0 */
+	u8 fdk_s; /* PLL mode control: integer or fraction */
+	u8 sdm_en;
+	u8 div;
+	u8 int_n; /* integer N PLL */
+	u32 ref_clk; /* dphy reference clock, unit: MHz */
+	u32 freq; /* panel config, unit: KHz */
+	u32 fvco; /* MHz */
+	u32 potential_fvco; /* MHz */
+	u32 nint; /* sigma delta modulator NINT control */
+	u32 kint; /* sigma delta modulator KINT control */
+	u8 lpf_sel; /* low pass filter control */
+	u8 out_sel; /* post divider control */
+	u8 vco_band; /* vco range */
+	u8 det_delay;
+};
+
+static struct pll_regs regs;
+static struct dphy_pll pll;
+
+/* sharkle */
+#define VCO_BAND_LOW	750
+#define VCO_BAND_MID	1100
+#define VCO_BAND_HIGH	1500
+#define PHY_REF_CLK	26000
+
+static int dphy_calc_pll_param(struct dphy_pll *pll)
+{
+	int i;
+	const u32 khz = 1000;
+	const u32 mhz = 1000000;
+	const unsigned long long factor = 100;
+	unsigned long long tmp;
+
+	pll->potential_fvco = pll->freq / khz; /* MHz */
+	pll->ref_clk = PHY_REF_CLK / khz; /* MHz */
+
+	for (i = 0; i < 4; ++i) {
+		if (pll->potential_fvco >= VCO_BAND_LOW &&
+			pll->potential_fvco <= VCO_BAND_HIGH) {
+			pll->fvco = pll->potential_fvco;
+			pll->out_sel = BIT(i);
+			break;
+		}
+		pll->potential_fvco <<= 1;
+	}
+	if (pll->fvco == 0)
+		return -EINVAL;
+
+	if (pll->fvco >= VCO_BAND_LOW && pll->fvco <= VCO_BAND_MID) {
+		/* vco band control */
+		pll->vco_band = 0x0;
+		/* low pass filter control */
+		pll->lpf_sel = 1;
+	} else if (pll->fvco > VCO_BAND_MID && pll->fvco <= VCO_BAND_HIGH) {
+		pll->vco_band = 0x1;
+		pll->lpf_sel = 0;
+	} else
+		return -EINVAL;
+
+	pll->nint = pll->fvco / pll->ref_clk;
+	tmp = pll->fvco * factor * mhz;
+	do_div(tmp, pll->ref_clk);
+	tmp = tmp - pll->nint * factor * mhz;
+	tmp *= BIT(20);
+	do_div(tmp, 100000000);
+	pll->kint = (u32)tmp;
+	pll->refin = 3; /* pre-divider bypass */
+	pll->sdm_en = true; /* use fraction N PLL */
+	pll->fdk_s = 0x1; /* fraction */
+	pll->cp_s = 0x0;
+	pll->det_delay = 0x1;
+
+	return 0;
+}
+
+static void dphy_set_pll_reg(struct regmap *regmap, struct dphy_pll *pll)
+{
+	int i;
+	u8 *val;
+
+	u8 regs_addr[] = {
+		0x03, 0x04, 0x06, 0x08, 0x09,
+		0x0a, 0x0b, 0x0e, 0x0f
+	};
+
+	regs._03.bits.prbs_bist = 1;
+	regs._03.bits.en_lp_treot = true;
+	regs._03.bits.lpf_sel = pll->lpf_sel;
+	regs._03.bits.txfifo_bypass = 0;
+	regs._04.bits.div = pll->div;
+	regs._04.bits.masterof8lane = 1;
+	regs._04.bits.cp_s = pll->cp_s;
+	regs._04.bits.fdk_s = pll->fdk_s;
+	regs._06.bits.nint = pll->nint;
+	regs._08.bits.vco_band = pll->vco_band;
+	regs._08.bits.sdm_en = pll->sdm_en;
+	regs._08.bits.refin = pll->refin;
+	regs._09.bits.kint_h = pll->kint >> 12;
+	regs._0a.bits.kint_m = (pll->kint >> 4) & 0xff;
+	regs._0b.bits.out_sel = pll->out_sel;
+	regs._0b.bits.kint_l = pll->kint & 0xf;
+	regs._0e.bits.pll_pu_byp = 0;
+	regs._0e.bits.pll_pu = 0;
+	regs._0e.bits.stopstate_sel = 1;
+	regs._0f.bits.det_delay = pll->det_delay;
+
+	val = (u8 *)&regs;
+
+	for (i = 0; i < sizeof(regs_addr); ++i) {
+		regmap_write(regmap, regs_addr[i], val[i]);
+		DRM_DEBUG("%02x: %02x\n", regs_addr[i], val[i]);
+	}
+}
+
+static int dphy_pll_config(struct dphy_context *ctx)
+{
+	int ret;
+	struct regmap *regmap = ctx->regmap;
+
+	pll.freq = ctx->freq;
+
+	/* FREQ = 26M * (NINT + KINT / 2^20) / out_sel */
+	ret = dphy_calc_pll_param(&pll);
+	if (ret) {
+		DRM_ERROR("failed to calculate dphy pll parameters\n");
+		return ret;
+	}
+	dphy_set_pll_reg(regmap, &pll);
+
+	return 0;
+}
+
+static void dphy_set_timing_regs(struct regmap *regmap, int type, u8 val[])
+{
+	switch (type) {
+	case REQUEST_TIME:
+		regmap_write(regmap, 0x31, val[CLK]);
+		regmap_write(regmap, 0x41, val[DATA]);
+		regmap_write(regmap, 0x51, val[DATA]);
+		regmap_write(regmap, 0x61, val[DATA]);
+		regmap_write(regmap, 0x71, val[DATA]);
+
+		regmap_write(regmap, 0x90, val[CLK]);
+		regmap_write(regmap, 0xa0, val[DATA]);
+		regmap_write(regmap, 0xb0, val[DATA]);
+		regmap_write(regmap, 0xc0, val[DATA]);
+		regmap_write(regmap, 0xd0, val[DATA]);
+		break;
+	case PREPARE_TIME:
+		regmap_write(regmap, 0x32, val[CLK]);
+		regmap_write(regmap, 0x42, val[DATA]);
+		regmap_write(regmap, 0x52, val[DATA]);
+		regmap_write(regmap, 0x62, val[DATA]);
+		regmap_write(regmap, 0x72, val[DATA]);
+
+		regmap_write(regmap, 0x91, val[CLK]);
+		regmap_write(regmap, 0xa1, val[DATA]);
+		regmap_write(regmap, 0xb1, val[DATA]);
+		regmap_write(regmap, 0xc1, val[DATA]);
+		regmap_write(regmap, 0xd1, val[DATA]);
+		break;
+	case ZERO_TIME:
+		regmap_write(regmap, 0x33, val[CLK]);
+		regmap_write(regmap, 0x43, val[DATA]);
+		regmap_write(regmap, 0x53, val[DATA]);
+		regmap_write(regmap, 0x63, val[DATA]);
+		regmap_write(regmap, 0x73, val[DATA]);
+
+		regmap_write(regmap, 0x92, val[CLK]);
+		regmap_write(regmap, 0xa2, val[DATA]);
+		regmap_write(regmap, 0xb2, val[DATA]);
+		regmap_write(regmap, 0xc2, val[DATA]);
+		regmap_write(regmap, 0xd2, val[DATA]);
+		break;
+	case TRAIL_TIME:
+		regmap_write(regmap, 0x34, val[CLK]);
+		regmap_write(regmap, 0x44, val[DATA]);
+		regmap_write(regmap, 0x54, val[DATA]);
+		regmap_write(regmap, 0x64, val[DATA]);
+		regmap_write(regmap, 0x74, val[DATA]);
+
+		regmap_write(regmap, 0x93, val[CLK]);
+		regmap_write(regmap, 0xa3, val[DATA]);
+		regmap_write(regmap, 0xb3, val[DATA]);
+		regmap_write(regmap, 0xc3, val[DATA]);
+		regmap_write(regmap, 0xd3, val[DATA]);
+		break;
+	case EXIT_TIME:
+		regmap_write(regmap, 0x36, val[CLK]);
+		regmap_write(regmap, 0x46, val[DATA]);
+		regmap_write(regmap, 0x56, val[DATA]);
+		regmap_write(regmap, 0x66, val[DATA]);
+		regmap_write(regmap, 0x76, val[DATA]);
+
+		regmap_write(regmap, 0x95, val[CLK]);
+		regmap_write(regmap, 0xA5, val[DATA]);
+		regmap_write(regmap, 0xB5, val[DATA]);
+		regmap_write(regmap, 0xc5, val[DATA]);
+		regmap_write(regmap, 0xd5, val[DATA]);
+		break;
+	case CLKPOST_TIME:
+		regmap_write(regmap, 0x35, val[CLK]);
+		regmap_write(regmap, 0x94, val[CLK]);
+		break;
+
+	/* the following just use default value */
+	case SETTLE_TIME:
+	case TA_GET:
+	case TA_GO:
+	case TA_SURE:
+		break;
+	default:
+		break;
+	}
+}
+
+static void dphy_timing_config(struct dphy_context *ctx)
+{
+	u8 val[2];
+	u32 tmp = 0;
+	u32 range[2], constant;
+	u32 t_ui, t_byteck, t_half_byteck;
+	const u32 factor = 2;
+	const u32 scale = 100;
+	struct regmap *regmap = ctx->regmap;
+
+	/* t_ui: 1 ui, byteck: 8 ui, half byteck: 4 ui */
+	t_ui = 1000 * scale / (ctx->freq / 1000);
+	t_byteck = t_ui << 3;
+	t_half_byteck = t_ui << 2;
+	constant = t_ui << 1;
+
+	/* REQUEST_TIME: HS T-LPX: LP-01
+	 * For T-LPX, mipi spec defined min value is 50ns,
+	 * but maybe it shouldn't be too small, because BTA,
+	 * LP-10, LP-00, LP-01, all of this is related to T-LPX.
+	 */
+	range[L] = 50 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * (factor << 1), t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_regs(regmap, REQUEST_TIME, val);
+
+	/* PREPARE_TIME: HS sequence: LP-00 */
+	range[L] = 38 * scale;
+	range[H] = 95 * scale;
+	tmp = AVERAGE(range[L], range[H]);
+	val[CLK] = DIV_ROUND_UP(AVERAGE(range[L], range[H]),
+			t_half_byteck) - 1;
+	range[L] = 40 * scale + 4 * t_ui;
+	range[H] = 85 * scale + 6 * t_ui;
+	tmp |= AVERAGE(range[L], range[H]) << 16;
+	val[DATA] = DIV_ROUND_UP(AVERAGE(range[L], range[H]),
+			t_half_byteck) - 1;
+	dphy_set_timing_regs(regmap, PREPARE_TIME, val);
+
+	/* ZERO_TIME: HS-ZERO */
+	range[L] = 300 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor + (tmp & 0xffff)
+			- 525 * t_byteck / 100, t_byteck) - 2;
+	range[L] = 145 * scale + 10 * t_ui;
+	val[DATA] = DIV_ROUND_UP(range[L] * factor
+			+ ((tmp >> 16) & 0xffff) - 525 * t_byteck / 100,
+			t_byteck) - 2;
+	dphy_set_timing_regs(regmap, ZERO_TIME, val);
+
+	/* TRAIL_TIME: HS-TRAIL */
+	range[L] = 60 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor - constant, t_half_byteck);
+	range[L] = max(8 * t_ui, 60 * scale + 4 * t_ui);
+	val[DATA] = DIV_ROUND_UP(range[L] * 3 / 2 - constant, t_half_byteck) - 2;
+	dphy_set_timing_regs(regmap, TRAIL_TIME, val);
+
+	/* EXIT_TIME: */
+	range[L] = 100 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_regs(regmap, EXIT_TIME, val);
+
+	/* CLKPOST_TIME: */
+	range[L] = 60 * scale + 52 * t_ui;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_regs(regmap, CLKPOST_TIME, val);
+
+	/* SETTLE_TIME:
+	 * This time is used for receiver. So for transmitter,
+	 * it can be ignored.
+	 */
+
+	/* TA_GO:
+	 * transmitter drives bridge state(LP-00) before releasing control,
+	 * reg 0x1f default value: 0x04, which is good.
+	 */
+
+	/* TA_SURE:
+	 * After LP-10 state and before bridge state(LP-00),
+	 * reg 0x20 default value: 0x01, which is good.
+	 */
+
+	/* TA_GET:
+	 * receiver drives Bridge state(LP-00) before releasing control
+	 * reg 0x21 default value: 0x03, which is good.
+	 */
+}
+
+/**
+ * Force D-PHY PLL to stay on while in ULPS
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param force (1) disable (0)
+ * @note To follow the programming model, use wakeup_pll function
+ */
+static void dphy_force_pll(struct dphy_context *ctx, int force)
+{
+	u8 data;
+	struct regmap *regmap = ctx->regmap;
+
+	if (force)
+		data = 0x03;
+	else
+		data = 0x0;
+
+	/* for megocores, to force pll, dphy register should be set */
+	regmap_write(regmap, 0x0e, data);
+}
+
+const struct dphy_pll_ops sharkle_dphy_pll_ops = {
+	.pll_config = dphy_pll_config,
+	.timing_config = dphy_timing_config,
+	.force_pll = dphy_force_pll,
+};
diff --git a/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
new file mode 100644
index 0000000..e90d1f7
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "sprd_dphy_api.h"
+#include "dsi/core/dsi_ctrl_r1p0_ppi.h"
+
+static int dphy_wait_pll_locked(struct dphy_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 50000; i++) {
+		if (dsi_phy_is_pll_locked(ctx))
+			return 0;
+		udelay(3);
+	}
+
+	DRM_ERROR("error: dphy pll can not be locked\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_datalane_stop_state(struct dphy_context *ctx, u8 mask)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_stop_state_datalane(ctx) == mask)
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait datalane stop-state time out\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_datalane_ulps_active(struct dphy_context *ctx, u8 mask)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_ulps_active_datalane(ctx) == mask)
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait datalane ulps-active time out\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_clklane_stop_state(struct dphy_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_stop_state_clklane(ctx))
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait clklane stop-state time out\n");
+	return -ETIMEDOUT;
+}
+
+static int dphy_wait_clklane_ulps_active(struct dphy_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_phy_is_ulps_active_clklane(ctx))
+			return 0;
+		udelay(10);
+	}
+
+	DRM_ERROR("wait clklane ulps-active time out\n");
+	return -ETIMEDOUT;
+}
+
+int sprd_dphy_power_on(struct sprd_dphy *dphy)
+{
+	const struct dphy_pll_ops *pll = dphy->pll;
+	struct dphy_context *ctx = &dphy->ctx;
+	int ret;
+
+	dsi_phy_rstz(ctx, 0);
+	dsi_phy_shutdownz(ctx, 0);
+	dsi_phy_clklane_en(ctx, 0);
+
+	dsi_phy_test_clr(ctx, 0);
+	dsi_phy_test_clr(ctx, 1);
+	dsi_phy_test_clr(ctx, 0);
+
+	pll->pll_config(ctx);
+	pll->timing_config(ctx);
+
+	dsi_phy_shutdownz(ctx, 1);
+	dsi_phy_rstz(ctx, 1);
+	dsi_phy_stop_wait_time(ctx, 0x1C);
+	dsi_phy_clklane_en(ctx, 1);
+	dsi_phy_datalane_en(ctx);
+
+	ret = dphy_wait_pll_locked(ctx);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void sprd_dphy_power_off(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_rstz(ctx, 0);
+	dsi_phy_shutdownz(ctx, 0);
+	dsi_phy_rstz(ctx, 1);
+}
+
+void sprd_dphy_data_ulps_enter(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+	u8 lane_mask = (1 << ctx->lanes) - 1;
+
+	dsi_phy_datalane_ulps_rqst(ctx, 1);
+	dphy_wait_datalane_ulps_active(ctx, lane_mask);
+	dsi_phy_datalane_ulps_rqst(ctx, 0);
+}
+
+void sprd_dphy_data_ulps_exit(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+	u8 lane_mask = (1 << ctx->lanes) - 1;
+
+	dsi_phy_datalane_ulps_exit(ctx, 1);
+	dphy_wait_datalane_stop_state(ctx, lane_mask);
+	dsi_phy_datalane_ulps_exit(ctx, 0);
+}
+
+void sprd_dphy_clk_ulps_enter(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_clklane_ulps_rqst(ctx, 1);
+	dphy_wait_clklane_ulps_active(ctx);
+	dsi_phy_clklane_ulps_rqst(ctx, 0);
+}
+
+void sprd_dphy_clk_ulps_exit(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_clklane_ulps_exit(ctx, 1);
+	dphy_wait_clklane_stop_state(ctx);
+	dsi_phy_clklane_ulps_exit(ctx, 0);
+}
+
+void sprd_dphy_force_pll(struct sprd_dphy *dphy, bool enable)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_force_pll(ctx, enable);
+}
+
+void sprd_dphy_hs_clk_en(struct sprd_dphy *dphy, bool enable)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_clk_hs_rqst(ctx, enable);
+	dphy_wait_pll_locked(ctx);
+}
+
+void sprd_dphy_test_write(struct sprd_dphy *dphy, u8 address, u8 data)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_test_en(ctx, 1);
+	dsi_phy_test_din(ctx, address);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+	dsi_phy_test_en(ctx, 0);
+	dsi_phy_test_din(ctx, data);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+}
+
+u8 sprd_dphy_test_read(struct sprd_dphy *dphy, u8 address)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+
+	dsi_phy_test_en(ctx, 1);
+	dsi_phy_test_din(ctx, address);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+	dsi_phy_test_en(ctx, 0);
+
+	udelay(1);
+
+	return dsi_phy_test_dout(ctx);
+}
diff --git a/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
new file mode 100644
index 0000000..5def380
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DPHY_API_H_
+#define _SPRD_DPHY_API_H_
+
+#include "sprd_dphy.h"
+
+int sprd_dphy_power_on(struct sprd_dphy *dphy);
+void sprd_dphy_power_off(struct sprd_dphy *dphy);
+void sprd_dphy_data_ulps_enter(struct sprd_dphy *dphy);
+void sprd_dphy_data_ulps_exit(struct sprd_dphy *dphy);
+void sprd_dphy_clk_ulps_enter(struct sprd_dphy *dphy);
+void sprd_dphy_clk_ulps_exit(struct sprd_dphy *dphy);
+void sprd_dphy_force_pll(struct sprd_dphy *dphy, bool enable);
+void sprd_dphy_hs_clk_en(struct sprd_dphy *dphy, bool enable);
+void sprd_dphy_test_write(struct sprd_dphy *dphy, u8 address, u8 data);
+u8 sprd_dphy_test_read(struct sprd_dphy *dphy, u8 address);
+
+#endif /* _SPRD_DPHY_API_H_ */
\ No newline at end of file
diff --git a/drivers/gpu/drm/sprd/dsi/Makefile b/drivers/gpu/drm/sprd/dsi/Makefile
new file mode 100644
index 0000000..d95189c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y += sprd_dsi_api.o
+
+obj-y += core/
+
diff --git a/drivers/gpu/drm/sprd/dsi/core/Makefile b/drivers/gpu/drm/sprd/dsi/core/Makefile
new file mode 100644
index 0000000..41f6477
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dsi_ctrl_r1p0.o dsi_ctrl_r1p0_ppi.o
+
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
new file mode 100644
index 0000000..83bc37c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include "dsi_ctrl_r1p0.h"
+
+/**
+ * Get DSI Host core version
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return ascii number of the version
+ */
+bool dsi_check_version(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	u32 version = readl(&reg->DSI_VERSION);
+
+	if (version == 0x100)
+		return true;
+	else if (version == 0x200)
+		return true;
+	else if (version == 0x300)
+		return true;
+	else
+		return false;
+}
+/**
+ * Modify power status of DSI Host core
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param on (1) or off (0)
+ */
+void dsi_power_enable(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(enable, &reg->SOFT_RESET);
+}
+/**
+ * Enable/disable DPI video mode
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_video_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(0, &reg->DSI_MODE_CFG);
+}
+/**
+ * Enable command mode (Generic interface)
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_cmd_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(1, &reg->DSI_MODE_CFG);
+}
+
+bool dsi_is_cmd_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	return readl(&reg->DSI_MODE_CFG);
+}
+/**
+ * Configure the read back virtual channel for the generic interface
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param vc to listen to on the line
+ */
+void dsi_rx_vcid(struct dsi_context *ctx, u8 vc)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x1C virtual_channel_id;
+
+	virtual_channel_id.val = readl(&reg->VIRTUAL_CHANNEL_ID);
+	virtual_channel_id.bits.gen_rx_vcid = vc;
+
+	writel(virtual_channel_id.val, &reg->VIRTUAL_CHANNEL_ID);
+}
+/**
+ * Write the DPI video virtual channel destination
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param vc virtual channel
+ */
+void dsi_video_vcid(struct dsi_context *ctx, u8 vc)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x1C virtual_channel_id;
+
+	virtual_channel_id.val = readl(&reg->VIRTUAL_CHANNEL_ID);
+	virtual_channel_id.bits.video_pkt_vcid = vc;
+
+	writel(virtual_channel_id.val, &reg->VIRTUAL_CHANNEL_ID);
+}
+/**
+ * Set DPI video mode type (burst/non-burst - with sync pulses or events)
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param type
+ * @return error code
+ */
+void dsi_dpi_video_burst_mode(struct dsi_context *ctx, int mode)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.vid_mode_type = mode;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Set DPI video color coding
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param color_coding enum (configuration and color depth)
+ * @return error code
+ */
+void dsi_dpi_color_coding(struct dsi_context *ctx, int coding)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x20 dpi_video_format;
+
+	dpi_video_format.val = readl(&reg->DPI_VIDEO_FORMAT);
+	dpi_video_format.bits.dpi_video_mode_format = coding;
+
+	writel(dpi_video_format.val, &reg->DPI_VIDEO_FORMAT);
+}
+/**
+ * Configure the Horizontal Line time
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle taken to transmit the total of the horizontal line
+ */
+void dsi_dpi_hline_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x2C video_line_time;
+
+	video_line_time.val = readl(&reg->VIDEO_LINE_TIME);
+	video_line_time.bits.video_line_time = byte_cycle;
+
+	writel(video_line_time.val, &reg->VIDEO_LINE_TIME);
+}
+/**
+ * Configure the Horizontal back porch time
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle taken to transmit the horizontal back porch
+ */
+void dsi_dpi_hbp_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x28 video_line_hblk_time;
+
+	video_line_hblk_time.val = readl(&reg->VIDEO_LINE_HBLK_TIME);
+	video_line_hblk_time.bits.video_line_hbp_time = byte_cycle;
+
+	writel(video_line_hblk_time.val, &reg->VIDEO_LINE_HBLK_TIME);
+}
+/**
+ * Configure the Horizontal sync time
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle taken to transmit the horizontal sync
+ */
+void dsi_dpi_hsync_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x28 video_line_hblk_time;
+
+	video_line_hblk_time.val = readl(&reg->VIDEO_LINE_HBLK_TIME);
+	video_line_hblk_time.bits.video_line_hsa_time = byte_cycle;
+
+	writel(video_line_hblk_time.val, &reg->VIDEO_LINE_HBLK_TIME);
+}
+/**
+ * Configure the vertical active lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vact(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x34 video_active_lines;
+
+	video_active_lines.val = readl(&reg->VIDEO_VACTIVE_LINES);
+	video_active_lines.bits.vactive_lines = lines;
+
+	writel(video_active_lines.val, &reg->VIDEO_VACTIVE_LINES);
+}
+/**
+ * Configure the vertical front porch lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vfp(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vfp_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+/**
+ * Configure the vertical back porch lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vbp(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vbp_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+/**
+ * Configure the vertical sync lines of the video stream
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param lines
+ */
+void dsi_dpi_vsync(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vsa_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+/**
+ * Enable return to low power mode inside horizontal front porch periods when
+ *  timing allows
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_dpi_hporch_lp_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+
+	vid_mode_cfg.bits.lp_hfp_en = enable;
+	vid_mode_cfg.bits.lp_hbp_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Enable return to low power mode inside vertical active lines periods when
+ *  timing allows
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_dpi_vporch_lp_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+
+	vid_mode_cfg.bits.lp_vact_en = enable;
+	vid_mode_cfg.bits.lp_vfp_en = enable;
+	vid_mode_cfg.bits.lp_vbp_en = enable;
+	vid_mode_cfg.bits.lp_vsa_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Enable FRAME BTA ACK
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_dpi_frame_ack_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.frame_bta_ack_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/**
+ * Write no of chunks to core - taken into consideration only when multi packet
+ * is enabled
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param no of chunks
+ */
+void dsi_dpi_chunk_num(struct dsi_context *ctx, u16 num)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x24 video_pkt_config;
+
+	video_pkt_config.val = readl(&reg->VIDEO_PKT_CONFIG);
+	video_pkt_config.bits.video_line_chunk_num = num;
+
+	writel(video_pkt_config.val, &reg->VIDEO_PKT_CONFIG);
+}
+/**
+ * Write the null packet size - will only be taken into account when null
+ * packets are enabled.
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param size of null packet
+ * @return error code
+ */
+void dsi_dpi_null_packet_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xC0 video_nullpkt_size;
+
+	video_nullpkt_size.val = readl(&reg->VIDEO_NULLPKT_SIZE);
+	video_nullpkt_size.bits.video_nullpkt_size = size;
+
+	writel(video_nullpkt_size.val, &reg->VIDEO_NULLPKT_SIZE);
+}
+/**
+ * Write video packet size. obligatory for sending video
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param size of video packet - containing information
+ * @return error code
+ */
+void dsi_dpi_video_packet_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x24 video_pkt_config;
+
+	video_pkt_config.val = readl(&reg->VIDEO_PKT_CONFIG);
+	video_pkt_config.bits.video_pkt_size = size;
+
+	writel(video_pkt_config.val, &reg->VIDEO_PKT_CONFIG);
+}
+/**
+ * Specifiy the size of the packet memory write start/continue
+ * @param instance pointer to structure holding the DSI Host core information
+ * @ size of the packet
+ * @note when different than zero (0) eDPI is enabled
+ */
+void dsi_edpi_max_pkt_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xC4 dcs_wm_pkt_size;
+
+	dcs_wm_pkt_size.val = readl(&reg->DCS_WM_PKT_SIZE);
+	dcs_wm_pkt_size.bits.dcs_wm_pkt_size = size;
+
+	writel(dcs_wm_pkt_size.val, &reg->DCS_WM_PKT_SIZE);
+}
+/**
+ * Enable tear effect acknowledge
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable (1) - disable (0)
+ */
+void dsi_tear_effect_ack_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x68 cmd_mode_cfg;
+
+	cmd_mode_cfg.val = readl(&reg->CMD_MODE_CFG);
+	cmd_mode_cfg.bits.tear_fx_en = enable;
+
+	writel(cmd_mode_cfg.val, &reg->CMD_MODE_CFG);
+}
+/**
+ * Set DCS command packet transmission to transmission type
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param no_of_param of command
+ * @param lp transmit in low power
+ * @return error code
+ */
+void dsi_cmd_mode_lp_cmd_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x68 cmd_mode_cfg;
+
+	cmd_mode_cfg.val = readl(&reg->CMD_MODE_CFG);
+
+	cmd_mode_cfg.bits.gen_sw_0p_tx = enable;
+	cmd_mode_cfg.bits.gen_sw_1p_tx = enable;
+	cmd_mode_cfg.bits.gen_sw_2p_tx = enable;
+	cmd_mode_cfg.bits.gen_lw_tx = enable;
+	cmd_mode_cfg.bits.dcs_sw_0p_tx = enable;
+	cmd_mode_cfg.bits.dcs_sw_1p_tx = enable;
+	cmd_mode_cfg.bits.dcs_lw_tx = enable;
+	cmd_mode_cfg.bits.max_rd_pkt_size = enable;
+
+	cmd_mode_cfg.bits.gen_sr_0p_tx = enable;
+	cmd_mode_cfg.bits.gen_sr_1p_tx = enable;
+	cmd_mode_cfg.bits.gen_sr_2p_tx = enable;
+	cmd_mode_cfg.bits.dcs_sr_0p_tx = enable;
+
+	writel(cmd_mode_cfg.val, &reg->CMD_MODE_CFG);
+}
+/**
+ * Set DCS read command packet transmission to transmission type
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param no_of_param of command
+ * @param lp transmit in low power
+ * @return error code
+ */
+void dsi_video_mode_lp_cmd_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.lp_cmd_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+
+/**
+ * Write command header in the generic interface
+ * (which also sends DCS commands) as a subset
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param vc of destination
+ * @param packet_type (or type of DCS command)
+ * @param ls_byte (if DCS, it is the DCS command)
+ * @param ms_byte (only parameter of short DCS packet)
+ * @return error code
+ */
+void dsi_set_packet_header(struct dsi_context *ctx,
+				   u8 vc,
+				   u8 type,
+				   u8 wc_lsb,
+				   u8 wc_msb)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x6C gen_hdr;
+
+	gen_hdr.bits.gen_dt = type;
+	gen_hdr.bits.gen_vc = vc;
+	gen_hdr.bits.gen_wc_lsbyte = wc_lsb;
+	gen_hdr.bits.gen_wc_msbyte = wc_msb;
+
+	writel(gen_hdr.val, &reg->GEN_HDR);
+}
+/**
+ * Write the payload of the long packet commands
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param payload array of bytes of payload
+ * @return error code
+ */
+void dsi_set_packet_payload(struct dsi_context *ctx, u32 payload)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(payload, &reg->GEN_PLD_DATA);
+}
+/**
+ * Write the payload of the long packet commands
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param payload pointer to 32-bit array to hold read information
+ * @return error code
+ */
+u32 dsi_get_rx_payload(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	return readl(&reg->GEN_PLD_DATA);
+}
+
+/**
+ * Enable Bus Turn-around request
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_bta_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(enable, &reg->TA_EN);
+}
+/**
+ * Enable EOTp reception
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_eotp_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xBC eotp_en;
+
+	eotp_en.val = readl(&reg->EOTP_EN);
+	eotp_en.bits.rx_eotp_en = enable;
+
+	writel(eotp_en.val, &reg->EOTP_EN);
+}
+/**
+ * Enable EOTp transmission
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_eotp_tx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xBC eotp_en;
+
+	eotp_en.val = readl(&reg->EOTP_EN);
+	eotp_en.bits.tx_eotp_en = enable;
+
+	writel(eotp_en.val, &reg->EOTP_EN);
+}
+/**
+ * Enable ECC reception, error correction and reporting
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_ecc_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xB4 rx_pkt_check_config;
+
+	rx_pkt_check_config.val = readl(&reg->RX_PKT_CHECK_CONFIG);
+	rx_pkt_check_config.bits.rx_pkt_ecc_en = enable;
+
+	writel(rx_pkt_check_config.val, &reg->RX_PKT_CHECK_CONFIG);
+}
+/**
+ * Enable CRC reception, error reporting
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ */
+void dsi_crc_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xB4 rx_pkt_check_config;
+
+	rx_pkt_check_config.val = readl(&reg->RX_PKT_CHECK_CONFIG);
+	rx_pkt_check_config.bits.rx_pkt_crc_en = enable;
+
+	writel(rx_pkt_check_config.val, &reg->RX_PKT_CHECK_CONFIG);
+}
+/**
+ * NOTE: dsi-ctrl-r1p0 only
+ *
+ * Get status of read command
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if busy
+ */
+bool dsi_is_bta_returned(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdcmd_done;
+}
+/**
+ * Get the FULL status of generic read payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo full
+ */
+bool dsi_is_rx_payload_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdata_fifo_full;
+}
+/**
+ * Get the EMPTY status of generic read payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo empty
+ */
+bool dsi_is_rx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdata_fifo_empty;
+}
+/**
+ * Get the FULL status of generic write payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo full
+ */
+bool dsi_is_tx_payload_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_wdata_fifo_full;
+}
+/**
+ * Get the EMPTY status of generic write payload fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo empty
+ */
+bool dsi_is_tx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_wdata_fifo_empty;
+}
+/**
+ * Get the FULL status of generic command fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo full
+ */
+bool dsi_is_tx_cmd_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_cmd_fifo_full;
+}
+/**
+ * Get the EMPTY status of generic command fifo
+ * @param instance pointer to structure holding the DSI Host core information
+ * @return 1 if fifo empty
+ */
+bool dsi_is_tx_cmd_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_cmd_fifo_empty;
+}
+
+/* only if DPI */
+/**
+ * DPI interface signal delay config
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle period for waiting after controller receiving HSYNC from
+ *       DPI interface to start read pixel data from memory.
+ */
+void dsi_dpi_sig_delay(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xD0 video_sig_delay_config;
+
+	video_sig_delay_config.val = readl(&reg->VIDEO_SIG_DELAY_CONFIG);
+	video_sig_delay_config.bits.video_sig_delay = byte_cycle;
+
+	writel(video_sig_delay_config.val, &reg->VIDEO_SIG_DELAY_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch data lane from high speed to low power
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_datalane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xAC phy_datalane_time_config;
+
+	phy_datalane_time_config.val = readl(&reg->PHY_DATALANE_TIME_CONFIG);
+	phy_datalane_time_config.bits.phy_datalane_hs_to_lp_time = byte_cycle;
+
+	writel(phy_datalane_time_config.val, &reg->PHY_DATALANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch the data lane from to low power high speed
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_datalane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xAC phy_datalane_time_config;
+
+	phy_datalane_time_config.val = readl(&reg->PHY_DATALANE_TIME_CONFIG);
+	phy_datalane_time_config.bits.phy_datalane_lp_to_hs_time = byte_cycle;
+
+	writel(phy_datalane_time_config.val, &reg->PHY_DATALANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch clock lane from high speed to low power
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_clklane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xA8 phy_clklane_time_config;
+
+	phy_clklane_time_config.val = readl(&reg->PHY_CLKLANE_TIME_CONFIG);
+	phy_clklane_time_config.bits.phy_clklane_hs_to_lp_time = byte_cycle;
+
+	writel(phy_clklane_time_config.val, &reg->PHY_CLKLANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch clock lane from to low power high speed
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_clklane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xA8 phy_clklane_time_config;
+
+	phy_clklane_time_config.val = readl(&reg->PHY_CLKLANE_TIME_CONFIG);
+	phy_clklane_time_config.bits.phy_clklane_lp_to_hs_time = byte_cycle;
+
+	writel(phy_clklane_time_config.val, &reg->PHY_CLKLANE_TIME_CONFIG);
+}
+/**
+ * Configure how many cycles of byte clock would the PHY module take
+ * to turn the bus around to start receiving
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle
+ * @return error code
+ */
+void dsi_max_read_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->MAX_READ_TIME);
+}
+/**
+ * Enable the automatic mechanism to stop providing clock in the clock
+ * lane when time allows
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param enable
+ * @return error code
+ */
+void dsi_nc_clk_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x74 phy_clk_lane_lp_ctrl;
+
+	phy_clk_lane_lp_ctrl.val = readl(&reg->PHY_CLK_LANE_LP_CTRL);
+	phy_clk_lane_lp_ctrl.bits.auto_clklane_ctrl_en = enable;
+
+	writel(phy_clk_lane_lp_ctrl.val, &reg->PHY_CLK_LANE_LP_CTRL);
+}
+/**
+ * Write transmission escape timeout
+ * a safe guard so that the state machine would reset if transmission
+ * takes too long
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param div
+ */
+void dsi_tx_escape_division(struct dsi_context *ctx, u8 div)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(div, &reg->TX_ESC_CLK_CONFIG);
+}
+/* PRESP Time outs */
+/**
+ * configure timeout divisions (so they would have more clock ticks)
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param div no of hs cycles before transiting back to LP in
+ *  (lane_clk / div)
+ */
+void dsi_timeout_clock_division(struct dsi_context *ctx, u8 div)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(div, &reg->TIMEOUT_CNT_CLK_CONFIG);
+}
+/**
+ * Configure the Low power receive time out
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle (of byte cycles)
+ */
+void dsi_lp_rx_timeout(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->LRX_H_TO_CONFIG);
+}
+/**
+ * Configure a high speed transmission time out
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param byte_cycle (byte cycles)
+ */
+void dsi_hs_tx_timeout(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->HTX_TO_CONFIG);
+}
+/**
+ * Get the error 0 interrupt register status
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask the mask to be read from the register
+ * @return error status 0 value
+ */
+u32 dsi_int0_status(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x08 protocol_int_sts;
+
+	protocol_int_sts.val = readl(&reg->PROTOCOL_INT_STS);
+	writel(protocol_int_sts.val, &reg->PROTOCOL_INT_CLR);
+
+	if (protocol_int_sts.bits.dphy_errors_0)
+		DRM_ERROR("dphy_err: escape entry error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_1)
+		DRM_ERROR("dphy_err: lp data transmission sync error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_2)
+		DRM_ERROR("dphy_err: control error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_3)
+		DRM_ERROR("dphy_err: LP0 contention error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_4)
+		DRM_ERROR("dphy_err: LP1 contention error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_0)
+		DRM_ERROR("ack_err: SoT error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_1)
+		DRM_ERROR("ack_err: SoT Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_2)
+		DRM_ERROR("ack_err: EoT Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_3)
+		DRM_ERROR("ack_err: Escape Mode Entry Command error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_4)
+		DRM_ERROR("ack_err: LP Transmit Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_5)
+		DRM_ERROR("ack_err: Peripheral Timeout error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_6)
+		DRM_ERROR("ack_err: False Control error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_7)
+		DRM_ERROR("ack_err: reserved (specific to device)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_8)
+		DRM_ERROR("ack_err: ECC error, single-bit (corrected)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_9)
+		DRM_ERROR("ack_err: ECC error, multi-bit (not corrected)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_10)
+		DRM_ERROR("ack_err: checksum error (long packet only)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_11)
+		DRM_ERROR("ack_err: not recognized DSI data type\n");
+
+	if (protocol_int_sts.bits.ack_with_err_12)
+		DRM_ERROR("ack_err: DSI VC ID Invalid\n");
+
+	if (protocol_int_sts.bits.ack_with_err_13)
+		DRM_ERROR("ack_err: invalid transmission length\n");
+
+	if (protocol_int_sts.bits.ack_with_err_14)
+		DRM_ERROR("ack_err: reserved (specific to device)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_15)
+		DRM_ERROR("ack_err: DSI protocol violation\n");
+
+	return 0;
+}
+/**
+ * Get the error 1 interrupt register status
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask the mask to be read from the register
+ * @return error status 1 value
+ */
+u32 dsi_int1_status(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x10 internal_int_sts;
+	u32 status = 0;
+
+	internal_int_sts.val = readl(&reg->INTERNAL_INT_STS);
+	writel(internal_int_sts.val, &reg->INTERNAL_INT_CLR);
+
+	if (internal_int_sts.bits.receive_pkt_size_err)
+		DRM_ERROR("receive packet size error\n");
+
+	if (internal_int_sts.bits.eotp_not_receive_err)
+		DRM_ERROR("EoTp packet is not received\n");
+
+	if (internal_int_sts.bits.gen_cmd_cmd_fifo_wr_err)
+		DRM_ERROR("cmd header-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_rdata_fifo_rd_err)
+		DRM_ERROR("cmd read-payload-fifo is empty\n");
+
+	if (internal_int_sts.bits.gen_cmd_rdata_fifo_wr_err)
+		DRM_ERROR("cmd read-payload-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_wdata_fifo_wr_err)
+		DRM_ERROR("cmd write-payload-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_wdata_fifo_rd_err)
+		DRM_ERROR("cmd write-payload-fifo is empty\n");
+
+	if (internal_int_sts.bits.dpi_pix_fifo_wr_err) {
+		DRM_ERROR("DPI pixel-fifo is full\n");
+		status |= DSI_INT_STS_NEED_SOFT_RESET;
+	}
+
+	if (internal_int_sts.bits.ecc_single_err)
+		DRM_ERROR("ECC single error in a received packet\n");
+
+	if (internal_int_sts.bits.ecc_multi_err)
+		DRM_ERROR("ECC multiple error in a received packet\n");
+
+	if (internal_int_sts.bits.crc_err)
+		DRM_ERROR("CRC error in the received packet payload\n");
+
+	if (internal_int_sts.bits.hs_tx_timeout)
+		DRM_ERROR("high-speed transmission timeout\n");
+
+	if (internal_int_sts.bits.lp_rx_timeout)
+		DRM_ERROR("low-power reception timeout\n");
+
+	return status;
+}
+/**
+ * Configure MASK (hiding) of interrupts coming from error 0 source
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask to be written to the register
+ */
+void dsi_int0_mask(struct dsi_context *ctx, u32 mask)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(mask, &reg->MASK_PROTOCOL_INT);
+}
+/**
+ * Configure MASK (hiding) of interrupts coming from error 1 source
+ * @param instance pointer to structure holding the DSI Host core information
+ * @param mask the mask to be written to the register
+ */
+void dsi_int1_mask(struct dsi_context *ctx, u32 mask)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(mask, &reg->MASK_INTERNAL_INT);
+}
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
new file mode 100644
index 0000000..08b9958
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
@@ -0,0 +1,1477 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DSI_CTRL_R1P0_H_
+#define _DSI_CTRL_R1P0_H_
+
+#include <asm/types.h>
+
+#include "sprd_dsi.h"
+
+struct dsi_reg {
+	union _0x00 {
+		u32 val;
+		struct _DSI_VERSION {
+		u32 dsi_version: 16;
+		u32 reserved: 16;
+		} bits;
+	} DSI_VERSION;
+
+	union _0x04 {
+		u32 val;
+		struct _SOFT_RESET {
+		/*
+		 * This bit configures the core either to work normal or to
+		 * reset. It's default value is 0. After the core configur-
+		 * ation, to enable the mipi_dsi_host, set this register to 1.
+		 * 1: power up     0: reset core
+		 */
+		u32 dsi_soft_reset: 1;
+
+		u32 reserved: 31;
+		} bits;
+	} SOFT_RESET;
+
+	union _0x08 {
+		u32 val;
+		struct _PROTOCOL_INT_STS {
+		/* ErrEsc escape entry error from Lane 0 */
+		u32 dphy_errors_0: 1;
+
+		/* ErrSyncEsc low-power data transmission synchronization
+		 * error from Lane 0
+		 */
+		u32 dphy_errors_1: 1;
+
+		/* ErrControl error from Lane 0 */
+		u32 dphy_errors_2: 1;
+
+		/* ErrContentionLP0 LP0 contention error from Lane 0 */
+		u32 dphy_errors_3: 1;
+
+		/* ErrContentionLP1 LP1 contention error from Lane 0 */
+		u32 dphy_errors_4: 1;
+
+		/* debug mode protocol errors */
+		u32 protocol_debug_err: 11;
+
+		/* SoT error from the Acknowledge error report */
+		u32 ack_with_err_0: 1;
+
+		/* SoT Sync error from the Acknowledge error report */
+		u32 ack_with_err_1: 1;
+
+		/* EoT Sync error from the Acknowledge error report */
+		u32 ack_with_err_2: 1;
+
+		/* Escape Mode Entry Command error from the Acknowledge
+		 * error report
+		 */
+		u32 ack_with_err_3: 1;
+
+		/* LP Transmit Sync error from the Acknowledge error report */
+		u32 ack_with_err_4: 1;
+
+		/* Peripheral Timeout error from the Acknowledge error report */
+		u32 ack_with_err_5: 1;
+
+		/* False Control error from the Acknowledge error report */
+		u32 ack_with_err_6: 1;
+
+		/* reserved (specific to device) from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_7: 1;
+
+		/* ECC error, single-bit (detected and corrected) from the
+		 * Acknowledge error report
+		 */
+		u32 ack_with_err_8: 1;
+
+		/* ECC error, multi-bit (detected, not corrected) from the
+		 * Acknowledge error report
+		 */
+		u32 ack_with_err_9: 1;
+
+		/* checksum error (long packet only) from the Acknowledge
+		 * error report
+		 */
+		u32 ack_with_err_10: 1;
+
+		/* not recognized DSI data type from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_11: 1;
+
+		/* DSI VC ID Invalid from the Acknowledge error report */
+		u32 ack_with_err_12: 1;
+
+		/* invalid transmission length from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_13: 1;
+
+		/* reserved (specific to device) from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_14: 1;
+
+		/* DSI protocol violation from the Acknowledge error report */
+		u32 ack_with_err_15: 1;
+
+		} bits;
+	} PROTOCOL_INT_STS;
+
+	union _0x0C {
+		u32 val;
+		struct _MASK_PROTOCOL_INT {
+		u32 mask_dphy_errors_0: 1;
+		u32 mask_dphy_errors_1: 1;
+		u32 mask_dphy_errors_2: 1;
+		u32 mask_dphy_errors_3: 1;
+		u32 mask_dphy_errors_4: 1;
+		u32 mask_protocol_debug_err: 11;
+		u32 mask_ack_with_err_0: 1;
+		u32 mask_ack_with_err_1: 1;
+		u32 mask_ack_with_err_2: 1;
+		u32 mask_ack_with_err_3: 1;
+		u32 mask_ack_with_err_4: 1;
+		u32 mask_ack_with_err_5: 1;
+		u32 mask_ack_with_err_6: 1;
+		u32 mask_ack_with_err_7: 1;
+		u32 mask_ack_with_err_8: 1;
+		u32 mask_ack_with_err_9: 1;
+		u32 mask_ack_with_err_10: 1;
+		u32 mask_ack_with_err_11: 1;
+		u32 mask_ack_with_err_12: 1;
+		u32 mask_ack_with_err_13: 1;
+		u32 mask_ack_with_err_14: 1;
+		u32 mask_ack_with_err_15: 1;
+		} bits;
+	} MASK_PROTOCOL_INT;
+
+	union _0x10 {
+		u32 val;
+		struct _INTERNAL_INT_STS {
+		/* This bit indicates that the packet size error is detected
+		 * during the packet reception.
+		 */
+		u32 receive_pkt_size_err: 1;
+
+		/* This bit indicates that the EoTp packet is not received at
+		 * the end of the incoming peripheral transmission
+		 */
+		u32 eotp_not_receive_err: 1;
+
+		/* This bit indicates that the system tried to write a command
+		 * through the Generic interface and the FIFO is full. There-
+		 * fore, the command is not written.
+		 */
+		u32 gen_cmd_cmd_fifo_wr_err: 1;
+
+		/* This bit indicates that during a DCS read data, the payload
+		 * FIFO becomes	empty and the data sent to the interface is
+		 * corrupted.
+		 */
+		u32 gen_cmd_rdata_fifo_rd_err: 1;
+
+		/* This bit indicates that during a generic interface packet
+		 * read back, the payload FIFO becomes full and the received
+		 * data is corrupted.
+		 */
+		u32 gen_cmd_rdata_fifo_wr_err: 1;
+
+		/* This bit indicates that the system tried to write a payload
+		 * data through the Generic interface and the FIFO is full.
+		 * Therefore, the payload is not written.
+		 */
+		u32 gen_cmd_wdata_fifo_wr_err: 1;
+
+		/* This bit indicates that during a Generic interface packet
+		 * build, the payload FIFO becomes empty and corrupt data is
+		 * sent.
+		 */
+		u32 gen_cmd_wdata_fifo_rd_err: 1;
+
+		/* This bit indicates that during a DPI pixel line storage,
+		 * the payload FIFO becomes full and the data stored is
+		 * corrupted.
+		 */
+		u32 dpi_pix_fifo_wr_err: 1;
+
+		/* internal debug error	*/
+		u32 internal_debug_err: 19;
+
+		/* This bit indicates that the ECC single error is detected
+		 * and corrected in a received packet.
+		 */
+		u32 ecc_single_err: 1;
+
+		/* This bit indicates that the ECC multiple error is detected
+		 * in a received packet.
+		 */
+		u32 ecc_multi_err: 1;
+
+		/* This bit indicates that the CRC error is detected in the
+		 * received packet payload.
+		 */
+		u32 crc_err: 1;
+
+		/* This bit indicates that the high-speed transmission timeout
+		 * counter reached the end and contention is detected.
+		 */
+		u32 hs_tx_timeout: 1;
+
+		/* This bit indicates that the low-power reception timeout
+		 * counter reached the end and contention is detected.
+		 */
+		u32 lp_rx_timeout: 1;
+
+		} bits;
+	} INTERNAL_INT_STS;
+
+	union _0x14 {
+		u32 val;
+		struct _MASK_INTERNAL_INT {
+		u32 mask_receive_pkt_size_err: 1;
+		u32 mask_eopt_not_receive_err: 1;
+		u32 mask_gen_cmd_cmd_fifo_wr_err: 1;
+		u32 mask_gen_cmd_rdata_fifo_rd_err: 1;
+		u32 mask_gen_cmd_rdata_fifo_wr_err: 1;
+		u32 mask_gen_cmd_wdata_fifo_wr_err: 1;
+		u32 mask_gen_cmd_wdata_fifo_rd_err: 1;
+		u32 mask_dpi_pix_fifo_wr_err: 1;
+		u32 mask_internal_debug_err: 19;
+		u32 mask_ecc_single_err: 1;
+		u32 mask_ecc_multi_err: 1;
+		u32 mask_crc_err: 1;
+		u32 mask_hs_tx_timeout: 1;
+		u32 mask_lp_rx_timeout: 1;
+		} bits;
+	} MASK_INTERNAL_INT;
+
+	union _0x18 {
+		u32 val;
+		struct _DSI_MODE_CFG {
+		/* This bit configures the operation mode
+		 * 0: Video mode ;   1: Command mode
+		 */
+		u32 cmd_video_mode: 1;
+
+		u32 reserved: 31;
+
+		} bits;
+	} DSI_MODE_CFG;
+
+	union _0x1C {
+		u32 val;
+		struct _VIRTUAL_CHANNEL_ID {
+		/* This field indicates the Generic interface read-back
+		 * virtual channel identification
+		 */
+		u32 gen_rx_vcid: 2;
+
+		/* This field configures the DPI virtual channel id that
+		 * is indexed to the VIDEO mode packets
+		 */
+		u32 video_pkt_vcid: 2;
+
+		u32 reserved: 28;
+
+		} bits;
+	} VIRTUAL_CHANNEL_ID;
+
+	union _0x20 {
+		u32 val;
+		struct _DPI_VIDEO_FORMAT {
+		/*
+		 * This field configures the DPI color coding as follows:
+		 * 0000: 16-bit configuration 1
+		 * 0001: 16-bit configuration 2
+		 * 0010: 16-bit configuration 3
+		 * 0011: 18-bit configuration 1
+		 * 0100: 18-bit configuration 2
+		 * 0101: 24-bit
+		 * 0110: 20-bit YCbCr 4:2:2 loosely packed
+		 * 0111: 24-bit YCbCr 4:2:2
+		 * 1000: 16-bit YCbCr 4:2:2
+		 * 1001: 30-bit
+		 * 1010: 36-bit
+		 * 1011: 12-bit YCbCr 4:2:0
+		 * 1100: Compression Display Stream
+		 * 1101-1111: 12-bit YCbCr 4:2:0
+		 */
+		u32 dpi_video_mode_format: 6;
+
+		/* When set to 1, this bit activates loosely packed
+		 * variant to 18-bit configurations
+		 */
+		u32 loosely18_en: 1;
+
+		u32 reserved: 25;
+
+		} bits;
+	} DPI_VIDEO_FORMAT;
+
+	union _0x24 {
+		u32 val;
+		struct _VIDEO_PKT_CONFIG {
+		/*
+		 * This field configures the number of pixels in a single
+		 * video packet. For 18-bit not loosely packed data types,
+		 * this number must be a multiple of 4. For YCbCr data
+		 * types, it must be a multiple of 2, as described in the
+		 * DSI specification.
+		 */
+		u32 video_pkt_size: 16;
+
+		/*
+		 * This register configures the number of chunks to be
+		 * transmitted during a Line period (a chunk consists of
+		 * a video packet and a null packet). If set to 0 or 1,
+		 * the video line is transmitted in a single packet. If
+		 * set to 1, the packet is part of a chunk, so a null packet
+		 * follows it if vid_null_size > 0. Otherwise, multiple chunks
+		 * are used to transmit each video line.
+		 */
+		u32 video_line_chunk_num: 16;
+
+		} bits;
+	} VIDEO_PKT_CONFIG;
+
+	union _0x28 {
+		u32 val;
+		struct _VIDEO_LINE_HBLK_TIME {
+		/* This field configures the Horizontal Back Porch period
+		 * in lane byte clock cycles
+		 */
+		u32 video_line_hbp_time: 16;
+
+		/* This field configures the Horizontal Synchronism Active
+		 * period in lane byte clock cycles
+		 */
+		u32 video_line_hsa_time: 16;
+
+		} bits;
+	} VIDEO_LINE_HBLK_TIME;
+
+	union _0x2C {
+		u32 val;
+		struct _VIDEO_LINE_TIME {
+		/* This field configures the size of the total line time
+		 * (HSA+HBP+HACT+HFP) counted in lane byte clock cycles
+		 */
+		u32 video_line_time: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} VIDEO_LINE_TIME;
+
+	union _0x30 {
+		u32 val;
+		struct _VIDEO_VBLK_LINES {
+		/* This field configures the Vertical Front Porch period
+		 * measured in number of horizontal lines
+		 */
+		u32 vfp_lines: 10;
+
+		/* This field configures the Vertical Back Porch period
+		 * measured in number of horizontal lines
+		 */
+		u32 vbp_lines: 10;
+
+		/* This field configures the Vertical Synchronism Active
+		 * period measured in number of horizontal lines
+		 */
+		u32 vsa_lines: 10;
+
+		u32 reserved: 2;
+
+		} bits;
+	} VIDEO_VBLK_LINES;
+
+	union _0x34 {
+		u32 val;
+		struct _VIDEO_VACTIVE_LINES {
+		/* This field configures the Vertical Active period measured
+		 * in number of horizontal lines
+		 */
+		u32 vactive_lines: 14;
+
+		u32 reserved: 18;
+
+		} bits;
+	} VIDEO_VACTIVE_LINES;
+
+	union _0x38 {
+		u32 val;
+		struct _VID_MODE_CFG {
+		/*
+		 * This field indicates the video mode transmission type as
+		 * follows:
+		 * 00: Non-burst with sync pulses
+		 * 01: Non-burst with sync events
+		 * 10 and 11: Burst mode
+		 */
+		u32 vid_mode_type: 2;
+
+		u32 reserved_0: 6;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VSA period when timing allows.
+		 */
+		u32 lp_vsa_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VBP period when timing allows.
+		 */
+		u32 lp_vbp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VFP period when timing allows.
+		 */
+		u32 lp_vfp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VACT period when timing allows.
+		 */
+		u32 lp_vact_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the HBP period when timing allows.
+		 */
+		u32 lp_hbp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the HFP period when timing allows.
+		 */
+		u32 lp_hfp_en: 1;
+
+		/* When set to 1, this bit enables the request for an ack-
+		 * nowledge response at the end of a frame.
+		 */
+		u32 frame_bta_ack_en: 1;
+
+		/* When set to 1, this bit enables the command transmission
+		 * only in low-power mode.
+		 */
+		u32 lp_cmd_en: 1;
+
+		u32 reserved_1: 16;
+
+		} bits;
+	} VID_MODE_CFG;
+
+	union _0x3C {
+		u32 val;
+		struct _SDF_MODE_CONFIG {
+		/*
+		 * This field defines the 3D mode on/off & display orientation:
+		 * 00: 3D mode off (2D mode on)
+		 * 01: 3D mode on, portrait orientation
+		 * 10: 3D mode on, landscape orientation
+		 * 11: Reserved
+		 */
+		u32 rf_3d_mode: 2;
+
+		/*
+		 * This field defines the 3D image format:
+		 * 00: Line (alternating lines of left and right data)
+		 * 01: Frame (alternating frames of left and right data)
+		 * 10: Pixel (alternating pixels of left and right data)
+		 * 11: Reserved
+		 */
+		u32 rf_3d_format: 2;
+
+		/*
+		 * This field defines whether there is a second VSYNC pulse
+		 * between Left and Right Images, when 3D Image Format is
+		 * Frame-based:
+		 * 0: No sync pulses between left and right data
+		 * 1: Sync pulse (HSYNC, VSYNC, blanking) between left and
+		 *    right data
+		 */
+		u32 second_vsync_en: 1;
+
+		/*
+		 * This bit defines the left or right order:
+		 * 0: Left eye data is sent first, and then the right eye data
+		 *    is sent.
+		 * 1: Right eye data is sent first, and then the left eye data
+		 *    is sent.
+		 */
+		u32 left_right_order: 1;
+
+		u32 reserved_0: 2;
+
+		/*
+		 * When set, causes the next VSS packet to include 3D control
+		 * payload in every VSS packet.
+		 */
+		u32 rf_3d_payload_en: 1;
+
+		u32 reserved_1: 23;
+
+		} bits;
+	} SDF_MODE_CONFIG;
+
+	union _0x40 {
+		u32 val;
+		struct _TIMEOUT_CNT_CLK_CONFIG {
+		/*
+		 * This field indicates the division factor for the Time Out
+		 * clock used as the timing unit in the configuration of HS to
+		 * LP and LP to HS transition error.
+		 */
+		u32 timeout_cnt_clk_config: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} TIMEOUT_CNT_CLK_CONFIG;
+
+	union _0x44 {
+		u32 val;
+		struct _HTX_TO_CONFIG {
+		/*
+		 * This field configures the timeout counter that triggers
+		 * a high speed transmission timeout contention detection
+		 * (measured in TO_CLK_DIVISION cycles).
+		 *
+		 * If using the non-burst mode and there is no sufficient
+		 * time to switch from HS to LP and back in the period which
+		 * is from one line data finishing to the next line sync
+		 * start, the DSI link returns the LP state once per frame,
+		 * then you should configure the TO_CLK_DIVISION and
+		 * hstx_to_cnt to be in accordance with:
+		 * hstx_to_cnt * lanebyteclkperiod * TO_CLK_DIVISION >= the
+		 * time of one FRAME data transmission * (1 + 10%)
+		 *
+		 * In burst mode, RGB pixel packets are time-compressed,
+		 * leaving more time during a scan line. Therefore, if in
+		 * burst mode and there is sufficient time to switch from HS
+		 * to LP and back in the period of time from one line data
+		 * finishing to the next line sync start, the DSI link can
+		 * return LP mode and back in this time interval to save power.
+		 * For this, configure the TO_CLK_DIVISION and hstx_to_cnt
+		 * to be in accordance with:
+		 * hstx_to_cnt * lanebyteclkperiod * TO_CLK_DIVISION >= the
+		 * time of one LINE data transmission * (1 + 10%)
+		 */
+		u32 htx_to_cnt_limit: 32;
+		} bits;
+	} HTX_TO_CONFIG;
+
+	union _0x48 {
+		u32 val;
+		struct _LRX_H_TO_CONFIG {
+		/*
+		 * This field configures the timeout counter that triggers
+		 * a low-power reception timeout contention detection (measured
+		 * in TO_CLK_DIVISION cycles).
+		 */
+		u32 lrx_h_to_cnt_limit: 32;
+		} bits;
+	} LRX_H_TO_CONFIG;
+
+	union _0x4C {
+		u32 val;
+		struct _RD_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a low-power read oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 lprd_presp_to_cnt_limit: 16;
+
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a high-speed read oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 hsrd_presp_to_cnt_limit: 16;
+
+		} bits;
+	} RD_PRESP_TO_CONFIG;
+
+	union _0x50 {
+		u32 val;
+		struct _HSWR_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link inactive after sending a high-speed write
+		 * operation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 hswr_presp_to_cnt_limit: 16;
+
+		u32 reserved_0: 8;
+
+		/*
+		 * When set to 1, this bit ensures that the peripheral response
+		 * timeout caused by hs_wr_to_cnt is used only once per eDPI
+		 * frame, when both the following conditions are met:
+		 * dpivsync_edpiwms has risen and fallen.
+		 * Packets originated from eDPI have been transmitted and its
+		 * FIFO is empty again In this scenario no non-eDPI requests
+		 * are sent to the D-PHY, even if there is traffic from generic
+		 * or DBI ready to be sent, making it return to stop state.
+		 * When it does so, PRESP_TO counter is activated and only when
+		 * it finishes does the controller send any other traffic that
+		 * is ready.
+		 */
+		u32 hswr_presp_to_mode: 1;
+
+		u32 reserved_1: 7;
+
+		} bits;
+	} HSWR_PRESP_TO_CONFIG;
+
+	union _0x54 {
+		u32 val;
+		struct _LPWR_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a low-power write oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 lpwr_presp_to_cnt_limit: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} LPWR_PRESP_TO_CONFIG;
+
+	union _0x58 {
+		u32 val;
+		struct _BTA_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after completing a Bus Turn-Around.
+		 * This period is measured in cycles of lanebyteclk. The
+		 * counting starts when the D-PHY enters the Stop state and
+		 * causes no interrupts.
+		 */
+		u32 bta_presp_to_cnt_limit: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} BTA_PRESP_TO_CONFIG;
+
+	union _0x5C {
+		u32 val;
+		struct _TX_ESC_CLK_CONFIG {
+		/*
+		 * This field indicates the division factor for the TX Escape
+		 * clock source (lanebyteclk). The values 0 and 1 stop the
+		 * TX_ESC clock generation.
+		 */
+		u32 tx_esc_clk_config: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} TX_ESC_CLK_CONFIG;
+
+	union _0x60 {
+		u32 val;
+		struct _VACT_CMD_TRANS_LIMIT {
+		/*
+		 * This field is used for the transmission of commands in
+		 * low-power mode. It defines the size, in bytes, of the
+		 * largest packet that can fit in a line during the VACT
+		 * region.
+		 */
+		u32 vact_cmd_trans_limit: 8;
+
+		u32 reserved: 24;
+
+		} bits;
+	} VACT_CMD_TRANS_LIMIT;
+
+	union _0x64 {
+		u32 val;
+		struct _VBLK_CMD_TRANS_LIMIT {
+		/*
+		 * This field is used for the transmission of commands in
+		 * low-power mode. It defines the size, in bytes, of the
+		 * largest packet that can fit in a line during the VSA, VBP,
+		 * and VFP regions.
+		 */
+		u32 vblk_cmd_trans_limit: 8;
+
+		u32 reserved: 24;
+
+		} bits;
+	} VBLK_CMD_TRANS_LIMIT;
+
+	union _0x68 {
+		u32 val;
+		struct _CMD_MODE_CFG {
+		/*
+		 * When set to 1, this bit enables the tearing effect
+		 * acknowledge request.
+		 */
+		u32 tear_fx_en: 1;
+
+		/*
+		 * When set to 1, this bit enables the acknowledge request
+		 * after each packet transmission.
+		 */
+		u32 ack_rqst_en: 1;
+
+		u32 reserved_0: 3;
+
+		u32 pps_tx: 1;
+		u32 exq_tx: 1;
+		u32 cmc_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * zero parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_0p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * one parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_1p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * two parameters command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_2p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * zero parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_0p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * one parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_1p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * two parameters command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_2p_tx: 1;
+
+		/*
+		 * This bit configures the Generic long write packet command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_lw_tx: 1;
+
+		u32 reserved_1: 1;
+
+		/*
+		 * This bit configures the DCS short write packet with zero
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sw_0p_tx: 1;
+
+		/*
+		 * This bit configures the DCS short write packet with one
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sw_1p_tx: 1;
+
+		/*
+		 * This bit configures the DCS short read packet with zero
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sr_0p_tx: 1;
+
+		/*
+		 * This bit configures the DCS long write packet command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_lw_tx: 1;
+
+		u32 reserved_2: 4;
+
+		/*
+		 * This bit configures the maximum read packet size command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 max_rd_pkt_size: 1;
+
+		u32 reserved_3: 7;
+
+		} bits;
+	} CMD_MODE_CFG;
+
+	union _0x6C {
+		u32 val;
+		struct _GEN_HDR {
+		/*
+		 * This field configures the packet data type of the header
+		 * packet.
+		 */
+		u32 gen_dt: 6;
+
+		/*
+		 * This field configures the virtual channel id of the header
+		 * packet.
+		 */
+		u32 gen_vc: 2;
+
+		/*
+		 * This field configures the least significant byte of the
+		 * header packet's Word count for long packets or data 0 for
+		 * short packets.
+		 */
+		u32 gen_wc_lsbyte: 8;
+
+		/*
+		 * This field configures the most significant byte of the
+		 * header packet's word count for long packets or data 1 for
+		 * short packets.
+		 */
+		u32 gen_wc_msbyte: 8;
+
+		u32 reserved: 8;
+
+		} bits;
+	} GEN_HDR;
+
+	union _0x70 {
+		u32 val;
+		struct _GEN_PLD_DATA {
+		/* This field indicates byte 1 of the packet payload. */
+		u32 gen_pld_b1: 8;
+
+		/* This field indicates byte 2 of the packet payload. */
+		u32 gen_pld_b2: 8;
+
+		/* This field indicates byte 3 of the packet payload. */
+		u32 gen_pld_b3: 8;
+
+		/* This field indicates byte 4 of the packet payload. */
+		u32 gen_pld_b4: 8;
+
+		} bits;
+	} GEN_PLD_DATA;
+
+	union _0x74 {
+		u32 val;
+		struct _PHY_CLK_LANE_LP_CTRL {
+		/* This bit controls the D-PHY PPI txrequestclkhs signal */
+		u32 phy_clklane_tx_req_hs: 1;
+
+		/* This bit enables the automatic mechanism to stop providing
+		 * clock in the clock lane when time allows.
+		 */
+		u32 auto_clklane_ctrl_en: 1;
+
+		u32 reserved: 30;
+		} bits;
+	} PHY_CLK_LANE_LP_CTRL;
+
+	union _0x78 {
+		u32 val;
+		struct _PHY_INTERFACE_CTRL {
+		/* When set to 0, this bit places the D-PHY macro in power-
+		 * down state.
+		 */
+		u32 rf_phy_shutdown: 1;
+
+		/* When set to 0, this bit places the digital section of the
+		 * D-PHY in the reset state.
+		 */
+		u32 rf_phy_reset_n: 1;
+
+		/* When set to 1, this bit enables the D-PHY Clock Lane
+		 * module.
+		 */
+		u32 rf_phy_clk_en: 1;
+
+		/* When the D-PHY is in ULPS, this bit enables the D-PHY PLL. */
+		u32 rf_phy_force_pll: 1;
+
+		/* ULPS mode Request on clock lane */
+		u32 rf_phy_clk_txrequlps: 1;
+
+		/* ULPS mode Exit on clock lane */
+		u32 rf_phy_clk_txexitulps: 1;
+
+		/* ULPS mode Request on all active data lanes */
+		u32 rf_phy_data_txrequlps: 1;
+
+		/* ULPS mode Exit on all active data lanes */
+		u32 rf_phy_data_txexitulps: 1;
+
+		u32 reserved: 24;
+		} bits;
+	} PHY_INTERFACE_CTRL;
+
+	union _0x7C {
+		u32 val;
+		struct _PHY_TX_TRIGGERS {
+		/* This field controls the trigger transmissions. */
+		u32 phy_tx_triggers: 4;
+
+		u32 reserved: 28;
+		} bits;
+	} PHY_TX_TRIGGERS;
+
+	union _0x80 {
+		u32 val;
+		struct _DESKEW_START {
+		u32 deskew_start: 1;
+		u32 reserved: 31;
+		} bits;
+	} DESKEW_START;
+
+	union _0x84 {
+		u32 val;
+		struct _DESKEW_MODE {
+		u32 deskew_mode: 2;
+		u32 reserved: 30;
+		} bits;
+	} DESKEW_MODE;
+
+	union _0x88 {
+		u32 val;
+		struct _DESKEW_TIME {
+		u32 deskew_time: 32;
+		} bits;
+	} DESKEW_TIME;
+
+	union _0x8C {
+		u32 val;
+		struct _DESKEW_PERIOD {
+		u32 deskew_period: 32;
+		} bits;
+	} DESKEW_PERIOD;
+
+	union _0x90 {
+		u32 val;
+		struct _DESKEW_BUSY {
+		u32 deskew_busy: 1;
+		u32 reserved: 31;
+		} bits;
+	} DESKEW_BUSY;
+
+	union _0x94 {
+		u32 val;
+		struct _DESKEW_LANE_MASK {
+		u32 deskew_lane0_mask: 1;
+		u32 deskew_lane1_mask: 1;
+		u32 deskew_lane2_mask: 1;
+		u32 deskew_lane3_mask: 1;
+		u32 reserved: 28;
+		} bits;
+	} DESKEW_LANE_MASK;
+
+	union _0x98 {
+		u32 val;
+		struct _CMD_MODE_STATUS {
+		/*
+		 * This bit is set when a read command is issued and cleared
+		 * when the entire response is stored in the FIFO.
+		 * Value after reset: 0x0
+		 *
+		 * NOTE:
+		 * For mipi-dsi-r1p0 IP, this bit is set immediately when
+		 *     the read cmd is set to the GEN_HDR register.
+		 *
+		 * For dsi-ctrl-r1p0 IP, this bit is set only after the read
+		 *     cmd was actually sent out from the controller.
+		 */
+		u32 gen_cmd_rdcmd_ongoing: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic read
+		 * payload FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_rdata_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic read
+		 * payload FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_rdata_fifo_full: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic write
+		 * payload FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_wdata_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic write
+		 * payload FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_wdata_fifo_full: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic
+		 * command FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_cmd_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic
+		 * command FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_cmd_fifo_full: 1;
+
+		/*
+		 * This bit is set when the entire response of read is
+		 * stored in the rx payload FIFO. And it will be cleared
+		 * automaticlly after read this bit each time.
+		 * Value after reset: 0x0
+		 *
+		 * NOTE: this bit is just supported for dsi-ctrl-r1p0 IP
+		 */
+		u32 gen_cmd_rdcmd_done: 1;
+
+		u32 reserved : 24;
+
+		} bits;
+	} CMD_MODE_STATUS;
+
+	union _0x9C {
+		u32 val;
+		struct _PHY_STATUS {
+		/* the status of phydirection D-PHY signal */
+		u32 phy_direction: 1;
+
+		/* the status of phylock D-PHY signal */
+		u32 phy_lock: 1;
+
+		/* the status of rxulpsesc0lane D-PHY signal */
+		u32 phy_rxulpsesc0lane: 1;
+
+		/* the status of phystopstateclklane D-PHY signal */
+		u32 phy_stopstateclklane: 1;
+
+		/* the status of phystopstate0lane D-PHY signal */
+		u32 phy_stopstate0lane: 1;
+
+		/* the status of phystopstate1lane D-PHY signal */
+		u32 phy_stopstate1lane: 1;
+
+		/* the status of phystopstate2lane D-PHY signal */
+		u32 phy_stopstate2lane: 1;
+
+		/* the status of phystopstate3lane D-PHY signal */
+		u32 phy_stopstate3lane: 1;
+
+		/* the status of phyulpsactivenotclk D-PHY signal */
+		u32 phy_ulpsactivenotclk: 1;
+
+		/* the status of ulpsactivenot0lane D-PHY signal */
+		u32 phy_ulpsactivenot0lane: 1;
+
+		/* the status of ulpsactivenot1lane D-PHY signal */
+		u32 phy_ulpsactivenot1lane: 1;
+
+		/* the status of ulpsactivenot2lane D-PHY signal */
+		u32 phy_ulpsactivenot2lane: 1;
+
+		/* the status of ulpsactivenot3lane D-PHY signal */
+		u32 phy_ulpsactivenot3lane: 1;
+
+		u32 reserved: 19;
+
+		} bits;
+	} PHY_STATUS;
+
+	union _0xA0 {
+		u32 val;
+		struct _PHY_MIN_STOP_TIME {
+		/* This field configures the minimum wait period to request
+		 * a high-speed transmission after the Stop state.
+		 */
+		u32 phy_min_stop_time: 8;
+
+		u32 reserved: 24;
+		} bits;
+	} PHY_MIN_STOP_TIME;
+
+	union _0xA4 {
+		u32 val;
+		struct _PHY_LANE_NUM_CONFIG {
+		/*
+		 * This field configures the number of active data lanes:
+		 * 00: One data lane (lane 0)
+		 * 01: Two data lanes (lanes 0 and 1)
+		 * 10: Three data lanes (lanes 0, 1, and 2)
+		 * 11: Four data lanes (lanes 0, 1, 2, and 3)
+		 */
+		u32 phy_lane_num: 2;
+
+		u32 reserved: 30;
+
+		} bits;
+	} PHY_LANE_NUM_CONFIG;
+
+	union _0xA8 {
+		u32 val;
+		struct _PHY_CLKLANE_TIME_CONFIG {
+		/*
+		 * This field configures the maximum time that the D-PHY
+		 * clock lane takes to go from low-power to high-speed
+		 * transmission measured in lane byte clock cycles.
+		 */
+		u32 phy_clklane_lp_to_hs_time: 16;
+
+		/*
+		 * This field configures the maximum time that the D-PHY
+		 * clock lane takes to go from high-speed to low-power
+		 * transmission measured in lane byte clock cycles.
+		 */
+		u32 phy_clklane_hs_to_lp_time: 16;
+
+		} bits;
+	} PHY_CLKLANE_TIME_CONFIG;
+
+	union _0xAC {
+		u32 val;
+		struct _PHY_DATALANE_TIME_CONFIG {
+		/*
+		 * This field configures the maximum time that the D-PHY data
+		 * lanes take to go from low-power to high-speed transmission
+		 * measured in lane byte clock cycles.
+		 */
+		u32 phy_datalane_lp_to_hs_time: 16;
+
+		/*
+		 * This field configures the maximum time that the D-PHY data
+		 * lanes take to go from high-speed to low-power transmission
+		 * measured in lane byte clock cycles.
+		 */
+		u32 phy_datalane_hs_to_lp_time: 16;
+
+		} bits;
+	} PHY_DATALANE_TIME_CONFIG;
+
+	union _0xB0 {
+		u32 val;
+		struct _MAX_READ_TIME {
+		/*
+		 * This field configures the maximum time required to perform
+		 * a read command in lane byte clock cycles. This register can
+		 * only be modified when no read command is in progress.
+		 */
+		u32 max_rd_time: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} MAX_READ_TIME;
+
+	union _0xB4 {
+		u32 val;
+		struct _RX_PKT_CHECK_CONFIG {
+		/* When set to 1, this bit enables the ECC reception, error
+		 * correction, and reporting.
+		 */
+		u32 rx_pkt_ecc_en: 1;
+
+		/* When set to 1, this bit enables the CRC reception and error
+		 * reporting.
+		 */
+		u32 rx_pkt_crc_en: 1;
+
+		u32 reserved: 30;
+
+		} bits;
+	} RX_PKT_CHECK_CONFIG;
+
+	union _0xB8 {
+		u32 val;
+		struct _TA_EN {
+		/* When set to 1, this bit enables the Bus Turn-Around (BTA)
+		 * request.
+		 */
+		u32 ta_en: 1;
+
+		u32 reserved: 31;
+
+		} bits;
+	} TA_EN;
+
+	union _0xBC {
+		u32 val;
+		struct _EOTP_EN {
+		/* When set to 1, this bit enables the EoTp transmission */
+		u32 tx_eotp_en: 1;
+
+		/* When set to 1, this bit enables the EoTp reception. */
+		u32 rx_eotp_en: 1;
+
+		u32 reserved: 30;
+
+		} bits;
+	} EOTP_EN;
+
+	union _0xC0 {
+		u32 val;
+		struct _VIDEO_NULLPKT_SIZE {
+		/*
+		 * This register configures the number of bytes inside a null
+		 * packet. Setting it to 0 disables the null packets.
+		 */
+		u32 video_nullpkt_size: 13;
+
+		u32 reserved: 19;
+
+		} bits;
+	} VIDEO_NULLPKT_SIZE;
+
+	union _0xC4 {
+		u32 val;
+		struct _DCS_WM_PKT_SIZE {
+		/*
+		 * This field configures the maximum allowed size for an eDPI
+		 * write memory command, measured in pixels. Automatic parti-
+		 * tioning of data obtained from eDPI is permanently enabled.
+		 */
+		u32 dcs_wm_pkt_size: 16;
+
+		u32 reserved: 16;
+		} bits;
+	} DCS_WM_PKT_SIZE;
+
+	union _0xC8 {
+		u32 val;
+		struct _PROTOCOL_INT_CLR {
+		u32 clr_dphy_errors_0: 1;
+		u32 clr_dphy_errors_1: 1;
+		u32 clr_dphy_errors_2: 1;
+		u32 clr_dphy_errors_3: 1;
+		u32 clr_dphy_errors_4: 1;
+		u32 clr_protocol_debug_err: 11;
+		u32 clr_ack_with_err_0: 1;
+		u32 clr_ack_with_err_1: 1;
+		u32 clr_ack_with_err_2: 1;
+		u32 clr_ack_with_err_3: 1;
+		u32 clr_ack_with_err_4: 1;
+		u32 clr_ack_with_err_5: 1;
+		u32 clr_ack_with_err_6: 1;
+		u32 clr_ack_with_err_7: 1;
+		u32 clr_ack_with_err_8: 1;
+		u32 clr_ack_with_err_9: 1;
+		u32 clr_ack_with_err_10: 1;
+		u32 clr_ack_with_err_11: 1;
+		u32 clr_ack_with_err_12: 1;
+		u32 clr_ack_with_err_13: 1;
+		u32 clr_ack_with_err_14: 1;
+		u32 clr_ack_with_err_15: 1;
+		} bits;
+	} PROTOCOL_INT_CLR;
+
+	union _0xCC {
+		u32 val;
+		struct _INTERNAL_INT_CLR {
+		u32 clr_receive_pkt_size_err: 1;
+		u32 clr_eopt_not_receive_err: 1;
+		u32 clr_gen_cmd_cmd_fifo_wr_err: 1;
+		u32 clr_gen_cmd_rdata_fifo_rd_err: 1;
+		u32 clr_gen_cmd_rdata_fifo_wr_err: 1;
+		u32 clr_gen_cmd_wdata_fifo_wr_err: 1;
+		u32 clr_gen_cmd_wdata_fifo_rd_err: 1;
+		u32 clr_dpi_pix_fifo_wr_err: 1;
+		u32 clr_internal_debug_err: 19;
+		u32 clr_ecc_single_err: 1;
+		u32 clr_ecc_multi_err: 1;
+		u32 clr_crc_err: 1;
+		u32 clr_hs_tx_timeout: 1;
+		u32 clr_lp_rx_timeout: 1;
+		} bits;
+	} INTERNAL_INT_CLR;
+
+	union _0xD0 {
+		u32 val;
+		struct _VIDEO_SIG_DELAY_CONFIG {
+
+		/*
+		 * DPI interface signal delay to be used in clk lanebyte
+		 * domain for control logic to read video data from pixel
+		 * memory in mannal mode, measured in clk_lanebyte cycles
+		 */
+		u32 video_sig_delay: 24;
+
+		/*
+		 * 1'b1: mannal mode
+		 *       dsi controller will use video_sig_delay value as
+		 *       the delay for the packet handle logic to read video
+		 *       data from pixel memory.
+		 *
+		 * 1'b0: auto mode
+		 *       dsi controller will auto calculate the delay for
+		 *       the packet handle logic to read video data from
+		 *       pixel memory.
+		 */
+		u32 video_sig_delay_mode: 1;
+
+		u32 reserved: 7;
+		} bits;
+	} VIDEO_SIG_DELAY_CONFIG;
+
+	u32 reservedD4_EC[7];
+
+	union _0xF0 {
+		u32 val;
+		struct _PHY_TST_CTRL0 {
+		/* PHY test interface clear (active high) */
+		u32 phy_testclr: 1;
+
+		/* This bit is used to clock the TESTDIN bus into the D-PHY */
+		u32 phy_testclk: 1;
+
+		u32 reserved: 30;
+		} bits;
+	} PHY_TST_CTRL0;
+
+	union _0xF4 {
+		u32 val;
+		struct _PHY_TST_CTRL1 {
+		/* PHY test interface input 8-bit data bus for internal
+		 * register programming and test functionalities access.
+		 */
+		u32 phy_testdin: 8;
+
+		/* PHY output 8-bit data bus for read-back and internal
+		 * probing functionalities.
+		 */
+		u32 phy_testdout: 8;
+
+		/*
+		 * PHY test interface operation selector:
+		 * 1: The address write operation is set on the falling edge
+		 *    of the testclk signal.
+		 * 0: The data write operation is set on the rising edge of
+		 *    the testclk signal.
+		 */
+		u32 phy_testen: 1;
+
+		u32 reserved: 15;
+		} bits;
+	} PHY_TST_CTRL1;
+
+	u32 reservedF8_1FC[66];
+
+	union _0x200 {
+		u32 val;
+		struct _INT_PLL_STS {
+		u32 int_pll_sts: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_STS;
+
+	union _0x204 {
+		u32 val;
+		struct _INT_PLL_MSK {
+		u32 int_pll_msk: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_MSK;
+
+	union _0x208 {
+		u32 val;
+		struct _INT_PLL_CLR {
+		u32 int_pll_clr: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_CLR;
+
+};
+
+bool dsi_check_version(struct dsi_context *ctx);
+void dsi_power_enable(struct dsi_context *ctx, int enable);
+void dsi_video_mode(struct dsi_context *ctx);
+void dsi_cmd_mode(struct dsi_context *ctx);
+bool dsi_is_cmd_mode(struct dsi_context *ctx);
+void dsi_rx_vcid(struct dsi_context *ctx, u8 vc);
+void dsi_video_vcid(struct dsi_context *ctx, u8 vc);
+void dsi_dpi_video_burst_mode(struct dsi_context *ctx, int mode);
+void dsi_dpi_color_coding(struct dsi_context *ctx, int coding);
+void dsi_dpi_sig_delay(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hline_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hsync_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hbp_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_vact(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vfp(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vbp(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vsync(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_hporch_lp_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_vporch_lp_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_frame_ack_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_chunk_num(struct dsi_context *ctx, u16 no);
+void dsi_dpi_null_packet_size(struct dsi_context *ctx, u16 size);
+void dsi_dpi_video_packet_size(struct dsi_context *ctx, u16 size);
+void dsi_edpi_max_pkt_size(struct dsi_context *ctx, u16 size);
+void dsi_tear_effect_ack_en(struct dsi_context *ctx, int enable);
+void dsi_cmd_mode_lp_cmd_en(struct dsi_context *ctx, int enable);
+void dsi_video_mode_lp_cmd_en(struct dsi_context *ctx, int enable);
+void dsi_set_packet_header(struct dsi_context *ctx, u8 vc, u8 type,
+						u8 wc_lsb, u8 wc_msb);
+void dsi_set_packet_payload(struct dsi_context *ctx, u32 payload);
+u32 dsi_get_rx_payload(struct dsi_context *ctx);
+void dsi_bta_en(struct dsi_context *ctx, int enable);
+void dsi_eotp_rx_en(struct dsi_context *ctx, int enable);
+void dsi_eotp_tx_en(struct dsi_context *ctx, int enable);
+void dsi_ecc_rx_en(struct dsi_context *ctx, int enable);
+void dsi_crc_rx_en(struct dsi_context *ctx, int enable);
+bool dsi_is_bta_returned(struct dsi_context *ctx);
+bool dsi_is_rx_payload_fifo_full(struct dsi_context *ctx);
+bool dsi_is_rx_payload_fifo_empty(struct dsi_context *ctx);
+bool dsi_is_tx_payload_fifo_full(struct dsi_context *ctx);
+bool dsi_is_tx_payload_fifo_empty(struct dsi_context *ctx);
+bool dsi_is_tx_cmd_fifo_full(struct dsi_context *ctx);
+bool dsi_is_tx_cmd_fifo_empty(struct dsi_context *ctx);
+void dsi_datalane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_datalane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_clklane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_clklane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_max_read_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_nc_clk_en(struct dsi_context *ctx, int enable);
+void dsi_tx_escape_division(struct dsi_context *ctx, u8 div);
+void dsi_timeout_clock_division(struct dsi_context *ctx, u8 div);
+void dsi_lp_rx_timeout(struct dsi_context *ctx, u16 count);
+void dsi_hs_tx_timeout(struct dsi_context *ctx, u16 count);
+u32 dsi_int0_status(struct dsi_context *ctx);
+u32 dsi_int1_status(struct dsi_context *ctx);
+void dsi_int0_mask(struct dsi_context *ctx, u32 mask);
+void dsi_int1_mask(struct dsi_context *ctx, u32 mask);
+
+#endif /* _DSI_CTRL_R1P0_H_ */
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
new file mode 100644
index 0000000..2102d7c
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "dsi_ctrl_r1p0.h"
+#include "dsi_ctrl_r1p0_ppi.h"
+#include "sprd_dphy.h"
+
+/**
+ * Reset D-PHY module
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param reset
+ */
+void dsi_phy_rstz(struct dphy_context *ctx, int level)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_reset_n = level;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Power up/down D-PHY module
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param enable (1: shutdown)
+ */
+void dsi_phy_shutdownz(struct dphy_context *ctx, int level)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_shutdown = level;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Force D-PHY PLL to stay on while in ULPS
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param force (1) disable (0)
+ * @note To follow the programming model, use wakeup_pll function
+ */
+void dsi_phy_force_pll(struct dphy_context *ctx, int force)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_force_pll = force;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_clklane_ulps_rqst(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_txrequlps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_clklane_ulps_exit(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_txexitulps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_datalane_ulps_rqst(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_data_txrequlps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+void dsi_phy_datalane_ulps_exit(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_data_txexitulps = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Configure minimum wait period for HS transmission request after a stop state
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param no_of_byte_cycles [in byte (lane) clock cycles]
+ */
+void dsi_phy_stop_wait_time(struct dphy_context *ctx, u8 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+
+	writel(byte_cycle, &reg->PHY_MIN_STOP_TIME);
+}
+
+/**
+ * Set number of active lanes
+ * @param phy: pointer to structure
+ *  which holds information about the d-phy module
+ * @param no_of_lanes
+ */
+void dsi_phy_datalane_en(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+
+	writel(ctx->lanes - 1, &reg->PHY_LANE_NUM_CONFIG);
+}
+
+/**
+ * Enable clock lane module
+ * @param phy pointer to structure
+ *  which holds information about the d-phy module
+ * @param en
+ */
+void dsi_phy_clklane_en(struct dphy_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_en = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/**
+ * Request the PHY module to start transmission of high speed clock.
+ * This causes the clock lane to start transmitting DDR clock on the
+ * lane interconnect.
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param enable
+ * @note this function should be called explicitly by user always except for
+ * transmitting
+ */
+void dsi_phy_clk_hs_rqst(struct dphy_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x74 phy_clk_lane_lp_ctrl;
+
+	phy_clk_lane_lp_ctrl.val = readl(&reg->PHY_CLK_LANE_LP_CTRL);
+	phy_clk_lane_lp_ctrl.bits.auto_clklane_ctrl_en = 0;
+	phy_clk_lane_lp_ctrl.bits.phy_clklane_tx_req_hs = enable;
+
+	writel(phy_clk_lane_lp_ctrl.val, &reg->PHY_CLK_LANE_LP_CTRL);
+}
+
+/**
+ * Get D-PHY PPI status
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param mask
+ * @return status
+ */
+u8 dsi_phy_is_pll_locked(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return phy_status.bits.phy_lock;
+}
+
+u8 dsi_phy_is_stop_state_datalane(struct dphy_context *ctx)
+{
+	u8 state = 0;
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	if (phy_status.bits.phy_stopstate0lane)
+		state |= BIT(0);
+	if (phy_status.bits.phy_stopstate1lane)
+		state |= BIT(1);
+	if (phy_status.bits.phy_stopstate2lane)
+		state |= BIT(2);
+	if (phy_status.bits.phy_stopstate3lane)
+		state |= BIT(3);
+
+	return state;
+}
+
+u8 dsi_phy_is_stop_state_clklane(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return phy_status.bits.phy_stopstateclklane;
+}
+
+u8 dsi_phy_is_ulps_active_datalane(struct dphy_context *ctx)
+{
+	u8 state = 0;
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	if (!phy_status.bits.phy_ulpsactivenot0lane)
+		state |= BIT(0);
+	if (!phy_status.bits.phy_ulpsactivenot1lane)
+		state |= BIT(1);
+	if (!phy_status.bits.phy_ulpsactivenot2lane)
+		state |= BIT(2);
+	if (!phy_status.bits.phy_ulpsactivenot3lane)
+		state |= BIT(3);
+
+	return state;
+}
+
+u8 dsi_phy_is_ulps_active_clklane(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return !phy_status.bits.phy_ulpsactivenotclk;
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param value
+ */
+void dsi_phy_test_clk(struct dphy_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF0 phy_tst_ctrl0;
+
+	phy_tst_ctrl0.val = readl(&reg->PHY_TST_CTRL0);
+	phy_tst_ctrl0.bits.phy_testclk = value;
+
+	writel(phy_tst_ctrl0.val, &reg->PHY_TST_CTRL0);
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param value
+ */
+void dsi_phy_test_clr(struct dphy_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF0 phy_tst_ctrl0;
+
+	phy_tst_ctrl0.val = readl(&reg->PHY_TST_CTRL0);
+	phy_tst_ctrl0.bits.phy_testclr = value;
+
+	writel(phy_tst_ctrl0.val, &reg->PHY_TST_CTRL0);
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param on_falling_edge
+ */
+void dsi_phy_test_en(struct dphy_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+	phy_tst_ctrl1.bits.phy_testen = value;
+
+	writel(phy_tst_ctrl1.val, &reg->PHY_TST_CTRL1);
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ */
+u8 dsi_phy_test_dout(struct dphy_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+
+	return phy_tst_ctrl1.bits.phy_testdout;
+}
+
+/**
+ * @param phy pointer to structure which holds information about the d-phy
+ * module
+ * @param test_data
+ */
+void dsi_phy_test_din(struct dphy_context *ctx, u8 data)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->ctrlbase;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+	phy_tst_ctrl1.bits.phy_testdin = data;
+
+	writel(phy_tst_ctrl1.val, &reg->PHY_TST_CTRL1);
+}
diff --git a/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
new file mode 100644
index 0000000..7477604
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DSI_CTRL_R1P0_PPI_H_
+#define _DSI_CTRL_R1P0_PPI_H_
+
+void dsi_phy_rstz(struct dphy_context *ctx, int level);
+void dsi_phy_shutdownz(struct dphy_context *ctx, int level);
+void dsi_phy_force_pll(struct dphy_context *ctx, int force);
+void dsi_phy_clklane_ulps_rqst(struct dphy_context *ctx, int en);
+void dsi_phy_clklane_ulps_exit(struct dphy_context *ctx, int en);
+void dsi_phy_datalane_ulps_rqst(struct dphy_context *ctx, int en);
+void dsi_phy_datalane_ulps_exit(struct dphy_context *ctx, int en);
+void dsi_phy_stop_wait_time(struct dphy_context *ctx, u8 byte_clk);
+void dsi_phy_datalane_en(struct dphy_context *ctx);
+void dsi_phy_clklane_en(struct dphy_context *ctx, int en);
+void dsi_phy_clk_hs_rqst(struct dphy_context *ctx, int en);
+u8 dsi_phy_is_pll_locked(struct dphy_context *ctx);
+u8 dsi_phy_is_stop_state_clklane(struct dphy_context *ctx);
+u8 dsi_phy_is_stop_state_datalane(struct dphy_context *ctx);
+u8 dsi_phy_is_ulps_active_datalane(struct dphy_context *ctx);
+u8 dsi_phy_is_ulps_active_clklane(struct dphy_context *ctx);
+void dsi_phy_test_clk(struct dphy_context *ctx, u8 level);
+void dsi_phy_test_clr(struct dphy_context *ctx, u8 level);
+void dsi_phy_test_en(struct dphy_context *ctx, u8 level);
+u8 dsi_phy_test_dout(struct dphy_context *ctx);
+void dsi_phy_test_din(struct dphy_context *ctx, u8 data);
+void dsi_phy_bist_en(struct dphy_context *ctx, int en);
+
+#endif /* _DSI_CTRL_R1P0_PPI_H_ */
\ No newline at end of file
diff --git a/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
new file mode 100644
index 0000000..98487ba
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/delay.h>
+
+#include "core/dsi_ctrl_r1p0.h"
+#include "sprd_dsi.h"
+
+static int dsi_wait_tx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_is_tx_payload_fifo_empty(ctx))
+			return 0;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int dsi_wait_tx_cmd_fifo_empty(struct dsi_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		if (dsi_is_tx_cmd_fifo_empty(ctx))
+			return 0;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int dsi_wait_rd_resp_completed(struct dsi_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < 10000; i++) {
+		if (dsi_is_bta_returned(ctx))
+			return 0;
+		udelay(10);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static u16 calc_bytes_per_pixel_x100(int coding)
+{
+	u16 Bpp_x100;
+
+	switch (coding) {
+	case COLOR_CODE_16BIT_CONFIG1:
+	case COLOR_CODE_16BIT_CONFIG2:
+	case COLOR_CODE_16BIT_CONFIG3:
+		Bpp_x100 = 200;
+		break;
+	case COLOR_CODE_18BIT_CONFIG1:
+	case COLOR_CODE_18BIT_CONFIG2:
+		Bpp_x100 = 225;
+		break;
+	case COLOR_CODE_24BIT:
+		Bpp_x100 = 300;
+		break;
+	case COLOR_CODE_COMPRESSTION:
+		Bpp_x100 = 100;
+		break;
+	case COLOR_CODE_20BIT_YCC422_LOOSELY:
+		Bpp_x100 = 250;
+		break;
+	case COLOR_CODE_24BIT_YCC422:
+		Bpp_x100 = 300;
+		break;
+	case COLOR_CODE_16BIT_YCC422:
+		Bpp_x100 = 200;
+		break;
+	case COLOR_CODE_30BIT:
+		Bpp_x100 = 375;
+		break;
+	case COLOR_CODE_36BIT:
+		Bpp_x100 = 450;
+		break;
+	case COLOR_CODE_12BIT_YCC420:
+		Bpp_x100 = 150;
+		break;
+	default:
+		DRM_ERROR("invalid color coding");
+		Bpp_x100 = -EINVAL;
+		break;
+	}
+
+	return Bpp_x100;
+}
+
+static u8 calc_video_size_step(int coding)
+{
+	u8 video_size_step;
+
+	switch (coding) {
+	case COLOR_CODE_16BIT_CONFIG1:
+	case COLOR_CODE_16BIT_CONFIG2:
+	case COLOR_CODE_16BIT_CONFIG3:
+	case COLOR_CODE_18BIT_CONFIG1:
+	case COLOR_CODE_18BIT_CONFIG2:
+	case COLOR_CODE_24BIT:
+	case COLOR_CODE_COMPRESSTION:
+		return video_size_step = 1;
+	case COLOR_CODE_20BIT_YCC422_LOOSELY:
+	case COLOR_CODE_24BIT_YCC422:
+	case COLOR_CODE_16BIT_YCC422:
+	case COLOR_CODE_30BIT:
+	case COLOR_CODE_36BIT:
+	case COLOR_CODE_12BIT_YCC420:
+		return video_size_step = 2;
+	default:
+		DRM_ERROR("invalid color coding");
+		return -EINVAL;
+	}
+}
+
+static u16 round_video_size(int coding, u16 video_size)
+{
+	switch (coding) {
+	case COLOR_CODE_16BIT_YCC422:
+	case COLOR_CODE_24BIT_YCC422:
+	case COLOR_CODE_20BIT_YCC422_LOOSELY:
+	case COLOR_CODE_12BIT_YCC420:
+		/* round up active H pixels to a multiple of 2 */
+		if ((video_size % 2) != 0)
+			video_size += 1;
+		break;
+	default:
+		break;
+	}
+
+	return video_size;
+}
+
+#define SPRD_MIPI_DSI_FMT_DSC 0xff
+static u32 fmt_to_coding(u32 fmt)
+{
+	switch (fmt) {
+	case MIPI_DSI_FMT_RGB565:
+		return COLOR_CODE_16BIT_CONFIG1;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return COLOR_CODE_18BIT_CONFIG1;
+	case MIPI_DSI_FMT_RGB666:
+	case MIPI_DSI_FMT_RGB888:
+		return COLOR_CODE_24BIT;
+	case SPRD_MIPI_DSI_FMT_DSC:
+		return COLOR_CODE_COMPRESSTION;
+	default:
+		DRM_ERROR("Unsupported format (%d)\n", fmt);
+		return COLOR_CODE_24BIT;
+	}
+}
+
+#define ns_to_cycle(ns, byte_clk) \
+	DIV_ROUND_UP((ns) * (byte_clk), 1000000)
+
+void sprd_dsi_power_on(struct sprd_dsi *dsi)
+{
+	int div;
+	struct dsi_context *ctx = &dsi->ctx;
+	u16 max_rd_time;
+	u16 data_hs2lp, data_lp2hs, clk_hs2lp, clk_lp2hs;
+
+	dsi_power_enable(ctx, 0);
+	dsi_int0_mask(ctx, 0xffffffff);
+	dsi_int1_mask(ctx, 0xffffffff);
+	dsi_cmd_mode(ctx);
+	dsi_eotp_rx_en(ctx, 0);
+	dsi_eotp_tx_en(ctx, 0);
+	dsi_ecc_rx_en(ctx, 1);
+	dsi_crc_rx_en(ctx, 1);
+	dsi_bta_en(ctx, 1);
+	dsi_video_vcid(ctx, 0);
+	dsi_rx_vcid(ctx, 0);
+
+	div = DIV_ROUND_UP(ctx->byte_clk, ctx->esc_clk);
+	dsi_tx_escape_division(ctx, div);
+
+	max_rd_time = ns_to_cycle(ctx->max_rd_time, ctx->byte_clk);
+	dsi_max_read_time(ctx, max_rd_time);
+
+	data_hs2lp = ns_to_cycle(ctx->data_hs2lp, ctx->byte_clk);
+	data_lp2hs = ns_to_cycle(ctx->data_lp2hs, ctx->byte_clk);
+	clk_hs2lp = ns_to_cycle(ctx->clk_hs2lp, ctx->byte_clk);
+	clk_lp2hs = ns_to_cycle(ctx->clk_lp2hs, ctx->byte_clk);
+	dsi_datalane_hs2lp_config(ctx, data_hs2lp);
+	dsi_datalane_lp2hs_config(ctx, data_lp2hs);
+	dsi_clklane_hs2lp_config(ctx, clk_hs2lp);
+	dsi_clklane_lp2hs_config(ctx, clk_lp2hs);
+
+	dsi_power_enable(ctx, 1);
+}
+
+/**
+ * Close DSI Host driver
+ * - Free up resources and shutdown host controller and PHY
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @return
+ */
+void sprd_dsi_power_off(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	dsi_int0_mask(ctx, 0xffffffff);
+	dsi_int1_mask(ctx, 0xffffffff);
+	dsi_power_enable(ctx, 0);
+}
+
+/**
+ * Configure DPI video interface
+ * - If not in burst mode, it will compute the video and null packet sizes
+ * according to necessity
+ * - Configure timers for data lanes and/or clock lane to return to LP when
+ * bandwidth is not filled by data
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @param param pointer to video stream-to-send information
+ * @return error code
+ */
+int sprd_dsi_dpi_video(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	struct videomode *vm = &dsi->ctx.vm;
+	u16 Bpp_x100;
+	u16 video_size;
+	u32 ratio_x1000;
+	u16 null_pkt_size = 0;
+	u8 video_size_step;
+	u32 hs_to;
+	u32 total_bytes;
+	u32 bytes_per_chunk;
+	u32 chunks = 0;
+	u32 bytes_left = 0;
+	u32 chunk_overhead;
+	const u8 pkt_header = 6;
+	u8 coding;
+	int div;
+	u16 hline;
+
+	coding = fmt_to_coding(ctx->format);
+	video_size = round_video_size(coding, vm->hactive);
+	Bpp_x100 = calc_bytes_per_pixel_x100(coding);
+	video_size_step = calc_video_size_step(coding);
+	ratio_x1000 = ctx->byte_clk * 1000 / (vm->pixelclock / 1000);
+	hline = vm->hactive + vm->hsync_len + vm->hfront_porch +
+		vm->hback_porch;
+
+	dsi_power_enable(ctx, 0);
+	dsi_dpi_frame_ack_en(ctx, ctx->frame_ack_en);
+	dsi_dpi_color_coding(ctx, coding);
+	dsi_dpi_video_burst_mode(ctx, ctx->burst_mode);
+	dsi_dpi_sig_delay(ctx, 95 * hline * ratio_x1000 / 100000);
+	dsi_dpi_hline_time(ctx, hline * ratio_x1000 / 1000);
+	dsi_dpi_hsync_time(ctx, vm->hsync_len * ratio_x1000 / 1000);
+	dsi_dpi_hbp_time(ctx, vm->hback_porch * ratio_x1000 / 1000);
+	dsi_dpi_vact(ctx, vm->vactive);
+	dsi_dpi_vfp(ctx, vm->vfront_porch);
+	dsi_dpi_vbp(ctx, vm->vback_porch);
+	dsi_dpi_vsync(ctx, vm->vsync_len);
+	dsi_dpi_hporch_lp_en(ctx, 1);
+	dsi_dpi_vporch_lp_en(ctx, 1);
+
+	hs_to = (hline * vm->vactive) + (2 * Bpp_x100) / 100;
+	for (div = 0x80; (div < hs_to) && (div > 2); div--) {
+		if ((hs_to % div) == 0) {
+			dsi_timeout_clock_division(ctx, div);
+			dsi_lp_rx_timeout(ctx, hs_to / div);
+			dsi_hs_tx_timeout(ctx, hs_to / div);
+			break;
+		}
+	}
+
+	if (ctx->burst_mode == VIDEO_BURST_WITH_SYNC_PULSES) {
+		dsi_dpi_video_packet_size(ctx, video_size);
+		dsi_dpi_null_packet_size(ctx, 0);
+		dsi_dpi_chunk_num(ctx, 0);
+	} else {
+		/* non burst transmission */
+		null_pkt_size = 0;
+
+		/* bytes to be sent - first as one chunk */
+		bytes_per_chunk = vm->hactive * Bpp_x100 / 100 + pkt_header;
+
+		/* hline total bytes from the DPI interface */
+		total_bytes = (vm->hactive + vm->hfront_porch) *
+				ratio_x1000 / ctx->lanes / 1000;
+
+		/* check if the pixels actually fit on the DSI link */
+		if (total_bytes < bytes_per_chunk) {
+			DRM_ERROR("current resolution can not be set\n");
+			return -EINVAL;
+		}
+
+		chunk_overhead = total_bytes - bytes_per_chunk;
+
+		/* overhead higher than 1 -> enable multi packets */
+		if (chunk_overhead > 1) {
+
+			/* multi packets */
+			for (video_size = video_size_step;
+			     video_size < vm->hactive;
+			     video_size += video_size_step) {
+
+				if (vm->hactive * 1000 / video_size % 1000)
+					continue;
+
+				chunks = vm->hactive / video_size;
+				bytes_per_chunk = Bpp_x100 * video_size / 100
+						  + pkt_header;
+				if (total_bytes >= (bytes_per_chunk * chunks)) {
+					bytes_left = total_bytes -
+						     bytes_per_chunk * chunks;
+					break;
+				}
+			}
+
+			/* prevent overflow (unsigned - unsigned) */
+			if (bytes_left > (pkt_header * chunks)) {
+				null_pkt_size = (bytes_left -
+						pkt_header * chunks) / chunks;
+				/* avoid register overflow */
+				if (null_pkt_size > 1023)
+					null_pkt_size = 1023;
+			}
+
+		} else {
+
+			/* single packet */
+			chunks = 1;
+
+			/* must be a multiple of 4 except 18 loosely */
+			for (video_size = vm->hactive;
+			    (video_size % video_size_step) != 0;
+			     video_size++)
+				;
+		}
+
+		dsi_dpi_video_packet_size(ctx, video_size);
+		dsi_dpi_null_packet_size(ctx, null_pkt_size);
+		dsi_dpi_chunk_num(ctx, chunks);
+	}
+
+	dsi_int0_mask(ctx, ctx->int0_mask);
+	dsi_int1_mask(ctx, ctx->int1_mask);
+	dsi_power_enable(ctx, 1);
+
+	return 0;
+}
+
+int sprd_dsi_edpi_video(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	const u32 fifo_depth = 1096;
+	const u32 word_length = 4;
+	u32 hactive = ctx->vm.hactive;
+	u32 Bpp_x100;
+	u32 max_fifo_len;
+	u8 coding;
+
+	coding = fmt_to_coding(ctx->format);
+	Bpp_x100 = calc_bytes_per_pixel_x100(coding);
+	max_fifo_len = word_length * fifo_depth * 100 / Bpp_x100;
+
+	dsi_power_enable(ctx, 0);
+	dsi_dpi_color_coding(ctx, coding);
+	dsi_tear_effect_ack_en(ctx, ctx->te_ack_en);
+
+	if (max_fifo_len > hactive)
+		dsi_edpi_max_pkt_size(ctx, hactive);
+	else
+		dsi_edpi_max_pkt_size(ctx, max_fifo_len);
+
+	dsi_int0_mask(ctx, ctx->int0_mask);
+	dsi_int1_mask(ctx, ctx->int1_mask);
+	dsi_power_enable(ctx, 1);
+
+	return 0;
+}
+
+/**
+ * Send a packet on the generic interface
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @param vc destination virtual channel
+ * @param data_type type of command, inserted in first byte of header
+ * @param params byte array of command parameters
+ * @param param_length length of the above array
+ * @return error code
+ * @note this function has an active delay to wait for the buffer to clear.
+ * The delay is limited to:
+ * (param_length / 4) x DSIH_FIFO_ACTIVE_WAIT x register access time
+ * @note the controller restricts the sending of .
+ * This function will not be able to send Null and Blanking packets due to
+ *  controller restriction
+ */
+int sprd_dsi_wr_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			const u8 *param, u16 len)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	u8 wc_lsbyte, wc_msbyte;
+	u32 payload;
+	int i, j, ret;
+
+	if (vc > 3)
+		return -EINVAL;
+
+
+	/* 1st: for long packet, must config payload first */
+	ret = dsi_wait_tx_payload_fifo_empty(ctx);
+	if (ret) {
+		DRM_ERROR("tx payload fifo is not empty\n");
+		return ret;
+	}
+
+	if (len > 2) {
+		for (i = 0, j = 0; i < len; i += j) {
+			payload = 0;
+			for (j = 0; (j < 4) && ((j + i) < (len)); j++)
+				payload |= param[i + j] << (j * 8);
+
+			dsi_set_packet_payload(ctx, payload);
+		}
+		wc_lsbyte = len & 0xff;
+		wc_msbyte = len >> 8;
+	} else {
+		wc_lsbyte = (len > 0) ? param[0] : 0;
+		wc_msbyte = (len > 1) ? param[1] : 0;
+	}
+
+	/* 2nd: then set packet header */
+	ret = dsi_wait_tx_cmd_fifo_empty(ctx);
+	if (ret) {
+		DRM_ERROR("tx cmd fifo is not empty\n");
+		return ret;
+	}
+
+	dsi_set_packet_header(ctx, vc, type, wc_lsbyte, wc_msbyte);
+
+	return 0;
+}
+
+
+/**
+ * Send READ packet to peripheral using the generic interface
+ * This will force command mode and stop video mode (because of BTA)
+ * @param dsi pointer to structure holding the DSI Host core information
+ * @param vc destination virtual channel
+ * @param data_type generic command type
+ * @param lsb_byte first command parameter
+ * @param msb_byte second command parameter
+ * @param bytes_to_read no of bytes to read (expected to arrive at buffer)
+ * @param read_buffer pointer to 8-bit array to hold the read buffer words
+ * return read_buffer_length
+ * @note this function has an active delay to wait for the buffer to clear.
+ * The delay is limited to 2 x DSIH_FIFO_ACTIVE_WAIT
+ * (waiting for command buffer, and waiting for receiving)
+ * @note this function will enable BTA
+ */
+int sprd_dsi_rd_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			u8 msb_byte, u8 lsb_byte,
+			u8 *buffer, u8 bytes_to_read)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	int i, ret;
+	int count = 0;
+	u32 temp;
+
+	if (vc > 3)
+		return -EINVAL;
+
+	/* 1st: send read command to peripheral */
+	if (!dsi_is_tx_cmd_fifo_empty(ctx))
+		return -EIO;
+
+	dsi_set_packet_header(ctx, vc, type, lsb_byte, msb_byte);
+
+	/* 2nd: wait peripheral response completed */
+	ret = dsi_wait_rd_resp_completed(ctx);
+	if (ret) {
+		DRM_ERROR("wait read response time out\n");
+		return ret;
+	}
+
+	/* 3rd: get data from rx payload fifo */
+	if (dsi_is_rx_payload_fifo_empty(ctx)) {
+		DRM_ERROR("rx payload fifo empty\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < 100; i++) {
+		temp = dsi_get_rx_payload(ctx);
+
+		if (count < bytes_to_read)
+			buffer[count++] = temp & 0xff;
+		if (count < bytes_to_read)
+			buffer[count++] = (temp >> 8) & 0xff;
+		if (count < bytes_to_read)
+			buffer[count++] = (temp >> 16) & 0xff;
+		if (count < bytes_to_read)
+			buffer[count++] = (temp >> 24) & 0xff;
+
+		if (dsi_is_rx_payload_fifo_empty(ctx))
+			return count;
+		else {
+			DRM_ERROR("read too many buffers\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+void sprd_dsi_set_work_mode(struct sprd_dsi *dsi, u8 mode)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (mode == DSI_MODE_CMD)
+		dsi_cmd_mode(ctx);
+	else
+		dsi_video_mode(ctx);
+}
+
+int sprd_dsi_get_work_mode(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (dsi_is_cmd_mode(ctx))
+		return DSI_MODE_CMD;
+	else
+		return DSI_MODE_VIDEO;
+}
+
+void sprd_dsi_lp_cmd_enable(struct sprd_dsi *dsi, bool enable)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (dsi_is_cmd_mode(ctx))
+		dsi_cmd_mode_lp_cmd_en(ctx, enable);
+	else
+		dsi_video_mode_lp_cmd_en(ctx, enable);
+}
+
+void sprd_dsi_nc_clk_en(struct sprd_dsi *dsi, bool enable)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	dsi_nc_clk_en(ctx, enable);
+}
+
+void sprd_dsi_state_reset(struct sprd_dsi *dsi)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	dsi_power_enable(ctx, 0);
+	udelay(100);
+	dsi_power_enable(ctx, 1);
+}
+
+u32 sprd_dsi_int_status(struct sprd_dsi *dsi, int index)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+	u32 status;
+
+	if (0 == index)
+		status = dsi_int0_status(ctx);
+	else if (1 == index)
+		status = dsi_int1_status(ctx);
+	else {
+		DRM_ERROR("invalid dsi IRQ index %d\n", index);
+		status = -EINVAL;
+	}
+
+	return status;
+}
+
+void sprd_dsi_int_mask(struct sprd_dsi *dsi, int index)
+{
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (0 == index)
+		dsi_int0_mask(ctx, dsi->ctx.int0_mask);
+	else if (1 == index)
+		dsi_int1_mask(ctx, dsi->ctx.int1_mask);
+	else
+		DRM_ERROR("invalid dsi IRQ index %d\n", index);
+}
diff --git a/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
new file mode 100644
index 0000000..91eef86
--- /dev/null
+++ b/drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DSI_API_H_
+#define _SPRD_DSI_API_H_
+
+void sprd_dsi_power_on(struct sprd_dsi *dsi);
+void sprd_dsi_power_off(struct sprd_dsi *dsi);
+int sprd_dsi_dpi_video(struct sprd_dsi *dsi);
+int sprd_dsi_edpi_video(struct sprd_dsi *dsi);
+int sprd_dsi_wr_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			const u8 *param, u16 len);
+int sprd_dsi_rd_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
+			u8 msb_byte, u8 lsb_byte,
+			u8 *buffer, u8 bytes_to_read);
+void sprd_dsi_set_work_mode(struct sprd_dsi *dsi, u8 mode);
+int sprd_dsi_get_work_mode(struct sprd_dsi *dsi);
+void sprd_dsi_lp_cmd_enable(struct sprd_dsi *dsi, bool enable);
+void sprd_dsi_nc_clk_en(struct sprd_dsi *dsi, bool enable);
+void sprd_dsi_state_reset(struct sprd_dsi *dsi);
+u32 sprd_dsi_int_status(struct sprd_dsi *dsi, int index);
+void sprd_dsi_int_mask(struct sprd_dsi *dsi, int index);
+
+#endif /* _SPRD_DSI_API_H_ */
diff --git a/drivers/gpu/drm/sprd/sprd_dphy.c b/drivers/gpu/drm/sprd/sprd_dphy.c
new file mode 100644
index 0000000..3dcd270
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dphy.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "dphy/sprd_dphy_api.h"
+#include "sprd_dphy.h"
+
+static int regmap_tst_io_write(void *context, u32 reg, u32 val)
+{
+	struct sprd_dphy *dphy = context;
+
+	if (val > 0xff || reg > 0xff)
+		return -EINVAL;
+
+	DRM_DEBUG("reg = 0x%02x, val = 0x%02x\n", reg, val);
+
+	sprd_dphy_test_write(dphy, reg, val);
+
+	return 0;
+}
+
+static int regmap_tst_io_read(void *context, u32 reg, u32 *val)
+{
+	struct sprd_dphy *dphy = context;
+	int ret;
+
+	if (reg > 0xff)
+		return -EINVAL;
+
+	ret = sprd_dphy_test_read(dphy, reg);
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+
+	DRM_DEBUG("reg = 0x%02x, val = 0x%02x\n", reg, *val);
+	return 0;
+}
+
+static struct regmap_bus regmap_tst_io = {
+	.reg_write = regmap_tst_io_write,
+	.reg_read = regmap_tst_io_read,
+};
+
+static const struct regmap_config byte_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static const struct regmap_config word_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int sprd_dphy_regmap_init(struct sprd_dphy *dphy)
+{
+	struct dphy_context *ctx = &dphy->ctx;
+	struct regmap *regmap;
+
+	if (ctx->apbbase)
+		regmap = devm_regmap_init_mmio(&dphy->dev,
+			(void __iomem *)ctx->apbbase, &word_config);
+	else
+		regmap = devm_regmap_init(&dphy->dev, &regmap_tst_io,
+					  dphy, &byte_config);
+
+	if (IS_ERR(regmap)) {
+		DRM_ERROR("dphy regmap init failed\n");
+		return PTR_ERR(regmap);
+	}
+
+	ctx->regmap = regmap;
+
+	return 0;
+}
+
+void sprd_dphy_ulps_enter(struct sprd_dphy *dphy)
+{
+	sprd_dphy_hs_clk_en(dphy, false);
+	sprd_dphy_data_ulps_enter(dphy);
+	sprd_dphy_clk_ulps_enter(dphy);
+}
+
+void sprd_dphy_ulps_exit(struct sprd_dphy *dphy)
+{
+	sprd_dphy_force_pll(dphy, true);
+	sprd_dphy_clk_ulps_exit(dphy);
+	sprd_dphy_data_ulps_exit(dphy);
+	sprd_dphy_force_pll(dphy, false);
+}
+
+int sprd_dphy_init(struct sprd_dphy *dphy)
+{
+	int ret;
+
+	ret = sprd_dphy_power_on(dphy);
+	if (ret) {
+		DRM_ERROR("sprd dphy initial failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+void sprd_dphy_fini(struct sprd_dphy *dphy)
+{
+	sprd_dphy_power_off(dphy);
+}
+
+static int sprd_dphy_context_init(struct sprd_dphy *dphy,
+				  struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dphy_context *ctx = &dphy->ctx;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->ctrlbase = (unsigned long)devm_ioremap(dev, res->start,
+					resource_size(res));
+	if (ctx->ctrlbase == 0) {
+		DRM_ERROR("failed to map dphy ctrl registers\n");
+		return -ENXIO;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		ctx->ctrlbase = (unsigned long)devm_ioremap(dev, res->start,
+					resource_size(res));
+		if (ctx->apbbase == 0) {
+			DRM_INFO("failed to map dphy apb registers\n");
+			return -ENXIO;
+		}
+	}
+
+	return 0;
+}
+
+static const struct sprd_dphy_ops sharkl3_dphy = {
+	.pll = &sharkle_dphy_pll_ops,
+};
+
+static const struct of_device_id dphy_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dsi-phy",
+	  .data = &sharkl3_dphy },
+	{ /* sentinel */ },
+};
+
+static int sprd_dphy_probe(struct platform_device *pdev)
+{
+	const struct sprd_dphy_ops *pdata;
+	struct sprd_dphy *dphy;
+	struct device *dsi_dev;
+	int ret;
+
+	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
+	if (!dphy)
+		return -ENOMEM;
+
+	dsi_dev = sprd_disp_pipe_get_input(&pdev->dev);
+	if (!dsi_dev)
+		return -ENODEV;
+
+	pdata = of_device_get_match_data(&pdev->dev);
+	if (pdata) {
+		dphy->pll = pdata->pll;
+	} else {
+		DRM_ERROR("No matching driver data found\n");
+		return -EINVAL;
+	}
+
+	ret = sprd_dphy_context_init(dphy, &pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = sprd_dphy_regmap_init(dphy);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, dphy);
+
+	return 0;
+}
+
+struct platform_driver sprd_dphy_driver = {
+	.probe	= sprd_dphy_probe,
+	.driver = {
+		.name  = "sprd-dphy-drv",
+		.of_match_table	= dphy_match_table,
+	}
+};
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc SoC MIPI DSI PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_dphy.h b/drivers/gpu/drm/sprd/sprd_dphy.h
new file mode 100644
index 0000000..ecf2d36
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dphy.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DPHY_H_
+#define _SPRD_DPHY_H_
+
+#include <asm/types.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_print.h>
+
+#include "disp_lib.h"
+
+struct dphy_context {
+	struct regmap *regmap;
+	unsigned long ctrlbase;
+	unsigned long apbbase;
+	u32 freq;
+	u8 lanes;
+};
+
+struct dphy_pll_ops {
+	int (*pll_config)(struct dphy_context *ctx);
+	void (*timing_config)(struct dphy_context *ctx);
+	void (*force_pll)(struct dphy_context *ctx, int force);
+};
+
+struct sprd_dphy_ops {
+	const struct dphy_pll_ops *pll;
+};
+
+struct sprd_dphy {
+	struct device dev;
+	struct dphy_context ctx;
+	const struct dphy_pll_ops *pll;
+};
+
+int sprd_dphy_init(struct sprd_dphy *dphy);
+void sprd_dphy_fini(struct sprd_dphy *dphy);
+void sprd_dphy_ulps_enter(struct sprd_dphy *dphy);
+void sprd_dphy_ulps_exit(struct sprd_dphy *dphy);
+
+extern const struct dphy_pll_ops sharkle_dphy_pll_ops;
+
+#endif /* _SPRD_DPHY_H_ */
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
index 5ec8e7c..ebea25f 100644
--- a/drivers/gpu/drm/sprd/sprd_dpu.c
+++ b/drivers/gpu/drm/sprd/sprd_dpu.c
@@ -489,6 +489,28 @@ static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
 	return 0;
 }
 
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	if (dpu->core->run)
+		dpu->core->run(ctx);
+
+	drm_crtc_vblank_on(&dpu->crtc);
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	if (dpu->core->stop)
+		dpu->core->stop(ctx);
+
+	drm_crtc_handle_vblank(&dpu->crtc);
+	drm_crtc_vblank_off(&dpu->crtc);
+}
+
+
 static void sprd_dpu_init(struct sprd_dpu *dpu)
 {
 	struct dpu_context *ctx = &dpu->ctx;
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
index 7d3c5e4..83a2a86 100644
--- a/drivers/gpu/drm/sprd/sprd_dpu.h
+++ b/drivers/gpu/drm/sprd/sprd_dpu.h
@@ -182,6 +182,9 @@ static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
 	return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
 }
 
+void sprd_dpu_run(struct sprd_dpu *dpu);
+void sprd_dpu_stop(struct sprd_dpu *dpu);
+
 extern const struct dpu_core_ops dpu_r2p0_core_ops;
 
 #endif
diff --git a/drivers/gpu/drm/sprd/sprd_dsi.c b/drivers/gpu/drm/sprd/sprd_dsi.c
new file mode 100644
index 0000000..452c43a
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dsi.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+#include <video/mipi_display.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+
+#include "disp_lib.h"
+#include "sprd_dpu.h"
+#include "sprd_dsi.h"
+#include "dsi/sprd_dsi_api.h"
+#include "dphy/sprd_dphy_api.h"
+
+#define encoder_to_dsi(encoder) \
+	container_of(encoder, struct sprd_dsi, encoder)
+#define host_to_dsi(host) \
+	container_of(host, struct sprd_dsi, host)
+#define connector_to_dsi(connector) \
+	container_of(connector, struct sprd_dsi, connector)
+
+static int sprd_dsi_init(struct sprd_dsi *dsi)
+{
+	sprd_dsi_power_on(dsi);
+
+	if (dsi->ctx.work_mode == DSI_MODE_VIDEO)
+		sprd_dsi_dpi_video(dsi);
+	else
+		sprd_dsi_edpi_video(dsi);
+
+	return 0;
+}
+
+static void sprd_dsi_fini(struct sprd_dsi *dsi)
+{
+	sprd_dsi_power_off(dsi);
+}
+
+static void sprd_dsi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct sprd_dsi *dsi = encoder_to_dsi(encoder);
+	struct sprd_dpu *dpu = crtc_to_dpu(encoder->crtc);
+
+	if (dsi->ctx.enabled) {
+		DRM_ERROR("dsi is initialized\n");
+		return;
+	}
+
+	sprd_dsi_init(dsi);
+	sprd_dphy_init(dsi->phy);
+
+	sprd_dsi_lp_cmd_enable(dsi, true);
+
+	if (dsi->panel) {
+		drm_panel_prepare(dsi->panel);
+		drm_panel_enable(dsi->panel);
+	}
+
+	sprd_dsi_set_work_mode(dsi, dsi->ctx.work_mode);
+	sprd_dsi_state_reset(dsi);
+
+	if (dsi->ctx.nc_clk_en)
+		sprd_dsi_nc_clk_en(dsi, true);
+	else
+		sprd_dphy_hs_clk_en(dsi->phy, true);
+
+	sprd_dpu_run(dpu);
+
+	dsi->ctx.enabled = true;
+}
+
+static void sprd_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct sprd_dsi *dsi = encoder_to_dsi(encoder);
+	struct sprd_dpu *dpu = crtc_to_dpu(encoder->crtc);
+
+	if (!dsi->ctx.enabled) {
+		DRM_ERROR("dsi isn't initialized\n");
+		return;
+	}
+
+	sprd_dpu_stop(dpu);
+	sprd_dsi_set_work_mode(dsi, DSI_MODE_CMD);
+	sprd_dsi_lp_cmd_enable(dsi, true);
+
+	if (dsi->panel) {
+		drm_panel_disable(dsi->panel);
+		sprd_dphy_ulps_enter(dsi->phy);
+		drm_panel_unprepare(dsi->panel);
+	}
+
+	sprd_dphy_fini(dsi->phy);
+	sprd_dsi_fini(dsi);
+
+	dsi->ctx.enabled = false;
+}
+
+static void sprd_dsi_encoder_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adj_mode)
+{
+	struct sprd_dsi *dsi = encoder_to_dsi(encoder);
+
+	DRM_DEBUG("%s() set mode: %s\n", __func__, dsi->mode->name);
+}
+
+static int sprd_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+				    struct drm_crtc_state *crtc_state,
+				    struct drm_connector_state *conn_state)
+{
+	return 0;
+}
+
+static const struct drm_encoder_helper_funcs sprd_encoder_helper_funcs = {
+	.atomic_check	= sprd_dsi_encoder_atomic_check,
+	.mode_set	= sprd_dsi_encoder_mode_set,
+	.enable		= sprd_dsi_encoder_enable,
+	.disable	= sprd_dsi_encoder_disable
+};
+
+static const struct drm_encoder_funcs sprd_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int sprd_dsi_encoder_init(struct drm_device *drm,
+			       struct sprd_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct device *dev = dsi->host.dev;
+	u32 crtc_mask;
+	int ret;
+
+	crtc_mask = drm_of_find_possible_crtcs(drm, dev->of_node);
+	if (!crtc_mask) {
+		DRM_ERROR("failed to find crtc mask\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("find possible crtcs: 0x%08x\n", crtc_mask);
+
+	encoder->possible_crtcs = crtc_mask;
+	ret = drm_encoder_init(drm, encoder, &sprd_encoder_funcs,
+			       DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init dsi encoder\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &sprd_encoder_helper_funcs);
+
+	return 0;
+}
+
+static int sprd_dsi_find_panel(struct sprd_dsi *dsi)
+{
+	struct device *dev = dsi->host.dev;
+	struct device_node *child, *lcds_node;
+	struct drm_panel *panel;
+
+	/* search /lcds child node first */
+	lcds_node = of_find_node_by_path("/lcds");
+	for_each_child_of_node(lcds_node, child) {
+		panel = of_drm_find_panel(child);
+		if (panel) {
+			dsi->panel = panel;
+			return 0;
+		}
+	}
+
+	/*
+	 * If /lcds child node search failed, we search
+	 * the child of dsi host node.
+	 */
+	for_each_child_of_node(dev->of_node, child) {
+		panel = of_drm_find_panel(child);
+		if (panel) {
+			dsi->panel = panel;
+			return 0;
+		}
+	}
+
+	DRM_ERROR("of_drm_find_panel() failed\n");
+	return -ENODEV;
+}
+
+static int sprd_dsi_phy_attach(struct sprd_dsi *dsi)
+{
+	struct device *dev;
+
+	dev = sprd_disp_pipe_get_output(&dsi->dev);
+	if (!dev)
+		return -ENODEV;
+
+	dsi->phy = dev_get_drvdata(dev);
+	if (!dsi->phy) {
+		DRM_ERROR("dsi attach phy failed\n");
+		return -EINVAL;
+	}
+
+	dsi->phy->ctx.lanes = dsi->ctx.lanes;
+	dsi->phy->ctx.freq = dsi->ctx.byte_clk * 8;
+
+	return 0;
+}
+
+static int sprd_dsi_host_attach(struct mipi_dsi_host *host,
+			   struct mipi_dsi_device *slave)
+{
+	struct sprd_dsi *dsi = host_to_dsi(host);
+	struct dsi_context *ctx = &dsi->ctx;
+	int ret;
+
+	dsi->slave = slave;
+	ctx->lanes = slave->lanes;
+	ctx->format = slave->format;
+	ctx->byte_clk = slave->hs_rate / 8;
+	ctx->esc_clk = slave->lp_rate;
+
+	if (slave->mode_flags & MIPI_DSI_MODE_VIDEO)
+		ctx->work_mode = DSI_MODE_VIDEO;
+	else
+		ctx->work_mode = DSI_MODE_CMD;
+
+	if (slave->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+		ctx->burst_mode = VIDEO_BURST_WITH_SYNC_PULSES;
+	else if (slave->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		ctx->burst_mode = VIDEO_NON_BURST_WITH_SYNC_PULSES;
+	else
+		ctx->burst_mode = VIDEO_NON_BURST_WITH_SYNC_EVENTS;
+
+	if (slave->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+		ctx->nc_clk_en = true;
+
+	ret = sprd_dsi_phy_attach(dsi);
+	if (ret)
+		return ret;
+
+	ret = sprd_dsi_find_panel(dsi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int sprd_dsi_host_detach(struct mipi_dsi_host *host,
+			   struct mipi_dsi_device *slave)
+{
+	/* do nothing */
+	return 0;
+}
+
+static ssize_t sprd_dsi_host_transfer(struct mipi_dsi_host *host,
+				const struct mipi_dsi_msg *msg)
+{
+	struct sprd_dsi *dsi = host_to_dsi(host);
+	const u8 *tx_buf = msg->tx_buf;
+
+	if (msg->rx_buf && msg->rx_len) {
+		u8 lsb = (msg->tx_len > 0) ? tx_buf[0] : 0;
+		u8 msb = (msg->tx_len > 1) ? tx_buf[1] : 0;
+
+		return sprd_dsi_rd_pkt(dsi, msg->channel, msg->type,
+				msb, lsb, msg->rx_buf, msg->rx_len);
+	}
+
+	if (msg->tx_buf && msg->tx_len)
+		return sprd_dsi_wr_pkt(dsi, msg->channel, msg->type,
+					tx_buf, msg->tx_len);
+
+	return 0;
+}
+
+static const struct mipi_dsi_host_ops sprd_dsi_host_ops = {
+	.attach = sprd_dsi_host_attach,
+	.detach = sprd_dsi_host_detach,
+	.transfer = sprd_dsi_host_transfer,
+};
+
+static int sprd_dsi_host_init(struct sprd_dsi *dsi, struct device *dev)
+{
+	int ret;
+
+	dsi->host.dev = dev;
+	dsi->host.ops = &sprd_dsi_host_ops;
+
+	ret = mipi_dsi_host_register(&dsi->host);
+	if (ret)
+		DRM_ERROR("failed to register dsi host\n");
+
+	return ret;
+}
+
+static int sprd_dsi_connector_get_modes(struct drm_connector *connector)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	return drm_panel_get_modes(dsi->panel, connector);
+}
+
+static enum drm_mode_status
+sprd_dsi_connector_mode_valid(struct drm_connector *connector,
+			 struct drm_display_mode *mode)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
+
+	if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+		dsi->mode = mode;
+		drm_display_mode_to_videomode(dsi->mode, &dsi->ctx.vm);
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+sprd_dsi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs sprd_dsi_connector_helper_funcs = {
+	.get_modes = sprd_dsi_connector_get_modes,
+	.mode_valid = sprd_dsi_connector_mode_valid,
+	.best_encoder = sprd_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+sprd_dsi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct sprd_dsi *dsi = connector_to_dsi(connector);
+
+	if (dsi->panel) {
+		drm_panel_attach(dsi->panel, connector);
+		return connector_status_connected;
+	}
+
+	return connector_status_disconnected;
+}
+
+static void sprd_dsi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs sprd_dsi_atomic_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = sprd_dsi_connector_detect,
+	.destroy = sprd_dsi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int sprd_dsi_connector_init(struct drm_device *drm, struct sprd_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_connector *connector = &dsi->connector;
+	int ret;
+
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	ret = drm_connector_init(drm, connector,
+				 &sprd_dsi_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DSI);
+	if (ret) {
+		DRM_ERROR("drm_connector_init() failed\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector,
+				 &sprd_dsi_connector_helper_funcs);
+
+	drm_connector_attach_encoder(connector, encoder);
+
+	return 0;
+}
+
+static int sprd_dsi_bridge_attach(struct sprd_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_bridge *bridge = dsi->bridge;
+	struct device *dev = dsi->host.dev;
+	struct device_node *bridge_node;
+	int ret;
+
+	bridge_node = of_graph_get_remote_node(dev->of_node, 2, 0);
+	if (!bridge_node)
+		return 0;
+
+	bridge = of_drm_find_bridge(bridge_node);
+	if (!bridge) {
+		DRM_ERROR("of_drm_find_bridge() failed\n");
+		return -ENODEV;
+	}
+	dsi->bridge = bridge;
+
+	ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+	if (ret) {
+		DRM_ERROR("failed to attach external bridge\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static irqreturn_t sprd_dsi_isr(int irq, void *data)
+{
+	u32 status = 0;
+	struct sprd_dsi *dsi = data;
+
+	if (dsi->ctx.irq0 == irq)
+		status = sprd_dsi_int_status(dsi, 0);
+	else if (dsi->ctx.irq1 == irq)
+		status = sprd_dsi_int_status(dsi, 1);
+
+	if (status & DSI_INT_STS_NEED_SOFT_RESET)
+		sprd_dsi_state_reset(dsi);
+
+	return IRQ_HANDLED;
+}
+
+static int sprd_dsi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct sprd_dsi *dsi = dev_get_drvdata(dev);
+	int ret;
+
+	ret = sprd_dsi_encoder_init(drm, dsi);
+	if (ret)
+		goto cleanup_host;
+
+	ret = sprd_dsi_connector_init(drm, dsi);
+	if (ret)
+		goto cleanup_encoder;
+
+	ret = sprd_dsi_bridge_attach(dsi);
+	if (ret)
+		goto cleanup_connector;
+
+	return 0;
+
+cleanup_connector:
+	drm_connector_cleanup(&dsi->connector);
+cleanup_encoder:
+	drm_encoder_cleanup(&dsi->encoder);
+cleanup_host:
+	mipi_dsi_host_unregister(&dsi->host);
+	return ret;
+}
+
+static void sprd_dsi_unbind(struct device *dev,
+			struct device *master, void *data)
+{
+	/* do nothing */
+	DRM_DEBUG("%s()\n", __func__);
+
+}
+
+static const struct component_ops dsi_component_ops = {
+	.bind	= sprd_dsi_bind,
+	.unbind	= sprd_dsi_unbind,
+};
+
+static int sprd_dsi_context_init(struct sprd_dsi *dsi,
+			struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dsi_context *ctx = &dsi->ctx;
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = (unsigned long)devm_ioremap(dev, res->start,
+					resource_size(res));
+	if (ctx->base == 0) {
+		DRM_ERROR("failed to map dsi host registers\n");
+		return -ENXIO;
+	}
+
+	ctx->irq0 = platform_get_irq(pdev, 0);
+	if (ctx->irq0 > 0) {
+		ret = request_irq(ctx->irq0, sprd_dsi_isr, 0, "DSI_INT0", dsi);
+		if (ret) {
+			DRM_ERROR("failed to request dsi irq int0!\n");
+			return -EINVAL;
+		}
+	}
+
+	ctx->irq1 = platform_get_irq(pdev, 1);
+	if (ctx->irq1 > 0) {
+		ret = request_irq(ctx->irq1, sprd_dsi_isr, 0, "DSI_INT1", dsi);
+		if (ret) {
+			DRM_ERROR("failed to request dsi irq int1!\n");
+			return -EINVAL;
+		}
+	}
+
+	ctx->data_hs2lp = 120;
+	ctx->data_lp2hs = 500;
+	ctx->clk_hs2lp = 4;
+	ctx->clk_lp2hs = 15;
+	ctx->max_rd_time = 6000;
+	ctx->int0_mask = 0xffffffff;
+	ctx->int1_mask = 0xffffffff;
+
+	return 0;
+}
+
+static const struct of_device_id dsi_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dsi-host" },
+	{ /* sentinel */ },
+};
+
+static int sprd_dsi_probe(struct platform_device *pdev)
+{
+	struct sprd_dsi *dsi;
+	int ret;
+
+	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi) {
+		DRM_ERROR("failed to allocate dsi data.\n");
+		return -ENOMEM;
+	}
+
+	ret = sprd_dsi_context_init(dsi, &pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = sprd_dsi_host_init(dsi, &pdev->dev);
+	if (ret)
+		return ret;
+	
+	platform_set_drvdata(pdev, dsi);
+
+	return component_add(&pdev->dev, &dsi_component_ops);
+}
+
+static int sprd_dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dsi_component_ops);
+
+	return 0;
+}
+
+struct platform_driver sprd_dsi_driver = {
+	.probe = sprd_dsi_probe,
+	.remove = sprd_dsi_remove,
+	.driver = {
+		.name = "sprd-dsi-drv",
+		.of_match_table = dsi_match_table,
+	},
+};
+
+MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
+MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc MIPI DSI HOST Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sprd/sprd_dsi.h b/drivers/gpu/drm/sprd/sprd_dsi.h
new file mode 100644
index 0000000..7f57f46
--- /dev/null
+++ b/drivers/gpu/drm/sprd/sprd_dsi.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef __SPRD_DSI_H__
+#define __SPRD_DSI_H__
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <video/videomode.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_print.h>
+#include <drm/drm_panel.h>
+
+#include "disp_lib.h"
+#include "sprd_dphy.h"
+
+#define DSI_INT_STS_NEED_SOFT_RESET	BIT(0)
+#define DSI_INT_STS_NEED_HARD_RESET	BIT(1)
+
+enum dsi_work_mode {
+	DSI_MODE_CMD = 0,
+	DSI_MODE_VIDEO
+};
+
+enum video_burst_mode {
+	VIDEO_NON_BURST_WITH_SYNC_PULSES = 0,
+	VIDEO_NON_BURST_WITH_SYNC_EVENTS,
+	VIDEO_BURST_WITH_SYNC_PULSES
+};
+
+enum dsi_color_coding {
+	COLOR_CODE_16BIT_CONFIG1 = 0,
+	COLOR_CODE_16BIT_CONFIG2,
+	COLOR_CODE_16BIT_CONFIG3,
+	COLOR_CODE_18BIT_CONFIG1,
+	COLOR_CODE_18BIT_CONFIG2,
+	COLOR_CODE_24BIT,
+	COLOR_CODE_20BIT_YCC422_LOOSELY,
+	COLOR_CODE_24BIT_YCC422,
+	COLOR_CODE_16BIT_YCC422,
+	COLOR_CODE_30BIT,
+	COLOR_CODE_36BIT,
+	COLOR_CODE_12BIT_YCC420,
+	COLOR_CODE_COMPRESSTION,
+	COLOR_CODE_MAX
+};
+
+struct dsi_context {
+	unsigned long base;
+	struct videomode vm;
+	bool enabled;
+
+	u8 lanes;
+	u32 format;
+	u8 work_mode;
+	u8 burst_mode;
+
+	int irq0;
+	int irq1;
+	u32 int0_mask;
+	u32 int1_mask;
+
+	/* byte clock [KHz] */
+	u32 byte_clk;
+	/* escape clock [KHz] */
+	u32 esc_clk;
+
+	/* maximum time (ns) for data lanes from HS to LP */
+	u16 data_hs2lp;
+	/* maximum time (ns) for data lanes from LP to HS */
+	u16 data_lp2hs;
+	/* maximum time (ns) for clk lanes from HS to LP */
+	u16 clk_hs2lp;
+	/* maximum time (ns) for clk lanes from LP to HS */
+	u16 clk_lp2hs;
+	/* maximum time (ns) for BTA operation - REQUIRED */
+	u16 max_rd_time;
+
+	/* is 18-bit loosely packets (valid only when BPP == 18) */
+	bool is_18_loosely;
+	/* enable receiving frame ack packets - for video mode */
+	bool frame_ack_en;
+	/* enable receiving tear effect ack packets - for cmd mode */
+	bool te_ack_en;
+	/* enable non coninuous clock for energy saving */
+	bool nc_clk_en;
+};
+
+struct sprd_dsi {
+	struct device dev;
+	struct mipi_dsi_host host;
+	struct mipi_dsi_device *slave;
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
+	struct drm_display_mode *mode;
+	struct sprd_dphy *phy;
+	struct dsi_context ctx;
+};
+
+#endif /* __SPRD_DSI_H__ */
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 0/6] Add Unisoc's drm kms module
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 10:12   ` Daniel Vetter
  -1 siblings, 0 replies; 50+ messages in thread
From: Daniel Vetter @ 2020-07-28 10:12 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, Dave Airlie,
	Rob Herring, Mark Rutland, orsonzhai, zhang.lyra,
	Linux Kernel Mailing List, dri-devel

On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
>
> From: Kevin Tang <kevin.tang@unisoc.com>

Hm still no ack for dt bindings? We need that for merging.

Also what's the maintainer plan here? Imo best would be to put this
into the drm-misc group maintainer model, with commit rights and all:

https://drm.pages.freedesktop.org/maintainer-tools/drm-misc.html

MAINTAINERS patch to do that would be good.
-Daniel

>
> ChangeList:
> v1:
> 1. only upstream modeset and atomic at first commit.
> 2. remove some unused code;
> 3. use alpha and blend_mode properties;
> 3. add yaml support;
> 4. remove auto-adaptive panel driver;
> 5. bugfix
>
> v2:
> 1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
> 2. remove gem drivers, use generic CMA handlers
> 3. remove redundant "module_init", all the sub modules loading by KMS
>
> v3:
> 1. multi crtc&encoder design have problem, so rollback to v1
>
> v4:
> 1. update to gcc-linaro-7.5.0
> 2. update to Linux 5.6-rc3
> 3. remove pm_runtime support
> 4. add COMPILE_TEST, remove unused kconfig
> 5. "drm_dev_put" on drm_unbind
> 6. fix some naming convention issue
> 7. remove semaphore lock for crtc flip
> 8. remove static variables
>
> v5:
> 1. optimize encoder and connector code implementation
> 2. use "platform_get_irq" and "platform_get_resource"
> 3. drop useless function return type, drop unless debug log
> 4. custom properties should be separate, so drop it
> 5. use DRM_XXX replase pr_xxx
> 6. drop dsi&dphy hal callback ops
> 7. drop unless callback ops checking
> 8. add comments for sprd dpu structure
>
> v6:
> 1. Access registers via readl/writel
> 2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> 3. Remove always true checks for dpu core ops
>
> Kevin Tang (6):
>   dt-bindings: display: add Unisoc's drm master bindings
>   drm/sprd: add Unisoc's drm kms master
>   dt-bindings: display: add Unisoc's dpu bindings
>   drm/sprd: add Unisoc's drm display controller driver
>   dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
>   drm/sprd: add Unisoc's drm mipi dsi&dphy driver
>
>  .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
>  .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
>  .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
>  .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
>  drivers/gpu/drm/Kconfig                            |    2 +
>  drivers/gpu/drm/Makefile                           |    1 +
>  drivers/gpu/drm/sprd/Kconfig                       |   12 +
>  drivers/gpu/drm/sprd/Makefile                      |   13 +
>  drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
>  drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
>  drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
>  drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
>  drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
>  drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
>  drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
>  drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
>  drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
>  drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
>  drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
>  drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
>  drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
>  drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
>  drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
>  33 files changed, 7074 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
>  create mode 100644 drivers/gpu/drm/sprd/Kconfig
>  create mode 100644 drivers/gpu/drm/sprd/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
>  create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
>
> --
> 2.7.4
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 0/6] Add Unisoc's drm kms module
@ 2020-07-28 10:12   ` Daniel Vetter
  0 siblings, 0 replies; 50+ messages in thread
From: Daniel Vetter @ 2020-07-28 10:12 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Mark Rutland, Dave Airlie, zhang.lyra, Linux Kernel Mailing List,
	Rob Herring, dri-devel, orsonzhai, Sean Paul

On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
>
> From: Kevin Tang <kevin.tang@unisoc.com>

Hm still no ack for dt bindings? We need that for merging.

Also what's the maintainer plan here? Imo best would be to put this
into the drm-misc group maintainer model, with commit rights and all:

https://drm.pages.freedesktop.org/maintainer-tools/drm-misc.html

MAINTAINERS patch to do that would be good.
-Daniel

>
> ChangeList:
> v1:
> 1. only upstream modeset and atomic at first commit.
> 2. remove some unused code;
> 3. use alpha and blend_mode properties;
> 3. add yaml support;
> 4. remove auto-adaptive panel driver;
> 5. bugfix
>
> v2:
> 1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
> 2. remove gem drivers, use generic CMA handlers
> 3. remove redundant "module_init", all the sub modules loading by KMS
>
> v3:
> 1. multi crtc&encoder design have problem, so rollback to v1
>
> v4:
> 1. update to gcc-linaro-7.5.0
> 2. update to Linux 5.6-rc3
> 3. remove pm_runtime support
> 4. add COMPILE_TEST, remove unused kconfig
> 5. "drm_dev_put" on drm_unbind
> 6. fix some naming convention issue
> 7. remove semaphore lock for crtc flip
> 8. remove static variables
>
> v5:
> 1. optimize encoder and connector code implementation
> 2. use "platform_get_irq" and "platform_get_resource"
> 3. drop useless function return type, drop unless debug log
> 4. custom properties should be separate, so drop it
> 5. use DRM_XXX replase pr_xxx
> 6. drop dsi&dphy hal callback ops
> 7. drop unless callback ops checking
> 8. add comments for sprd dpu structure
>
> v6:
> 1. Access registers via readl/writel
> 2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> 3. Remove always true checks for dpu core ops
>
> Kevin Tang (6):
>   dt-bindings: display: add Unisoc's drm master bindings
>   drm/sprd: add Unisoc's drm kms master
>   dt-bindings: display: add Unisoc's dpu bindings
>   drm/sprd: add Unisoc's drm display controller driver
>   dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
>   drm/sprd: add Unisoc's drm mipi dsi&dphy driver
>
>  .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
>  .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
>  .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
>  .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
>  drivers/gpu/drm/Kconfig                            |    2 +
>  drivers/gpu/drm/Makefile                           |    1 +
>  drivers/gpu/drm/sprd/Kconfig                       |   12 +
>  drivers/gpu/drm/sprd/Makefile                      |   13 +
>  drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
>  drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
>  drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
>  drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
>  drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
>  drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
>  drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
>  drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
>  drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
>  drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
>  drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
>  drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
>  drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
>  drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
>  drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
>  33 files changed, 7074 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
>  create mode 100644 drivers/gpu/drm/sprd/Kconfig
>  create mode 100644 drivers/gpu/drm/sprd/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
>  create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
>
> --
> 2.7.4
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
  2020-07-28 10:07   ` Kevin Tang
@ 2020-07-28 12:25     ` Randy Dunlap
  -1 siblings, 0 replies; 50+ messages in thread
From: Randy Dunlap @ 2020-07-28 12:25 UTC (permalink / raw)
  To: Kevin Tang, maarten.lankhorst, mripard, sean, airlied, daniel,
	robh+dt, mark.rutland
  Cc: orsonzhai, zhang.lyra, linux-kernel, dri-devel

On 7/28/20 3:07 AM, Kevin Tang wrote:
> diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
> new file mode 100644
> index 0000000..b189a54
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_SPRD
> +	tristate "DRM Support for Unisoc SoCs Platform"
> +	depends on ARCH_SPRD || COMPILE_TEST
> +	depends on DRM && OF
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_MIPI_DSI
> +	help
> +	  Choose this option if you have a Unisoc chipsets.

	                                          chipset.

> +	  If M is selected the module will be called sprd-drm.

	                                             sprd_drm.

> +
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> new file mode 100644
> index 0000000..86d95d9
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +subdir-ccflags-y += -I$(srctree)/$(src)
> +
> +obj-y := sprd_drm.o


-- 
~Randy


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
@ 2020-07-28 12:25     ` Randy Dunlap
  0 siblings, 0 replies; 50+ messages in thread
From: Randy Dunlap @ 2020-07-28 12:25 UTC (permalink / raw)
  To: Kevin Tang, maarten.lankhorst, mripard, sean, airlied, daniel,
	robh+dt, mark.rutland
  Cc: orsonzhai, linux-kernel, dri-devel, zhang.lyra

On 7/28/20 3:07 AM, Kevin Tang wrote:
> diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
> new file mode 100644
> index 0000000..b189a54
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_SPRD
> +	tristate "DRM Support for Unisoc SoCs Platform"
> +	depends on ARCH_SPRD || COMPILE_TEST
> +	depends on DRM && OF
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_MIPI_DSI
> +	help
> +	  Choose this option if you have a Unisoc chipsets.

	                                          chipset.

> +	  If M is selected the module will be called sprd-drm.

	                                             sprd_drm.

> +
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> new file mode 100644
> index 0000000..86d95d9
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +subdir-ccflags-y += -I$(srctree)/$(src)
> +
> +obj-y := sprd_drm.o


-- 
~Randy

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver
  2020-07-28 10:07   ` Kevin Tang
  (?)
@ 2020-07-28 16:33   ` kernel test robot
  -1 siblings, 0 replies; 50+ messages in thread
From: kernel test robot @ 2020-07-28 16:33 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 12339 bytes --]

Hi Kevin,

[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v5.8-rc7]
[cannot apply to next-20200728]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Kevin-Tang/Add-Unisoc-s-drm-kms-module/20200728-181033
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arc-allyesconfig (attached as .config)
compiler: arc-elf-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:163:6: warning: no previous prototype for 'sprd_dsi_power_on' [-Wmissing-prototypes]
     163 | void sprd_dsi_power_on(struct sprd_dsi *dsi)
         |      ^~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:206:6: warning: no previous prototype for 'sprd_dsi_power_off' [-Wmissing-prototypes]
     206 | void sprd_dsi_power_off(struct sprd_dsi *dsi)
         |      ^~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:225:5: warning: no previous prototype for 'sprd_dsi_dpi_video' [-Wmissing-prototypes]
     225 | int sprd_dsi_dpi_video(struct sprd_dsi *dsi)
         |     ^~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:355:5: warning: no previous prototype for 'sprd_dsi_edpi_video' [-Wmissing-prototypes]
     355 | int sprd_dsi_edpi_video(struct sprd_dsi *dsi)
         |     ^~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:400:5: warning: no previous prototype for 'sprd_dsi_wr_pkt' [-Wmissing-prototypes]
     400 | int sprd_dsi_wr_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
         |     ^~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:463:5: warning: no previous prototype for 'sprd_dsi_rd_pkt' [-Wmissing-prototypes]
     463 | int sprd_dsi_rd_pkt(struct sprd_dsi *dsi, u8 vc, u8 type,
         |     ^~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:517:6: warning: no previous prototype for 'sprd_dsi_set_work_mode' [-Wmissing-prototypes]
     517 | void sprd_dsi_set_work_mode(struct sprd_dsi *dsi, u8 mode)
         |      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:527:5: warning: no previous prototype for 'sprd_dsi_get_work_mode' [-Wmissing-prototypes]
     527 | int sprd_dsi_get_work_mode(struct sprd_dsi *dsi)
         |     ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:537:6: warning: no previous prototype for 'sprd_dsi_lp_cmd_enable' [-Wmissing-prototypes]
     537 | void sprd_dsi_lp_cmd_enable(struct sprd_dsi *dsi, bool enable)
         |      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:547:6: warning: no previous prototype for 'sprd_dsi_nc_clk_en' [-Wmissing-prototypes]
     547 | void sprd_dsi_nc_clk_en(struct sprd_dsi *dsi, bool enable)
         |      ^~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:554:6: warning: no previous prototype for 'sprd_dsi_state_reset' [-Wmissing-prototypes]
     554 | void sprd_dsi_state_reset(struct sprd_dsi *dsi)
         |      ^~~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:563:5: warning: no previous prototype for 'sprd_dsi_int_status' [-Wmissing-prototypes]
     563 | u32 sprd_dsi_int_status(struct sprd_dsi *dsi, int index)
         |     ^~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c:580:6: warning: no previous prototype for 'sprd_dsi_int_mask' [-Wmissing-prototypes]
     580 | void sprd_dsi_int_mask(struct sprd_dsi *dsi, int index)
         |      ^~~~~~~~~~~~~~~~~

vim +/sprd_dsi_power_on +163 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c

   159	
   160	#define ns_to_cycle(ns, byte_clk) \
   161		DIV_ROUND_UP((ns) * (byte_clk), 1000000)
   162	
 > 163	void sprd_dsi_power_on(struct sprd_dsi *dsi)
   164	{
   165		int div;
   166		struct dsi_context *ctx = &dsi->ctx;
   167		u16 max_rd_time;
   168		u16 data_hs2lp, data_lp2hs, clk_hs2lp, clk_lp2hs;
   169	
   170		dsi_power_enable(ctx, 0);
   171		dsi_int0_mask(ctx, 0xffffffff);
   172		dsi_int1_mask(ctx, 0xffffffff);
   173		dsi_cmd_mode(ctx);
   174		dsi_eotp_rx_en(ctx, 0);
   175		dsi_eotp_tx_en(ctx, 0);
   176		dsi_ecc_rx_en(ctx, 1);
   177		dsi_crc_rx_en(ctx, 1);
   178		dsi_bta_en(ctx, 1);
   179		dsi_video_vcid(ctx, 0);
   180		dsi_rx_vcid(ctx, 0);
   181	
   182		div = DIV_ROUND_UP(ctx->byte_clk, ctx->esc_clk);
   183		dsi_tx_escape_division(ctx, div);
   184	
   185		max_rd_time = ns_to_cycle(ctx->max_rd_time, ctx->byte_clk);
   186		dsi_max_read_time(ctx, max_rd_time);
   187	
   188		data_hs2lp = ns_to_cycle(ctx->data_hs2lp, ctx->byte_clk);
   189		data_lp2hs = ns_to_cycle(ctx->data_lp2hs, ctx->byte_clk);
   190		clk_hs2lp = ns_to_cycle(ctx->clk_hs2lp, ctx->byte_clk);
   191		clk_lp2hs = ns_to_cycle(ctx->clk_lp2hs, ctx->byte_clk);
   192		dsi_datalane_hs2lp_config(ctx, data_hs2lp);
   193		dsi_datalane_lp2hs_config(ctx, data_lp2hs);
   194		dsi_clklane_hs2lp_config(ctx, clk_hs2lp);
   195		dsi_clklane_lp2hs_config(ctx, clk_lp2hs);
   196	
   197		dsi_power_enable(ctx, 1);
   198	}
   199	
   200	/**
   201	 * Close DSI Host driver
   202	 * - Free up resources and shutdown host controller and PHY
   203	 * @param dsi pointer to structure holding the DSI Host core information
   204	 * @return
   205	 */
 > 206	void sprd_dsi_power_off(struct sprd_dsi *dsi)
   207	{
   208		struct dsi_context *ctx = &dsi->ctx;
   209	
   210		dsi_int0_mask(ctx, 0xffffffff);
   211		dsi_int1_mask(ctx, 0xffffffff);
   212		dsi_power_enable(ctx, 0);
   213	}
   214	
   215	/**
   216	 * Configure DPI video interface
   217	 * - If not in burst mode, it will compute the video and null packet sizes
   218	 * according to necessity
   219	 * - Configure timers for data lanes and/or clock lane to return to LP when
   220	 * bandwidth is not filled by data
   221	 * @param dsi pointer to structure holding the DSI Host core information
   222	 * @param param pointer to video stream-to-send information
   223	 * @return error code
   224	 */
 > 225	int sprd_dsi_dpi_video(struct sprd_dsi *dsi)
   226	{
   227		struct dsi_context *ctx = &dsi->ctx;
   228		struct videomode *vm = &dsi->ctx.vm;
   229		u16 Bpp_x100;
   230		u16 video_size;
   231		u32 ratio_x1000;
   232		u16 null_pkt_size = 0;
   233		u8 video_size_step;
   234		u32 hs_to;
   235		u32 total_bytes;
   236		u32 bytes_per_chunk;
   237		u32 chunks = 0;
   238		u32 bytes_left = 0;
   239		u32 chunk_overhead;
   240		const u8 pkt_header = 6;
   241		u8 coding;
   242		int div;
   243		u16 hline;
   244	
   245		coding = fmt_to_coding(ctx->format);
   246		video_size = round_video_size(coding, vm->hactive);
   247		Bpp_x100 = calc_bytes_per_pixel_x100(coding);
   248		video_size_step = calc_video_size_step(coding);
   249		ratio_x1000 = ctx->byte_clk * 1000 / (vm->pixelclock / 1000);
   250		hline = vm->hactive + vm->hsync_len + vm->hfront_porch +
   251			vm->hback_porch;
   252	
   253		dsi_power_enable(ctx, 0);
   254		dsi_dpi_frame_ack_en(ctx, ctx->frame_ack_en);
   255		dsi_dpi_color_coding(ctx, coding);
   256		dsi_dpi_video_burst_mode(ctx, ctx->burst_mode);
   257		dsi_dpi_sig_delay(ctx, 95 * hline * ratio_x1000 / 100000);
   258		dsi_dpi_hline_time(ctx, hline * ratio_x1000 / 1000);
   259		dsi_dpi_hsync_time(ctx, vm->hsync_len * ratio_x1000 / 1000);
   260		dsi_dpi_hbp_time(ctx, vm->hback_porch * ratio_x1000 / 1000);
   261		dsi_dpi_vact(ctx, vm->vactive);
   262		dsi_dpi_vfp(ctx, vm->vfront_porch);
   263		dsi_dpi_vbp(ctx, vm->vback_porch);
   264		dsi_dpi_vsync(ctx, vm->vsync_len);
   265		dsi_dpi_hporch_lp_en(ctx, 1);
   266		dsi_dpi_vporch_lp_en(ctx, 1);
   267	
   268		hs_to = (hline * vm->vactive) + (2 * Bpp_x100) / 100;
   269		for (div = 0x80; (div < hs_to) && (div > 2); div--) {
   270			if ((hs_to % div) == 0) {
   271				dsi_timeout_clock_division(ctx, div);
   272				dsi_lp_rx_timeout(ctx, hs_to / div);
   273				dsi_hs_tx_timeout(ctx, hs_to / div);
   274				break;
   275			}
   276		}
   277	
   278		if (ctx->burst_mode == VIDEO_BURST_WITH_SYNC_PULSES) {
   279			dsi_dpi_video_packet_size(ctx, video_size);
   280			dsi_dpi_null_packet_size(ctx, 0);
   281			dsi_dpi_chunk_num(ctx, 0);
   282		} else {
   283			/* non burst transmission */
   284			null_pkt_size = 0;
   285	
   286			/* bytes to be sent - first as one chunk */
   287			bytes_per_chunk = vm->hactive * Bpp_x100 / 100 + pkt_header;
   288	
   289			/* hline total bytes from the DPI interface */
   290			total_bytes = (vm->hactive + vm->hfront_porch) *
   291					ratio_x1000 / ctx->lanes / 1000;
   292	
   293			/* check if the pixels actually fit on the DSI link */
   294			if (total_bytes < bytes_per_chunk) {
   295				DRM_ERROR("current resolution can not be set\n");
   296				return -EINVAL;
   297			}
   298	
   299			chunk_overhead = total_bytes - bytes_per_chunk;
   300	
   301			/* overhead higher than 1 -> enable multi packets */
   302			if (chunk_overhead > 1) {
   303	
   304				/* multi packets */
   305				for (video_size = video_size_step;
   306				     video_size < vm->hactive;
   307				     video_size += video_size_step) {
   308	
   309					if (vm->hactive * 1000 / video_size % 1000)
   310						continue;
   311	
   312					chunks = vm->hactive / video_size;
   313					bytes_per_chunk = Bpp_x100 * video_size / 100
   314							  + pkt_header;
   315					if (total_bytes >= (bytes_per_chunk * chunks)) {
   316						bytes_left = total_bytes -
   317							     bytes_per_chunk * chunks;
   318						break;
   319					}
   320				}
   321	
   322				/* prevent overflow (unsigned - unsigned) */
   323				if (bytes_left > (pkt_header * chunks)) {
   324					null_pkt_size = (bytes_left -
   325							pkt_header * chunks) / chunks;
   326					/* avoid register overflow */
   327					if (null_pkt_size > 1023)
   328						null_pkt_size = 1023;
   329				}
   330	
   331			} else {
   332	
   333				/* single packet */
   334				chunks = 1;
   335	
   336				/* must be a multiple of 4 except 18 loosely */
   337				for (video_size = vm->hactive;
   338				    (video_size % video_size_step) != 0;
   339				     video_size++)
   340					;
   341			}
   342	
   343			dsi_dpi_video_packet_size(ctx, video_size);
   344			dsi_dpi_null_packet_size(ctx, null_pkt_size);
   345			dsi_dpi_chunk_num(ctx, chunks);
   346		}
   347	
   348		dsi_int0_mask(ctx, ctx->int0_mask);
   349		dsi_int1_mask(ctx, ctx->int1_mask);
   350		dsi_power_enable(ctx, 1);
   351	
   352		return 0;
   353	}
   354	
 > 355	int sprd_dsi_edpi_video(struct sprd_dsi *dsi)
   356	{
   357		struct dsi_context *ctx = &dsi->ctx;
   358		const u32 fifo_depth = 1096;
   359		const u32 word_length = 4;
   360		u32 hactive = ctx->vm.hactive;
   361		u32 Bpp_x100;
   362		u32 max_fifo_len;
   363		u8 coding;
   364	
   365		coding = fmt_to_coding(ctx->format);
   366		Bpp_x100 = calc_bytes_per_pixel_x100(coding);
   367		max_fifo_len = word_length * fifo_depth * 100 / Bpp_x100;
   368	
   369		dsi_power_enable(ctx, 0);
   370		dsi_dpi_color_coding(ctx, coding);
   371		dsi_tear_effect_ack_en(ctx, ctx->te_ack_en);
   372	
   373		if (max_fifo_len > hactive)
   374			dsi_edpi_max_pkt_size(ctx, hactive);
   375		else
   376			dsi_edpi_max_pkt_size(ctx, max_fifo_len);
   377	
   378		dsi_int0_mask(ctx, ctx->int0_mask);
   379		dsi_int1_mask(ctx, ctx->int1_mask);
   380		dsi_power_enable(ctx, 1);
   381	
   382		return 0;
   383	}
   384	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 64651 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
  2020-07-28 10:07   ` Kevin Tang
@ 2020-07-28 20:27     ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 20:27 UTC (permalink / raw)
  To: Kevin Tang
  Cc: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, orsonzhai, linux-kernel, dri-devel, zhang.lyra

Hi Kevin

On Tue, Jul 28, 2020 at 06:07:54PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> The Unisoc DRM master device is a virtual device needed to list all
> DPU devices or other display interface nodes that comprise the
> graphics subsystem
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> new file mode 100644
> index 0000000..b5792c0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
drm seems like a sub-optimal name.
How about usign the compatible name "display-subsystem" as it is a bit
more specific (but not good).

> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0

Any chance this can be (GPL-2.0-only OR BSD-2-Clause).
I noticed that for example clock/sprd,sc9863a-clk.yaml uses this license
so I hope this is OK.

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Unisoc DRM master device
> +
> +maintainers:
> +  - Mark Rutland <mark.rutland@arm.com>
> +
> +description: |
> +  The Unisoc DRM master device is a virtual device needed to list all
> +  DPU devices or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: sprd,display-subsystem
> +
> +  ports:
> +    description:
> +      Should contain a list of phandles pointing to display interface port
> +      of DPU devices.
Add type - like this:
$ref: /schemas/types.yaml#/definitions/phandle-array

See for example display/rockchip/rockchip-drm.yaml

Any specific reason why this is not a ports node like used by many other
display bindings?
In other words - I think this is too simple.

> +
> +required:
> +  - compatible
> +  - ports
> +

Add:
additionalProperties: false

so we catch if other properties sneak in.

> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "sprd,display-subsystem";
> +        ports = <&dpu_out>;
> +    };
> +
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
@ 2020-07-28 20:27     ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 20:27 UTC (permalink / raw)
  To: Kevin Tang
  Cc: mark.rutland, airlied, zhang.lyra, linux-kernel, robh+dt,
	dri-devel, orsonzhai, sean

Hi Kevin

On Tue, Jul 28, 2020 at 06:07:54PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> The Unisoc DRM master device is a virtual device needed to list all
> DPU devices or other display interface nodes that comprise the
> graphics subsystem
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> new file mode 100644
> index 0000000..b5792c0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
drm seems like a sub-optimal name.
How about usign the compatible name "display-subsystem" as it is a bit
more specific (but not good).

> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0

Any chance this can be (GPL-2.0-only OR BSD-2-Clause).
I noticed that for example clock/sprd,sc9863a-clk.yaml uses this license
so I hope this is OK.

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Unisoc DRM master device
> +
> +maintainers:
> +  - Mark Rutland <mark.rutland@arm.com>
> +
> +description: |
> +  The Unisoc DRM master device is a virtual device needed to list all
> +  DPU devices or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: sprd,display-subsystem
> +
> +  ports:
> +    description:
> +      Should contain a list of phandles pointing to display interface port
> +      of DPU devices.
Add type - like this:
$ref: /schemas/types.yaml#/definitions/phandle-array

See for example display/rockchip/rockchip-drm.yaml

Any specific reason why this is not a ports node like used by many other
display bindings?
In other words - I think this is too simple.

> +
> +required:
> +  - compatible
> +  - ports
> +

Add:
additionalProperties: false

so we catch if other properties sneak in.

> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "sprd,display-subsystem";
> +        ports = <&dpu_out>;
> +    };
> +
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
  2020-07-28 10:07   ` Kevin Tang
@ 2020-07-28 20:45     ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 20:45 UTC (permalink / raw)
  To: Kevin Tang
  Cc: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, orsonzhai, linux-kernel, dri-devel, zhang.lyra

Hi Kevin.

Nice split of the driver.
Some feedback in the following.
Most to bring the driver up-to-date with what have happened since
we saw it last time.
Keeping up with the changes in drm is not always easy.

	Sam


On Tue, Jul 28, 2020 at 06:07:55PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> Adds drm support for the Unisoc's display subsystem.
> 
> This is drm kms driver, this driver provides support for the
> application framework in Android, Yocto and more.
> 
> Application framework can access Unisoc's display internel
> peripherals through libdrm or libkms, it's test ok by modetest
> (DRM/KMS test tool) and Android HWComposer.
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  drivers/gpu/drm/Kconfig         |   2 +
>  drivers/gpu/drm/Makefile        |   1 +
>  drivers/gpu/drm/sprd/Kconfig    |  12 +++
>  drivers/gpu/drm/sprd/Makefile   |   5 +
>  drivers/gpu/drm/sprd/sprd_drm.c | 226 ++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
>  6 files changed, 262 insertions(+)
>  create mode 100644 drivers/gpu/drm/sprd/Kconfig
>  create mode 100644 drivers/gpu/drm/sprd/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index c4fd57d..7fe7ab91 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
>  
>  source "drivers/gpu/drm/tidss/Kconfig"
>  
> +source "drivers/gpu/drm/sprd/Kconfig"
> +
>  # Keep legacy drivers last
>  
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 2c0e5a7..ee2ccd9 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
>  obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
>  obj-$(CONFIG_DRM_MCDE) += mcde/
>  obj-$(CONFIG_DRM_TIDSS) += tidss/
> +obj-$(CONFIG_DRM_SPRD) += sprd/
> diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
> new file mode 100644
> index 0000000..b189a54
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_SPRD
> +	tristate "DRM Support for Unisoc SoCs Platform"
> +	depends on ARCH_SPRD || COMPILE_TEST
> +	depends on DRM && OF
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_MIPI_DSI
> +	help
> +	  Choose this option if you have a Unisoc chipsets.
> +	  If M is selected the module will be called sprd-drm.
> +
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> new file mode 100644
> index 0000000..86d95d9
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +subdir-ccflags-y += -I$(srctree)/$(src)
subdir-ccflags-y is not needed - drop it.

> +
> +obj-y := sprd_drm.o
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> new file mode 100644
> index 0000000..4706185
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> @@ -0,0 +1,226 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_graph.h>
> +#include <linux/of_platform.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "sprd_drm.h"
> +
> +#define DRIVER_NAME	"sprd"
> +#define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
> +#define DRIVER_DATE	"20200201"
> +#define DRIVER_MAJOR	1
> +#define DRIVER_MINOR	0
> +
> +static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
> +	.fb_create = drm_gem_fb_create,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static void sprd_drm_mode_config_init(struct drm_device *drm)
> +{
> +	drm_mode_config_init(drm);
The documentation of this functions says:

FIXME: This function is deprecated and drivers should be converted over to
drmm_mode_config_init().


> +
> +	drm->mode_config.min_width = 0;
> +	drm->mode_config.min_height = 0;
> +	drm->mode_config.max_width = 8192;
> +	drm->mode_config.max_height = 8192;
> +	drm->mode_config.allow_fb_modifiers = true;
> +
> +	drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
> +	drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
> +}
> +
> +DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
> +
> +static struct drm_driver sprd_drm_drv = {
> +	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
> +	.fops			= &sprd_drm_fops,
> +
> +	/* GEM Operations */
> +  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
> +
> +	.name			= DRIVER_NAME,
> +	.desc			= DRIVER_DESC,
> +	.date			= DRIVER_DATE,
> +	.major			= DRIVER_MAJOR,
> +	.minor			= DRIVER_MINOR,
> +};
> +
> +static int sprd_drm_bind(struct device *dev)
> +{
> +	struct drm_device *drm;
> +	struct sprd_drm *sprd;
> +	int err;
> +
> +	drm = drm_dev_alloc(&sprd_drm_drv, dev);
> +	if (IS_ERR(drm))
> +		return PTR_ERR(drm);
> +
> +	dev_set_drvdata(dev, drm);
> +
> +	sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL);
> +	if (!sprd)
> +		err = -ENOMEM;

Embed drm_device in sprd_drm and use devm_drm_dev_alloc
See other drivers and drm_drv.c how to do it.
drm_drv.c has a nice example explained in one of the comment blocks.

> +
> +	drm->dev_private = sprd;
dev_private is deprecated. Alwyas use upclassing.

> +
> +	sprd_drm_mode_config_init(drm);
> +
> +	/* bind and init sub drivers */
> +	err = component_bind_all(drm->dev, drm);
> +	if (err) {
> +		DRM_ERROR("failed to bind all component.\n");
> +		goto err_dc_cleanup;
> +	}
> +
> +	/* vblank init */
> +	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
> +	if (err) {
> +		DRM_ERROR("failed to initialize vblank.\n");
> +		goto err_unbind_all;
> +	}
> +	/* with irq_enabled = true, we can use the vblank feature. */
> +	drm->irq_enabled = true;
Can drm_irq_install() be used?
Then this flag shall not be set by the driver.

> +
> +	/* reset all the states of crtc/plane/encoder/connector */
> +	drm_mode_config_reset(drm);
> +
> +	/* init kms poll for handling hpd */
> +	drm_kms_helper_poll_init(drm);
> +
> +	err = drm_dev_register(drm, 0);
> +	if (err < 0)
> +		goto err_kms_helper_poll_fini;
> +
> +	return 0;
> +
> +err_kms_helper_poll_fini:
> +	drm_kms_helper_poll_fini(drm);
> +err_unbind_all:
> +	component_unbind_all(drm->dev, drm);
> +err_dc_cleanup:
> +	drm_mode_config_cleanup(drm);
> +	return err;
> +}
> +
> +static void sprd_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm);
> +
> +	drm_kms_helper_poll_fini(drm);
> +
> +	drm_mode_config_cleanup(drm);
> +
> +	component_unbind_all(drm->dev, drm);
> +	drm->dev_private = NULL;
> +	dev_set_drvdata(dev, NULL);
> +
> +	drm_dev_put(drm);
> +}
> +
> +static const struct component_master_ops drm_component_ops = {
> +	.bind = sprd_drm_bind,
> +	.unbind = sprd_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static int sprd_drm_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
> +	if (ret) {
> +		DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops);
> +}
> +
> +static int sprd_drm_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &drm_component_ops);
> +	return 0;
> +}
> +
> +static void sprd_drm_shutdown(struct platform_device *pdev)
> +{
> +	struct drm_device *drm = platform_get_drvdata(pdev);
> +
> +	if (!drm) {
> +		DRM_WARN("drm device is not available, no shutdown\n");
> +		return;
> +	}
> +
> +	drm_atomic_helper_shutdown(drm);
> +}
> +
> +static const struct of_device_id drm_match_table[] = {
> +	{ .compatible = "sprd,display-subsystem", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, drm_match_table);
> +
> +static struct platform_driver sprd_drm_driver = {
> +	.probe = sprd_drm_probe,
> +	.remove = sprd_drm_remove,
> +	.shutdown = sprd_drm_shutdown,
> +	.driver = {
> +		.name = "sprd-drm-drv",
> +		.of_match_table = drm_match_table,
> +	},
> +};
> +
> +static struct platform_driver *sprd_drm_drivers[]  = {
> +	&sprd_drm_driver,
> +};
> +
> +static int __init sprd_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(sprd_drm_drivers,
> +					ARRAY_SIZE(sprd_drm_drivers));
> +	return ret;
> +}
> +
> +static void __exit sprd_drm_exit(void)
> +{
> +	platform_unregister_drivers(sprd_drm_drivers,
> +				    ARRAY_SIZE(sprd_drm_drivers));
> +}
> +
> +module_init(sprd_drm_init);
> +module_exit(sprd_drm_exit);
> +
> +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> +MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> new file mode 100644
> index 0000000..edf0881
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#ifndef _SPRD_DRM_H_
> +#define _SPRD_DRM_H_
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_print.h>
> +
> +struct sprd_drm {
> +	struct drm_device *drm;
> +};
> +
> +#endif /* _SPRD_DRM_H_ */
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
@ 2020-07-28 20:45     ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 20:45 UTC (permalink / raw)
  To: Kevin Tang
  Cc: mark.rutland, airlied, zhang.lyra, linux-kernel, robh+dt,
	dri-devel, orsonzhai, sean

Hi Kevin.

Nice split of the driver.
Some feedback in the following.
Most to bring the driver up-to-date with what have happened since
we saw it last time.
Keeping up with the changes in drm is not always easy.

	Sam


On Tue, Jul 28, 2020 at 06:07:55PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> Adds drm support for the Unisoc's display subsystem.
> 
> This is drm kms driver, this driver provides support for the
> application framework in Android, Yocto and more.
> 
> Application framework can access Unisoc's display internel
> peripherals through libdrm or libkms, it's test ok by modetest
> (DRM/KMS test tool) and Android HWComposer.
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  drivers/gpu/drm/Kconfig         |   2 +
>  drivers/gpu/drm/Makefile        |   1 +
>  drivers/gpu/drm/sprd/Kconfig    |  12 +++
>  drivers/gpu/drm/sprd/Makefile   |   5 +
>  drivers/gpu/drm/sprd/sprd_drm.c | 226 ++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
>  6 files changed, 262 insertions(+)
>  create mode 100644 drivers/gpu/drm/sprd/Kconfig
>  create mode 100644 drivers/gpu/drm/sprd/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index c4fd57d..7fe7ab91 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
>  
>  source "drivers/gpu/drm/tidss/Kconfig"
>  
> +source "drivers/gpu/drm/sprd/Kconfig"
> +
>  # Keep legacy drivers last
>  
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 2c0e5a7..ee2ccd9 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
>  obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
>  obj-$(CONFIG_DRM_MCDE) += mcde/
>  obj-$(CONFIG_DRM_TIDSS) += tidss/
> +obj-$(CONFIG_DRM_SPRD) += sprd/
> diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
> new file mode 100644
> index 0000000..b189a54
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_SPRD
> +	tristate "DRM Support for Unisoc SoCs Platform"
> +	depends on ARCH_SPRD || COMPILE_TEST
> +	depends on DRM && OF
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_MIPI_DSI
> +	help
> +	  Choose this option if you have a Unisoc chipsets.
> +	  If M is selected the module will be called sprd-drm.
> +
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> new file mode 100644
> index 0000000..86d95d9
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +subdir-ccflags-y += -I$(srctree)/$(src)
subdir-ccflags-y is not needed - drop it.

> +
> +obj-y := sprd_drm.o
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> new file mode 100644
> index 0000000..4706185
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> @@ -0,0 +1,226 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_graph.h>
> +#include <linux/of_platform.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include "sprd_drm.h"
> +
> +#define DRIVER_NAME	"sprd"
> +#define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
> +#define DRIVER_DATE	"20200201"
> +#define DRIVER_MAJOR	1
> +#define DRIVER_MINOR	0
> +
> +static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
> +	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> +};
> +
> +static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
> +	.fb_create = drm_gem_fb_create,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static void sprd_drm_mode_config_init(struct drm_device *drm)
> +{
> +	drm_mode_config_init(drm);
The documentation of this functions says:

FIXME: This function is deprecated and drivers should be converted over to
drmm_mode_config_init().


> +
> +	drm->mode_config.min_width = 0;
> +	drm->mode_config.min_height = 0;
> +	drm->mode_config.max_width = 8192;
> +	drm->mode_config.max_height = 8192;
> +	drm->mode_config.allow_fb_modifiers = true;
> +
> +	drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
> +	drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
> +}
> +
> +DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
> +
> +static struct drm_driver sprd_drm_drv = {
> +	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
> +	.fops			= &sprd_drm_fops,
> +
> +	/* GEM Operations */
> +  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
> +
> +	.name			= DRIVER_NAME,
> +	.desc			= DRIVER_DESC,
> +	.date			= DRIVER_DATE,
> +	.major			= DRIVER_MAJOR,
> +	.minor			= DRIVER_MINOR,
> +};
> +
> +static int sprd_drm_bind(struct device *dev)
> +{
> +	struct drm_device *drm;
> +	struct sprd_drm *sprd;
> +	int err;
> +
> +	drm = drm_dev_alloc(&sprd_drm_drv, dev);
> +	if (IS_ERR(drm))
> +		return PTR_ERR(drm);
> +
> +	dev_set_drvdata(dev, drm);
> +
> +	sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL);
> +	if (!sprd)
> +		err = -ENOMEM;

Embed drm_device in sprd_drm and use devm_drm_dev_alloc
See other drivers and drm_drv.c how to do it.
drm_drv.c has a nice example explained in one of the comment blocks.

> +
> +	drm->dev_private = sprd;
dev_private is deprecated. Alwyas use upclassing.

> +
> +	sprd_drm_mode_config_init(drm);
> +
> +	/* bind and init sub drivers */
> +	err = component_bind_all(drm->dev, drm);
> +	if (err) {
> +		DRM_ERROR("failed to bind all component.\n");
> +		goto err_dc_cleanup;
> +	}
> +
> +	/* vblank init */
> +	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
> +	if (err) {
> +		DRM_ERROR("failed to initialize vblank.\n");
> +		goto err_unbind_all;
> +	}
> +	/* with irq_enabled = true, we can use the vblank feature. */
> +	drm->irq_enabled = true;
Can drm_irq_install() be used?
Then this flag shall not be set by the driver.

> +
> +	/* reset all the states of crtc/plane/encoder/connector */
> +	drm_mode_config_reset(drm);
> +
> +	/* init kms poll for handling hpd */
> +	drm_kms_helper_poll_init(drm);
> +
> +	err = drm_dev_register(drm, 0);
> +	if (err < 0)
> +		goto err_kms_helper_poll_fini;
> +
> +	return 0;
> +
> +err_kms_helper_poll_fini:
> +	drm_kms_helper_poll_fini(drm);
> +err_unbind_all:
> +	component_unbind_all(drm->dev, drm);
> +err_dc_cleanup:
> +	drm_mode_config_cleanup(drm);
> +	return err;
> +}
> +
> +static void sprd_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm);
> +
> +	drm_kms_helper_poll_fini(drm);
> +
> +	drm_mode_config_cleanup(drm);
> +
> +	component_unbind_all(drm->dev, drm);
> +	drm->dev_private = NULL;
> +	dev_set_drvdata(dev, NULL);
> +
> +	drm_dev_put(drm);
> +}
> +
> +static const struct component_master_ops drm_component_ops = {
> +	.bind = sprd_drm_bind,
> +	.unbind = sprd_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static int sprd_drm_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
> +	if (ret) {
> +		DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops);
> +}
> +
> +static int sprd_drm_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &drm_component_ops);
> +	return 0;
> +}
> +
> +static void sprd_drm_shutdown(struct platform_device *pdev)
> +{
> +	struct drm_device *drm = platform_get_drvdata(pdev);
> +
> +	if (!drm) {
> +		DRM_WARN("drm device is not available, no shutdown\n");
> +		return;
> +	}
> +
> +	drm_atomic_helper_shutdown(drm);
> +}
> +
> +static const struct of_device_id drm_match_table[] = {
> +	{ .compatible = "sprd,display-subsystem", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, drm_match_table);
> +
> +static struct platform_driver sprd_drm_driver = {
> +	.probe = sprd_drm_probe,
> +	.remove = sprd_drm_remove,
> +	.shutdown = sprd_drm_shutdown,
> +	.driver = {
> +		.name = "sprd-drm-drv",
> +		.of_match_table = drm_match_table,
> +	},
> +};
> +
> +static struct platform_driver *sprd_drm_drivers[]  = {
> +	&sprd_drm_driver,
> +};
> +
> +static int __init sprd_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_register_drivers(sprd_drm_drivers,
> +					ARRAY_SIZE(sprd_drm_drivers));
> +	return ret;
> +}
> +
> +static void __exit sprd_drm_exit(void)
> +{
> +	platform_unregister_drivers(sprd_drm_drivers,
> +				    ARRAY_SIZE(sprd_drm_drivers));
> +}
> +
> +module_init(sprd_drm_init);
> +module_exit(sprd_drm_exit);
> +
> +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> +MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> new file mode 100644
> index 0000000..edf0881
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#ifndef _SPRD_DRM_H_
> +#define _SPRD_DRM_H_
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_print.h>
> +
> +struct sprd_drm {
> +	struct drm_device *drm;
> +};
> +
> +#endif /* _SPRD_DRM_H_ */
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 3/6] dt-bindings: display: add Unisoc's dpu bindings
  2020-07-28 10:07   ` Kevin Tang
@ 2020-07-28 20:51     ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 20:51 UTC (permalink / raw)
  To: Kevin Tang
  Cc: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, orsonzhai, linux-kernel, dri-devel, zhang.lyra

Hi Kevin.

On Tue, Jul 28, 2020 at 06:07:56PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
> which transfers the image data from a video memory buffer to an internal
> LCD interface.
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  .../devicetree/bindings/display/sprd/dpu.yaml      | 82 ++++++++++++++++++++++

Could we name it after the compatible "sharkl3-dpu.yaml"?

>  1 file changed, 82 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/sprd/dpu.yaml b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
> new file mode 100644
> index 0000000..54ba9b6f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
> @@ -0,0 +1,82 @@
> +# SPDX-License-Identifier: GPL-2.0
Can this be: (GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/sprd/dpu.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Unisoc SoC Display Processor Unit (DPU)
> +
> +maintainers:
> +  - Mark Rutland <mark.rutland@arm.com>
> +
> +description: |
> +  DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
> +  which transfers the image data from a video memory buffer to an internal
> +  LCD interface.
> +
> +properties:
> +  compatible:
> +    const: sprd,sharkl3-dpu
> +
> +  reg:
> +    maxItems: 1
> +    description:
> +      Physical base address and length of the DPU registers set
> +
> +  interrupts:
> +    maxItems: 1
> +    description:
> +      The interrupt signal from DPU
> +
> +  clocks:
> +    minItems: 2
> +
> +  clock-names:
> +    items:
> +      - const: clk_src_128m
> +      - const: clk_src_384m
> +
> +  power-domains:
> +    description: A phandle to DPU power domain node.
maxItems: 1

> +
> +  iommus:
> +    description: A phandle to DPU iommu node.
maxItems: 1

> +
> +  port:
> +    type: object
> +    description:
> +      A port node with endpoint definitions as defined in
> +      Documentation/devicetree/bindings/media/video-interfaces.txt.
> +      That port should be the output endpoint, usually output to
> +      the associated DSI.
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/sprd,sc9860-clk.h>
> +    dpu: dpu@0x63000000 {
> +          compatible = "sprd,sharkl3-dpu";
> +          reg = <0x0 0x63000000 0x0 0x1000>;
> +          interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
> +          clock-names = "clk_src_128m",
> +      	        "clk_src_384m";
> +
> +          clocks = <&pll CLK_TWPLL_128M>,
> +                <&pll CLK_TWPLL_384M>;
> +
> +          dpu_port: port {
> +                  dpu_out: endpoint {
> +                          remote-endpoint = <&dsi_in>;
> +                  };
> +          };
> +    };

Be consistent in indent. Now it is a mix of 6 and 8 spaces.
(My preference is 4 spaces, but 2,4,6,8 are all OK)

End the file with and end statement like this:
<empty line>
...

> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 3/6] dt-bindings: display: add Unisoc's dpu bindings
@ 2020-07-28 20:51     ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 20:51 UTC (permalink / raw)
  To: Kevin Tang
  Cc: mark.rutland, airlied, zhang.lyra, linux-kernel, robh+dt,
	dri-devel, orsonzhai, sean

Hi Kevin.

On Tue, Jul 28, 2020 at 06:07:56PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
> which transfers the image data from a video memory buffer to an internal
> LCD interface.
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  .../devicetree/bindings/display/sprd/dpu.yaml      | 82 ++++++++++++++++++++++

Could we name it after the compatible "sharkl3-dpu.yaml"?

>  1 file changed, 82 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/sprd/dpu.yaml b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
> new file mode 100644
> index 0000000..54ba9b6f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sprd/dpu.yaml
> @@ -0,0 +1,82 @@
> +# SPDX-License-Identifier: GPL-2.0
Can this be: (GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/sprd/dpu.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Unisoc SoC Display Processor Unit (DPU)
> +
> +maintainers:
> +  - Mark Rutland <mark.rutland@arm.com>
> +
> +description: |
> +  DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs
> +  which transfers the image data from a video memory buffer to an internal
> +  LCD interface.
> +
> +properties:
> +  compatible:
> +    const: sprd,sharkl3-dpu
> +
> +  reg:
> +    maxItems: 1
> +    description:
> +      Physical base address and length of the DPU registers set
> +
> +  interrupts:
> +    maxItems: 1
> +    description:
> +      The interrupt signal from DPU
> +
> +  clocks:
> +    minItems: 2
> +
> +  clock-names:
> +    items:
> +      - const: clk_src_128m
> +      - const: clk_src_384m
> +
> +  power-domains:
> +    description: A phandle to DPU power domain node.
maxItems: 1

> +
> +  iommus:
> +    description: A phandle to DPU iommu node.
maxItems: 1

> +
> +  port:
> +    type: object
> +    description:
> +      A port node with endpoint definitions as defined in
> +      Documentation/devicetree/bindings/media/video-interfaces.txt.
> +      That port should be the output endpoint, usually output to
> +      the associated DSI.
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/sprd,sc9860-clk.h>
> +    dpu: dpu@0x63000000 {
> +          compatible = "sprd,sharkl3-dpu";
> +          reg = <0x0 0x63000000 0x0 0x1000>;
> +          interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
> +          clock-names = "clk_src_128m",
> +      	        "clk_src_384m";
> +
> +          clocks = <&pll CLK_TWPLL_128M>,
> +                <&pll CLK_TWPLL_384M>;
> +
> +          dpu_port: port {
> +                  dpu_out: endpoint {
> +                          remote-endpoint = <&dsi_in>;
> +                  };
> +          };
> +    };

Be consistent in indent. Now it is a mix of 6 and 8 spaces.
(My preference is 4 spaces, but 2,4,6,8 are all OK)

End the file with and end statement like this:
<empty line>
...

> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 10:07   ` Kevin Tang
@ 2020-07-28 21:13     ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 21:13 UTC (permalink / raw)
  To: Kevin Tang
  Cc: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, orsonzhai, linux-kernel, dri-devel, zhang.lyra

Hi Kevin.

Some feedback in the following.
I lost track of thing for the atomic modesettting stuff and I hope other
will review that.

	Sam

On Tue, Jul 28, 2020 at 06:07:57PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> 
> RFC v6:
>   - Access registers via readl/writel
>   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
>   - Remove always true checks for dpu core ops
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  drivers/gpu/drm/sprd/Makefile       |   5 +-
>  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
>  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
>  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
>  7 files changed, 1346 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> 
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> index 86d95d9..88ab32a 100644
> --- a/drivers/gpu/drm/sprd/Makefile
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -2,4 +2,7 @@
>  
>  subdir-ccflags-y += -I$(srctree)/$(src)
Not needed - drop.

>  
> -obj-y := sprd_drm.o
> +obj-y := sprd_drm.o \
> +	sprd_dpu.o
> +
> +obj-y += dpu/

Until there are several DPU's there is no need for a separate directory.
Make it simpler by keeping it all in drm/sprd/*

> diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> new file mode 100644
> index 0000000..40278b6
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-y += dpu_r2p0.o
> diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> new file mode 100644
> index 0000000..4b9521d
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/wait.h>
> +#include <linux/workqueue.h>
> +
> +#include "sprd_dpu.h"
> +
> +/* DPU registers size, 4 Bytes(32 Bits) */
> +#define DPU_REG_SIZE	0x04
> +
> +/* Layer registers offset */
> +#define DPU_LAY_REG_OFFSET	0x0C
> +
> +#define DPU_LAY_REG(reg, index) \
> +	(reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
Use a static inline to get better typecheck.

> +#define DPU_REG_RD(reg) readl_relaxed(reg)
> +
> +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> +
> +#define DPU_REG_SET(reg, mask) \
> +	writel_relaxed(readl_relaxed(reg) | mask, reg)
> +
> +#define DPU_REG_CLR(reg, mask) \
> +	writel_relaxed(readl_relaxed(reg) & ~mask, reg)
I am no fan of macros used like this.

Maybe use static inlines and add struct dpu_context * as first argument.
Then adding base can be hidden away.


I had a hard time convincing myself that _relaxed was the right
variants. If I get it correct we may see writes re-ordered with the
_relaxed variants wich would be no good when dealign with interrupts.
But I may miss somethign here.

> +
> +/* Global control registers */
> +#define REG_DPU_CTRL	0x04
> +#define REG_DPU_CFG0	0x08
> +#define REG_DPU_CFG1	0x0C
> +#define REG_DPU_CFG2	0x10
> +#define REG_PANEL_SIZE	0x20
> +#define REG_BLEND_SIZE	0x24
> +#define REG_BG_COLOR	0x2C
> +
> +/* Layer0 control registers */
> +#define REG_LAY_BASE_ADDR0	0x30
> +#define REG_LAY_BASE_ADDR1	0x34
> +#define REG_LAY_BASE_ADDR2	0x38
> +#define REG_LAY_CTRL		0x40
> +#define REG_LAY_SIZE		0x44
> +#define REG_LAY_PITCH		0x48
> +#define REG_LAY_POS		0x4C
> +#define REG_LAY_ALPHA		0x50
> +#define REG_LAY_PALLETE		0x58
> +#define REG_LAY_CROP_START	0x5C
> +
> +/* Interrupt control registers */
> +#define REG_DPU_INT_EN		0x1E0
> +#define REG_DPU_INT_CLR		0x1E4
> +#define REG_DPU_INT_STS		0x1E8
> +
> +/* DPI control registers */
> +#define REG_DPI_CTRL		0x1F0
> +#define REG_DPI_H_TIMING	0x1F4
> +#define REG_DPI_V_TIMING	0x1F8
> +
> +/* MMU control registers */
> +#define REG_MMU_EN			0x800
> +#define REG_MMU_VPN_RANGE		0x80C
> +#define REG_MMU_VAOR_ADDR_RD		0x818
> +#define REG_MMU_VAOR_ADDR_WR		0x81C
> +#define REG_MMU_INV_ADDR_RD		0x820
> +#define REG_MMU_INV_ADDR_WR		0x824
> +#define REG_MMU_PPN1			0x83C
> +#define REG_MMU_RANGE1			0x840
> +#define REG_MMU_PPN2			0x844
> +#define REG_MMU_RANGE2			0x848
> +
> +/* Global control bits */
> +#define BIT_DPU_RUN			BIT(0)
> +#define BIT_DPU_STOP			BIT(1)
> +#define BIT_DPU_REG_UPDATE		BIT(2)
> +#define BIT_DPU_IF_EDPI			BIT(0)
> +#define BIT_DPU_COEF_NARROW_RANGE		BIT(4)
> +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD	BIT(5)
> +
> +/* Layer control bits */
> +#define BIT_DPU_LAY_EN				BIT(0)
> +
> +/* Interrupt control & status bits */
> +#define BIT_DPU_INT_DONE		BIT(0)
> +#define BIT_DPU_INT_TE			BIT(1)
> +#define BIT_DPU_INT_ERR			BIT(2)
> +#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
> +#define BIT_DPU_INT_VSYNC		BIT(5)
> +#define BIT_DPU_INT_FBC_PLD_ERR		BIT(8)
> +#define BIT_DPU_INT_FBC_HDR_ERR		BIT(9)
> +#define BIT_DPU_INT_MMU_VAOR_RD		BIT(16)
> +#define BIT_DPU_INT_MMU_VAOR_WR		BIT(17)
> +#define BIT_DPU_INT_MMU_INV_RD		BIT(18)
> +#define BIT_DPU_INT_MMU_INV_WR		BIT(19)
> +
> +/* DPI control bits */
> +#define BIT_DPU_EDPI_TE_EN		BIT(8)
> +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD	BIT(10)
> +#define BIT_DPU_DPI_HALT_EN		BIT(16)
> +
> +
> +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> +{
> +	u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> +			BIT_DPU_INT_MMU_VAOR_WR |
> +			BIT_DPU_INT_MMU_INV_RD |
> +			BIT_DPU_INT_MMU_INV_WR;
> +	u32 val = reg_val & mmu_mask;
> +	int i;
> +
> +	if (val) {
> +		DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);

General comment, applies for the whole patch.

Use drm_err(drm, ...), drm_info(drm, ...) etc.

Use upclassing to get sprd_dpu - and then you have a drm_device *
(After drm_device is embedded in sprd_dpu as suggested in the other
mail)

> +
> +		if (val & BIT_DPU_INT_MMU_INV_RD)
> +			DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> +		if (val & BIT_DPU_INT_MMU_INV_WR)
> +			DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> +		if (val & BIT_DPU_INT_MMU_VAOR_RD)
> +			DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> +		if (val & BIT_DPU_INT_MMU_VAOR_WR)
> +			DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> +
> +		for (i = 0; i < 8; i++) {
> +			reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> +			if (reg_val & 0x1)
> +				DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> +		}
> +	}
> +
> +	return val;
> +}
> +
> +static void dpu_clean_all(struct dpu_context *ctx)
> +{
> +	int i;
> +
> +	for (i = 0; i < 8; i++)
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> +}
> +
> +static u32 dpu_isr(struct dpu_context *ctx)
> +{
> +	u32 reg_val, int_mask = 0;
> +
> +	reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> +
> +	/* disable err interrupt */
> +	if (reg_val & BIT_DPU_INT_ERR)
> +		int_mask |= BIT_DPU_INT_ERR;
> +
> +	/* dpu update done isr */
> +	if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> +		ctx->evt_update = true;
> +		wake_up_interruptible_all(&ctx->wait_queue);
> +	}
> +
> +	/* dpu stop done isr */
> +	if (reg_val & BIT_DPU_INT_DONE) {
> +		ctx->evt_stop = true;
> +		wake_up_interruptible_all(&ctx->wait_queue);
> +	}
> +
> +	/* dpu ifbc payload error isr */
> +	if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> +		int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +		DRM_ERROR("dpu ifbc payload error\n");
> +	}
> +
> +	/* dpu ifbc header error isr */
> +	if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> +		int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +		DRM_ERROR("dpu ifbc header error\n");
> +	}
> +
> +	int_mask |= check_mmu_isr(ctx, reg_val);
> +
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> +	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> +
> +	return reg_val;
> +}
> +
> +static int dpu_wait_stop_done(struct dpu_context *ctx)
> +{
> +	int rc;
> +
> +	if (ctx->stopped)
> +		return 0;
> +
> +	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> +					       msecs_to_jiffies(500));
> +	ctx->evt_stop = false;
> +
> +	ctx->stopped = true;
> +
> +	if (!rc) {
> +		DRM_ERROR("dpu wait for stop done time out!\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dpu_wait_update_done(struct dpu_context *ctx)
> +{
> +	int rc;
> +
> +	ctx->evt_update = false;
> +
> +	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> +					       msecs_to_jiffies(500));
> +
> +	if (!rc) {
> +		DRM_ERROR("dpu wait for reg update done time out!\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static void dpu_stop(struct dpu_context *ctx)
> +{
> +	if (ctx->if_type == SPRD_DPU_IF_DPI)
> +		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> +
> +	dpu_wait_stop_done(ctx);
> +}
> +
> +static void dpu_run(struct dpu_context *ctx)
> +{
> +	DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +	ctx->stopped = false;
> +}
> +
> +static void dpu_init(struct dpu_context *ctx)
> +{
> +	u32 reg_val, size;
> +
> +	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> +
> +	DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> +	DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> +
> +	reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> +	DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> +	DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> +	DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> +
> +	if (ctx->stopped)
> +		dpu_clean_all(ctx);
> +
> +	DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> +	DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> +	DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> +	DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> +	DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> +	DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> +
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> +}
> +
> +static void dpu_fini(struct dpu_context *ctx)
> +{
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> +}
> +
> +static void dpu_layer(struct dpu_context *ctx,
> +		    struct dpu_layer *hwlayer)
No linebreak needed here.

> +{
> +	const struct drm_format_info *info;
> +	u32 size, offset, ctrl, pitch;
> +	int i;
> +
> +	offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> +
> +	if (hwlayer->src_w && hwlayer->src_h)
> +		size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> +	else
> +		size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> +
> +	for (i = 0; i < hwlayer->planes; i++)
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> +				hwlayer->index), hwlayer->addr[i]);
> +
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> +			hwlayer->index), offset);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> +			hwlayer->index), size);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> +			hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> +			hwlayer->index), hwlayer->alpha);
> +
> +	info = drm_format_info(hwlayer->format);
> +	if (hwlayer->planes == 3) {
> +		/* UV pitch is 1/2 of Y pitch*/
> +		pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> +				(hwlayer->pitch[0] / info->cpp[0] << 15);
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +				hwlayer->index), pitch);
> +	} else {
> +		pitch = hwlayer->pitch[0] / info->cpp[0];
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +				hwlayer->index), pitch);
> +	}
> +
> +	ctrl = hwlayer->format |
> +		hwlayer->blending |
> +		(hwlayer->rotation & 0x7) << 20;
> +
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +			hwlayer->index), ctrl);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +			hwlayer->index), BIT_DPU_LAY_EN);
> +
> +	DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> +				hwlayer->dst_x, hwlayer->dst_y,
> +				hwlayer->dst_w, hwlayer->dst_h);
> +	DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> +				hwlayer->src_x, hwlayer->src_y,
> +				hwlayer->src_w, hwlayer->src_h);
> +}
> +
> +static void dpu_flip(struct dpu_context *ctx,
> +		     struct dpu_layer layers[], u8 count)
> +{
> +	int i;
> +	u32 reg_val;
> +
> +	/*
> +	 * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> +	 * registers in EDPI mode. So the config registers can only be
> +	 * updated in the rising edge of DPU_RUN bit.
> +	 */
> +	if (ctx->if_type == SPRD_DPU_IF_EDPI)
> +		dpu_wait_stop_done(ctx);
> +
> +	/* reset the bgcolor to black */
> +	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +	/* disable all the layers */
> +	dpu_clean_all(ctx);
> +
> +	/* start configure dpu layers */
> +	for (i = 0; i < count; i++)
> +		dpu_layer(ctx, &layers[i]);
> +
> +	/* update trigger and wait */
> +	if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +		if (!ctx->stopped) {
> +			DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> +			dpu_wait_update_done(ctx);
> +		}
> +
> +		DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> +	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +		ctx->stopped = false;
> +	}
> +
> +	/*
> +	 * If the following interrupt was disabled in isr,
> +	 * re-enable it.
> +	 */
> +	reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> +		  BIT_DPU_INT_FBC_HDR_ERR |
> +		  BIT_DPU_INT_MMU_VAOR_RD |
> +		  BIT_DPU_INT_MMU_VAOR_WR |
> +		  BIT_DPU_INT_MMU_INV_RD |
> +		  BIT_DPU_INT_MMU_INV_WR;
> +	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> +
> +}
> +
> +static void dpu_dpi_init(struct dpu_context *ctx)
> +{
> +	u32 int_mask = 0;
> +	u32 reg_val;
> +
> +	if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +		/* use dpi as interface */
> +		DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +		/* disable Halt function for SPRD DSI */
> +		DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> +
> +		/* select te from external pad */
> +		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +		/* set dpi timing */
> +		reg_val = ctx->vm.hsync_len << 0 |
> +			  ctx->vm.hback_porch << 8 |
> +			  ctx->vm.hfront_porch << 20;
> +		DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> +
> +		reg_val = ctx->vm.vsync_len << 0 |
> +			  ctx->vm.vback_porch << 8 |
> +			  ctx->vm.vfront_porch << 20;
> +		DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> +
> +		if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> +			DRM_WARN("Warning: (vsync + vbp) < 32, "
> +				"underflow risk!\n");
> +
> +		/* enable dpu update done INT */
> +		int_mask |= BIT_DPU_INT_UPDATE_DONE;
> +		/* enable dpu DONE  INT */
> +		int_mask |= BIT_DPU_INT_DONE;
> +		/* enable dpu dpi vsync */
> +		int_mask |= BIT_DPU_INT_VSYNC;
> +		/* enable dpu TE INT */
> +		int_mask |= BIT_DPU_INT_TE;
> +		/* enable underflow err INT */
> +		int_mask |= BIT_DPU_INT_ERR;
> +	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +		/* use edpi as interface */
> +		DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +		/* use external te */
> +		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +		/* enable te */
> +		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> +
> +		/* enable stop DONE INT */
> +		int_mask |= BIT_DPU_INT_DONE;
> +		/* enable TE INT */
> +		int_mask |= BIT_DPU_INT_TE;
> +	}
> +
> +	/* enable ifbc payload error INT */
> +	int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +	/* enable ifbc header error INT */
> +	int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +	/* enable iommu va out of range read error INT */
> +	int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> +	/* enable iommu va out of range write error INT */
> +	int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> +	/* enable iommu invalid read error INT */
> +	int_mask |= BIT_DPU_INT_MMU_INV_RD;
> +	/* enable iommu invalid write error INT */
> +	int_mask |= BIT_DPU_INT_MMU_INV_WR;
> +
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> +}
> +
> +static void enable_vsync(struct dpu_context *ctx)
> +{
> +	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static void disable_vsync(struct dpu_context *ctx)
> +{
> +	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static const u32 primary_fmts[] = {
> +	DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> +	DRM_FORMAT_YVU420,
> +};
One format per line improves readability a lot.
Maybe sort alphbetically too.

> +
> +static void dpu_capability(struct dpu_context *ctx,
> +			struct dpu_capability *cap)
Drop linebreak

> +{
> +	cap->max_layers = 6;
> +	cap->fmts_ptr = primary_fmts;
> +	cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> +}
> +
> +const struct dpu_core_ops dpu_r2p0_core_ops = {
> +	.init = dpu_init,
> +	.fini = dpu_fini,
> +	.run = dpu_run,
> +	.stop = dpu_stop,
> +	.isr = dpu_isr,
> +	.ifconfig = dpu_dpi_init,
> +	.capability = dpu_capability,
> +	.flip = dpu_flip,
> +	.enable_vsync = enable_vsync,
> +	.disable_vsync = disable_vsync,
> +};
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> new file mode 100644
> index 0000000..5ec8e7c
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> @@ -0,0 +1,646 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/dma-buf.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "sprd_drm.h"
> +#include "sprd_dpu.h"
> +
> +struct sprd_plane {
> +	struct drm_plane plane;
> +	u32 index;
> +	u32 addr[4];
> +	u32 pitch[4];
> +	u32 format;
> +	u32 rotation;
> +	u32 blend_mode;
> +};
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu);
> +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> +
> +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> +{
> +	return container_of(plane, struct sprd_plane, plane);
> +}
> +
> +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> +{
> +	switch (fourcc) {
> +	case DRM_FORMAT_BGRA8888:
> +		/* BGRA8888 -> ARGB8888 */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +		break;
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +		/* RGBA8888 -> ABGR8888 */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_ABGR8888:
> +		/* RB switch */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_ARGB8888:
> +		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +		break;
> +	case DRM_FORMAT_XBGR8888:
> +		/* RB switch */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_XRGB8888:
> +		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +		break;
> +	case DRM_FORMAT_BGR565:
> +		/* RB switch */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_RGB565:
> +		*format |= BIT_DPU_LAY_FORMAT_RGB565;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		/* 2-Lane: Yuv420 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_NO_SWITCH;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		/* 2-Lane: Yuv420 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		break;
> +	case DRM_FORMAT_NV16:
> +		/* 2-Lane: Yuv422 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		break;
> +	case DRM_FORMAT_NV61:
> +		/* 2-Lane: Yuv422 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_NO_SWITCH;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_NO_SWITCH;
> +		break;
> +	case DRM_FORMAT_YVU420:
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> +{
> +	switch (angle) {
> +	case DRM_MODE_ROTATE_0:
> +		*rotation = DPU_LAYER_ROTATION_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		*rotation = DPU_LAYER_ROTATION_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		*rotation = DPU_LAYER_ROTATION_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		*rotation = DPU_LAYER_ROTATION_270;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		*rotation = DPU_LAYER_ROTATION_180_M;
> +		break;
> +	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> +		*rotation = DPU_LAYER_ROTATION_90_M;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		*rotation = DPU_LAYER_ROTATION_0_M;
> +		break;
> +	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> +		*rotation = DPU_LAYER_ROTATION_270_M;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sprd_plane_atomic_check(struct drm_plane *plane,
> +				  struct drm_plane_state *state)
> +{
> +	struct sprd_plane *p = to_sprd_plane(plane);
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *cma_obj;
> +	int i, ret;
> +	u32 addr;
> +
> +	if (!state->fb || !state->crtc)
> +		return 0;
> +
> +	ret = sprd_plane_format_convert(fb->format->format,
> +					&p->format);
> +	if (ret < 0) {
> +		DRM_ERROR("Invalid plane format\n");
> +		return ret;
> +	}
> +
> +	ret = sprd_plane_rotation_convert(state->rotation,
> +					&p->rotation);
> +	if (ret < 0) {
> +		DRM_ERROR("Invalid plane rotation\n");
> +		return ret;
> +	}
> +
> +	switch (state->pixel_blend_mode) {
> +	case DRM_MODE_BLEND_COVERAGE:
> +		/* alpha mode select - combo alpha */
> +		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +		/* Normal mode */
> +		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> +		break;
> +	case DRM_MODE_BLEND_PREMULTI:
> +		/* alpha mode select - combo alpha */
> +		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +		/* Pre-mult mode */
> +		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> +		break;
> +	case DRM_MODE_BLEND_PIXEL_NONE:
> +	default:
> +		/* don't do blending, maybe RGBX */
> +		/* alpha mode select - layer alpha */
> +		p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> +		break;
> +	}
> +
> +	for (i = 0; i < fb->format->num_planes; i++) {
> +		cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> +		addr = cma_obj->paddr + fb->offsets[i];
> +		if (addr % 16) {
> +			DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> +				i, addr);
> +			return -EFAULT;
> +		}
> +
> +		p->addr[i] = addr;
> +		p->pitch[i] = fb->pitches[i];
> +	}
> +
> +	return 0;
> +}
> +
> +static void sprd_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = plane->state->fb;
> +	struct sprd_plane *p = to_sprd_plane(plane);
> +	struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> +	struct dpu_layer *layer = &dpu->layers[p->index];
> +	int i;
> +
> +	if (!state->crtc || !state->fb)
> +		return;
> +
> +	layer->index = p->index;
> +	layer->src_x = state->src_x >> 16;
> +	layer->src_y = state->src_y >> 16;
> +	layer->src_w = state->src_w >> 16;
> +	layer->src_h = state->src_h >> 16;
> +	layer->dst_x = state->crtc_x;
> +	layer->dst_y = state->crtc_y;
> +	layer->dst_w = state->crtc_w;
> +	layer->dst_h = state->crtc_h;
> +	layer->alpha = state->alpha;
> +	layer->format = p->format;
> +	layer->blending = p->blend_mode;
> +	layer->rotation = p->rotation;
> +	layer->planes = fb->format->num_planes;
> +
> +	for (i = 0; i < layer->planes; i++) {
> +		layer->addr[i] = p->addr[i];
> +		layer->pitch[i] = p->pitch[i];
> +	}
> +
> +	dpu->pending_planes++;
> +}
> +
> +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> +{
> +	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				       BIT(DRM_MODE_BLEND_PREMULTI) |
> +				       BIT(DRM_MODE_BLEND_COVERAGE);
> +
> +	/* create rotation property */
> +	drm_plane_create_rotation_property(&p->plane,
> +					   DRM_MODE_ROTATE_0,
> +					   DRM_MODE_ROTATE_MASK |
> +					   DRM_MODE_REFLECT_MASK);
> +
> +	/* create alpha property */
> +	drm_plane_create_alpha_property(&p->plane);
> +
> +	/* create blend mode property */
> +	drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> +
> +	/* create zpos property */
> +	drm_plane_create_zpos_immutable_property(&p->plane, index);
> +}
> +
> +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> +	.atomic_check = sprd_plane_atomic_check,
> +	.atomic_update = sprd_plane_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs sprd_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> +					struct sprd_dpu *dpu)
> +{
> +	struct drm_plane *primary = NULL;
> +	struct sprd_plane *p = NULL;
> +	struct dpu_capability cap = {};
> +	int ret, i;
> +
> +	dpu->core->capability(&dpu->ctx, &cap);
> +
> +	dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> +				  sizeof(struct dpu_layer), GFP_KERNEL);
> +	if (!dpu->layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < cap.max_layers; i++) {
> +
> +		p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> +		if (!p)
> +			return ERR_PTR(-ENOMEM);
> +
> +		ret = drm_universal_plane_init(drm, &p->plane, 1,
> +					       &sprd_plane_funcs, cap.fmts_ptr,
> +					       cap.fmts_cnt, NULL,
> +					       DRM_PLANE_TYPE_PRIMARY, NULL);
> +		if (ret) {
> +			DRM_ERROR("fail to init primary plane\n");
> +			return ERR_PTR(ret);
> +		}
> +
> +		drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> +
> +		sprd_plane_create_properties(p, i);
> +
> +		p->index = i;
> +		if (i == 0)
> +			primary = &p->plane;
> +	}
> +
> +	return primary;
> +}
> +
> +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> +					const struct drm_display_mode *mode)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> +
> +	if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> +		drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> +
> +		if ((mode->hdisplay == mode->htotal) ||
> +		    (mode->vdisplay == mode->vtotal))
> +			dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> +		else
> +			dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> +				   struct drm_crtc_state *old_state)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	sprd_dpu_init(dpu);
> +
> +	enable_irq(dpu->ctx.irq);
> +}
> +
> +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *old_state)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +	struct drm_device *drm = dpu->crtc.dev;
> +
> +	disable_irq(dpu->ctx.irq);
> +
> +	sprd_dpu_fini(dpu);
> +
> +	spin_lock_irq(&drm->event_lock);
> +	if (crtc->state->event) {
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		crtc->state->event = NULL;
> +	}
> +	spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> +				 struct drm_crtc_state *state)
> +{
> +	DRM_DEBUG("%s()\n", __func__);
> +
> +	return 0;
> +}
> +
> +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> +
> +	dpu->pending_planes = 0;
> +}
> +
> +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +	struct drm_device *drm = dpu->crtc.dev;
> +
> +	if (dpu->pending_planes)
> +		dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> +
> +	spin_lock_irq(&drm->event_lock);
> +	if (crtc->state->event) {
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		crtc->state->event = NULL;
> +	}
> +	spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	dpu->core->enable_vsync(&dpu->ctx);
> +
> +	return 0;
> +}
> +
> +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	dpu->core->disable_vsync(&dpu->ctx);
> +}
> +
> +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> +	.mode_valid	= sprd_crtc_mode_valid,
> +	.atomic_check	= sprd_crtc_atomic_check,
> +	.atomic_begin	= sprd_crtc_atomic_begin,
> +	.atomic_flush	= sprd_crtc_atomic_flush,
> +	.atomic_enable	= sprd_crtc_atomic_enable,
> +	.atomic_disable	= sprd_crtc_atomic_disable,
> +};
> +
> +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> +	.destroy	= drm_crtc_cleanup,
> +	.set_config	= drm_atomic_helper_set_config,
> +	.page_flip	= drm_atomic_helper_page_flip,
> +	.reset		= drm_atomic_helper_crtc_reset,
> +	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
> +	.enable_vblank	= sprd_crtc_enable_vblank,
> +	.disable_vblank	= sprd_crtc_disable_vblank,
> +};
> +
> +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> +			 struct drm_plane *primary)
> +{
> +	struct device_node *port;
> +	int ret;
> +
> +	/*
> +	 * set crtc port so that drm_of_find_possible_crtcs call works
> +	 */
> +	port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> +	if (!port) {
> +		DRM_ERROR("find 'ports' phandle of %s failed\n",
> +			  drm->dev->of_node->full_name);
> +		return -EINVAL;
> +	}
> +	of_node_put(port);
> +	crtc->port = port;
> +
> +	ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> +					&sprd_crtc_funcs, NULL);
> +	if (ret) {
> +		DRM_ERROR("failed to init crtc.\n");
> +		return ret;
> +	}
> +
> +	drm_mode_crtc_set_gamma_size(crtc, 256);
> +
> +	drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> +
> +	return 0;
> +}
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu)
> +{
> +	struct dpu_context *ctx = &dpu->ctx;
> +
> +	dpu->core->init(ctx);
> +	dpu->core->ifconfig(ctx);
> +}
> +
> +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> +{
> +	struct dpu_context *ctx = &dpu->ctx;
> +
> +	dpu->core->fini(ctx);
> +}
> +
> +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> +{
> +	struct sprd_dpu *dpu = data;
> +	struct dpu_context *ctx = &dpu->ctx;
> +	u32 int_mask = 0;
> +
> +	int_mask = dpu->core->isr(ctx);
> +
> +	if (int_mask & BIT_DPU_INT_ERR)
> +		DRM_WARN("Warning: dpu underflow!\n");
> +
> +	if (int_mask & BIT_DPU_INT_VSYNC)
> +		drm_crtc_handle_vblank(&dpu->crtc);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm = data;
> +	struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +	struct drm_plane *plane;
> +	int ret;
> +
> +	plane = sprd_plane_init(drm, dpu);
> +	if (IS_ERR_OR_NULL(plane)) {
> +		ret = PTR_ERR(plane);
> +		return ret;
> +	}
> +
> +	ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> +	void *data)
> +{
> +	struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +
> +	drm_crtc_cleanup(&dpu->crtc);
> +}
> +
> +static const struct component_ops dpu_component_ops = {
> +	.bind = sprd_dpu_bind,
> +	.unbind = sprd_dpu_unbind,
> +};
> +
> +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> +				struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct dpu_context *ctx = &dpu->ctx;
> +	struct resource *res;
> +	int ret;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> +	if (!ctx->base) {
> +		DRM_ERROR("failed to map dpu registers\n");
> +		return -EFAULT;
> +	}
> +
> +	ctx->irq = platform_get_irq(pdev, 0);
> +	if (ctx->irq < 0) {
> +		DRM_ERROR("failed to get dpu irq\n");
> +		return ctx->irq;
> +	}
> +
> +	irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> +	ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> +					0, "DPU", dpu);
> +	if (ret) {
> +		DRM_ERROR("failed to register dpu irq handler\n");
> +		return ret;
> +	}
> +
> +	init_waitqueue_head(&ctx->wait_queue);
> +
> +	return 0;
> +}
> +
> +static const struct sprd_dpu_ops sharkl3_dpu = {
> +	.core = &dpu_r2p0_core_ops,
> +};
> +
> +static const struct of_device_id dpu_match_table[] = {
> +	{ .compatible = "sprd,sharkl3-dpu",
> +	  .data = &sharkl3_dpu },
> +	{ /* sentinel */ },
> +};
> +
> +static int sprd_dpu_probe(struct platform_device *pdev)
> +{
> +	const struct sprd_dpu_ops *pdata;
> +	struct sprd_dpu *dpu;
> +	int ret;
> +
> +	dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> +	if (!dpu)
> +		return -ENOMEM;
> +
> +	pdata = of_device_get_match_data(&pdev->dev);
> +	if (pdata) {
> +		dpu->core = pdata->core;
> +	} else {
> +		DRM_ERROR("No matching driver data found\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = sprd_dpu_context_init(dpu, &pdev->dev);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, dpu);
> +
> +	return component_add(&pdev->dev, &dpu_component_ops);
> +}
> +
> +static int sprd_dpu_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &dpu_component_ops);
> +	return 0;
> +}
> +
> +struct platform_driver sprd_dpu_driver = {
> +	.probe = sprd_dpu_probe,
> +	.remove = sprd_dpu_remove,
> +	.driver = {
> +		.name = "sprd-dpu-drv",
> +		.of_match_table = dpu_match_table,
> +	},
> +};
> +
> +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> new file mode 100644
> index 0000000..7d3c5e4
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> @@ -0,0 +1,187 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#ifndef __SPRD_DPU_H__
> +#define __SPRD_DPU_H__
> +
> +#include <linux/bug.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_vblank.h>
> +#include <uapi/drm/drm_mode.h>
> +
> +#define BIT_DPU_INT_DONE_		BIT(0)
> +#define BIT_DPU_INT_TE			BIT(1)
> +#define BIT_DPU_INT_ERR			BIT(2)
> +#define BIT_DPU_INT_EDPI_TE		BIT(3)
> +#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
> +#define BIT_DPU_INT_VSYNC		BIT(5)
> +#define BIT_DPU_INT_WB_DONE		BIT(6)
> +#define BIT_DPU_INT_WB_ERR		BIT(7)
> +
> +#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
> +#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
> +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
> +#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
> +#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
> +#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
> +#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
> +#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
> +#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
> +
> +enum {
> +	SPRD_DPU_IF_DBI = 0,
> +	SPRD_DPU_IF_DPI,
> +	SPRD_DPU_IF_EDPI,
> +	SPRD_DPU_IF_LIMIT
> +};
> +
> +enum {
> +	DPU_LAYER_ROTATION_0,
> +	DPU_LAYER_ROTATION_90,
> +	DPU_LAYER_ROTATION_180,
> +	DPU_LAYER_ROTATION_270,
> +	DPU_LAYER_ROTATION_0_M,
> +	DPU_LAYER_ROTATION_90_M,
> +	DPU_LAYER_ROTATION_180_M,
> +	DPU_LAYER_ROTATION_270_M,
> +};
> +
> +struct dpu_layer {
> +	u8 index;
> +	u8 planes;
> +	u32 addr[4];
> +	u32 pitch[4];
> +	s16 src_x;
> +	s16 src_y;
> +	s16 src_w;
> +	s16 src_h;
> +	s16 dst_x;
> +	s16 dst_y;
> +	u16 dst_w;
> +	u16 dst_h;
> +	u32 format;
> +	u32 alpha;
> +	u32 blending;
> +	u32 rotation;
> +};
> +
> +/**
> + * Sprd DPU capability structure
> + *
> + * @max_layers: maximum number of layers available
> + * @fmts_ptr: A pointer to array of supported pixel formats
> + * @fmts_cnt: the number of format on @fmts_ptr
> + */
> +struct dpu_capability {
> +	u32 max_layers;
> +	const u32 *fmts_ptr;
> +	u32 fmts_cnt;
> +};
> +
> +/**
> + * Sprd DPU core callback ops
> + *
> + * This structure decribes the display controller common
> + * callback ops
> + *
> + * @init: initial DPU core
> + * @fini: cleanup DPU core
> + * @run: enable DPU output
> + * @stop: disable DPU output
> + * @enable_vsync: enable vblank interrupt
> + * @disable_vsync: disable vblank interrupt
> + * @isr: function pointer to the isr
> + * @ifconfig: initial DPI interface
> + * @flip: commit CRTC planes to DPU
> + * @capability: callback for DPU capabilities
> + */
> +struct dpu_context;
> +struct dpu_core_ops {
> +	void (*init)(struct dpu_context *ctx);
> +	void (*fini)(struct dpu_context *ctx);
> +	void (*run)(struct dpu_context *ctx);
> +	void (*stop)(struct dpu_context *ctx);
> +	void (*enable_vsync)(struct dpu_context *ctx);
> +	void (*disable_vsync)(struct dpu_context *ctx);
> +	u32 (*isr)(struct dpu_context *ctx);
> +	void (*ifconfig)(struct dpu_context *ctx);
> +	void (*flip)(struct dpu_context *ctx,
> +		     struct dpu_layer layers[], u8 count);
> +	void (*capability)(struct dpu_context *ctx,
> +			struct dpu_capability *cap);
> +};
> +
> +/**
> + * Sprd DPU context structure
> + *
> + * @base: DPU controller base address
> + * @irq: IRQ number to install the handler for
> + * @if_type: The type of DPI interface, default is DPI mode.
> + * @vm: videomode structure to use for DPU and DPI initialization
> + * @stopped: indicates whether DPU are stopped
> + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> + * DPU stop register done interrupt signal.
> + * @evt_update: wait queue condition for DPU shadow register
> + * @evt_stop: wait queue condition for DPU stop register
> + */
> +struct dpu_context {
> +	void __iomem *base;
> +	int irq;
> +	u8 if_type;
> +	struct videomode vm;
> +	bool stopped;
> +	wait_queue_head_t wait_queue;
> +	bool evt_update;
> +	bool evt_stop;
> +};
> +
> +/**
> + * Sprd DPU device structure
> + *
> + * @crtc: DRM crtc
> + * @ctx: A pointer to the DPU's implementation specific context
> + * @core: pointer to callbacks for DPU core functionality
> + * @layers: active DPU layers ready to commit
> + * @pending_planes: the number of layers on @layers
> + */
> +struct sprd_dpu {
> +	struct drm_crtc crtc;
> +	struct dpu_context ctx;
> +	const struct dpu_core_ops *core;
> +	struct dpu_layer *layers;
> +	u8 pending_planes;
> +};
> +
> +/**
> + * Sprd DPU H/W callback ops match table structure
> + * The structure used for matching a specific device callback ops
> + *
> + * @core: pointer to callbacks for DPU core functionality
> + */
> +struct sprd_dpu_ops {
> +	const struct dpu_core_ops *core;
> +};
> +
> +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> +{
> +	return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> +}
> +
> +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> +
> +#endif
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> index 4706185..200020f 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.c
> +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
>  
>  static struct platform_driver *sprd_drm_drivers[]  = {
>  	&sprd_drm_driver,
> +	&sprd_dpu_driver,
>  };
>  
>  static int __init sprd_drm_init(void)
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> index edf0881..3c32f3a 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.h
> +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> @@ -13,4 +13,6 @@ struct sprd_drm {
>  	struct drm_device *drm;
>  };
>  
> +extern struct platform_driver sprd_dpu_driver;
> +
>  #endif /* _SPRD_DRM_H_ */
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
@ 2020-07-28 21:13     ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 21:13 UTC (permalink / raw)
  To: Kevin Tang
  Cc: mark.rutland, airlied, zhang.lyra, linux-kernel, robh+dt,
	dri-devel, orsonzhai, sean

Hi Kevin.

Some feedback in the following.
I lost track of thing for the atomic modesettting stuff and I hope other
will review that.

	Sam

On Tue, Jul 28, 2020 at 06:07:57PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> 
> RFC v6:
>   - Access registers via readl/writel
>   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
>   - Remove always true checks for dpu core ops
> 
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  drivers/gpu/drm/sprd/Makefile       |   5 +-
>  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
>  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
>  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
>  7 files changed, 1346 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> 
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> index 86d95d9..88ab32a 100644
> --- a/drivers/gpu/drm/sprd/Makefile
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -2,4 +2,7 @@
>  
>  subdir-ccflags-y += -I$(srctree)/$(src)
Not needed - drop.

>  
> -obj-y := sprd_drm.o
> +obj-y := sprd_drm.o \
> +	sprd_dpu.o
> +
> +obj-y += dpu/

Until there are several DPU's there is no need for a separate directory.
Make it simpler by keeping it all in drm/sprd/*

> diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> new file mode 100644
> index 0000000..40278b6
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-y += dpu_r2p0.o
> diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> new file mode 100644
> index 0000000..4b9521d
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/wait.h>
> +#include <linux/workqueue.h>
> +
> +#include "sprd_dpu.h"
> +
> +/* DPU registers size, 4 Bytes(32 Bits) */
> +#define DPU_REG_SIZE	0x04
> +
> +/* Layer registers offset */
> +#define DPU_LAY_REG_OFFSET	0x0C
> +
> +#define DPU_LAY_REG(reg, index) \
> +	(reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
Use a static inline to get better typecheck.

> +#define DPU_REG_RD(reg) readl_relaxed(reg)
> +
> +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> +
> +#define DPU_REG_SET(reg, mask) \
> +	writel_relaxed(readl_relaxed(reg) | mask, reg)
> +
> +#define DPU_REG_CLR(reg, mask) \
> +	writel_relaxed(readl_relaxed(reg) & ~mask, reg)
I am no fan of macros used like this.

Maybe use static inlines and add struct dpu_context * as first argument.
Then adding base can be hidden away.


I had a hard time convincing myself that _relaxed was the right
variants. If I get it correct we may see writes re-ordered with the
_relaxed variants wich would be no good when dealign with interrupts.
But I may miss somethign here.

> +
> +/* Global control registers */
> +#define REG_DPU_CTRL	0x04
> +#define REG_DPU_CFG0	0x08
> +#define REG_DPU_CFG1	0x0C
> +#define REG_DPU_CFG2	0x10
> +#define REG_PANEL_SIZE	0x20
> +#define REG_BLEND_SIZE	0x24
> +#define REG_BG_COLOR	0x2C
> +
> +/* Layer0 control registers */
> +#define REG_LAY_BASE_ADDR0	0x30
> +#define REG_LAY_BASE_ADDR1	0x34
> +#define REG_LAY_BASE_ADDR2	0x38
> +#define REG_LAY_CTRL		0x40
> +#define REG_LAY_SIZE		0x44
> +#define REG_LAY_PITCH		0x48
> +#define REG_LAY_POS		0x4C
> +#define REG_LAY_ALPHA		0x50
> +#define REG_LAY_PALLETE		0x58
> +#define REG_LAY_CROP_START	0x5C
> +
> +/* Interrupt control registers */
> +#define REG_DPU_INT_EN		0x1E0
> +#define REG_DPU_INT_CLR		0x1E4
> +#define REG_DPU_INT_STS		0x1E8
> +
> +/* DPI control registers */
> +#define REG_DPI_CTRL		0x1F0
> +#define REG_DPI_H_TIMING	0x1F4
> +#define REG_DPI_V_TIMING	0x1F8
> +
> +/* MMU control registers */
> +#define REG_MMU_EN			0x800
> +#define REG_MMU_VPN_RANGE		0x80C
> +#define REG_MMU_VAOR_ADDR_RD		0x818
> +#define REG_MMU_VAOR_ADDR_WR		0x81C
> +#define REG_MMU_INV_ADDR_RD		0x820
> +#define REG_MMU_INV_ADDR_WR		0x824
> +#define REG_MMU_PPN1			0x83C
> +#define REG_MMU_RANGE1			0x840
> +#define REG_MMU_PPN2			0x844
> +#define REG_MMU_RANGE2			0x848
> +
> +/* Global control bits */
> +#define BIT_DPU_RUN			BIT(0)
> +#define BIT_DPU_STOP			BIT(1)
> +#define BIT_DPU_REG_UPDATE		BIT(2)
> +#define BIT_DPU_IF_EDPI			BIT(0)
> +#define BIT_DPU_COEF_NARROW_RANGE		BIT(4)
> +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD	BIT(5)
> +
> +/* Layer control bits */
> +#define BIT_DPU_LAY_EN				BIT(0)
> +
> +/* Interrupt control & status bits */
> +#define BIT_DPU_INT_DONE		BIT(0)
> +#define BIT_DPU_INT_TE			BIT(1)
> +#define BIT_DPU_INT_ERR			BIT(2)
> +#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
> +#define BIT_DPU_INT_VSYNC		BIT(5)
> +#define BIT_DPU_INT_FBC_PLD_ERR		BIT(8)
> +#define BIT_DPU_INT_FBC_HDR_ERR		BIT(9)
> +#define BIT_DPU_INT_MMU_VAOR_RD		BIT(16)
> +#define BIT_DPU_INT_MMU_VAOR_WR		BIT(17)
> +#define BIT_DPU_INT_MMU_INV_RD		BIT(18)
> +#define BIT_DPU_INT_MMU_INV_WR		BIT(19)
> +
> +/* DPI control bits */
> +#define BIT_DPU_EDPI_TE_EN		BIT(8)
> +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD	BIT(10)
> +#define BIT_DPU_DPI_HALT_EN		BIT(16)
> +
> +
> +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> +{
> +	u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> +			BIT_DPU_INT_MMU_VAOR_WR |
> +			BIT_DPU_INT_MMU_INV_RD |
> +			BIT_DPU_INT_MMU_INV_WR;
> +	u32 val = reg_val & mmu_mask;
> +	int i;
> +
> +	if (val) {
> +		DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);

General comment, applies for the whole patch.

Use drm_err(drm, ...), drm_info(drm, ...) etc.

Use upclassing to get sprd_dpu - and then you have a drm_device *
(After drm_device is embedded in sprd_dpu as suggested in the other
mail)

> +
> +		if (val & BIT_DPU_INT_MMU_INV_RD)
> +			DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> +		if (val & BIT_DPU_INT_MMU_INV_WR)
> +			DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> +		if (val & BIT_DPU_INT_MMU_VAOR_RD)
> +			DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> +		if (val & BIT_DPU_INT_MMU_VAOR_WR)
> +			DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> +				DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> +
> +		for (i = 0; i < 8; i++) {
> +			reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> +			if (reg_val & 0x1)
> +				DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> +					DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> +		}
> +	}
> +
> +	return val;
> +}
> +
> +static void dpu_clean_all(struct dpu_context *ctx)
> +{
> +	int i;
> +
> +	for (i = 0; i < 8; i++)
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> +}
> +
> +static u32 dpu_isr(struct dpu_context *ctx)
> +{
> +	u32 reg_val, int_mask = 0;
> +
> +	reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> +
> +	/* disable err interrupt */
> +	if (reg_val & BIT_DPU_INT_ERR)
> +		int_mask |= BIT_DPU_INT_ERR;
> +
> +	/* dpu update done isr */
> +	if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> +		ctx->evt_update = true;
> +		wake_up_interruptible_all(&ctx->wait_queue);
> +	}
> +
> +	/* dpu stop done isr */
> +	if (reg_val & BIT_DPU_INT_DONE) {
> +		ctx->evt_stop = true;
> +		wake_up_interruptible_all(&ctx->wait_queue);
> +	}
> +
> +	/* dpu ifbc payload error isr */
> +	if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> +		int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +		DRM_ERROR("dpu ifbc payload error\n");
> +	}
> +
> +	/* dpu ifbc header error isr */
> +	if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> +		int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +		DRM_ERROR("dpu ifbc header error\n");
> +	}
> +
> +	int_mask |= check_mmu_isr(ctx, reg_val);
> +
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> +	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> +
> +	return reg_val;
> +}
> +
> +static int dpu_wait_stop_done(struct dpu_context *ctx)
> +{
> +	int rc;
> +
> +	if (ctx->stopped)
> +		return 0;
> +
> +	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> +					       msecs_to_jiffies(500));
> +	ctx->evt_stop = false;
> +
> +	ctx->stopped = true;
> +
> +	if (!rc) {
> +		DRM_ERROR("dpu wait for stop done time out!\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dpu_wait_update_done(struct dpu_context *ctx)
> +{
> +	int rc;
> +
> +	ctx->evt_update = false;
> +
> +	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> +					       msecs_to_jiffies(500));
> +
> +	if (!rc) {
> +		DRM_ERROR("dpu wait for reg update done time out!\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static void dpu_stop(struct dpu_context *ctx)
> +{
> +	if (ctx->if_type == SPRD_DPU_IF_DPI)
> +		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> +
> +	dpu_wait_stop_done(ctx);
> +}
> +
> +static void dpu_run(struct dpu_context *ctx)
> +{
> +	DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +	ctx->stopped = false;
> +}
> +
> +static void dpu_init(struct dpu_context *ctx)
> +{
> +	u32 reg_val, size;
> +
> +	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> +
> +	DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> +	DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> +
> +	reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> +	DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> +	DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> +	DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> +
> +	if (ctx->stopped)
> +		dpu_clean_all(ctx);
> +
> +	DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> +	DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> +	DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> +	DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> +	DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> +	DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> +
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> +}
> +
> +static void dpu_fini(struct dpu_context *ctx)
> +{
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> +}
> +
> +static void dpu_layer(struct dpu_context *ctx,
> +		    struct dpu_layer *hwlayer)
No linebreak needed here.

> +{
> +	const struct drm_format_info *info;
> +	u32 size, offset, ctrl, pitch;
> +	int i;
> +
> +	offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> +
> +	if (hwlayer->src_w && hwlayer->src_h)
> +		size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> +	else
> +		size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> +
> +	for (i = 0; i < hwlayer->planes; i++)
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> +				hwlayer->index), hwlayer->addr[i]);
> +
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> +			hwlayer->index), offset);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> +			hwlayer->index), size);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> +			hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> +			hwlayer->index), hwlayer->alpha);
> +
> +	info = drm_format_info(hwlayer->format);
> +	if (hwlayer->planes == 3) {
> +		/* UV pitch is 1/2 of Y pitch*/
> +		pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> +				(hwlayer->pitch[0] / info->cpp[0] << 15);
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +				hwlayer->index), pitch);
> +	} else {
> +		pitch = hwlayer->pitch[0] / info->cpp[0];
> +		DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +				hwlayer->index), pitch);
> +	}
> +
> +	ctrl = hwlayer->format |
> +		hwlayer->blending |
> +		(hwlayer->rotation & 0x7) << 20;
> +
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +			hwlayer->index), ctrl);
> +	DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +			hwlayer->index), BIT_DPU_LAY_EN);
> +
> +	DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> +				hwlayer->dst_x, hwlayer->dst_y,
> +				hwlayer->dst_w, hwlayer->dst_h);
> +	DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> +				hwlayer->src_x, hwlayer->src_y,
> +				hwlayer->src_w, hwlayer->src_h);
> +}
> +
> +static void dpu_flip(struct dpu_context *ctx,
> +		     struct dpu_layer layers[], u8 count)
> +{
> +	int i;
> +	u32 reg_val;
> +
> +	/*
> +	 * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> +	 * registers in EDPI mode. So the config registers can only be
> +	 * updated in the rising edge of DPU_RUN bit.
> +	 */
> +	if (ctx->if_type == SPRD_DPU_IF_EDPI)
> +		dpu_wait_stop_done(ctx);
> +
> +	/* reset the bgcolor to black */
> +	DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +	/* disable all the layers */
> +	dpu_clean_all(ctx);
> +
> +	/* start configure dpu layers */
> +	for (i = 0; i < count; i++)
> +		dpu_layer(ctx, &layers[i]);
> +
> +	/* update trigger and wait */
> +	if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +		if (!ctx->stopped) {
> +			DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> +			dpu_wait_update_done(ctx);
> +		}
> +
> +		DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> +	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +		DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +		ctx->stopped = false;
> +	}
> +
> +	/*
> +	 * If the following interrupt was disabled in isr,
> +	 * re-enable it.
> +	 */
> +	reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> +		  BIT_DPU_INT_FBC_HDR_ERR |
> +		  BIT_DPU_INT_MMU_VAOR_RD |
> +		  BIT_DPU_INT_MMU_VAOR_WR |
> +		  BIT_DPU_INT_MMU_INV_RD |
> +		  BIT_DPU_INT_MMU_INV_WR;
> +	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> +
> +}
> +
> +static void dpu_dpi_init(struct dpu_context *ctx)
> +{
> +	u32 int_mask = 0;
> +	u32 reg_val;
> +
> +	if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +		/* use dpi as interface */
> +		DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +		/* disable Halt function for SPRD DSI */
> +		DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> +
> +		/* select te from external pad */
> +		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +		/* set dpi timing */
> +		reg_val = ctx->vm.hsync_len << 0 |
> +			  ctx->vm.hback_porch << 8 |
> +			  ctx->vm.hfront_porch << 20;
> +		DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> +
> +		reg_val = ctx->vm.vsync_len << 0 |
> +			  ctx->vm.vback_porch << 8 |
> +			  ctx->vm.vfront_porch << 20;
> +		DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> +
> +		if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> +			DRM_WARN("Warning: (vsync + vbp) < 32, "
> +				"underflow risk!\n");
> +
> +		/* enable dpu update done INT */
> +		int_mask |= BIT_DPU_INT_UPDATE_DONE;
> +		/* enable dpu DONE  INT */
> +		int_mask |= BIT_DPU_INT_DONE;
> +		/* enable dpu dpi vsync */
> +		int_mask |= BIT_DPU_INT_VSYNC;
> +		/* enable dpu TE INT */
> +		int_mask |= BIT_DPU_INT_TE;
> +		/* enable underflow err INT */
> +		int_mask |= BIT_DPU_INT_ERR;
> +	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +		/* use edpi as interface */
> +		DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +		/* use external te */
> +		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +		/* enable te */
> +		DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> +
> +		/* enable stop DONE INT */
> +		int_mask |= BIT_DPU_INT_DONE;
> +		/* enable TE INT */
> +		int_mask |= BIT_DPU_INT_TE;
> +	}
> +
> +	/* enable ifbc payload error INT */
> +	int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +	/* enable ifbc header error INT */
> +	int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +	/* enable iommu va out of range read error INT */
> +	int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> +	/* enable iommu va out of range write error INT */
> +	int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> +	/* enable iommu invalid read error INT */
> +	int_mask |= BIT_DPU_INT_MMU_INV_RD;
> +	/* enable iommu invalid write error INT */
> +	int_mask |= BIT_DPU_INT_MMU_INV_WR;
> +
> +	DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> +}
> +
> +static void enable_vsync(struct dpu_context *ctx)
> +{
> +	DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static void disable_vsync(struct dpu_context *ctx)
> +{
> +	DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static const u32 primary_fmts[] = {
> +	DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> +	DRM_FORMAT_YVU420,
> +};
One format per line improves readability a lot.
Maybe sort alphbetically too.

> +
> +static void dpu_capability(struct dpu_context *ctx,
> +			struct dpu_capability *cap)
Drop linebreak

> +{
> +	cap->max_layers = 6;
> +	cap->fmts_ptr = primary_fmts;
> +	cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> +}
> +
> +const struct dpu_core_ops dpu_r2p0_core_ops = {
> +	.init = dpu_init,
> +	.fini = dpu_fini,
> +	.run = dpu_run,
> +	.stop = dpu_stop,
> +	.isr = dpu_isr,
> +	.ifconfig = dpu_dpi_init,
> +	.capability = dpu_capability,
> +	.flip = dpu_flip,
> +	.enable_vsync = enable_vsync,
> +	.disable_vsync = disable_vsync,
> +};
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> new file mode 100644
> index 0000000..5ec8e7c
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> @@ -0,0 +1,646 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/dma-buf.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "sprd_drm.h"
> +#include "sprd_dpu.h"
> +
> +struct sprd_plane {
> +	struct drm_plane plane;
> +	u32 index;
> +	u32 addr[4];
> +	u32 pitch[4];
> +	u32 format;
> +	u32 rotation;
> +	u32 blend_mode;
> +};
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu);
> +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> +
> +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> +{
> +	return container_of(plane, struct sprd_plane, plane);
> +}
> +
> +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> +{
> +	switch (fourcc) {
> +	case DRM_FORMAT_BGRA8888:
> +		/* BGRA8888 -> ARGB8888 */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +		break;
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +		/* RGBA8888 -> ABGR8888 */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_ABGR8888:
> +		/* RB switch */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_ARGB8888:
> +		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +		break;
> +	case DRM_FORMAT_XBGR8888:
> +		/* RB switch */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_XRGB8888:
> +		*format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +		break;
> +	case DRM_FORMAT_BGR565:
> +		/* RB switch */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		/* FALLTHRU */
> +	case DRM_FORMAT_RGB565:
> +		*format |= BIT_DPU_LAY_FORMAT_RGB565;
> +		break;
> +	case DRM_FORMAT_NV12:
> +		/* 2-Lane: Yuv420 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_NO_SWITCH;
> +		break;
> +	case DRM_FORMAT_NV21:
> +		/* 2-Lane: Yuv420 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		break;
> +	case DRM_FORMAT_NV16:
> +		/* 2-Lane: Yuv422 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		break;
> +	case DRM_FORMAT_NV61:
> +		/* 2-Lane: Yuv422 */
> +		*format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_NO_SWITCH;
> +		break;
> +	case DRM_FORMAT_YUV420:
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_NO_SWITCH;
> +		break;
> +	case DRM_FORMAT_YVU420:
> +		*format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +		/* Y endian */
> +		*format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +		/* UV endian */
> +		*format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> +{
> +	switch (angle) {
> +	case DRM_MODE_ROTATE_0:
> +		*rotation = DPU_LAYER_ROTATION_0;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		*rotation = DPU_LAYER_ROTATION_90;
> +		break;
> +	case DRM_MODE_ROTATE_180:
> +		*rotation = DPU_LAYER_ROTATION_180;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		*rotation = DPU_LAYER_ROTATION_270;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		*rotation = DPU_LAYER_ROTATION_180_M;
> +		break;
> +	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> +		*rotation = DPU_LAYER_ROTATION_90_M;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		*rotation = DPU_LAYER_ROTATION_0_M;
> +		break;
> +	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> +		*rotation = DPU_LAYER_ROTATION_270_M;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sprd_plane_atomic_check(struct drm_plane *plane,
> +				  struct drm_plane_state *state)
> +{
> +	struct sprd_plane *p = to_sprd_plane(plane);
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *cma_obj;
> +	int i, ret;
> +	u32 addr;
> +
> +	if (!state->fb || !state->crtc)
> +		return 0;
> +
> +	ret = sprd_plane_format_convert(fb->format->format,
> +					&p->format);
> +	if (ret < 0) {
> +		DRM_ERROR("Invalid plane format\n");
> +		return ret;
> +	}
> +
> +	ret = sprd_plane_rotation_convert(state->rotation,
> +					&p->rotation);
> +	if (ret < 0) {
> +		DRM_ERROR("Invalid plane rotation\n");
> +		return ret;
> +	}
> +
> +	switch (state->pixel_blend_mode) {
> +	case DRM_MODE_BLEND_COVERAGE:
> +		/* alpha mode select - combo alpha */
> +		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +		/* Normal mode */
> +		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> +		break;
> +	case DRM_MODE_BLEND_PREMULTI:
> +		/* alpha mode select - combo alpha */
> +		p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +		/* Pre-mult mode */
> +		p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> +		break;
> +	case DRM_MODE_BLEND_PIXEL_NONE:
> +	default:
> +		/* don't do blending, maybe RGBX */
> +		/* alpha mode select - layer alpha */
> +		p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> +		break;
> +	}
> +
> +	for (i = 0; i < fb->format->num_planes; i++) {
> +		cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> +		addr = cma_obj->paddr + fb->offsets[i];
> +		if (addr % 16) {
> +			DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> +				i, addr);
> +			return -EFAULT;
> +		}
> +
> +		p->addr[i] = addr;
> +		p->pitch[i] = fb->pitches[i];
> +	}
> +
> +	return 0;
> +}
> +
> +static void sprd_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = plane->state->fb;
> +	struct sprd_plane *p = to_sprd_plane(plane);
> +	struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> +	struct dpu_layer *layer = &dpu->layers[p->index];
> +	int i;
> +
> +	if (!state->crtc || !state->fb)
> +		return;
> +
> +	layer->index = p->index;
> +	layer->src_x = state->src_x >> 16;
> +	layer->src_y = state->src_y >> 16;
> +	layer->src_w = state->src_w >> 16;
> +	layer->src_h = state->src_h >> 16;
> +	layer->dst_x = state->crtc_x;
> +	layer->dst_y = state->crtc_y;
> +	layer->dst_w = state->crtc_w;
> +	layer->dst_h = state->crtc_h;
> +	layer->alpha = state->alpha;
> +	layer->format = p->format;
> +	layer->blending = p->blend_mode;
> +	layer->rotation = p->rotation;
> +	layer->planes = fb->format->num_planes;
> +
> +	for (i = 0; i < layer->planes; i++) {
> +		layer->addr[i] = p->addr[i];
> +		layer->pitch[i] = p->pitch[i];
> +	}
> +
> +	dpu->pending_planes++;
> +}
> +
> +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> +{
> +	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				       BIT(DRM_MODE_BLEND_PREMULTI) |
> +				       BIT(DRM_MODE_BLEND_COVERAGE);
> +
> +	/* create rotation property */
> +	drm_plane_create_rotation_property(&p->plane,
> +					   DRM_MODE_ROTATE_0,
> +					   DRM_MODE_ROTATE_MASK |
> +					   DRM_MODE_REFLECT_MASK);
> +
> +	/* create alpha property */
> +	drm_plane_create_alpha_property(&p->plane);
> +
> +	/* create blend mode property */
> +	drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> +
> +	/* create zpos property */
> +	drm_plane_create_zpos_immutable_property(&p->plane, index);
> +}
> +
> +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> +	.atomic_check = sprd_plane_atomic_check,
> +	.atomic_update = sprd_plane_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs sprd_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> +					struct sprd_dpu *dpu)
> +{
> +	struct drm_plane *primary = NULL;
> +	struct sprd_plane *p = NULL;
> +	struct dpu_capability cap = {};
> +	int ret, i;
> +
> +	dpu->core->capability(&dpu->ctx, &cap);
> +
> +	dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> +				  sizeof(struct dpu_layer), GFP_KERNEL);
> +	if (!dpu->layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < cap.max_layers; i++) {
> +
> +		p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> +		if (!p)
> +			return ERR_PTR(-ENOMEM);
> +
> +		ret = drm_universal_plane_init(drm, &p->plane, 1,
> +					       &sprd_plane_funcs, cap.fmts_ptr,
> +					       cap.fmts_cnt, NULL,
> +					       DRM_PLANE_TYPE_PRIMARY, NULL);
> +		if (ret) {
> +			DRM_ERROR("fail to init primary plane\n");
> +			return ERR_PTR(ret);
> +		}
> +
> +		drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> +
> +		sprd_plane_create_properties(p, i);
> +
> +		p->index = i;
> +		if (i == 0)
> +			primary = &p->plane;
> +	}
> +
> +	return primary;
> +}
> +
> +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> +					const struct drm_display_mode *mode)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> +
> +	if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> +		drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> +
> +		if ((mode->hdisplay == mode->htotal) ||
> +		    (mode->vdisplay == mode->vtotal))
> +			dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> +		else
> +			dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> +				   struct drm_crtc_state *old_state)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	sprd_dpu_init(dpu);
> +
> +	enable_irq(dpu->ctx.irq);
> +}
> +
> +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *old_state)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +	struct drm_device *drm = dpu->crtc.dev;
> +
> +	disable_irq(dpu->ctx.irq);
> +
> +	sprd_dpu_fini(dpu);
> +
> +	spin_lock_irq(&drm->event_lock);
> +	if (crtc->state->event) {
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		crtc->state->event = NULL;
> +	}
> +	spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> +				 struct drm_crtc_state *state)
> +{
> +	DRM_DEBUG("%s()\n", __func__);
> +
> +	return 0;
> +}
> +
> +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> +
> +	dpu->pending_planes = 0;
> +}
> +
> +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +	struct drm_device *drm = dpu->crtc.dev;
> +
> +	if (dpu->pending_planes)
> +		dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> +
> +	spin_lock_irq(&drm->event_lock);
> +	if (crtc->state->event) {
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		crtc->state->event = NULL;
> +	}
> +	spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	dpu->core->enable_vsync(&dpu->ctx);
> +
> +	return 0;
> +}
> +
> +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +	dpu->core->disable_vsync(&dpu->ctx);
> +}
> +
> +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> +	.mode_valid	= sprd_crtc_mode_valid,
> +	.atomic_check	= sprd_crtc_atomic_check,
> +	.atomic_begin	= sprd_crtc_atomic_begin,
> +	.atomic_flush	= sprd_crtc_atomic_flush,
> +	.atomic_enable	= sprd_crtc_atomic_enable,
> +	.atomic_disable	= sprd_crtc_atomic_disable,
> +};
> +
> +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> +	.destroy	= drm_crtc_cleanup,
> +	.set_config	= drm_atomic_helper_set_config,
> +	.page_flip	= drm_atomic_helper_page_flip,
> +	.reset		= drm_atomic_helper_crtc_reset,
> +	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
> +	.enable_vblank	= sprd_crtc_enable_vblank,
> +	.disable_vblank	= sprd_crtc_disable_vblank,
> +};
> +
> +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> +			 struct drm_plane *primary)
> +{
> +	struct device_node *port;
> +	int ret;
> +
> +	/*
> +	 * set crtc port so that drm_of_find_possible_crtcs call works
> +	 */
> +	port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> +	if (!port) {
> +		DRM_ERROR("find 'ports' phandle of %s failed\n",
> +			  drm->dev->of_node->full_name);
> +		return -EINVAL;
> +	}
> +	of_node_put(port);
> +	crtc->port = port;
> +
> +	ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> +					&sprd_crtc_funcs, NULL);
> +	if (ret) {
> +		DRM_ERROR("failed to init crtc.\n");
> +		return ret;
> +	}
> +
> +	drm_mode_crtc_set_gamma_size(crtc, 256);
> +
> +	drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> +
> +	return 0;
> +}
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu)
> +{
> +	struct dpu_context *ctx = &dpu->ctx;
> +
> +	dpu->core->init(ctx);
> +	dpu->core->ifconfig(ctx);
> +}
> +
> +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> +{
> +	struct dpu_context *ctx = &dpu->ctx;
> +
> +	dpu->core->fini(ctx);
> +}
> +
> +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> +{
> +	struct sprd_dpu *dpu = data;
> +	struct dpu_context *ctx = &dpu->ctx;
> +	u32 int_mask = 0;
> +
> +	int_mask = dpu->core->isr(ctx);
> +
> +	if (int_mask & BIT_DPU_INT_ERR)
> +		DRM_WARN("Warning: dpu underflow!\n");
> +
> +	if (int_mask & BIT_DPU_INT_VSYNC)
> +		drm_crtc_handle_vblank(&dpu->crtc);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm = data;
> +	struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +	struct drm_plane *plane;
> +	int ret;
> +
> +	plane = sprd_plane_init(drm, dpu);
> +	if (IS_ERR_OR_NULL(plane)) {
> +		ret = PTR_ERR(plane);
> +		return ret;
> +	}
> +
> +	ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> +	void *data)
> +{
> +	struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +
> +	drm_crtc_cleanup(&dpu->crtc);
> +}
> +
> +static const struct component_ops dpu_component_ops = {
> +	.bind = sprd_dpu_bind,
> +	.unbind = sprd_dpu_unbind,
> +};
> +
> +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> +				struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct dpu_context *ctx = &dpu->ctx;
> +	struct resource *res;
> +	int ret;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> +	if (!ctx->base) {
> +		DRM_ERROR("failed to map dpu registers\n");
> +		return -EFAULT;
> +	}
> +
> +	ctx->irq = platform_get_irq(pdev, 0);
> +	if (ctx->irq < 0) {
> +		DRM_ERROR("failed to get dpu irq\n");
> +		return ctx->irq;
> +	}
> +
> +	irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> +	ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> +					0, "DPU", dpu);
> +	if (ret) {
> +		DRM_ERROR("failed to register dpu irq handler\n");
> +		return ret;
> +	}
> +
> +	init_waitqueue_head(&ctx->wait_queue);
> +
> +	return 0;
> +}
> +
> +static const struct sprd_dpu_ops sharkl3_dpu = {
> +	.core = &dpu_r2p0_core_ops,
> +};
> +
> +static const struct of_device_id dpu_match_table[] = {
> +	{ .compatible = "sprd,sharkl3-dpu",
> +	  .data = &sharkl3_dpu },
> +	{ /* sentinel */ },
> +};
> +
> +static int sprd_dpu_probe(struct platform_device *pdev)
> +{
> +	const struct sprd_dpu_ops *pdata;
> +	struct sprd_dpu *dpu;
> +	int ret;
> +
> +	dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> +	if (!dpu)
> +		return -ENOMEM;
> +
> +	pdata = of_device_get_match_data(&pdev->dev);
> +	if (pdata) {
> +		dpu->core = pdata->core;
> +	} else {
> +		DRM_ERROR("No matching driver data found\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = sprd_dpu_context_init(dpu, &pdev->dev);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, dpu);
> +
> +	return component_add(&pdev->dev, &dpu_component_ops);
> +}
> +
> +static int sprd_dpu_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &dpu_component_ops);
> +	return 0;
> +}
> +
> +struct platform_driver sprd_dpu_driver = {
> +	.probe = sprd_dpu_probe,
> +	.remove = sprd_dpu_remove,
> +	.driver = {
> +		.name = "sprd-dpu-drv",
> +		.of_match_table = dpu_match_table,
> +	},
> +};
> +
> +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> new file mode 100644
> index 0000000..7d3c5e4
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> @@ -0,0 +1,187 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#ifndef __SPRD_DPU_H__
> +#define __SPRD_DPU_H__
> +
> +#include <linux/bug.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_vblank.h>
> +#include <uapi/drm/drm_mode.h>
> +
> +#define BIT_DPU_INT_DONE_		BIT(0)
> +#define BIT_DPU_INT_TE			BIT(1)
> +#define BIT_DPU_INT_ERR			BIT(2)
> +#define BIT_DPU_INT_EDPI_TE		BIT(3)
> +#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
> +#define BIT_DPU_INT_VSYNC		BIT(5)
> +#define BIT_DPU_INT_WB_DONE		BIT(6)
> +#define BIT_DPU_INT_WB_ERR		BIT(7)
> +
> +#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
> +#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
> +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
> +#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
> +#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
> +#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
> +#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
> +#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
> +#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
> +
> +enum {
> +	SPRD_DPU_IF_DBI = 0,
> +	SPRD_DPU_IF_DPI,
> +	SPRD_DPU_IF_EDPI,
> +	SPRD_DPU_IF_LIMIT
> +};
> +
> +enum {
> +	DPU_LAYER_ROTATION_0,
> +	DPU_LAYER_ROTATION_90,
> +	DPU_LAYER_ROTATION_180,
> +	DPU_LAYER_ROTATION_270,
> +	DPU_LAYER_ROTATION_0_M,
> +	DPU_LAYER_ROTATION_90_M,
> +	DPU_LAYER_ROTATION_180_M,
> +	DPU_LAYER_ROTATION_270_M,
> +};
> +
> +struct dpu_layer {
> +	u8 index;
> +	u8 planes;
> +	u32 addr[4];
> +	u32 pitch[4];
> +	s16 src_x;
> +	s16 src_y;
> +	s16 src_w;
> +	s16 src_h;
> +	s16 dst_x;
> +	s16 dst_y;
> +	u16 dst_w;
> +	u16 dst_h;
> +	u32 format;
> +	u32 alpha;
> +	u32 blending;
> +	u32 rotation;
> +};
> +
> +/**
> + * Sprd DPU capability structure
> + *
> + * @max_layers: maximum number of layers available
> + * @fmts_ptr: A pointer to array of supported pixel formats
> + * @fmts_cnt: the number of format on @fmts_ptr
> + */
> +struct dpu_capability {
> +	u32 max_layers;
> +	const u32 *fmts_ptr;
> +	u32 fmts_cnt;
> +};
> +
> +/**
> + * Sprd DPU core callback ops
> + *
> + * This structure decribes the display controller common
> + * callback ops
> + *
> + * @init: initial DPU core
> + * @fini: cleanup DPU core
> + * @run: enable DPU output
> + * @stop: disable DPU output
> + * @enable_vsync: enable vblank interrupt
> + * @disable_vsync: disable vblank interrupt
> + * @isr: function pointer to the isr
> + * @ifconfig: initial DPI interface
> + * @flip: commit CRTC planes to DPU
> + * @capability: callback for DPU capabilities
> + */
> +struct dpu_context;
> +struct dpu_core_ops {
> +	void (*init)(struct dpu_context *ctx);
> +	void (*fini)(struct dpu_context *ctx);
> +	void (*run)(struct dpu_context *ctx);
> +	void (*stop)(struct dpu_context *ctx);
> +	void (*enable_vsync)(struct dpu_context *ctx);
> +	void (*disable_vsync)(struct dpu_context *ctx);
> +	u32 (*isr)(struct dpu_context *ctx);
> +	void (*ifconfig)(struct dpu_context *ctx);
> +	void (*flip)(struct dpu_context *ctx,
> +		     struct dpu_layer layers[], u8 count);
> +	void (*capability)(struct dpu_context *ctx,
> +			struct dpu_capability *cap);
> +};
> +
> +/**
> + * Sprd DPU context structure
> + *
> + * @base: DPU controller base address
> + * @irq: IRQ number to install the handler for
> + * @if_type: The type of DPI interface, default is DPI mode.
> + * @vm: videomode structure to use for DPU and DPI initialization
> + * @stopped: indicates whether DPU are stopped
> + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> + * DPU stop register done interrupt signal.
> + * @evt_update: wait queue condition for DPU shadow register
> + * @evt_stop: wait queue condition for DPU stop register
> + */
> +struct dpu_context {
> +	void __iomem *base;
> +	int irq;
> +	u8 if_type;
> +	struct videomode vm;
> +	bool stopped;
> +	wait_queue_head_t wait_queue;
> +	bool evt_update;
> +	bool evt_stop;
> +};
> +
> +/**
> + * Sprd DPU device structure
> + *
> + * @crtc: DRM crtc
> + * @ctx: A pointer to the DPU's implementation specific context
> + * @core: pointer to callbacks for DPU core functionality
> + * @layers: active DPU layers ready to commit
> + * @pending_planes: the number of layers on @layers
> + */
> +struct sprd_dpu {
> +	struct drm_crtc crtc;
> +	struct dpu_context ctx;
> +	const struct dpu_core_ops *core;
> +	struct dpu_layer *layers;
> +	u8 pending_planes;
> +};
> +
> +/**
> + * Sprd DPU H/W callback ops match table structure
> + * The structure used for matching a specific device callback ops
> + *
> + * @core: pointer to callbacks for DPU core functionality
> + */
> +struct sprd_dpu_ops {
> +	const struct dpu_core_ops *core;
> +};
> +
> +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> +{
> +	return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> +}
> +
> +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> +
> +#endif
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> index 4706185..200020f 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.c
> +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
>  
>  static struct platform_driver *sprd_drm_drivers[]  = {
>  	&sprd_drm_driver,
> +	&sprd_dpu_driver,
>  };
>  
>  static int __init sprd_drm_init(void)
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> index edf0881..3c32f3a 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.h
> +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> @@ -13,4 +13,6 @@ struct sprd_drm {
>  	struct drm_device *drm;
>  };
>  
> +extern struct platform_driver sprd_dpu_driver;
> +
>  #endif /* _SPRD_DRM_H_ */
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 0/6] Add Unisoc's drm kms module
  2020-07-28 10:07 ` Kevin Tang
@ 2020-07-28 21:20   ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 21:20 UTC (permalink / raw)
  To: Kevin Tang
  Cc: maarten.lankhorst, mripard, sean, airlied, daniel, robh+dt,
	mark.rutland, orsonzhai, linux-kernel, dri-devel, zhang.lyra

Hi Kevin.

Thanks for submitting this set of drivers.

To better review the pataches can you please give some kind of high
level overview.

An ascii block diagram that identifies all the relevant blocks and how
they relate would be great.

This makes it easier to verify if the right modelling is used.

	Sam

On Tue, Jul 28, 2020 at 06:07:53PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> ChangeList:
> v1:
> 1. only upstream modeset and atomic at first commit. 
> 2. remove some unused code;
> 3. use alpha and blend_mode properties;
> 3. add yaml support;
> 4. remove auto-adaptive panel driver;
> 5. bugfix
> 
> v2:
> 1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
> 2. remove gem drivers, use generic CMA handlers
> 3. remove redundant "module_init", all the sub modules loading by KMS
> 
> v3:
> 1. multi crtc&encoder design have problem, so rollback to v1
> 
> v4:
> 1. update to gcc-linaro-7.5.0
> 2. update to Linux 5.6-rc3
> 3. remove pm_runtime support
> 4. add COMPILE_TEST, remove unused kconfig
> 5. "drm_dev_put" on drm_unbind
> 6. fix some naming convention issue
> 7. remove semaphore lock for crtc flip
> 8. remove static variables
> 
> v5:
> 1. optimize encoder and connector code implementation
> 2. use "platform_get_irq" and "platform_get_resource"
> 3. drop useless function return type, drop unless debug log
> 4. custom properties should be separate, so drop it
> 5. use DRM_XXX replase pr_xxx
> 6. drop dsi&dphy hal callback ops
> 7. drop unless callback ops checking
> 8. add comments for sprd dpu structure
> 
> v6:
> 1. Access registers via readl/writel
> 2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> 3. Remove always true checks for dpu core ops
> 
> Kevin Tang (6):
>   dt-bindings: display: add Unisoc's drm master bindings
>   drm/sprd: add Unisoc's drm kms master
>   dt-bindings: display: add Unisoc's dpu bindings
>   drm/sprd: add Unisoc's drm display controller driver
>   dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
>   drm/sprd: add Unisoc's drm mipi dsi&dphy driver
> 
>  .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
>  .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
>  .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
>  .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
>  drivers/gpu/drm/Kconfig                            |    2 +
>  drivers/gpu/drm/Makefile                           |    1 +
>  drivers/gpu/drm/sprd/Kconfig                       |   12 +
>  drivers/gpu/drm/sprd/Makefile                      |   13 +
>  drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
>  drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
>  drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
>  drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
>  drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
>  drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
>  drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
>  drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
>  drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
>  drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
>  drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
>  drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
>  drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
>  drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
>  drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
>  33 files changed, 7074 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
>  create mode 100644 drivers/gpu/drm/sprd/Kconfig
>  create mode 100644 drivers/gpu/drm/sprd/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
>  create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
> 
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 0/6] Add Unisoc's drm kms module
@ 2020-07-28 21:20   ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-07-28 21:20 UTC (permalink / raw)
  To: Kevin Tang
  Cc: mark.rutland, airlied, zhang.lyra, linux-kernel, robh+dt,
	dri-devel, orsonzhai, sean

Hi Kevin.

Thanks for submitting this set of drivers.

To better review the pataches can you please give some kind of high
level overview.

An ascii block diagram that identifies all the relevant blocks and how
they relate would be great.

This makes it easier to verify if the right modelling is used.

	Sam

On Tue, Jul 28, 2020 at 06:07:53PM +0800, Kevin Tang wrote:
> From: Kevin Tang <kevin.tang@unisoc.com>
> 
> ChangeList:
> v1:
> 1. only upstream modeset and atomic at first commit. 
> 2. remove some unused code;
> 3. use alpha and blend_mode properties;
> 3. add yaml support;
> 4. remove auto-adaptive panel driver;
> 5. bugfix
> 
> v2:
> 1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
> 2. remove gem drivers, use generic CMA handlers
> 3. remove redundant "module_init", all the sub modules loading by KMS
> 
> v3:
> 1. multi crtc&encoder design have problem, so rollback to v1
> 
> v4:
> 1. update to gcc-linaro-7.5.0
> 2. update to Linux 5.6-rc3
> 3. remove pm_runtime support
> 4. add COMPILE_TEST, remove unused kconfig
> 5. "drm_dev_put" on drm_unbind
> 6. fix some naming convention issue
> 7. remove semaphore lock for crtc flip
> 8. remove static variables
> 
> v5:
> 1. optimize encoder and connector code implementation
> 2. use "platform_get_irq" and "platform_get_resource"
> 3. drop useless function return type, drop unless debug log
> 4. custom properties should be separate, so drop it
> 5. use DRM_XXX replase pr_xxx
> 6. drop dsi&dphy hal callback ops
> 7. drop unless callback ops checking
> 8. add comments for sprd dpu structure
> 
> v6:
> 1. Access registers via readl/writel
> 2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> 3. Remove always true checks for dpu core ops
> 
> Kevin Tang (6):
>   dt-bindings: display: add Unisoc's drm master bindings
>   drm/sprd: add Unisoc's drm kms master
>   dt-bindings: display: add Unisoc's dpu bindings
>   drm/sprd: add Unisoc's drm display controller driver
>   dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
>   drm/sprd: add Unisoc's drm mipi dsi&dphy driver
> 
>  .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
>  .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
>  .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
>  .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
>  drivers/gpu/drm/Kconfig                            |    2 +
>  drivers/gpu/drm/Makefile                           |    1 +
>  drivers/gpu/drm/sprd/Kconfig                       |   12 +
>  drivers/gpu/drm/sprd/Makefile                      |   13 +
>  drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
>  drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
>  drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
>  drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
>  drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
>  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
>  drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
>  drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
>  drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
>  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
>  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
>  drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
>  drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
>  drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
>  drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
>  drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
>  drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
>  drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
>  33 files changed, 7074 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
>  create mode 100644 drivers/gpu/drm/sprd/Kconfig
>  create mode 100644 drivers/gpu/drm/sprd/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
>  create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
>  create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
>  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
> 
> -- 
> 2.7.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 10:07   ` Kevin Tang
@ 2020-07-28 21:51     ` Daniel Vetter
  -1 siblings, 0 replies; 50+ messages in thread
From: Daniel Vetter @ 2020-07-28 21:51 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, Dave Airlie,
	Rob Herring, Mark Rutland, orsonzhai, zhang.lyra,
	Linux Kernel Mailing List, dri-devel

On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
>
> From: Kevin Tang <kevin.tang@unisoc.com>
>
> Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
>
> RFC v6:
>   - Access registers via readl/writel
>   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
>   - Remove always true checks for dpu core ops
>
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>

Quickly scrolled through this, and the entire thing very much leaves a
midlayer heavy aftertaste. Do we really need stuff like struct dpu_layer
and struct dpu_core_ops? They only seem to complicate the code base, and
seem to have no real reason. The indirection with first computing register
values into a sprd_plane/crtc structure, and then writing it into hardware
is also a bit much - I recommend to only do that if you have to compute
values in _check to validate them, so that the computation doesn't have to
be repeated in the commit phase functions.

Also, the layer and pending_flips stuff in sprd_dpu don't work with
atomic, that races. You have to put all that stuff into state objects, or
if it's some data shared with interrupt handlers (doesn't seem to be the
case here), it needs its own locking, and any data you need in the
interrupt handler must be copied over.

Also no devm_kzalloc for anything containined a drm_* structure, that's
the wrong lifetime.

So yeah high level review is that I think this driver would benefit a lot
from a pile of demidlayer.

Cheers, Daniel

> ---
>  drivers/gpu/drm/sprd/Makefile       |   5 +-
>  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
>  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
>  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
>  7 files changed, 1346 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
>
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> index 86d95d9..88ab32a 100644
> --- a/drivers/gpu/drm/sprd/Makefile
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -2,4 +2,7 @@
>
>  subdir-ccflags-y += -I$(srctree)/$(src)
>
> -obj-y := sprd_drm.o
> +obj-y := sprd_drm.o \
> +       sprd_dpu.o
> +
> +obj-y += dpu/
> diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> new file mode 100644
> index 0000000..40278b6
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-y += dpu_r2p0.o
> diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> new file mode 100644
> index 0000000..4b9521d
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/wait.h>
> +#include <linux/workqueue.h>
> +
> +#include "sprd_dpu.h"
> +
> +/* DPU registers size, 4 Bytes(32 Bits) */
> +#define DPU_REG_SIZE   0x04
> +
> +/* Layer registers offset */
> +#define DPU_LAY_REG_OFFSET     0x0C
> +
> +#define DPU_LAY_REG(reg, index) \
> +       (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> +
> +#define DPU_REG_RD(reg) readl_relaxed(reg)
> +
> +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> +
> +#define DPU_REG_SET(reg, mask) \
> +       writel_relaxed(readl_relaxed(reg) | mask, reg)
> +
> +#define DPU_REG_CLR(reg, mask) \
> +       writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> +
> +/* Global control registers */
> +#define REG_DPU_CTRL   0x04
> +#define REG_DPU_CFG0   0x08
> +#define REG_DPU_CFG1   0x0C
> +#define REG_DPU_CFG2   0x10
> +#define REG_PANEL_SIZE 0x20
> +#define REG_BLEND_SIZE 0x24
> +#define REG_BG_COLOR   0x2C
> +
> +/* Layer0 control registers */
> +#define REG_LAY_BASE_ADDR0     0x30
> +#define REG_LAY_BASE_ADDR1     0x34
> +#define REG_LAY_BASE_ADDR2     0x38
> +#define REG_LAY_CTRL           0x40
> +#define REG_LAY_SIZE           0x44
> +#define REG_LAY_PITCH          0x48
> +#define REG_LAY_POS            0x4C
> +#define REG_LAY_ALPHA          0x50
> +#define REG_LAY_PALLETE                0x58
> +#define REG_LAY_CROP_START     0x5C
> +
> +/* Interrupt control registers */
> +#define REG_DPU_INT_EN         0x1E0
> +#define REG_DPU_INT_CLR                0x1E4
> +#define REG_DPU_INT_STS                0x1E8
> +
> +/* DPI control registers */
> +#define REG_DPI_CTRL           0x1F0
> +#define REG_DPI_H_TIMING       0x1F4
> +#define REG_DPI_V_TIMING       0x1F8
> +
> +/* MMU control registers */
> +#define REG_MMU_EN                     0x800
> +#define REG_MMU_VPN_RANGE              0x80C
> +#define REG_MMU_VAOR_ADDR_RD           0x818
> +#define REG_MMU_VAOR_ADDR_WR           0x81C
> +#define REG_MMU_INV_ADDR_RD            0x820
> +#define REG_MMU_INV_ADDR_WR            0x824
> +#define REG_MMU_PPN1                   0x83C
> +#define REG_MMU_RANGE1                 0x840
> +#define REG_MMU_PPN2                   0x844
> +#define REG_MMU_RANGE2                 0x848
> +
> +/* Global control bits */
> +#define BIT_DPU_RUN                    BIT(0)
> +#define BIT_DPU_STOP                   BIT(1)
> +#define BIT_DPU_REG_UPDATE             BIT(2)
> +#define BIT_DPU_IF_EDPI                        BIT(0)
> +#define BIT_DPU_COEF_NARROW_RANGE              BIT(4)
> +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD       BIT(5)
> +
> +/* Layer control bits */
> +#define BIT_DPU_LAY_EN                         BIT(0)
> +
> +/* Interrupt control & status bits */
> +#define BIT_DPU_INT_DONE               BIT(0)
> +#define BIT_DPU_INT_TE                 BIT(1)
> +#define BIT_DPU_INT_ERR                        BIT(2)
> +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> +#define BIT_DPU_INT_VSYNC              BIT(5)
> +#define BIT_DPU_INT_FBC_PLD_ERR                BIT(8)
> +#define BIT_DPU_INT_FBC_HDR_ERR                BIT(9)
> +#define BIT_DPU_INT_MMU_VAOR_RD                BIT(16)
> +#define BIT_DPU_INT_MMU_VAOR_WR                BIT(17)
> +#define BIT_DPU_INT_MMU_INV_RD         BIT(18)
> +#define BIT_DPU_INT_MMU_INV_WR         BIT(19)
> +
> +/* DPI control bits */
> +#define BIT_DPU_EDPI_TE_EN             BIT(8)
> +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10)
> +#define BIT_DPU_DPI_HALT_EN            BIT(16)
> +
> +
> +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> +{
> +       u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> +                       BIT_DPU_INT_MMU_VAOR_WR |
> +                       BIT_DPU_INT_MMU_INV_RD |
> +                       BIT_DPU_INT_MMU_INV_WR;
> +       u32 val = reg_val & mmu_mask;
> +       int i;
> +
> +       if (val) {
> +               DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
> +
> +               if (val & BIT_DPU_INT_MMU_INV_RD)
> +                       DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> +               if (val & BIT_DPU_INT_MMU_INV_WR)
> +                       DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> +               if (val & BIT_DPU_INT_MMU_VAOR_RD)
> +                       DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> +               if (val & BIT_DPU_INT_MMU_VAOR_WR)
> +                       DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> +
> +               for (i = 0; i < 8; i++) {
> +                       reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> +                       if (reg_val & 0x1)
> +                               DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> +               }
> +       }
> +
> +       return val;
> +}
> +
> +static void dpu_clean_all(struct dpu_context *ctx)
> +{
> +       int i;
> +
> +       for (i = 0; i < 8; i++)
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> +}
> +
> +static u32 dpu_isr(struct dpu_context *ctx)
> +{
> +       u32 reg_val, int_mask = 0;
> +
> +       reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> +
> +       /* disable err interrupt */
> +       if (reg_val & BIT_DPU_INT_ERR)
> +               int_mask |= BIT_DPU_INT_ERR;
> +
> +       /* dpu update done isr */
> +       if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> +               ctx->evt_update = true;
> +               wake_up_interruptible_all(&ctx->wait_queue);
> +       }
> +
> +       /* dpu stop done isr */
> +       if (reg_val & BIT_DPU_INT_DONE) {
> +               ctx->evt_stop = true;
> +               wake_up_interruptible_all(&ctx->wait_queue);
> +       }
> +
> +       /* dpu ifbc payload error isr */
> +       if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> +               int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +               DRM_ERROR("dpu ifbc payload error\n");
> +       }
> +
> +       /* dpu ifbc header error isr */
> +       if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> +               int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +               DRM_ERROR("dpu ifbc header error\n");
> +       }
> +
> +       int_mask |= check_mmu_isr(ctx, reg_val);
> +
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> +
> +       return reg_val;
> +}
> +
> +static int dpu_wait_stop_done(struct dpu_context *ctx)
> +{
> +       int rc;
> +
> +       if (ctx->stopped)
> +               return 0;
> +
> +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> +                                              msecs_to_jiffies(500));
> +       ctx->evt_stop = false;
> +
> +       ctx->stopped = true;
> +
> +       if (!rc) {
> +               DRM_ERROR("dpu wait for stop done time out!\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +
> +static int dpu_wait_update_done(struct dpu_context *ctx)
> +{
> +       int rc;
> +
> +       ctx->evt_update = false;
> +
> +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> +                                              msecs_to_jiffies(500));
> +
> +       if (!rc) {
> +               DRM_ERROR("dpu wait for reg update done time out!\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +
> +static void dpu_stop(struct dpu_context *ctx)
> +{
> +       if (ctx->if_type == SPRD_DPU_IF_DPI)
> +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> +
> +       dpu_wait_stop_done(ctx);
> +}
> +
> +static void dpu_run(struct dpu_context *ctx)
> +{
> +       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +       ctx->stopped = false;
> +}
> +
> +static void dpu_init(struct dpu_context *ctx)
> +{
> +       u32 reg_val, size;
> +
> +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +       size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> +
> +       DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> +       DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> +
> +       reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> +       DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> +       DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> +       DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> +
> +       if (ctx->stopped)
> +               dpu_clean_all(ctx);
> +
> +       DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> +       DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> +       DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> +       DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> +       DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> +       DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> +
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> +}
> +
> +static void dpu_fini(struct dpu_context *ctx)
> +{
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> +}
> +
> +static void dpu_layer(struct dpu_context *ctx,
> +                   struct dpu_layer *hwlayer)
> +{
> +       const struct drm_format_info *info;
> +       u32 size, offset, ctrl, pitch;
> +       int i;
> +
> +       offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> +
> +       if (hwlayer->src_w && hwlayer->src_h)
> +               size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> +       else
> +               size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> +
> +       for (i = 0; i < hwlayer->planes; i++)
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> +                               hwlayer->index), hwlayer->addr[i]);
> +
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> +                       hwlayer->index), offset);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> +                       hwlayer->index), size);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> +                       hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> +                       hwlayer->index), hwlayer->alpha);
> +
> +       info = drm_format_info(hwlayer->format);
> +       if (hwlayer->planes == 3) {
> +               /* UV pitch is 1/2 of Y pitch*/
> +               pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> +                               (hwlayer->pitch[0] / info->cpp[0] << 15);
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +                               hwlayer->index), pitch);
> +       } else {
> +               pitch = hwlayer->pitch[0] / info->cpp[0];
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +                               hwlayer->index), pitch);
> +       }
> +
> +       ctrl = hwlayer->format |
> +               hwlayer->blending |
> +               (hwlayer->rotation & 0x7) << 20;
> +
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +                       hwlayer->index), ctrl);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +                       hwlayer->index), BIT_DPU_LAY_EN);
> +
> +       DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> +                               hwlayer->dst_x, hwlayer->dst_y,
> +                               hwlayer->dst_w, hwlayer->dst_h);
> +       DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> +                               hwlayer->src_x, hwlayer->src_y,
> +                               hwlayer->src_w, hwlayer->src_h);
> +}
> +
> +static void dpu_flip(struct dpu_context *ctx,
> +                    struct dpu_layer layers[], u8 count)
> +{
> +       int i;
> +       u32 reg_val;
> +
> +       /*
> +        * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> +        * registers in EDPI mode. So the config registers can only be
> +        * updated in the rising edge of DPU_RUN bit.
> +        */
> +       if (ctx->if_type == SPRD_DPU_IF_EDPI)
> +               dpu_wait_stop_done(ctx);
> +
> +       /* reset the bgcolor to black */
> +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +       /* disable all the layers */
> +       dpu_clean_all(ctx);
> +
> +       /* start configure dpu layers */
> +       for (i = 0; i < count; i++)
> +               dpu_layer(ctx, &layers[i]);
> +
> +       /* update trigger and wait */
> +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +               if (!ctx->stopped) {
> +                       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> +                       dpu_wait_update_done(ctx);
> +               }
> +
> +               DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +               ctx->stopped = false;
> +       }
> +
> +       /*
> +        * If the following interrupt was disabled in isr,
> +        * re-enable it.
> +        */
> +       reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> +                 BIT_DPU_INT_FBC_HDR_ERR |
> +                 BIT_DPU_INT_MMU_VAOR_RD |
> +                 BIT_DPU_INT_MMU_VAOR_WR |
> +                 BIT_DPU_INT_MMU_INV_RD |
> +                 BIT_DPU_INT_MMU_INV_WR;
> +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> +
> +}
> +
> +static void dpu_dpi_init(struct dpu_context *ctx)
> +{
> +       u32 int_mask = 0;
> +       u32 reg_val;
> +
> +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +               /* use dpi as interface */
> +               DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +               /* disable Halt function for SPRD DSI */
> +               DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> +
> +               /* select te from external pad */
> +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +               /* set dpi timing */
> +               reg_val = ctx->vm.hsync_len << 0 |
> +                         ctx->vm.hback_porch << 8 |
> +                         ctx->vm.hfront_porch << 20;
> +               DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> +
> +               reg_val = ctx->vm.vsync_len << 0 |
> +                         ctx->vm.vback_porch << 8 |
> +                         ctx->vm.vfront_porch << 20;
> +               DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> +
> +               if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> +                       DRM_WARN("Warning: (vsync + vbp) < 32, "
> +                               "underflow risk!\n");
> +
> +               /* enable dpu update done INT */
> +               int_mask |= BIT_DPU_INT_UPDATE_DONE;
> +               /* enable dpu DONE  INT */
> +               int_mask |= BIT_DPU_INT_DONE;
> +               /* enable dpu dpi vsync */
> +               int_mask |= BIT_DPU_INT_VSYNC;
> +               /* enable dpu TE INT */
> +               int_mask |= BIT_DPU_INT_TE;
> +               /* enable underflow err INT */
> +               int_mask |= BIT_DPU_INT_ERR;
> +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +               /* use edpi as interface */
> +               DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +               /* use external te */
> +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +               /* enable te */
> +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> +
> +               /* enable stop DONE INT */
> +               int_mask |= BIT_DPU_INT_DONE;
> +               /* enable TE INT */
> +               int_mask |= BIT_DPU_INT_TE;
> +       }
> +
> +       /* enable ifbc payload error INT */
> +       int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +       /* enable ifbc header error INT */
> +       int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +       /* enable iommu va out of range read error INT */
> +       int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> +       /* enable iommu va out of range write error INT */
> +       int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> +       /* enable iommu invalid read error INT */
> +       int_mask |= BIT_DPU_INT_MMU_INV_RD;
> +       /* enable iommu invalid write error INT */
> +       int_mask |= BIT_DPU_INT_MMU_INV_WR;
> +
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> +}
> +
> +static void enable_vsync(struct dpu_context *ctx)
> +{
> +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static void disable_vsync(struct dpu_context *ctx)
> +{
> +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static const u32 primary_fmts[] = {
> +       DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> +       DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> +       DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> +       DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> +       DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> +       DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> +       DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> +       DRM_FORMAT_YVU420,
> +};
> +
> +static void dpu_capability(struct dpu_context *ctx,
> +                       struct dpu_capability *cap)
> +{
> +       cap->max_layers = 6;
> +       cap->fmts_ptr = primary_fmts;
> +       cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> +}
> +
> +const struct dpu_core_ops dpu_r2p0_core_ops = {
> +       .init = dpu_init,
> +       .fini = dpu_fini,
> +       .run = dpu_run,
> +       .stop = dpu_stop,
> +       .isr = dpu_isr,
> +       .ifconfig = dpu_dpi_init,
> +       .capability = dpu_capability,
> +       .flip = dpu_flip,
> +       .enable_vsync = enable_vsync,
> +       .disable_vsync = disable_vsync,
> +};
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> new file mode 100644
> index 0000000..5ec8e7c
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> @@ -0,0 +1,646 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/dma-buf.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "sprd_drm.h"
> +#include "sprd_dpu.h"
> +
> +struct sprd_plane {
> +       struct drm_plane plane;
> +       u32 index;
> +       u32 addr[4];
> +       u32 pitch[4];
> +       u32 format;
> +       u32 rotation;
> +       u32 blend_mode;
> +};
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu);
> +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> +
> +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> +{
> +       return container_of(plane, struct sprd_plane, plane);
> +}
> +
> +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> +{
> +       switch (fourcc) {
> +       case DRM_FORMAT_BGRA8888:
> +               /* BGRA8888 -> ARGB8888 */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +               break;
> +       case DRM_FORMAT_RGBX8888:
> +       case DRM_FORMAT_RGBA8888:
> +               /* RGBA8888 -> ABGR8888 */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_ABGR8888:
> +               /* RB switch */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_ARGB8888:
> +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +               break;
> +       case DRM_FORMAT_XBGR8888:
> +               /* RB switch */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_XRGB8888:
> +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +               break;
> +       case DRM_FORMAT_BGR565:
> +               /* RB switch */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_RGB565:
> +               *format |= BIT_DPU_LAY_FORMAT_RGB565;
> +               break;
> +       case DRM_FORMAT_NV12:
> +               /* 2-Lane: Yuv420 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_NO_SWITCH;
> +               break;
> +       case DRM_FORMAT_NV21:
> +               /* 2-Lane: Yuv420 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               break;
> +       case DRM_FORMAT_NV16:
> +               /* 2-Lane: Yuv422 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               break;
> +       case DRM_FORMAT_NV61:
> +               /* 2-Lane: Yuv422 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_NO_SWITCH;
> +               break;
> +       case DRM_FORMAT_YUV420:
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_NO_SWITCH;
> +               break;
> +       case DRM_FORMAT_YVU420:
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> +{
> +       switch (angle) {
> +       case DRM_MODE_ROTATE_0:
> +               *rotation = DPU_LAYER_ROTATION_0;
> +               break;
> +       case DRM_MODE_ROTATE_90:
> +               *rotation = DPU_LAYER_ROTATION_90;
> +               break;
> +       case DRM_MODE_ROTATE_180:
> +               *rotation = DPU_LAYER_ROTATION_180;
> +               break;
> +       case DRM_MODE_ROTATE_270:
> +               *rotation = DPU_LAYER_ROTATION_270;
> +               break;
> +       case DRM_MODE_REFLECT_Y:
> +               *rotation = DPU_LAYER_ROTATION_180_M;
> +               break;
> +       case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> +               *rotation = DPU_LAYER_ROTATION_90_M;
> +               break;
> +       case DRM_MODE_REFLECT_X:
> +               *rotation = DPU_LAYER_ROTATION_0_M;
> +               break;
> +       case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> +               *rotation = DPU_LAYER_ROTATION_270_M;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int sprd_plane_atomic_check(struct drm_plane *plane,
> +                                 struct drm_plane_state *state)
> +{
> +       struct sprd_plane *p = to_sprd_plane(plane);
> +       struct drm_framebuffer *fb = state->fb;
> +       struct drm_gem_cma_object *cma_obj;
> +       int i, ret;
> +       u32 addr;
> +
> +       if (!state->fb || !state->crtc)
> +               return 0;
> +
> +       ret = sprd_plane_format_convert(fb->format->format,
> +                                       &p->format);
> +       if (ret < 0) {
> +               DRM_ERROR("Invalid plane format\n");
> +               return ret;
> +       }
> +
> +       ret = sprd_plane_rotation_convert(state->rotation,
> +                                       &p->rotation);
> +       if (ret < 0) {
> +               DRM_ERROR("Invalid plane rotation\n");
> +               return ret;
> +       }
> +
> +       switch (state->pixel_blend_mode) {
> +       case DRM_MODE_BLEND_COVERAGE:
> +               /* alpha mode select - combo alpha */
> +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +               /* Normal mode */
> +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> +               break;
> +       case DRM_MODE_BLEND_PREMULTI:
> +               /* alpha mode select - combo alpha */
> +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +               /* Pre-mult mode */
> +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> +               break;
> +       case DRM_MODE_BLEND_PIXEL_NONE:
> +       default:
> +               /* don't do blending, maybe RGBX */
> +               /* alpha mode select - layer alpha */
> +               p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> +               break;
> +       }
> +
> +       for (i = 0; i < fb->format->num_planes; i++) {
> +               cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> +               addr = cma_obj->paddr + fb->offsets[i];
> +               if (addr % 16) {
> +                       DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> +                               i, addr);
> +                       return -EFAULT;
> +               }
> +
> +               p->addr[i] = addr;
> +               p->pitch[i] = fb->pitches[i];
> +       }
> +
> +       return 0;
> +}
> +
> +static void sprd_plane_atomic_update(struct drm_plane *plane,
> +                                   struct drm_plane_state *old_state)
> +{
> +       struct drm_plane_state *state = plane->state;
> +       struct drm_framebuffer *fb = plane->state->fb;
> +       struct sprd_plane *p = to_sprd_plane(plane);
> +       struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> +       struct dpu_layer *layer = &dpu->layers[p->index];
> +       int i;
> +
> +       if (!state->crtc || !state->fb)
> +               return;
> +
> +       layer->index = p->index;
> +       layer->src_x = state->src_x >> 16;
> +       layer->src_y = state->src_y >> 16;
> +       layer->src_w = state->src_w >> 16;
> +       layer->src_h = state->src_h >> 16;
> +       layer->dst_x = state->crtc_x;
> +       layer->dst_y = state->crtc_y;
> +       layer->dst_w = state->crtc_w;
> +       layer->dst_h = state->crtc_h;
> +       layer->alpha = state->alpha;
> +       layer->format = p->format;
> +       layer->blending = p->blend_mode;
> +       layer->rotation = p->rotation;
> +       layer->planes = fb->format->num_planes;
> +
> +       for (i = 0; i < layer->planes; i++) {
> +               layer->addr[i] = p->addr[i];
> +               layer->pitch[i] = p->pitch[i];
> +       }
> +
> +       dpu->pending_planes++;
> +}
> +
> +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> +{
> +       unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +                                      BIT(DRM_MODE_BLEND_PREMULTI) |
> +                                      BIT(DRM_MODE_BLEND_COVERAGE);
> +
> +       /* create rotation property */
> +       drm_plane_create_rotation_property(&p->plane,
> +                                          DRM_MODE_ROTATE_0,
> +                                          DRM_MODE_ROTATE_MASK |
> +                                          DRM_MODE_REFLECT_MASK);
> +
> +       /* create alpha property */
> +       drm_plane_create_alpha_property(&p->plane);
> +
> +       /* create blend mode property */
> +       drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> +
> +       /* create zpos property */
> +       drm_plane_create_zpos_immutable_property(&p->plane, index);
> +}
> +
> +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> +       .atomic_check = sprd_plane_atomic_check,
> +       .atomic_update = sprd_plane_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs sprd_plane_funcs = {
> +       .update_plane = drm_atomic_helper_update_plane,
> +       .disable_plane  = drm_atomic_helper_disable_plane,
> +       .destroy = drm_plane_cleanup,
> +       .reset = drm_atomic_helper_plane_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> +                                       struct sprd_dpu *dpu)
> +{
> +       struct drm_plane *primary = NULL;
> +       struct sprd_plane *p = NULL;
> +       struct dpu_capability cap = {};
> +       int ret, i;
> +
> +       dpu->core->capability(&dpu->ctx, &cap);
> +
> +       dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> +                                 sizeof(struct dpu_layer), GFP_KERNEL);
> +       if (!dpu->layers)
> +               return ERR_PTR(-ENOMEM);
> +
> +       for (i = 0; i < cap.max_layers; i++) {
> +
> +               p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> +               if (!p)
> +                       return ERR_PTR(-ENOMEM);
> +
> +               ret = drm_universal_plane_init(drm, &p->plane, 1,
> +                                              &sprd_plane_funcs, cap.fmts_ptr,
> +                                              cap.fmts_cnt, NULL,
> +                                              DRM_PLANE_TYPE_PRIMARY, NULL);
> +               if (ret) {
> +                       DRM_ERROR("fail to init primary plane\n");
> +                       return ERR_PTR(ret);
> +               }
> +
> +               drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> +
> +               sprd_plane_create_properties(p, i);
> +
> +               p->index = i;
> +               if (i == 0)
> +                       primary = &p->plane;
> +       }
> +
> +       return primary;
> +}
> +
> +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> +                                       const struct drm_display_mode *mode)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> +
> +       if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> +               drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> +
> +               if ((mode->hdisplay == mode->htotal) ||
> +                   (mode->vdisplay == mode->vtotal))
> +                       dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> +               else
> +                       dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> +       }
> +
> +       return MODE_OK;
> +}
> +
> +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> +                                  struct drm_crtc_state *old_state)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       sprd_dpu_init(dpu);
> +
> +       enable_irq(dpu->ctx.irq);
> +}
> +
> +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> +                                   struct drm_crtc_state *old_state)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +       struct drm_device *drm = dpu->crtc.dev;
> +
> +       disable_irq(dpu->ctx.irq);
> +
> +       sprd_dpu_fini(dpu);
> +
> +       spin_lock_irq(&drm->event_lock);
> +       if (crtc->state->event) {
> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +               crtc->state->event = NULL;
> +       }
> +       spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> +                                struct drm_crtc_state *state)
> +{
> +       DRM_DEBUG("%s()\n", __func__);
> +
> +       return 0;
> +}
> +
> +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> +                                 struct drm_crtc_state *old_state)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> +
> +       dpu->pending_planes = 0;
> +}
> +
> +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> +                                 struct drm_crtc_state *old_state)
> +
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +       struct drm_device *drm = dpu->crtc.dev;
> +
> +       if (dpu->pending_planes)
> +               dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> +
> +       spin_lock_irq(&drm->event_lock);
> +       if (crtc->state->event) {
> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +               crtc->state->event = NULL;
> +       }
> +       spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       dpu->core->enable_vsync(&dpu->ctx);
> +
> +       return 0;
> +}
> +
> +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       dpu->core->disable_vsync(&dpu->ctx);
> +}
> +
> +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> +       .mode_valid     = sprd_crtc_mode_valid,
> +       .atomic_check   = sprd_crtc_atomic_check,
> +       .atomic_begin   = sprd_crtc_atomic_begin,
> +       .atomic_flush   = sprd_crtc_atomic_flush,
> +       .atomic_enable  = sprd_crtc_atomic_enable,
> +       .atomic_disable = sprd_crtc_atomic_disable,
> +};
> +
> +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> +       .destroy        = drm_crtc_cleanup,
> +       .set_config     = drm_atomic_helper_set_config,
> +       .page_flip      = drm_atomic_helper_page_flip,
> +       .reset          = drm_atomic_helper_crtc_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +       .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> +       .enable_vblank  = sprd_crtc_enable_vblank,
> +       .disable_vblank = sprd_crtc_disable_vblank,
> +};
> +
> +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> +                        struct drm_plane *primary)
> +{
> +       struct device_node *port;
> +       int ret;
> +
> +       /*
> +        * set crtc port so that drm_of_find_possible_crtcs call works
> +        */
> +       port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> +       if (!port) {
> +               DRM_ERROR("find 'ports' phandle of %s failed\n",
> +                         drm->dev->of_node->full_name);
> +               return -EINVAL;
> +       }
> +       of_node_put(port);
> +       crtc->port = port;
> +
> +       ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> +                                       &sprd_crtc_funcs, NULL);
> +       if (ret) {
> +               DRM_ERROR("failed to init crtc.\n");
> +               return ret;
> +       }
> +
> +       drm_mode_crtc_set_gamma_size(crtc, 256);
> +
> +       drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> +
> +       return 0;
> +}
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu)
> +{
> +       struct dpu_context *ctx = &dpu->ctx;
> +
> +       dpu->core->init(ctx);
> +       dpu->core->ifconfig(ctx);
> +}
> +
> +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> +{
> +       struct dpu_context *ctx = &dpu->ctx;
> +
> +       dpu->core->fini(ctx);
> +}
> +
> +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> +{
> +       struct sprd_dpu *dpu = data;
> +       struct dpu_context *ctx = &dpu->ctx;
> +       u32 int_mask = 0;
> +
> +       int_mask = dpu->core->isr(ctx);
> +
> +       if (int_mask & BIT_DPU_INT_ERR)
> +               DRM_WARN("Warning: dpu underflow!\n");
> +
> +       if (int_mask & BIT_DPU_INT_VSYNC)
> +               drm_crtc_handle_vblank(&dpu->crtc);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct drm_device *drm = data;
> +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +       struct drm_plane *plane;
> +       int ret;
> +
> +       plane = sprd_plane_init(drm, dpu);
> +       if (IS_ERR_OR_NULL(plane)) {
> +               ret = PTR_ERR(plane);
> +               return ret;
> +       }
> +
> +       ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> +       void *data)
> +{
> +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +
> +       drm_crtc_cleanup(&dpu->crtc);
> +}
> +
> +static const struct component_ops dpu_component_ops = {
> +       .bind = sprd_dpu_bind,
> +       .unbind = sprd_dpu_unbind,
> +};
> +
> +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> +                               struct device *dev)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct dpu_context *ctx = &dpu->ctx;
> +       struct resource *res;
> +       int ret;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> +       if (!ctx->base) {
> +               DRM_ERROR("failed to map dpu registers\n");
> +               return -EFAULT;
> +       }
> +
> +       ctx->irq = platform_get_irq(pdev, 0);
> +       if (ctx->irq < 0) {
> +               DRM_ERROR("failed to get dpu irq\n");
> +               return ctx->irq;
> +       }
> +
> +       irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> +       ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> +                                       0, "DPU", dpu);
> +       if (ret) {
> +               DRM_ERROR("failed to register dpu irq handler\n");
> +               return ret;
> +       }
> +
> +       init_waitqueue_head(&ctx->wait_queue);
> +
> +       return 0;
> +}
> +
> +static const struct sprd_dpu_ops sharkl3_dpu = {
> +       .core = &dpu_r2p0_core_ops,
> +};
> +
> +static const struct of_device_id dpu_match_table[] = {
> +       { .compatible = "sprd,sharkl3-dpu",
> +         .data = &sharkl3_dpu },
> +       { /* sentinel */ },
> +};
> +
> +static int sprd_dpu_probe(struct platform_device *pdev)
> +{
> +       const struct sprd_dpu_ops *pdata;
> +       struct sprd_dpu *dpu;
> +       int ret;
> +
> +       dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> +       if (!dpu)
> +               return -ENOMEM;
> +
> +       pdata = of_device_get_match_data(&pdev->dev);
> +       if (pdata) {
> +               dpu->core = pdata->core;
> +       } else {
> +               DRM_ERROR("No matching driver data found\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = sprd_dpu_context_init(dpu, &pdev->dev);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, dpu);
> +
> +       return component_add(&pdev->dev, &dpu_component_ops);
> +}
> +
> +static int sprd_dpu_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &dpu_component_ops);
> +       return 0;
> +}
> +
> +struct platform_driver sprd_dpu_driver = {
> +       .probe = sprd_dpu_probe,
> +       .remove = sprd_dpu_remove,
> +       .driver = {
> +               .name = "sprd-dpu-drv",
> +               .of_match_table = dpu_match_table,
> +       },
> +};
> +
> +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> new file mode 100644
> index 0000000..7d3c5e4
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> @@ -0,0 +1,187 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#ifndef __SPRD_DPU_H__
> +#define __SPRD_DPU_H__
> +
> +#include <linux/bug.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_vblank.h>
> +#include <uapi/drm/drm_mode.h>
> +
> +#define BIT_DPU_INT_DONE_              BIT(0)
> +#define BIT_DPU_INT_TE                 BIT(1)
> +#define BIT_DPU_INT_ERR                        BIT(2)
> +#define BIT_DPU_INT_EDPI_TE            BIT(3)
> +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> +#define BIT_DPU_INT_VSYNC              BIT(5)
> +#define BIT_DPU_INT_WB_DONE            BIT(6)
> +#define BIT_DPU_INT_WB_ERR             BIT(7)
> +
> +#define BIT_DPU_LAY_LAYER_ALPHA                        (0x01 << 2)
> +#define BIT_DPU_LAY_COMBO_ALPHA                        (0x02 << 2)
> +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE               (0x00 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE               (0x01 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE               (0x02 << 4)
> +#define BIT_DPU_LAY_FORMAT_ARGB8888                    (0x03 << 4)
> +#define BIT_DPU_LAY_FORMAT_RGB565                      (0x04 << 4)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3               (0x00 << 8)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0               (0x01 << 8)
> +#define BIT_DPU_LAY_NO_SWITCH                  (0x00 << 10)
> +#define BIT_DPU_LAY_RB_OR_UV_SWITCH            (0x01 << 10)
> +#define BIT_DPU_LAY_MODE_BLEND_NORMAL          (0x00 << 16)
> +#define BIT_DPU_LAY_MODE_BLEND_PREMULT         (0x01 << 16)
> +
> +enum {
> +       SPRD_DPU_IF_DBI = 0,
> +       SPRD_DPU_IF_DPI,
> +       SPRD_DPU_IF_EDPI,
> +       SPRD_DPU_IF_LIMIT
> +};
> +
> +enum {
> +       DPU_LAYER_ROTATION_0,
> +       DPU_LAYER_ROTATION_90,
> +       DPU_LAYER_ROTATION_180,
> +       DPU_LAYER_ROTATION_270,
> +       DPU_LAYER_ROTATION_0_M,
> +       DPU_LAYER_ROTATION_90_M,
> +       DPU_LAYER_ROTATION_180_M,
> +       DPU_LAYER_ROTATION_270_M,
> +};
> +
> +struct dpu_layer {
> +       u8 index;
> +       u8 planes;
> +       u32 addr[4];
> +       u32 pitch[4];
> +       s16 src_x;
> +       s16 src_y;
> +       s16 src_w;
> +       s16 src_h;
> +       s16 dst_x;
> +       s16 dst_y;
> +       u16 dst_w;
> +       u16 dst_h;
> +       u32 format;
> +       u32 alpha;
> +       u32 blending;
> +       u32 rotation;
> +};
> +
> +/**
> + * Sprd DPU capability structure
> + *
> + * @max_layers: maximum number of layers available
> + * @fmts_ptr: A pointer to array of supported pixel formats
> + * @fmts_cnt: the number of format on @fmts_ptr
> + */
> +struct dpu_capability {
> +       u32 max_layers;
> +       const u32 *fmts_ptr;
> +       u32 fmts_cnt;
> +};
> +
> +/**
> + * Sprd DPU core callback ops
> + *
> + * This structure decribes the display controller common
> + * callback ops
> + *
> + * @init: initial DPU core
> + * @fini: cleanup DPU core
> + * @run: enable DPU output
> + * @stop: disable DPU output
> + * @enable_vsync: enable vblank interrupt
> + * @disable_vsync: disable vblank interrupt
> + * @isr: function pointer to the isr
> + * @ifconfig: initial DPI interface
> + * @flip: commit CRTC planes to DPU
> + * @capability: callback for DPU capabilities
> + */
> +struct dpu_context;
> +struct dpu_core_ops {
> +       void (*init)(struct dpu_context *ctx);
> +       void (*fini)(struct dpu_context *ctx);
> +       void (*run)(struct dpu_context *ctx);
> +       void (*stop)(struct dpu_context *ctx);
> +       void (*enable_vsync)(struct dpu_context *ctx);
> +       void (*disable_vsync)(struct dpu_context *ctx);
> +       u32 (*isr)(struct dpu_context *ctx);
> +       void (*ifconfig)(struct dpu_context *ctx);
> +       void (*flip)(struct dpu_context *ctx,
> +                    struct dpu_layer layers[], u8 count);
> +       void (*capability)(struct dpu_context *ctx,
> +                       struct dpu_capability *cap);
> +};
> +
> +/**
> + * Sprd DPU context structure
> + *
> + * @base: DPU controller base address
> + * @irq: IRQ number to install the handler for
> + * @if_type: The type of DPI interface, default is DPI mode.
> + * @vm: videomode structure to use for DPU and DPI initialization
> + * @stopped: indicates whether DPU are stopped
> + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> + * DPU stop register done interrupt signal.
> + * @evt_update: wait queue condition for DPU shadow register
> + * @evt_stop: wait queue condition for DPU stop register
> + */
> +struct dpu_context {
> +       void __iomem *base;
> +       int irq;
> +       u8 if_type;
> +       struct videomode vm;
> +       bool stopped;
> +       wait_queue_head_t wait_queue;
> +       bool evt_update;
> +       bool evt_stop;
> +};
> +
> +/**
> + * Sprd DPU device structure
> + *
> + * @crtc: DRM crtc
> + * @ctx: A pointer to the DPU's implementation specific context
> + * @core: pointer to callbacks for DPU core functionality
> + * @layers: active DPU layers ready to commit
> + * @pending_planes: the number of layers on @layers
> + */
> +struct sprd_dpu {
> +       struct drm_crtc crtc;
> +       struct dpu_context ctx;
> +       const struct dpu_core_ops *core;
> +       struct dpu_layer *layers;
> +       u8 pending_planes;
> +};
> +
> +/**
> + * Sprd DPU H/W callback ops match table structure
> + * The structure used for matching a specific device callback ops
> + *
> + * @core: pointer to callbacks for DPU core functionality
> + */
> +struct sprd_dpu_ops {
> +       const struct dpu_core_ops *core;
> +};
> +
> +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> +{
> +       return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> +}
> +
> +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> +
> +#endif
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> index 4706185..200020f 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.c
> +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
>
>  static struct platform_driver *sprd_drm_drivers[]  = {
>         &sprd_drm_driver,
> +       &sprd_dpu_driver,
>  };
>
>  static int __init sprd_drm_init(void)
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> index edf0881..3c32f3a 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.h
> +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> @@ -13,4 +13,6 @@ struct sprd_drm {
>         struct drm_device *drm;
>  };
>
> +extern struct platform_driver sprd_dpu_driver;
> +
>  #endif /* _SPRD_DRM_H_ */
> --
> 2.7.4
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
@ 2020-07-28 21:51     ` Daniel Vetter
  0 siblings, 0 replies; 50+ messages in thread
From: Daniel Vetter @ 2020-07-28 21:51 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Mark Rutland, Dave Airlie, zhang.lyra, Linux Kernel Mailing List,
	Rob Herring, dri-devel, orsonzhai, Sean Paul

On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
>
> From: Kevin Tang <kevin.tang@unisoc.com>
>
> Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
>
> RFC v6:
>   - Access registers via readl/writel
>   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
>   - Remove always true checks for dpu core ops
>
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>

Quickly scrolled through this, and the entire thing very much leaves a
midlayer heavy aftertaste. Do we really need stuff like struct dpu_layer
and struct dpu_core_ops? They only seem to complicate the code base, and
seem to have no real reason. The indirection with first computing register
values into a sprd_plane/crtc structure, and then writing it into hardware
is also a bit much - I recommend to only do that if you have to compute
values in _check to validate them, so that the computation doesn't have to
be repeated in the commit phase functions.

Also, the layer and pending_flips stuff in sprd_dpu don't work with
atomic, that races. You have to put all that stuff into state objects, or
if it's some data shared with interrupt handlers (doesn't seem to be the
case here), it needs its own locking, and any data you need in the
interrupt handler must be copied over.

Also no devm_kzalloc for anything containined a drm_* structure, that's
the wrong lifetime.

So yeah high level review is that I think this driver would benefit a lot
from a pile of demidlayer.

Cheers, Daniel

> ---
>  drivers/gpu/drm/sprd/Makefile       |   5 +-
>  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
>  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
>  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
>  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
>  7 files changed, 1346 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
>  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
>  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
>
> diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> index 86d95d9..88ab32a 100644
> --- a/drivers/gpu/drm/sprd/Makefile
> +++ b/drivers/gpu/drm/sprd/Makefile
> @@ -2,4 +2,7 @@
>
>  subdir-ccflags-y += -I$(srctree)/$(src)
>
> -obj-y := sprd_drm.o
> +obj-y := sprd_drm.o \
> +       sprd_dpu.o
> +
> +obj-y += dpu/
> diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> new file mode 100644
> index 0000000..40278b6
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-y += dpu_r2p0.o
> diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> new file mode 100644
> index 0000000..4b9521d
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/wait.h>
> +#include <linux/workqueue.h>
> +
> +#include "sprd_dpu.h"
> +
> +/* DPU registers size, 4 Bytes(32 Bits) */
> +#define DPU_REG_SIZE   0x04
> +
> +/* Layer registers offset */
> +#define DPU_LAY_REG_OFFSET     0x0C
> +
> +#define DPU_LAY_REG(reg, index) \
> +       (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> +
> +#define DPU_REG_RD(reg) readl_relaxed(reg)
> +
> +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> +
> +#define DPU_REG_SET(reg, mask) \
> +       writel_relaxed(readl_relaxed(reg) | mask, reg)
> +
> +#define DPU_REG_CLR(reg, mask) \
> +       writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> +
> +/* Global control registers */
> +#define REG_DPU_CTRL   0x04
> +#define REG_DPU_CFG0   0x08
> +#define REG_DPU_CFG1   0x0C
> +#define REG_DPU_CFG2   0x10
> +#define REG_PANEL_SIZE 0x20
> +#define REG_BLEND_SIZE 0x24
> +#define REG_BG_COLOR   0x2C
> +
> +/* Layer0 control registers */
> +#define REG_LAY_BASE_ADDR0     0x30
> +#define REG_LAY_BASE_ADDR1     0x34
> +#define REG_LAY_BASE_ADDR2     0x38
> +#define REG_LAY_CTRL           0x40
> +#define REG_LAY_SIZE           0x44
> +#define REG_LAY_PITCH          0x48
> +#define REG_LAY_POS            0x4C
> +#define REG_LAY_ALPHA          0x50
> +#define REG_LAY_PALLETE                0x58
> +#define REG_LAY_CROP_START     0x5C
> +
> +/* Interrupt control registers */
> +#define REG_DPU_INT_EN         0x1E0
> +#define REG_DPU_INT_CLR                0x1E4
> +#define REG_DPU_INT_STS                0x1E8
> +
> +/* DPI control registers */
> +#define REG_DPI_CTRL           0x1F0
> +#define REG_DPI_H_TIMING       0x1F4
> +#define REG_DPI_V_TIMING       0x1F8
> +
> +/* MMU control registers */
> +#define REG_MMU_EN                     0x800
> +#define REG_MMU_VPN_RANGE              0x80C
> +#define REG_MMU_VAOR_ADDR_RD           0x818
> +#define REG_MMU_VAOR_ADDR_WR           0x81C
> +#define REG_MMU_INV_ADDR_RD            0x820
> +#define REG_MMU_INV_ADDR_WR            0x824
> +#define REG_MMU_PPN1                   0x83C
> +#define REG_MMU_RANGE1                 0x840
> +#define REG_MMU_PPN2                   0x844
> +#define REG_MMU_RANGE2                 0x848
> +
> +/* Global control bits */
> +#define BIT_DPU_RUN                    BIT(0)
> +#define BIT_DPU_STOP                   BIT(1)
> +#define BIT_DPU_REG_UPDATE             BIT(2)
> +#define BIT_DPU_IF_EDPI                        BIT(0)
> +#define BIT_DPU_COEF_NARROW_RANGE              BIT(4)
> +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD       BIT(5)
> +
> +/* Layer control bits */
> +#define BIT_DPU_LAY_EN                         BIT(0)
> +
> +/* Interrupt control & status bits */
> +#define BIT_DPU_INT_DONE               BIT(0)
> +#define BIT_DPU_INT_TE                 BIT(1)
> +#define BIT_DPU_INT_ERR                        BIT(2)
> +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> +#define BIT_DPU_INT_VSYNC              BIT(5)
> +#define BIT_DPU_INT_FBC_PLD_ERR                BIT(8)
> +#define BIT_DPU_INT_FBC_HDR_ERR                BIT(9)
> +#define BIT_DPU_INT_MMU_VAOR_RD                BIT(16)
> +#define BIT_DPU_INT_MMU_VAOR_WR                BIT(17)
> +#define BIT_DPU_INT_MMU_INV_RD         BIT(18)
> +#define BIT_DPU_INT_MMU_INV_WR         BIT(19)
> +
> +/* DPI control bits */
> +#define BIT_DPU_EDPI_TE_EN             BIT(8)
> +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10)
> +#define BIT_DPU_DPI_HALT_EN            BIT(16)
> +
> +
> +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> +{
> +       u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> +                       BIT_DPU_INT_MMU_VAOR_WR |
> +                       BIT_DPU_INT_MMU_INV_RD |
> +                       BIT_DPU_INT_MMU_INV_WR;
> +       u32 val = reg_val & mmu_mask;
> +       int i;
> +
> +       if (val) {
> +               DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
> +
> +               if (val & BIT_DPU_INT_MMU_INV_RD)
> +                       DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> +               if (val & BIT_DPU_INT_MMU_INV_WR)
> +                       DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> +               if (val & BIT_DPU_INT_MMU_VAOR_RD)
> +                       DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> +               if (val & BIT_DPU_INT_MMU_VAOR_WR)
> +                       DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> +
> +               for (i = 0; i < 8; i++) {
> +                       reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> +                       if (reg_val & 0x1)
> +                               DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> +               }
> +       }
> +
> +       return val;
> +}
> +
> +static void dpu_clean_all(struct dpu_context *ctx)
> +{
> +       int i;
> +
> +       for (i = 0; i < 8; i++)
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> +}
> +
> +static u32 dpu_isr(struct dpu_context *ctx)
> +{
> +       u32 reg_val, int_mask = 0;
> +
> +       reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> +
> +       /* disable err interrupt */
> +       if (reg_val & BIT_DPU_INT_ERR)
> +               int_mask |= BIT_DPU_INT_ERR;
> +
> +       /* dpu update done isr */
> +       if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> +               ctx->evt_update = true;
> +               wake_up_interruptible_all(&ctx->wait_queue);
> +       }
> +
> +       /* dpu stop done isr */
> +       if (reg_val & BIT_DPU_INT_DONE) {
> +               ctx->evt_stop = true;
> +               wake_up_interruptible_all(&ctx->wait_queue);
> +       }
> +
> +       /* dpu ifbc payload error isr */
> +       if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> +               int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +               DRM_ERROR("dpu ifbc payload error\n");
> +       }
> +
> +       /* dpu ifbc header error isr */
> +       if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> +               int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +               DRM_ERROR("dpu ifbc header error\n");
> +       }
> +
> +       int_mask |= check_mmu_isr(ctx, reg_val);
> +
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> +
> +       return reg_val;
> +}
> +
> +static int dpu_wait_stop_done(struct dpu_context *ctx)
> +{
> +       int rc;
> +
> +       if (ctx->stopped)
> +               return 0;
> +
> +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> +                                              msecs_to_jiffies(500));
> +       ctx->evt_stop = false;
> +
> +       ctx->stopped = true;
> +
> +       if (!rc) {
> +               DRM_ERROR("dpu wait for stop done time out!\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +
> +static int dpu_wait_update_done(struct dpu_context *ctx)
> +{
> +       int rc;
> +
> +       ctx->evt_update = false;
> +
> +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> +                                              msecs_to_jiffies(500));
> +
> +       if (!rc) {
> +               DRM_ERROR("dpu wait for reg update done time out!\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +
> +static void dpu_stop(struct dpu_context *ctx)
> +{
> +       if (ctx->if_type == SPRD_DPU_IF_DPI)
> +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> +
> +       dpu_wait_stop_done(ctx);
> +}
> +
> +static void dpu_run(struct dpu_context *ctx)
> +{
> +       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +       ctx->stopped = false;
> +}
> +
> +static void dpu_init(struct dpu_context *ctx)
> +{
> +       u32 reg_val, size;
> +
> +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +       size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> +
> +       DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> +       DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> +
> +       reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> +       DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> +       DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> +       DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> +
> +       if (ctx->stopped)
> +               dpu_clean_all(ctx);
> +
> +       DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> +       DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> +       DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> +       DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> +       DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> +       DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> +
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> +}
> +
> +static void dpu_fini(struct dpu_context *ctx)
> +{
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> +}
> +
> +static void dpu_layer(struct dpu_context *ctx,
> +                   struct dpu_layer *hwlayer)
> +{
> +       const struct drm_format_info *info;
> +       u32 size, offset, ctrl, pitch;
> +       int i;
> +
> +       offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> +
> +       if (hwlayer->src_w && hwlayer->src_h)
> +               size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> +       else
> +               size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> +
> +       for (i = 0; i < hwlayer->planes; i++)
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> +                               hwlayer->index), hwlayer->addr[i]);
> +
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> +                       hwlayer->index), offset);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> +                       hwlayer->index), size);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> +                       hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> +                       hwlayer->index), hwlayer->alpha);
> +
> +       info = drm_format_info(hwlayer->format);
> +       if (hwlayer->planes == 3) {
> +               /* UV pitch is 1/2 of Y pitch*/
> +               pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> +                               (hwlayer->pitch[0] / info->cpp[0] << 15);
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +                               hwlayer->index), pitch);
> +       } else {
> +               pitch = hwlayer->pitch[0] / info->cpp[0];
> +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> +                               hwlayer->index), pitch);
> +       }
> +
> +       ctrl = hwlayer->format |
> +               hwlayer->blending |
> +               (hwlayer->rotation & 0x7) << 20;
> +
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +                       hwlayer->index), ctrl);
> +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> +                       hwlayer->index), BIT_DPU_LAY_EN);
> +
> +       DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> +                               hwlayer->dst_x, hwlayer->dst_y,
> +                               hwlayer->dst_w, hwlayer->dst_h);
> +       DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> +                               hwlayer->src_x, hwlayer->src_y,
> +                               hwlayer->src_w, hwlayer->src_h);
> +}
> +
> +static void dpu_flip(struct dpu_context *ctx,
> +                    struct dpu_layer layers[], u8 count)
> +{
> +       int i;
> +       u32 reg_val;
> +
> +       /*
> +        * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> +        * registers in EDPI mode. So the config registers can only be
> +        * updated in the rising edge of DPU_RUN bit.
> +        */
> +       if (ctx->if_type == SPRD_DPU_IF_EDPI)
> +               dpu_wait_stop_done(ctx);
> +
> +       /* reset the bgcolor to black */
> +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> +
> +       /* disable all the layers */
> +       dpu_clean_all(ctx);
> +
> +       /* start configure dpu layers */
> +       for (i = 0; i < count; i++)
> +               dpu_layer(ctx, &layers[i]);
> +
> +       /* update trigger and wait */
> +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +               if (!ctx->stopped) {
> +                       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> +                       dpu_wait_update_done(ctx);
> +               }
> +
> +               DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> +
> +               ctx->stopped = false;
> +       }
> +
> +       /*
> +        * If the following interrupt was disabled in isr,
> +        * re-enable it.
> +        */
> +       reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> +                 BIT_DPU_INT_FBC_HDR_ERR |
> +                 BIT_DPU_INT_MMU_VAOR_RD |
> +                 BIT_DPU_INT_MMU_VAOR_WR |
> +                 BIT_DPU_INT_MMU_INV_RD |
> +                 BIT_DPU_INT_MMU_INV_WR;
> +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> +
> +}
> +
> +static void dpu_dpi_init(struct dpu_context *ctx)
> +{
> +       u32 int_mask = 0;
> +       u32 reg_val;
> +
> +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> +               /* use dpi as interface */
> +               DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +               /* disable Halt function for SPRD DSI */
> +               DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> +
> +               /* select te from external pad */
> +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +               /* set dpi timing */
> +               reg_val = ctx->vm.hsync_len << 0 |
> +                         ctx->vm.hback_porch << 8 |
> +                         ctx->vm.hfront_porch << 20;
> +               DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> +
> +               reg_val = ctx->vm.vsync_len << 0 |
> +                         ctx->vm.vback_porch << 8 |
> +                         ctx->vm.vfront_porch << 20;
> +               DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> +
> +               if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> +                       DRM_WARN("Warning: (vsync + vbp) < 32, "
> +                               "underflow risk!\n");
> +
> +               /* enable dpu update done INT */
> +               int_mask |= BIT_DPU_INT_UPDATE_DONE;
> +               /* enable dpu DONE  INT */
> +               int_mask |= BIT_DPU_INT_DONE;
> +               /* enable dpu dpi vsync */
> +               int_mask |= BIT_DPU_INT_VSYNC;
> +               /* enable dpu TE INT */
> +               int_mask |= BIT_DPU_INT_TE;
> +               /* enable underflow err INT */
> +               int_mask |= BIT_DPU_INT_ERR;
> +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> +               /* use edpi as interface */
> +               DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> +
> +               /* use external te */
> +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> +
> +               /* enable te */
> +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> +
> +               /* enable stop DONE INT */
> +               int_mask |= BIT_DPU_INT_DONE;
> +               /* enable TE INT */
> +               int_mask |= BIT_DPU_INT_TE;
> +       }
> +
> +       /* enable ifbc payload error INT */
> +       int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> +       /* enable ifbc header error INT */
> +       int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> +       /* enable iommu va out of range read error INT */
> +       int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> +       /* enable iommu va out of range write error INT */
> +       int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> +       /* enable iommu invalid read error INT */
> +       int_mask |= BIT_DPU_INT_MMU_INV_RD;
> +       /* enable iommu invalid write error INT */
> +       int_mask |= BIT_DPU_INT_MMU_INV_WR;
> +
> +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> +}
> +
> +static void enable_vsync(struct dpu_context *ctx)
> +{
> +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static void disable_vsync(struct dpu_context *ctx)
> +{
> +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> +}
> +
> +static const u32 primary_fmts[] = {
> +       DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> +       DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> +       DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> +       DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> +       DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> +       DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> +       DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> +       DRM_FORMAT_YVU420,
> +};
> +
> +static void dpu_capability(struct dpu_context *ctx,
> +                       struct dpu_capability *cap)
> +{
> +       cap->max_layers = 6;
> +       cap->fmts_ptr = primary_fmts;
> +       cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> +}
> +
> +const struct dpu_core_ops dpu_r2p0_core_ops = {
> +       .init = dpu_init,
> +       .fini = dpu_fini,
> +       .run = dpu_run,
> +       .stop = dpu_stop,
> +       .isr = dpu_isr,
> +       .ifconfig = dpu_dpi_init,
> +       .capability = dpu_capability,
> +       .flip = dpu_flip,
> +       .enable_vsync = enable_vsync,
> +       .disable_vsync = disable_vsync,
> +};
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> new file mode 100644
> index 0000000..5ec8e7c
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> @@ -0,0 +1,646 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#include <linux/component.h>
> +#include <linux/dma-buf.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "sprd_drm.h"
> +#include "sprd_dpu.h"
> +
> +struct sprd_plane {
> +       struct drm_plane plane;
> +       u32 index;
> +       u32 addr[4];
> +       u32 pitch[4];
> +       u32 format;
> +       u32 rotation;
> +       u32 blend_mode;
> +};
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu);
> +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> +
> +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> +{
> +       return container_of(plane, struct sprd_plane, plane);
> +}
> +
> +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> +{
> +       switch (fourcc) {
> +       case DRM_FORMAT_BGRA8888:
> +               /* BGRA8888 -> ARGB8888 */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +               break;
> +       case DRM_FORMAT_RGBX8888:
> +       case DRM_FORMAT_RGBA8888:
> +               /* RGBA8888 -> ABGR8888 */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_ABGR8888:
> +               /* RB switch */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_ARGB8888:
> +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +               break;
> +       case DRM_FORMAT_XBGR8888:
> +               /* RB switch */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_XRGB8888:
> +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> +               break;
> +       case DRM_FORMAT_BGR565:
> +               /* RB switch */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               /* FALLTHRU */
> +       case DRM_FORMAT_RGB565:
> +               *format |= BIT_DPU_LAY_FORMAT_RGB565;
> +               break;
> +       case DRM_FORMAT_NV12:
> +               /* 2-Lane: Yuv420 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_NO_SWITCH;
> +               break;
> +       case DRM_FORMAT_NV21:
> +               /* 2-Lane: Yuv420 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               break;
> +       case DRM_FORMAT_NV16:
> +               /* 2-Lane: Yuv422 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               break;
> +       case DRM_FORMAT_NV61:
> +               /* 2-Lane: Yuv422 */
> +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_NO_SWITCH;
> +               break;
> +       case DRM_FORMAT_YUV420:
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_NO_SWITCH;
> +               break;
> +       case DRM_FORMAT_YVU420:
> +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> +               /* Y endian */
> +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> +               /* UV endian */
> +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> +{
> +       switch (angle) {
> +       case DRM_MODE_ROTATE_0:
> +               *rotation = DPU_LAYER_ROTATION_0;
> +               break;
> +       case DRM_MODE_ROTATE_90:
> +               *rotation = DPU_LAYER_ROTATION_90;
> +               break;
> +       case DRM_MODE_ROTATE_180:
> +               *rotation = DPU_LAYER_ROTATION_180;
> +               break;
> +       case DRM_MODE_ROTATE_270:
> +               *rotation = DPU_LAYER_ROTATION_270;
> +               break;
> +       case DRM_MODE_REFLECT_Y:
> +               *rotation = DPU_LAYER_ROTATION_180_M;
> +               break;
> +       case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> +               *rotation = DPU_LAYER_ROTATION_90_M;
> +               break;
> +       case DRM_MODE_REFLECT_X:
> +               *rotation = DPU_LAYER_ROTATION_0_M;
> +               break;
> +       case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> +               *rotation = DPU_LAYER_ROTATION_270_M;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int sprd_plane_atomic_check(struct drm_plane *plane,
> +                                 struct drm_plane_state *state)
> +{
> +       struct sprd_plane *p = to_sprd_plane(plane);
> +       struct drm_framebuffer *fb = state->fb;
> +       struct drm_gem_cma_object *cma_obj;
> +       int i, ret;
> +       u32 addr;
> +
> +       if (!state->fb || !state->crtc)
> +               return 0;
> +
> +       ret = sprd_plane_format_convert(fb->format->format,
> +                                       &p->format);
> +       if (ret < 0) {
> +               DRM_ERROR("Invalid plane format\n");
> +               return ret;
> +       }
> +
> +       ret = sprd_plane_rotation_convert(state->rotation,
> +                                       &p->rotation);
> +       if (ret < 0) {
> +               DRM_ERROR("Invalid plane rotation\n");
> +               return ret;
> +       }
> +
> +       switch (state->pixel_blend_mode) {
> +       case DRM_MODE_BLEND_COVERAGE:
> +               /* alpha mode select - combo alpha */
> +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +               /* Normal mode */
> +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> +               break;
> +       case DRM_MODE_BLEND_PREMULTI:
> +               /* alpha mode select - combo alpha */
> +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> +               /* Pre-mult mode */
> +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> +               break;
> +       case DRM_MODE_BLEND_PIXEL_NONE:
> +       default:
> +               /* don't do blending, maybe RGBX */
> +               /* alpha mode select - layer alpha */
> +               p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> +               break;
> +       }
> +
> +       for (i = 0; i < fb->format->num_planes; i++) {
> +               cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> +               addr = cma_obj->paddr + fb->offsets[i];
> +               if (addr % 16) {
> +                       DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> +                               i, addr);
> +                       return -EFAULT;
> +               }
> +
> +               p->addr[i] = addr;
> +               p->pitch[i] = fb->pitches[i];
> +       }
> +
> +       return 0;
> +}
> +
> +static void sprd_plane_atomic_update(struct drm_plane *plane,
> +                                   struct drm_plane_state *old_state)
> +{
> +       struct drm_plane_state *state = plane->state;
> +       struct drm_framebuffer *fb = plane->state->fb;
> +       struct sprd_plane *p = to_sprd_plane(plane);
> +       struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> +       struct dpu_layer *layer = &dpu->layers[p->index];
> +       int i;
> +
> +       if (!state->crtc || !state->fb)
> +               return;
> +
> +       layer->index = p->index;
> +       layer->src_x = state->src_x >> 16;
> +       layer->src_y = state->src_y >> 16;
> +       layer->src_w = state->src_w >> 16;
> +       layer->src_h = state->src_h >> 16;
> +       layer->dst_x = state->crtc_x;
> +       layer->dst_y = state->crtc_y;
> +       layer->dst_w = state->crtc_w;
> +       layer->dst_h = state->crtc_h;
> +       layer->alpha = state->alpha;
> +       layer->format = p->format;
> +       layer->blending = p->blend_mode;
> +       layer->rotation = p->rotation;
> +       layer->planes = fb->format->num_planes;
> +
> +       for (i = 0; i < layer->planes; i++) {
> +               layer->addr[i] = p->addr[i];
> +               layer->pitch[i] = p->pitch[i];
> +       }
> +
> +       dpu->pending_planes++;
> +}
> +
> +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> +{
> +       unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +                                      BIT(DRM_MODE_BLEND_PREMULTI) |
> +                                      BIT(DRM_MODE_BLEND_COVERAGE);
> +
> +       /* create rotation property */
> +       drm_plane_create_rotation_property(&p->plane,
> +                                          DRM_MODE_ROTATE_0,
> +                                          DRM_MODE_ROTATE_MASK |
> +                                          DRM_MODE_REFLECT_MASK);
> +
> +       /* create alpha property */
> +       drm_plane_create_alpha_property(&p->plane);
> +
> +       /* create blend mode property */
> +       drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> +
> +       /* create zpos property */
> +       drm_plane_create_zpos_immutable_property(&p->plane, index);
> +}
> +
> +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> +       .atomic_check = sprd_plane_atomic_check,
> +       .atomic_update = sprd_plane_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs sprd_plane_funcs = {
> +       .update_plane = drm_atomic_helper_update_plane,
> +       .disable_plane  = drm_atomic_helper_disable_plane,
> +       .destroy = drm_plane_cleanup,
> +       .reset = drm_atomic_helper_plane_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> +                                       struct sprd_dpu *dpu)
> +{
> +       struct drm_plane *primary = NULL;
> +       struct sprd_plane *p = NULL;
> +       struct dpu_capability cap = {};
> +       int ret, i;
> +
> +       dpu->core->capability(&dpu->ctx, &cap);
> +
> +       dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> +                                 sizeof(struct dpu_layer), GFP_KERNEL);
> +       if (!dpu->layers)
> +               return ERR_PTR(-ENOMEM);
> +
> +       for (i = 0; i < cap.max_layers; i++) {
> +
> +               p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> +               if (!p)
> +                       return ERR_PTR(-ENOMEM);
> +
> +               ret = drm_universal_plane_init(drm, &p->plane, 1,
> +                                              &sprd_plane_funcs, cap.fmts_ptr,
> +                                              cap.fmts_cnt, NULL,
> +                                              DRM_PLANE_TYPE_PRIMARY, NULL);
> +               if (ret) {
> +                       DRM_ERROR("fail to init primary plane\n");
> +                       return ERR_PTR(ret);
> +               }
> +
> +               drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> +
> +               sprd_plane_create_properties(p, i);
> +
> +               p->index = i;
> +               if (i == 0)
> +                       primary = &p->plane;
> +       }
> +
> +       return primary;
> +}
> +
> +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> +                                       const struct drm_display_mode *mode)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> +
> +       if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> +               drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> +
> +               if ((mode->hdisplay == mode->htotal) ||
> +                   (mode->vdisplay == mode->vtotal))
> +                       dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> +               else
> +                       dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> +       }
> +
> +       return MODE_OK;
> +}
> +
> +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> +                                  struct drm_crtc_state *old_state)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       sprd_dpu_init(dpu);
> +
> +       enable_irq(dpu->ctx.irq);
> +}
> +
> +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> +                                   struct drm_crtc_state *old_state)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +       struct drm_device *drm = dpu->crtc.dev;
> +
> +       disable_irq(dpu->ctx.irq);
> +
> +       sprd_dpu_fini(dpu);
> +
> +       spin_lock_irq(&drm->event_lock);
> +       if (crtc->state->event) {
> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +               crtc->state->event = NULL;
> +       }
> +       spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> +                                struct drm_crtc_state *state)
> +{
> +       DRM_DEBUG("%s()\n", __func__);
> +
> +       return 0;
> +}
> +
> +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> +                                 struct drm_crtc_state *old_state)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> +
> +       dpu->pending_planes = 0;
> +}
> +
> +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> +                                 struct drm_crtc_state *old_state)
> +
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +       struct drm_device *drm = dpu->crtc.dev;
> +
> +       if (dpu->pending_planes)
> +               dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> +
> +       spin_lock_irq(&drm->event_lock);
> +       if (crtc->state->event) {
> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +               crtc->state->event = NULL;
> +       }
> +       spin_unlock_irq(&drm->event_lock);
> +}
> +
> +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       dpu->core->enable_vsync(&dpu->ctx);
> +
> +       return 0;
> +}
> +
> +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> +
> +       dpu->core->disable_vsync(&dpu->ctx);
> +}
> +
> +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> +       .mode_valid     = sprd_crtc_mode_valid,
> +       .atomic_check   = sprd_crtc_atomic_check,
> +       .atomic_begin   = sprd_crtc_atomic_begin,
> +       .atomic_flush   = sprd_crtc_atomic_flush,
> +       .atomic_enable  = sprd_crtc_atomic_enable,
> +       .atomic_disable = sprd_crtc_atomic_disable,
> +};
> +
> +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> +       .destroy        = drm_crtc_cleanup,
> +       .set_config     = drm_atomic_helper_set_config,
> +       .page_flip      = drm_atomic_helper_page_flip,
> +       .reset          = drm_atomic_helper_crtc_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +       .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> +       .enable_vblank  = sprd_crtc_enable_vblank,
> +       .disable_vblank = sprd_crtc_disable_vblank,
> +};
> +
> +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> +                        struct drm_plane *primary)
> +{
> +       struct device_node *port;
> +       int ret;
> +
> +       /*
> +        * set crtc port so that drm_of_find_possible_crtcs call works
> +        */
> +       port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> +       if (!port) {
> +               DRM_ERROR("find 'ports' phandle of %s failed\n",
> +                         drm->dev->of_node->full_name);
> +               return -EINVAL;
> +       }
> +       of_node_put(port);
> +       crtc->port = port;
> +
> +       ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> +                                       &sprd_crtc_funcs, NULL);
> +       if (ret) {
> +               DRM_ERROR("failed to init crtc.\n");
> +               return ret;
> +       }
> +
> +       drm_mode_crtc_set_gamma_size(crtc, 256);
> +
> +       drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> +
> +       return 0;
> +}
> +
> +static void sprd_dpu_init(struct sprd_dpu *dpu)
> +{
> +       struct dpu_context *ctx = &dpu->ctx;
> +
> +       dpu->core->init(ctx);
> +       dpu->core->ifconfig(ctx);
> +}
> +
> +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> +{
> +       struct dpu_context *ctx = &dpu->ctx;
> +
> +       dpu->core->fini(ctx);
> +}
> +
> +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> +{
> +       struct sprd_dpu *dpu = data;
> +       struct dpu_context *ctx = &dpu->ctx;
> +       u32 int_mask = 0;
> +
> +       int_mask = dpu->core->isr(ctx);
> +
> +       if (int_mask & BIT_DPU_INT_ERR)
> +               DRM_WARN("Warning: dpu underflow!\n");
> +
> +       if (int_mask & BIT_DPU_INT_VSYNC)
> +               drm_crtc_handle_vblank(&dpu->crtc);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct drm_device *drm = data;
> +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +       struct drm_plane *plane;
> +       int ret;
> +
> +       plane = sprd_plane_init(drm, dpu);
> +       if (IS_ERR_OR_NULL(plane)) {
> +               ret = PTR_ERR(plane);
> +               return ret;
> +       }
> +
> +       ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> +       void *data)
> +{
> +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> +
> +       drm_crtc_cleanup(&dpu->crtc);
> +}
> +
> +static const struct component_ops dpu_component_ops = {
> +       .bind = sprd_dpu_bind,
> +       .unbind = sprd_dpu_unbind,
> +};
> +
> +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> +                               struct device *dev)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct dpu_context *ctx = &dpu->ctx;
> +       struct resource *res;
> +       int ret;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> +       if (!ctx->base) {
> +               DRM_ERROR("failed to map dpu registers\n");
> +               return -EFAULT;
> +       }
> +
> +       ctx->irq = platform_get_irq(pdev, 0);
> +       if (ctx->irq < 0) {
> +               DRM_ERROR("failed to get dpu irq\n");
> +               return ctx->irq;
> +       }
> +
> +       irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> +       ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> +                                       0, "DPU", dpu);
> +       if (ret) {
> +               DRM_ERROR("failed to register dpu irq handler\n");
> +               return ret;
> +       }
> +
> +       init_waitqueue_head(&ctx->wait_queue);
> +
> +       return 0;
> +}
> +
> +static const struct sprd_dpu_ops sharkl3_dpu = {
> +       .core = &dpu_r2p0_core_ops,
> +};
> +
> +static const struct of_device_id dpu_match_table[] = {
> +       { .compatible = "sprd,sharkl3-dpu",
> +         .data = &sharkl3_dpu },
> +       { /* sentinel */ },
> +};
> +
> +static int sprd_dpu_probe(struct platform_device *pdev)
> +{
> +       const struct sprd_dpu_ops *pdata;
> +       struct sprd_dpu *dpu;
> +       int ret;
> +
> +       dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> +       if (!dpu)
> +               return -ENOMEM;
> +
> +       pdata = of_device_get_match_data(&pdev->dev);
> +       if (pdata) {
> +               dpu->core = pdata->core;
> +       } else {
> +               DRM_ERROR("No matching driver data found\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = sprd_dpu_context_init(dpu, &pdev->dev);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, dpu);
> +
> +       return component_add(&pdev->dev, &dpu_component_ops);
> +}
> +
> +static int sprd_dpu_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &dpu_component_ops);
> +       return 0;
> +}
> +
> +struct platform_driver sprd_dpu_driver = {
> +       .probe = sprd_dpu_probe,
> +       .remove = sprd_dpu_remove,
> +       .driver = {
> +               .name = "sprd-dpu-drv",
> +               .of_match_table = dpu_match_table,
> +       },
> +};
> +
> +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> new file mode 100644
> index 0000000..7d3c5e4
> --- /dev/null
> +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> @@ -0,0 +1,187 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020 Unisoc Inc.
> + */
> +
> +#ifndef __SPRD_DPU_H__
> +#define __SPRD_DPU_H__
> +
> +#include <linux/bug.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_vblank.h>
> +#include <uapi/drm/drm_mode.h>
> +
> +#define BIT_DPU_INT_DONE_              BIT(0)
> +#define BIT_DPU_INT_TE                 BIT(1)
> +#define BIT_DPU_INT_ERR                        BIT(2)
> +#define BIT_DPU_INT_EDPI_TE            BIT(3)
> +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> +#define BIT_DPU_INT_VSYNC              BIT(5)
> +#define BIT_DPU_INT_WB_DONE            BIT(6)
> +#define BIT_DPU_INT_WB_ERR             BIT(7)
> +
> +#define BIT_DPU_LAY_LAYER_ALPHA                        (0x01 << 2)
> +#define BIT_DPU_LAY_COMBO_ALPHA                        (0x02 << 2)
> +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE               (0x00 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE               (0x01 << 4)
> +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE               (0x02 << 4)
> +#define BIT_DPU_LAY_FORMAT_ARGB8888                    (0x03 << 4)
> +#define BIT_DPU_LAY_FORMAT_RGB565                      (0x04 << 4)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3               (0x00 << 8)
> +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0               (0x01 << 8)
> +#define BIT_DPU_LAY_NO_SWITCH                  (0x00 << 10)
> +#define BIT_DPU_LAY_RB_OR_UV_SWITCH            (0x01 << 10)
> +#define BIT_DPU_LAY_MODE_BLEND_NORMAL          (0x00 << 16)
> +#define BIT_DPU_LAY_MODE_BLEND_PREMULT         (0x01 << 16)
> +
> +enum {
> +       SPRD_DPU_IF_DBI = 0,
> +       SPRD_DPU_IF_DPI,
> +       SPRD_DPU_IF_EDPI,
> +       SPRD_DPU_IF_LIMIT
> +};
> +
> +enum {
> +       DPU_LAYER_ROTATION_0,
> +       DPU_LAYER_ROTATION_90,
> +       DPU_LAYER_ROTATION_180,
> +       DPU_LAYER_ROTATION_270,
> +       DPU_LAYER_ROTATION_0_M,
> +       DPU_LAYER_ROTATION_90_M,
> +       DPU_LAYER_ROTATION_180_M,
> +       DPU_LAYER_ROTATION_270_M,
> +};
> +
> +struct dpu_layer {
> +       u8 index;
> +       u8 planes;
> +       u32 addr[4];
> +       u32 pitch[4];
> +       s16 src_x;
> +       s16 src_y;
> +       s16 src_w;
> +       s16 src_h;
> +       s16 dst_x;
> +       s16 dst_y;
> +       u16 dst_w;
> +       u16 dst_h;
> +       u32 format;
> +       u32 alpha;
> +       u32 blending;
> +       u32 rotation;
> +};
> +
> +/**
> + * Sprd DPU capability structure
> + *
> + * @max_layers: maximum number of layers available
> + * @fmts_ptr: A pointer to array of supported pixel formats
> + * @fmts_cnt: the number of format on @fmts_ptr
> + */
> +struct dpu_capability {
> +       u32 max_layers;
> +       const u32 *fmts_ptr;
> +       u32 fmts_cnt;
> +};
> +
> +/**
> + * Sprd DPU core callback ops
> + *
> + * This structure decribes the display controller common
> + * callback ops
> + *
> + * @init: initial DPU core
> + * @fini: cleanup DPU core
> + * @run: enable DPU output
> + * @stop: disable DPU output
> + * @enable_vsync: enable vblank interrupt
> + * @disable_vsync: disable vblank interrupt
> + * @isr: function pointer to the isr
> + * @ifconfig: initial DPI interface
> + * @flip: commit CRTC planes to DPU
> + * @capability: callback for DPU capabilities
> + */
> +struct dpu_context;
> +struct dpu_core_ops {
> +       void (*init)(struct dpu_context *ctx);
> +       void (*fini)(struct dpu_context *ctx);
> +       void (*run)(struct dpu_context *ctx);
> +       void (*stop)(struct dpu_context *ctx);
> +       void (*enable_vsync)(struct dpu_context *ctx);
> +       void (*disable_vsync)(struct dpu_context *ctx);
> +       u32 (*isr)(struct dpu_context *ctx);
> +       void (*ifconfig)(struct dpu_context *ctx);
> +       void (*flip)(struct dpu_context *ctx,
> +                    struct dpu_layer layers[], u8 count);
> +       void (*capability)(struct dpu_context *ctx,
> +                       struct dpu_capability *cap);
> +};
> +
> +/**
> + * Sprd DPU context structure
> + *
> + * @base: DPU controller base address
> + * @irq: IRQ number to install the handler for
> + * @if_type: The type of DPI interface, default is DPI mode.
> + * @vm: videomode structure to use for DPU and DPI initialization
> + * @stopped: indicates whether DPU are stopped
> + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> + * DPU stop register done interrupt signal.
> + * @evt_update: wait queue condition for DPU shadow register
> + * @evt_stop: wait queue condition for DPU stop register
> + */
> +struct dpu_context {
> +       void __iomem *base;
> +       int irq;
> +       u8 if_type;
> +       struct videomode vm;
> +       bool stopped;
> +       wait_queue_head_t wait_queue;
> +       bool evt_update;
> +       bool evt_stop;
> +};
> +
> +/**
> + * Sprd DPU device structure
> + *
> + * @crtc: DRM crtc
> + * @ctx: A pointer to the DPU's implementation specific context
> + * @core: pointer to callbacks for DPU core functionality
> + * @layers: active DPU layers ready to commit
> + * @pending_planes: the number of layers on @layers
> + */
> +struct sprd_dpu {
> +       struct drm_crtc crtc;
> +       struct dpu_context ctx;
> +       const struct dpu_core_ops *core;
> +       struct dpu_layer *layers;
> +       u8 pending_planes;
> +};
> +
> +/**
> + * Sprd DPU H/W callback ops match table structure
> + * The structure used for matching a specific device callback ops
> + *
> + * @core: pointer to callbacks for DPU core functionality
> + */
> +struct sprd_dpu_ops {
> +       const struct dpu_core_ops *core;
> +};
> +
> +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> +{
> +       return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> +}
> +
> +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> +
> +#endif
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> index 4706185..200020f 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.c
> +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
>
>  static struct platform_driver *sprd_drm_drivers[]  = {
>         &sprd_drm_driver,
> +       &sprd_dpu_driver,
>  };
>
>  static int __init sprd_drm_init(void)
> diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> index edf0881..3c32f3a 100644
> --- a/drivers/gpu/drm/sprd/sprd_drm.h
> +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> @@ -13,4 +13,6 @@ struct sprd_drm {
>         struct drm_device *drm;
>  };
>
> +extern struct platform_driver sprd_dpu_driver;
> +
>  #endif /* _SPRD_DRM_H_ */
> --
> 2.7.4
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 0/6] Add Unisoc's drm kms module
  2020-07-28 10:12   ` Daniel Vetter
@ 2020-08-04 17:29     ` Rob Herring
  -1 siblings, 0 replies; 50+ messages in thread
From: Rob Herring @ 2020-08-04 17:29 UTC (permalink / raw)
  To: Daniel Vetter, Kevin Tang
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, Dave Airlie,
	Mark Rutland, Orson Zhai, Lyra Zhang, Linux Kernel Mailing List,
	dri-devel

On Tue, Jul 28, 2020 at 4:12 AM Daniel Vetter <daniel@ffwll.ch> wrote:
>
> On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
> >
> > From: Kevin Tang <kevin.tang@unisoc.com>
>
> Hm still no ack for dt bindings? We need that for merging.

If it's not sent to the DT list so it gets into patchwork that's never
going to happen. Nor are any semi-automated tests of the schema.


> Also what's the maintainer plan here? Imo best would be to put this
> into the drm-misc group maintainer model, with commit rights and all:
>
> https://drm.pages.freedesktop.org/maintainer-tools/drm-misc.html
>
> MAINTAINERS patch to do that would be good.
> -Daniel
>
> >
> > ChangeList:
> > v1:
> > 1. only upstream modeset and atomic at first commit.
> > 2. remove some unused code;
> > 3. use alpha and blend_mode properties;
> > 3. add yaml support;
> > 4. remove auto-adaptive panel driver;
> > 5. bugfix
> >
> > v2:
> > 1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
> > 2. remove gem drivers, use generic CMA handlers
> > 3. remove redundant "module_init", all the sub modules loading by KMS
> >
> > v3:
> > 1. multi crtc&encoder design have problem, so rollback to v1
> >
> > v4:
> > 1. update to gcc-linaro-7.5.0
> > 2. update to Linux 5.6-rc3
> > 3. remove pm_runtime support
> > 4. add COMPILE_TEST, remove unused kconfig
> > 5. "drm_dev_put" on drm_unbind
> > 6. fix some naming convention issue
> > 7. remove semaphore lock for crtc flip
> > 8. remove static variables
> >
> > v5:
> > 1. optimize encoder and connector code implementation
> > 2. use "platform_get_irq" and "platform_get_resource"
> > 3. drop useless function return type, drop unless debug log
> > 4. custom properties should be separate, so drop it
> > 5. use DRM_XXX replase pr_xxx
> > 6. drop dsi&dphy hal callback ops
> > 7. drop unless callback ops checking
> > 8. add comments for sprd dpu structure
> >
> > v6:
> > 1. Access registers via readl/writel
> > 2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> > 3. Remove always true checks for dpu core ops
> >
> > Kevin Tang (6):
> >   dt-bindings: display: add Unisoc's drm master bindings
> >   drm/sprd: add Unisoc's drm kms master
> >   dt-bindings: display: add Unisoc's dpu bindings
> >   drm/sprd: add Unisoc's drm display controller driver
> >   dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
> >   drm/sprd: add Unisoc's drm mipi dsi&dphy driver
> >
> >  .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
> >  .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
> >  .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
> >  .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
> >  drivers/gpu/drm/Kconfig                            |    2 +
> >  drivers/gpu/drm/Makefile                           |    1 +
> >  drivers/gpu/drm/sprd/Kconfig                       |   12 +
> >  drivers/gpu/drm/sprd/Makefile                      |   13 +
> >  drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
> >  drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
> >  drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
> >  drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
> >  drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
> >  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
> >  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
> >  drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
> >  drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
> >  drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
> >  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
> >  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
> >  drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
> >  drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
> >  drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
> >  drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
> >  drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
> >  drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
> >  drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
> >  33 files changed, 7074 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
> >  create mode 100644 drivers/gpu/drm/sprd/Kconfig
> >  create mode 100644 drivers/gpu/drm/sprd/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
> >  create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
> >
> > --
> > 2.7.4
> >
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 0/6] Add Unisoc's drm kms module
@ 2020-08-04 17:29     ` Rob Herring
  0 siblings, 0 replies; 50+ messages in thread
From: Rob Herring @ 2020-08-04 17:29 UTC (permalink / raw)
  To: Daniel Vetter, Kevin Tang
  Cc: Mark Rutland, Dave Airlie, Lyra Zhang, Linux Kernel Mailing List,
	dri-devel, Orson Zhai, Sean Paul

On Tue, Jul 28, 2020 at 4:12 AM Daniel Vetter <daniel@ffwll.ch> wrote:
>
> On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
> >
> > From: Kevin Tang <kevin.tang@unisoc.com>
>
> Hm still no ack for dt bindings? We need that for merging.

If it's not sent to the DT list so it gets into patchwork that's never
going to happen. Nor are any semi-automated tests of the schema.


> Also what's the maintainer plan here? Imo best would be to put this
> into the drm-misc group maintainer model, with commit rights and all:
>
> https://drm.pages.freedesktop.org/maintainer-tools/drm-misc.html
>
> MAINTAINERS patch to do that would be good.
> -Daniel
>
> >
> > ChangeList:
> > v1:
> > 1. only upstream modeset and atomic at first commit.
> > 2. remove some unused code;
> > 3. use alpha and blend_mode properties;
> > 3. add yaml support;
> > 4. remove auto-adaptive panel driver;
> > 5. bugfix
> >
> > v2:
> > 1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
> > 2. remove gem drivers, use generic CMA handlers
> > 3. remove redundant "module_init", all the sub modules loading by KMS
> >
> > v3:
> > 1. multi crtc&encoder design have problem, so rollback to v1
> >
> > v4:
> > 1. update to gcc-linaro-7.5.0
> > 2. update to Linux 5.6-rc3
> > 3. remove pm_runtime support
> > 4. add COMPILE_TEST, remove unused kconfig
> > 5. "drm_dev_put" on drm_unbind
> > 6. fix some naming convention issue
> > 7. remove semaphore lock for crtc flip
> > 8. remove static variables
> >
> > v5:
> > 1. optimize encoder and connector code implementation
> > 2. use "platform_get_irq" and "platform_get_resource"
> > 3. drop useless function return type, drop unless debug log
> > 4. custom properties should be separate, so drop it
> > 5. use DRM_XXX replase pr_xxx
> > 6. drop dsi&dphy hal callback ops
> > 7. drop unless callback ops checking
> > 8. add comments for sprd dpu structure
> >
> > v6:
> > 1. Access registers via readl/writel
> > 2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> > 3. Remove always true checks for dpu core ops
> >
> > Kevin Tang (6):
> >   dt-bindings: display: add Unisoc's drm master bindings
> >   drm/sprd: add Unisoc's drm kms master
> >   dt-bindings: display: add Unisoc's dpu bindings
> >   drm/sprd: add Unisoc's drm display controller driver
> >   dt-bindings: display: add Unisoc's mipi dsi&dphy bindings
> >   drm/sprd: add Unisoc's drm mipi dsi&dphy driver
> >
> >  .../devicetree/bindings/display/sprd/dphy.yaml     |   75 +
> >  .../devicetree/bindings/display/sprd/dpu.yaml      |   82 ++
> >  .../devicetree/bindings/display/sprd/drm.yaml      |   36 +
> >  .../devicetree/bindings/display/sprd/dsi.yaml      |   98 ++
> >  drivers/gpu/drm/Kconfig                            |    2 +
> >  drivers/gpu/drm/Makefile                           |    1 +
> >  drivers/gpu/drm/sprd/Kconfig                       |   12 +
> >  drivers/gpu/drm/sprd/Makefile                      |   13 +
> >  drivers/gpu/drm/sprd/disp_lib.c                    |   57 +
> >  drivers/gpu/drm/sprd/disp_lib.h                    |   16 +
> >  drivers/gpu/drm/sprd/dphy/Makefile                 |    7 +
> >  drivers/gpu/drm/sprd/dphy/pll/Makefile             |    3 +
> >  drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c  |  473 +++++++
> >  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c          |  201 +++
> >  drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h          |   22 +
> >  drivers/gpu/drm/sprd/dpu/Makefile                  |    3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c                |  503 +++++++
> >  drivers/gpu/drm/sprd/dsi/Makefile                  |    8 +
> >  drivers/gpu/drm/sprd/dsi/core/Makefile             |    4 +
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c      |  964 +++++++++++++
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h      | 1477 ++++++++++++++++++++
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c  |  328 +++++
> >  drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h  |   32 +
> >  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c            |  590 ++++++++
> >  drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h            |   26 +
> >  drivers/gpu/drm/sprd/sprd_dphy.c                   |  209 +++
> >  drivers/gpu/drm/sprd/sprd_dphy.h                   |   50 +
> >  drivers/gpu/drm/sprd/sprd_dpu.c                    |  668 +++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h                    |  190 +++
> >  drivers/gpu/drm/sprd/sprd_drm.c                    |  227 +++
> >  drivers/gpu/drm/sprd/sprd_drm.h                    |   18 +
> >  drivers/gpu/drm/sprd/sprd_dsi.c                    |  571 ++++++++
> >  drivers/gpu/drm/sprd/sprd_dsi.h                    |  108 ++
> >  33 files changed, 7074 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/dphy.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/dsi.yaml
> >  create mode 100644 drivers/gpu/drm/sprd/Kconfig
> >  create mode 100644 drivers/gpu/drm/sprd/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/disp_lib.c
> >  create mode 100644 drivers/gpu/drm/sprd/disp_lib.h
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c
> >  create mode 100644 drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c
> >  create mode 100644 drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dphy.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
> >
> > --
> > 2.7.4
> >
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
  2020-07-28 20:27     ` Sam Ravnborg
@ 2020-08-26 17:11       ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-26 17:11 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Rob Herring, Mark Rutland, Orson Zhai,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, Chunyan Zhang

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午4:27写道:
>
> Hi Kevin
>
> On Tue, Jul 28, 2020 at 06:07:54PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > The Unisoc DRM master device is a virtual device needed to list all
> > DPU devices or other display interface nodes that comprise the
> > graphics subsystem
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
> >  1 file changed, 36 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> > new file mode 100644
> > index 0000000..b5792c0
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> drm seems like a sub-optimal name.
> How about usign the compatible name "display-subsystem" as it is a bit
> more specific (but not good).
You're right, maybe "sprd,drm.yaml" or "sprd-drm.yaml" will be better
>
> > @@ -0,0 +1,36 @@
> > +# SPDX-License-Identifier: GPL-2.0
>
> Any chance this can be (GPL-2.0-only OR BSD-2-Clause).
> I noticed that for example clock/sprd,sc9863a-clk.yaml uses this license
> so I hope this is OK.
I will add GPL-2.0-only to it.
>
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Unisoc DRM master device
> > +
> > +maintainers:
> > +  - Mark Rutland <mark.rutland@arm.com>
> > +
> > +description: |
> > +  The Unisoc DRM master device is a virtual device needed to list all
> > +  DPU devices or other display interface nodes that comprise the
> > +  graphics subsystem.
> > +
> > +properties:
> > +  compatible:
> > +    const: sprd,display-subsystem
> > +
> > +  ports:
> > +    description:
> > +      Should contain a list of phandles pointing to display interface port
> > +      of DPU devices.
> Add type - like this:
> $ref: /schemas/types.yaml#/definitions/phandle-array
>
> See for example display/rockchip/rockchip-drm.yaml
>
> Any specific reason why this is not a ports node like used by many other
> display bindings?
> In other words - I think this is too simple.
We only support one display pipeline now, other interface, like
DP(DisplayPort), HDMI...will be add later...

  ports:
    $ref: /schemas/types.yaml#/definitions/phandle-array
    description: |
      Should contain a list of phandles pointing to display interface port
      of dpu devices.. dpu definitions as defined in
      Documentation/devicetree/bindings/display/sprd/sprd,dpu.yaml


>
> > +
> > +required:
> > +  - compatible
> > +  - ports
> > +
>
> Add:
> additionalProperties: false
>
> so we catch if other properties sneak in.
>
> > +examples:
> > +  - |
> > +    display-subsystem {
> > +        compatible = "sprd,display-subsystem";
> > +        ports = <&dpu_out>;
> > +    };
> > +
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
@ 2020-08-26 17:11       ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-26 17:11 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Mark Rutland, David Airlie, Chunyan Zhang,
	Linux-Kernel@Vger. Kernel. Org, Rob Herring, ML dri-devel,
	Orson Zhai, Sean Paul

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午4:27写道:
>
> Hi Kevin
>
> On Tue, Jul 28, 2020 at 06:07:54PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > The Unisoc DRM master device is a virtual device needed to list all
> > DPU devices or other display interface nodes that comprise the
> > graphics subsystem
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
> >  1 file changed, 36 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> > new file mode 100644
> > index 0000000..b5792c0
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> drm seems like a sub-optimal name.
> How about usign the compatible name "display-subsystem" as it is a bit
> more specific (but not good).
You're right, maybe "sprd,drm.yaml" or "sprd-drm.yaml" will be better
>
> > @@ -0,0 +1,36 @@
> > +# SPDX-License-Identifier: GPL-2.0
>
> Any chance this can be (GPL-2.0-only OR BSD-2-Clause).
> I noticed that for example clock/sprd,sc9863a-clk.yaml uses this license
> so I hope this is OK.
I will add GPL-2.0-only to it.
>
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Unisoc DRM master device
> > +
> > +maintainers:
> > +  - Mark Rutland <mark.rutland@arm.com>
> > +
> > +description: |
> > +  The Unisoc DRM master device is a virtual device needed to list all
> > +  DPU devices or other display interface nodes that comprise the
> > +  graphics subsystem.
> > +
> > +properties:
> > +  compatible:
> > +    const: sprd,display-subsystem
> > +
> > +  ports:
> > +    description:
> > +      Should contain a list of phandles pointing to display interface port
> > +      of DPU devices.
> Add type - like this:
> $ref: /schemas/types.yaml#/definitions/phandle-array
>
> See for example display/rockchip/rockchip-drm.yaml
>
> Any specific reason why this is not a ports node like used by many other
> display bindings?
> In other words - I think this is too simple.
We only support one display pipeline now, other interface, like
DP(DisplayPort), HDMI...will be add later...

  ports:
    $ref: /schemas/types.yaml#/definitions/phandle-array
    description: |
      Should contain a list of phandles pointing to display interface port
      of dpu devices.. dpu definitions as defined in
      Documentation/devicetree/bindings/display/sprd/sprd,dpu.yaml


>
> > +
> > +required:
> > +  - compatible
> > +  - ports
> > +
>
> Add:
> additionalProperties: false
>
> so we catch if other properties sneak in.
>
> > +examples:
> > +  - |
> > +    display-subsystem {
> > +        compatible = "sprd,display-subsystem";
> > +        ports = <&dpu_out>;
> > +    };
> > +
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
  2020-07-28 20:45     ` Sam Ravnborg
@ 2020-08-28 16:04       ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-28 16:04 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Rob Herring, Mark Rutland, Orson Zhai,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, Chunyan Zhang

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午4:45写道:
>
> Hi Kevin.
>
> Nice split of the driver.
> Some feedback in the following.
> Most to bring the driver up-to-date with what have happened since
> we saw it last time.
> Keeping up with the changes in drm is not always easy.
>
>         Sam
>
Hi Sam,
Thanks for your feedback,
I am really busy sometimes, but will try my best to keep up with others.

>
> On Tue, Jul 28, 2020 at 06:07:55PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds drm support for the Unisoc's display subsystem.
> >
> > This is drm kms driver, this driver provides support for the
> > application framework in Android, Yocto and more.
> >
> > Application framework can access Unisoc's display internel
> > peripherals through libdrm or libkms, it's test ok by modetest
> > (DRM/KMS test tool) and Android HWComposer.
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  drivers/gpu/drm/Kconfig         |   2 +
> >  drivers/gpu/drm/Makefile        |   1 +
> >  drivers/gpu/drm/sprd/Kconfig    |  12 +++
> >  drivers/gpu/drm/sprd/Makefile   |   5 +
> >  drivers/gpu/drm/sprd/sprd_drm.c | 226 ++++++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
> >  6 files changed, 262 insertions(+)
> >  create mode 100644 drivers/gpu/drm/sprd/Kconfig
> >  create mode 100644 drivers/gpu/drm/sprd/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
> >
> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> > index c4fd57d..7fe7ab91 100644
> > --- a/drivers/gpu/drm/Kconfig
> > +++ b/drivers/gpu/drm/Kconfig
> > @@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
> >
> >  source "drivers/gpu/drm/tidss/Kconfig"
> >
> > +source "drivers/gpu/drm/sprd/Kconfig"
> > +
> >  # Keep legacy drivers last
> >
> >  menuconfig DRM_LEGACY
> > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> > index 2c0e5a7..ee2ccd9 100644
> > --- a/drivers/gpu/drm/Makefile
> > +++ b/drivers/gpu/drm/Makefile
> > @@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
> >  obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
> >  obj-$(CONFIG_DRM_MCDE) += mcde/
> >  obj-$(CONFIG_DRM_TIDSS) += tidss/
> > +obj-$(CONFIG_DRM_SPRD) += sprd/
> > diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
> > new file mode 100644
> > index 0000000..b189a54
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/Kconfig
> > @@ -0,0 +1,12 @@
> > +config DRM_SPRD
> > +     tristate "DRM Support for Unisoc SoCs Platform"
> > +     depends on ARCH_SPRD || COMPILE_TEST
> > +     depends on DRM && OF
> > +     select DRM_KMS_HELPER
> > +     select DRM_GEM_CMA_HELPER
> > +     select DRM_KMS_CMA_HELPER
> > +     select DRM_MIPI_DSI
> > +     help
> > +       Choose this option if you have a Unisoc chipsets.
> > +       If M is selected the module will be called sprd-drm.
> > +
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > new file mode 100644
> > index 0000000..86d95d9
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +subdir-ccflags-y += -I$(srctree)/$(src)
> subdir-ccflags-y is not needed - drop it.
>
> > +
> > +obj-y := sprd_drm.o
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > new file mode 100644
> > index 0000000..4706185
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -0,0 +1,226 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/of_platform.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_drv.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_vblank.h>
> > +
> > +#include "sprd_drm.h"
> > +
> > +#define DRIVER_NAME  "sprd"
> > +#define DRIVER_DESC  "Spreadtrum SoCs' DRM Driver"
> > +#define DRIVER_DATE  "20200201"
> > +#define DRIVER_MAJOR 1
> > +#define DRIVER_MINOR 0
> > +
> > +static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
> > +     .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> > +};
> > +
> > +static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
> > +     .fb_create = drm_gem_fb_create,
> > +     .atomic_check = drm_atomic_helper_check,
> > +     .atomic_commit = drm_atomic_helper_commit,
> > +};
> > +
> > +static void sprd_drm_mode_config_init(struct drm_device *drm)
> > +{
> > +     drm_mode_config_init(drm);
> The documentation of this functions says:
>
> FIXME: This function is deprecated and drivers should be converted over to
> drmm_mode_config_init().
>
>
> > +
> > +     drm->mode_config.min_width = 0;
> > +     drm->mode_config.min_height = 0;
> > +     drm->mode_config.max_width = 8192;
> > +     drm->mode_config.max_height = 8192;
> > +     drm->mode_config.allow_fb_modifiers = true;
> > +
> > +     drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
> > +     drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
> > +}
> > +
> > +DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
> > +
> > +static struct drm_driver sprd_drm_drv = {
> > +     .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
> > +     .fops                   = &sprd_drm_fops,
> > +
> > +     /* GEM Operations */
> > +     DRM_GEM_CMA_VMAP_DRIVER_OPS,
> > +
> > +     .name                   = DRIVER_NAME,
> > +     .desc                   = DRIVER_DESC,
> > +     .date                   = DRIVER_DATE,
> > +     .major                  = DRIVER_MAJOR,
> > +     .minor                  = DRIVER_MINOR,
> > +};
> > +
> > +static int sprd_drm_bind(struct device *dev)
> > +{
> > +     struct drm_device *drm;
> > +     struct sprd_drm *sprd;
> > +     int err;
> > +
> > +     drm = drm_dev_alloc(&sprd_drm_drv, dev);
> > +     if (IS_ERR(drm))
> > +             return PTR_ERR(drm);
> > +
> > +     dev_set_drvdata(dev, drm);
> > +
> > +     sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL);
> > +     if (!sprd)
> > +             err = -ENOMEM;
>
> Embed drm_device in sprd_drm and use devm_drm_dev_alloc
> See other drivers and drm_drv.c how to do it.
> drm_drv.c has a nice example explained in one of the comment blocks.
>
> > +
> > +     drm->dev_private = sprd;
> dev_private is deprecated. Alwyas use upclassing.
dev_private is deprecated ? I see everyone is still using it, so it
will be deprecated in the future?
>
> > +
> > +     sprd_drm_mode_config_init(drm);
> > +
> > +     /* bind and init sub drivers */
> > +     err = component_bind_all(drm->dev, drm);
> > +     if (err) {
> > +             DRM_ERROR("failed to bind all component.\n");
> > +             goto err_dc_cleanup;
> > +     }
> > +
> > +     /* vblank init */
> > +     err = drm_vblank_init(drm, drm->mode_config.num_crtc);
> > +     if (err) {
> > +             DRM_ERROR("failed to initialize vblank.\n");
> > +             goto err_unbind_all;
> > +     }
> > +     /* with irq_enabled = true, we can use the vblank feature. */
> > +     drm->irq_enabled = true;
> Can drm_irq_install() be used?
> Then this flag shall not be set by the driver, And the interrupt numbers on different Soc are not necessarily the same
>
We need to set "IRQ_NOAUTOEN" flag for CRTC IRQ, you can see that we
manually turn the interrupt on and off by crtc
atomic_enable/atomic_disable
and the interrupt number on different Soc are not necessarily the
same. So intall interrupt in kms maybe is not suitable for us...
> > +
> > +     /* reset all the states of crtc/plane/encoder/connector */
> > +     drm_mode_config_reset(drm);
> > +
> > +     /* init kms poll for handling hpd */
> > +     drm_kms_helper_poll_init(drm);
> > +
> > +     err = drm_dev_register(drm, 0);
> > +     if (err < 0)
> > +             goto err_kms_helper_poll_fini;
> > +
> > +     return 0;
> > +
> > +err_kms_helper_poll_fini:
> > +     drm_kms_helper_poll_fini(drm);
> > +err_unbind_all:
> > +     component_unbind_all(drm->dev, drm);
> > +err_dc_cleanup:
> > +     drm_mode_config_cleanup(drm);
> > +     return err;
> > +}
> > +
> > +static void sprd_drm_unbind(struct device *dev)
> > +{
> > +     struct drm_device *drm = dev_get_drvdata(dev);
> > +
> > +     drm_dev_unregister(drm);
> > +
> > +     drm_kms_helper_poll_fini(drm);
> > +
> > +     drm_mode_config_cleanup(drm);
> > +
> > +     component_unbind_all(drm->dev, drm);
> > +     drm->dev_private = NULL;
> > +     dev_set_drvdata(dev, NULL);
> > +
> > +     drm_dev_put(drm);
> > +}
> > +
> > +static const struct component_master_ops drm_component_ops = {
> > +     .bind = sprd_drm_bind,
> > +     .unbind = sprd_drm_unbind,
> > +};
> > +
> > +static int compare_of(struct device *dev, void *data)
> > +{
> > +     return dev->of_node == data;
> > +}
> > +
> > +static int sprd_drm_probe(struct platform_device *pdev)
> > +{
> > +     int ret;
> > +
> > +     ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
> > +     if (ret) {
> > +             DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops);
> > +}
> > +
> > +static int sprd_drm_remove(struct platform_device *pdev)
> > +{
> > +     component_master_del(&pdev->dev, &drm_component_ops);
> > +     return 0;
> > +}
> > +
> > +static void sprd_drm_shutdown(struct platform_device *pdev)
> > +{
> > +     struct drm_device *drm = platform_get_drvdata(pdev);
> > +
> > +     if (!drm) {
> > +             DRM_WARN("drm device is not available, no shutdown\n");
> > +             return;
> > +     }
> > +
> > +     drm_atomic_helper_shutdown(drm);
> > +}
> > +
> > +static const struct of_device_id drm_match_table[] = {
> > +     { .compatible = "sprd,display-subsystem", },
> > +     { /* sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(of, drm_match_table);
> > +
> > +static struct platform_driver sprd_drm_driver = {
> > +     .probe = sprd_drm_probe,
> > +     .remove = sprd_drm_remove,
> > +     .shutdown = sprd_drm_shutdown,
> > +     .driver = {
> > +             .name = "sprd-drm-drv",
> > +             .of_match_table = drm_match_table,
> > +     },
> > +};
> > +
> > +static struct platform_driver *sprd_drm_drivers[]  = {
> > +     &sprd_drm_driver,
> > +};
> > +
> > +static int __init sprd_drm_init(void)
> > +{
> > +     int ret;
> > +
> > +     ret = platform_register_drivers(sprd_drm_drivers,
> > +                                     ARRAY_SIZE(sprd_drm_drivers));
> > +     return ret;
> > +}
> > +
> > +static void __exit sprd_drm_exit(void)
> > +{
> > +     platform_unregister_drivers(sprd_drm_drivers,
> > +                                 ARRAY_SIZE(sprd_drm_drivers));
> > +}
> > +
> > +module_init(sprd_drm_init);
> > +module_exit(sprd_drm_exit);
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > new file mode 100644
> > index 0000000..edf0881
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef _SPRD_DRM_H_
> > +#define _SPRD_DRM_H_
> > +
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_print.h>
> > +
> > +struct sprd_drm {
> > +     struct drm_device *drm;
> > +};
> > +
> > +#endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
@ 2020-08-28 16:04       ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-28 16:04 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Mark Rutland, David Airlie, Chunyan Zhang,
	Linux-Kernel@Vger. Kernel. Org, Rob Herring, ML dri-devel,
	Orson Zhai, Sean Paul

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午4:45写道:
>
> Hi Kevin.
>
> Nice split of the driver.
> Some feedback in the following.
> Most to bring the driver up-to-date with what have happened since
> we saw it last time.
> Keeping up with the changes in drm is not always easy.
>
>         Sam
>
Hi Sam,
Thanks for your feedback,
I am really busy sometimes, but will try my best to keep up with others.

>
> On Tue, Jul 28, 2020 at 06:07:55PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds drm support for the Unisoc's display subsystem.
> >
> > This is drm kms driver, this driver provides support for the
> > application framework in Android, Yocto and more.
> >
> > Application framework can access Unisoc's display internel
> > peripherals through libdrm or libkms, it's test ok by modetest
> > (DRM/KMS test tool) and Android HWComposer.
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  drivers/gpu/drm/Kconfig         |   2 +
> >  drivers/gpu/drm/Makefile        |   1 +
> >  drivers/gpu/drm/sprd/Kconfig    |  12 +++
> >  drivers/gpu/drm/sprd/Makefile   |   5 +
> >  drivers/gpu/drm/sprd/sprd_drm.c | 226 ++++++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
> >  6 files changed, 262 insertions(+)
> >  create mode 100644 drivers/gpu/drm/sprd/Kconfig
> >  create mode 100644 drivers/gpu/drm/sprd/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
> >
> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> > index c4fd57d..7fe7ab91 100644
> > --- a/drivers/gpu/drm/Kconfig
> > +++ b/drivers/gpu/drm/Kconfig
> > @@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
> >
> >  source "drivers/gpu/drm/tidss/Kconfig"
> >
> > +source "drivers/gpu/drm/sprd/Kconfig"
> > +
> >  # Keep legacy drivers last
> >
> >  menuconfig DRM_LEGACY
> > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> > index 2c0e5a7..ee2ccd9 100644
> > --- a/drivers/gpu/drm/Makefile
> > +++ b/drivers/gpu/drm/Makefile
> > @@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
> >  obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
> >  obj-$(CONFIG_DRM_MCDE) += mcde/
> >  obj-$(CONFIG_DRM_TIDSS) += tidss/
> > +obj-$(CONFIG_DRM_SPRD) += sprd/
> > diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig
> > new file mode 100644
> > index 0000000..b189a54
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/Kconfig
> > @@ -0,0 +1,12 @@
> > +config DRM_SPRD
> > +     tristate "DRM Support for Unisoc SoCs Platform"
> > +     depends on ARCH_SPRD || COMPILE_TEST
> > +     depends on DRM && OF
> > +     select DRM_KMS_HELPER
> > +     select DRM_GEM_CMA_HELPER
> > +     select DRM_KMS_CMA_HELPER
> > +     select DRM_MIPI_DSI
> > +     help
> > +       Choose this option if you have a Unisoc chipsets.
> > +       If M is selected the module will be called sprd-drm.
> > +
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > new file mode 100644
> > index 0000000..86d95d9
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +subdir-ccflags-y += -I$(srctree)/$(src)
> subdir-ccflags-y is not needed - drop it.
>
> > +
> > +obj-y := sprd_drm.o
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > new file mode 100644
> > index 0000000..4706185
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -0,0 +1,226 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/of_platform.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_drv.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_vblank.h>
> > +
> > +#include "sprd_drm.h"
> > +
> > +#define DRIVER_NAME  "sprd"
> > +#define DRIVER_DESC  "Spreadtrum SoCs' DRM Driver"
> > +#define DRIVER_DATE  "20200201"
> > +#define DRIVER_MAJOR 1
> > +#define DRIVER_MINOR 0
> > +
> > +static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
> > +     .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
> > +};
> > +
> > +static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
> > +     .fb_create = drm_gem_fb_create,
> > +     .atomic_check = drm_atomic_helper_check,
> > +     .atomic_commit = drm_atomic_helper_commit,
> > +};
> > +
> > +static void sprd_drm_mode_config_init(struct drm_device *drm)
> > +{
> > +     drm_mode_config_init(drm);
> The documentation of this functions says:
>
> FIXME: This function is deprecated and drivers should be converted over to
> drmm_mode_config_init().
>
>
> > +
> > +     drm->mode_config.min_width = 0;
> > +     drm->mode_config.min_height = 0;
> > +     drm->mode_config.max_width = 8192;
> > +     drm->mode_config.max_height = 8192;
> > +     drm->mode_config.allow_fb_modifiers = true;
> > +
> > +     drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
> > +     drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
> > +}
> > +
> > +DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
> > +
> > +static struct drm_driver sprd_drm_drv = {
> > +     .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
> > +     .fops                   = &sprd_drm_fops,
> > +
> > +     /* GEM Operations */
> > +     DRM_GEM_CMA_VMAP_DRIVER_OPS,
> > +
> > +     .name                   = DRIVER_NAME,
> > +     .desc                   = DRIVER_DESC,
> > +     .date                   = DRIVER_DATE,
> > +     .major                  = DRIVER_MAJOR,
> > +     .minor                  = DRIVER_MINOR,
> > +};
> > +
> > +static int sprd_drm_bind(struct device *dev)
> > +{
> > +     struct drm_device *drm;
> > +     struct sprd_drm *sprd;
> > +     int err;
> > +
> > +     drm = drm_dev_alloc(&sprd_drm_drv, dev);
> > +     if (IS_ERR(drm))
> > +             return PTR_ERR(drm);
> > +
> > +     dev_set_drvdata(dev, drm);
> > +
> > +     sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL);
> > +     if (!sprd)
> > +             err = -ENOMEM;
>
> Embed drm_device in sprd_drm and use devm_drm_dev_alloc
> See other drivers and drm_drv.c how to do it.
> drm_drv.c has a nice example explained in one of the comment blocks.
>
> > +
> > +     drm->dev_private = sprd;
> dev_private is deprecated. Alwyas use upclassing.
dev_private is deprecated ? I see everyone is still using it, so it
will be deprecated in the future?
>
> > +
> > +     sprd_drm_mode_config_init(drm);
> > +
> > +     /* bind and init sub drivers */
> > +     err = component_bind_all(drm->dev, drm);
> > +     if (err) {
> > +             DRM_ERROR("failed to bind all component.\n");
> > +             goto err_dc_cleanup;
> > +     }
> > +
> > +     /* vblank init */
> > +     err = drm_vblank_init(drm, drm->mode_config.num_crtc);
> > +     if (err) {
> > +             DRM_ERROR("failed to initialize vblank.\n");
> > +             goto err_unbind_all;
> > +     }
> > +     /* with irq_enabled = true, we can use the vblank feature. */
> > +     drm->irq_enabled = true;
> Can drm_irq_install() be used?
> Then this flag shall not be set by the driver, And the interrupt numbers on different Soc are not necessarily the same
>
We need to set "IRQ_NOAUTOEN" flag for CRTC IRQ, you can see that we
manually turn the interrupt on and off by crtc
atomic_enable/atomic_disable
and the interrupt number on different Soc are not necessarily the
same. So intall interrupt in kms maybe is not suitable for us...
> > +
> > +     /* reset all the states of crtc/plane/encoder/connector */
> > +     drm_mode_config_reset(drm);
> > +
> > +     /* init kms poll for handling hpd */
> > +     drm_kms_helper_poll_init(drm);
> > +
> > +     err = drm_dev_register(drm, 0);
> > +     if (err < 0)
> > +             goto err_kms_helper_poll_fini;
> > +
> > +     return 0;
> > +
> > +err_kms_helper_poll_fini:
> > +     drm_kms_helper_poll_fini(drm);
> > +err_unbind_all:
> > +     component_unbind_all(drm->dev, drm);
> > +err_dc_cleanup:
> > +     drm_mode_config_cleanup(drm);
> > +     return err;
> > +}
> > +
> > +static void sprd_drm_unbind(struct device *dev)
> > +{
> > +     struct drm_device *drm = dev_get_drvdata(dev);
> > +
> > +     drm_dev_unregister(drm);
> > +
> > +     drm_kms_helper_poll_fini(drm);
> > +
> > +     drm_mode_config_cleanup(drm);
> > +
> > +     component_unbind_all(drm->dev, drm);
> > +     drm->dev_private = NULL;
> > +     dev_set_drvdata(dev, NULL);
> > +
> > +     drm_dev_put(drm);
> > +}
> > +
> > +static const struct component_master_ops drm_component_ops = {
> > +     .bind = sprd_drm_bind,
> > +     .unbind = sprd_drm_unbind,
> > +};
> > +
> > +static int compare_of(struct device *dev, void *data)
> > +{
> > +     return dev->of_node == data;
> > +}
> > +
> > +static int sprd_drm_probe(struct platform_device *pdev)
> > +{
> > +     int ret;
> > +
> > +     ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
> > +     if (ret) {
> > +             DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops);
> > +}
> > +
> > +static int sprd_drm_remove(struct platform_device *pdev)
> > +{
> > +     component_master_del(&pdev->dev, &drm_component_ops);
> > +     return 0;
> > +}
> > +
> > +static void sprd_drm_shutdown(struct platform_device *pdev)
> > +{
> > +     struct drm_device *drm = platform_get_drvdata(pdev);
> > +
> > +     if (!drm) {
> > +             DRM_WARN("drm device is not available, no shutdown\n");
> > +             return;
> > +     }
> > +
> > +     drm_atomic_helper_shutdown(drm);
> > +}
> > +
> > +static const struct of_device_id drm_match_table[] = {
> > +     { .compatible = "sprd,display-subsystem", },
> > +     { /* sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(of, drm_match_table);
> > +
> > +static struct platform_driver sprd_drm_driver = {
> > +     .probe = sprd_drm_probe,
> > +     .remove = sprd_drm_remove,
> > +     .shutdown = sprd_drm_shutdown,
> > +     .driver = {
> > +             .name = "sprd-drm-drv",
> > +             .of_match_table = drm_match_table,
> > +     },
> > +};
> > +
> > +static struct platform_driver *sprd_drm_drivers[]  = {
> > +     &sprd_drm_driver,
> > +};
> > +
> > +static int __init sprd_drm_init(void)
> > +{
> > +     int ret;
> > +
> > +     ret = platform_register_drivers(sprd_drm_drivers,
> > +                                     ARRAY_SIZE(sprd_drm_drivers));
> > +     return ret;
> > +}
> > +
> > +static void __exit sprd_drm_exit(void)
> > +{
> > +     platform_unregister_drivers(sprd_drm_drivers,
> > +                                 ARRAY_SIZE(sprd_drm_drivers));
> > +}
> > +
> > +module_init(sprd_drm_init);
> > +module_exit(sprd_drm_exit);
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > new file mode 100644
> > index 0000000..edf0881
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef _SPRD_DRM_H_
> > +#define _SPRD_DRM_H_
> > +
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_print.h>
> > +
> > +struct sprd_drm {
> > +     struct drm_device *drm;
> > +};
> > +
> > +#endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 21:13     ` Sam Ravnborg
@ 2020-08-28 17:08       ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-28 17:08 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Rob Herring, Mark Rutland, Orson Zhai,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, Chunyan Zhang

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午5:14写道:
>
> Hi Kevin.
>
> Some feedback in the following.
> I lost track of thing for the atomic modesettting stuff and I hope other
> will review that.
>
>         Sam
>
> On Tue, Jul 28, 2020 at 06:07:57PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> Not needed - drop.
>
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +     sprd_dpu.o
> > +
> > +obj-y += dpu/
>
> Until there are several DPU's there is no need for a separate directory.
> Make it simpler by keeping it all in drm/sprd/*
Ok I will move DPU IP code to drm/sprd
>
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE 0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET   0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +     (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> Use a static inline to get better typecheck.
>
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> I am no fan of macros used like this.
>
> Maybe use static inlines and add struct dpu_context * as first argument.
> Then adding base can be hidden away.

Maybe a good idea, i will try it, like this:
static inline void DPU_REG_CLR(dpu_context *ctx, u32 reg, u32 mask) {
    writel_relaxed(readl_relaxed(ctx->base + reg) & ~mask, ctx->base + reg);
}

>
>
> I had a hard time convincing myself that _relaxed was the right
> variants. If I get it correct we may see writes re-ordered with the
> _relaxed variants wich would be no good when dealign with interrupts.
> But I may miss somethign here.
>
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL 0x04
> > +#define REG_DPU_CFG0 0x08
> > +#define REG_DPU_CFG1 0x0C
> > +#define REG_DPU_CFG2 0x10
> > +#define REG_PANEL_SIZE       0x20
> > +#define REG_BLEND_SIZE       0x24
> > +#define REG_BG_COLOR 0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0   0x30
> > +#define REG_LAY_BASE_ADDR1   0x34
> > +#define REG_LAY_BASE_ADDR2   0x38
> > +#define REG_LAY_CTRL         0x40
> > +#define REG_LAY_SIZE         0x44
> > +#define REG_LAY_PITCH                0x48
> > +#define REG_LAY_POS          0x4C
> > +#define REG_LAY_ALPHA                0x50
> > +#define REG_LAY_PALLETE              0x58
> > +#define REG_LAY_CROP_START   0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN               0x1E0
> > +#define REG_DPU_INT_CLR              0x1E4
> > +#define REG_DPU_INT_STS              0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL         0x1F0
> > +#define REG_DPI_H_TIMING     0x1F4
> > +#define REG_DPI_V_TIMING     0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                   0x800
> > +#define REG_MMU_VPN_RANGE            0x80C
> > +#define REG_MMU_VAOR_ADDR_RD         0x818
> > +#define REG_MMU_VAOR_ADDR_WR         0x81C
> > +#define REG_MMU_INV_ADDR_RD          0x820
> > +#define REG_MMU_INV_ADDR_WR          0x824
> > +#define REG_MMU_PPN1                 0x83C
> > +#define REG_MMU_RANGE1                       0x840
> > +#define REG_MMU_PPN2                 0x844
> > +#define REG_MMU_RANGE2                       0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                  BIT(0)
> > +#define BIT_DPU_STOP                 BIT(1)
> > +#define BIT_DPU_REG_UPDATE           BIT(2)
> > +#define BIT_DPU_IF_EDPI                      BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE            BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD     BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                               BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE             BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR              BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR              BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD              BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR              BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD               BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR               BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN           BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD       BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN          BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +     u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                     BIT_DPU_INT_MMU_VAOR_WR |
> > +                     BIT_DPU_INT_MMU_INV_RD |
> > +                     BIT_DPU_INT_MMU_INV_WR;
> > +     u32 val = reg_val & mmu_mask;
> > +     int i;
> > +
> > +     if (val) {
> > +             DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
>
> General comment, applies for the whole patch.
>
> Use drm_err(drm, ...), drm_info(drm, ...) etc.
>
> Use upclassing to get sprd_dpu - and then you have a drm_device *
> (After drm_device is embedded in sprd_dpu as suggested in the other
> mail)
Ok, i will replace DRM_XXX with drm_xxx.
>
> > +
> > +             if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                     DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                     DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                     DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                     DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> > +
> > +             for (i = 0; i < 8; i++) {
> > +                     reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                     if (reg_val & 0x1)
> > +                             DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +             }
> > +     }
> > +
> > +     return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < 8; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, int_mask = 0;
> > +
> > +     reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +     /* disable err interrupt */
> > +     if (reg_val & BIT_DPU_INT_ERR)
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +
> > +     /* dpu update done isr */
> > +     if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +             ctx->evt_update = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu stop done isr */
> > +     if (reg_val & BIT_DPU_INT_DONE) {
> > +             ctx->evt_stop = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu ifbc payload error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +             DRM_ERROR("dpu ifbc payload error\n");
> > +     }
> > +
> > +     /* dpu ifbc header error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +             DRM_ERROR("dpu ifbc header error\n");
> > +     }
> > +
> > +     int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +     return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     if (ctx->stopped)
> > +             return 0;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> > +                                            msecs_to_jiffies(500));
> > +     ctx->evt_stop = false;
> > +
> > +     ctx->stopped = true;
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for stop done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     ctx->evt_update = false;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> > +                                            msecs_to_jiffies(500));
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for reg update done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +     dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +     ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, size;
> > +
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +     DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +     DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +     reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +     if (ctx->stopped)
> > +             dpu_clean_all(ctx);
> > +
> > +     DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                 struct dpu_layer *hwlayer)
> No linebreak needed here.
>
> > +{
> > +     const struct drm_format_info *info;
> > +     u32 size, offset, ctrl, pitch;
> > +     int i;
> > +
> > +     offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +     if (hwlayer->src_w && hwlayer->src_h)
> > +             size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> > +     else
> > +             size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> > +
> > +     for (i = 0; i < hwlayer->planes; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                             hwlayer->index), hwlayer->addr[i]);
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                     hwlayer->index), offset);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                     hwlayer->index), size);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                     hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                     hwlayer->index), hwlayer->alpha);
> > +
> > +     info = drm_format_info(hwlayer->format);
> > +     if (hwlayer->planes == 3) {
> > +             /* UV pitch is 1/2 of Y pitch*/
> > +             pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                             (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     } else {
> > +             pitch = hwlayer->pitch[0] / info->cpp[0];
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     }
> > +
> > +     ctrl = hwlayer->format |
> > +             hwlayer->blending |
> > +             (hwlayer->rotation & 0x7) << 20;
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), ctrl);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +     DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                             hwlayer->dst_x, hwlayer->dst_y,
> > +                             hwlayer->dst_w, hwlayer->dst_h);
> > +     DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> > +                             hwlayer->src_x, hwlayer->src_y,
> > +                             hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count)
> > +{
> > +     int i;
> > +     u32 reg_val;
> > +
> > +     /*
> > +      * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +      * registers in EDPI mode. So the config registers can only be
> > +      * updated in the rising edge of DPU_RUN bit.
> > +      */
> > +     if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +             dpu_wait_stop_done(ctx);
> > +
> > +     /* reset the bgcolor to black */
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     /* disable all the layers */
> > +     dpu_clean_all(ctx);
> > +
> > +     /* start configure dpu layers */
> > +     for (i = 0; i < count; i++)
> > +             dpu_layer(ctx, &layers[i]);
> > +
> > +     /* update trigger and wait */
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             if (!ctx->stopped) {
> > +                     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> > +                     dpu_wait_update_done(ctx);
> > +             }
> > +
> > +             DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +             ctx->stopped = false;
> > +     }
> > +
> > +     /*
> > +      * If the following interrupt was disabled in isr,
> > +      * re-enable it.
> > +      */
> > +     reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +               BIT_DPU_INT_FBC_HDR_ERR |
> > +               BIT_DPU_INT_MMU_VAOR_RD |
> > +               BIT_DPU_INT_MMU_VAOR_WR |
> > +               BIT_DPU_INT_MMU_INV_RD |
> > +               BIT_DPU_INT_MMU_INV_WR;
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +     u32 int_mask = 0;
> > +     u32 reg_val;
> > +
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             /* use dpi as interface */
> > +             DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* disable Halt function for SPRD DSI */
> > +             DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> > +
> > +             /* select te from external pad */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* set dpi timing */
> > +             reg_val = ctx->vm.hsync_len << 0 |
> > +                       ctx->vm.hback_porch << 8 |
> > +                       ctx->vm.hfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +             reg_val = ctx->vm.vsync_len << 0 |
> > +                       ctx->vm.vback_porch << 8 |
> > +                       ctx->vm.vfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                     DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                             "underflow risk!\n");
> > +
> > +             /* enable dpu update done INT */
> > +             int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +             /* enable dpu DONE  INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable dpu dpi vsync */
> > +             int_mask |= BIT_DPU_INT_VSYNC;
> > +             /* enable dpu TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +             /* enable underflow err INT */
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             /* use edpi as interface */
> > +             DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* use external te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* enable te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> > +
> > +             /* enable stop DONE INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +     }
> > +
> > +     /* enable ifbc payload error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +     /* enable ifbc header error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +     /* enable iommu va out of range read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +     /* enable iommu va out of range write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +     /* enable iommu invalid read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +     /* enable iommu invalid write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +     DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +     DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +     DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +     DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +     DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +     DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +     DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +     DRM_FORMAT_YVU420,
> > +};
> One format per line improves readability a lot.
> Maybe sort alphbetically too.
>
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap)
> Drop linebreak
>
> > +{
> > +     cap->max_layers = 6;
> > +     cap->fmts_ptr = primary_fmts;
> > +     cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +     .init = dpu_init,
> > +     .fini = dpu_fini,
> > +     .run = dpu_run,
> > +     .stop = dpu_stop,
> > +     .isr = dpu_isr,
> > +     .ifconfig = dpu_dpi_init,
> > +     .capability = dpu_capability,
> > +     .flip = dpu_flip,
> > +     .enable_vsync = enable_vsync,
> > +     .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +     struct drm_plane plane;
> > +     u32 index;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     u32 format;
> > +     u32 rotation;
> > +     u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +     return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +     switch (fourcc) {
> > +     case DRM_FORMAT_BGRA8888:
> > +             /* BGRA8888 -> ARGB8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_RGBX8888:
> > +     case DRM_FORMAT_RGBA8888:
> > +             /* RGBA8888 -> ABGR8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ABGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ARGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_XBGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_XRGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_BGR565:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_RGB565:
> > +             *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +             break;
> > +     case DRM_FORMAT_NV12:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV21:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV16:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV61:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YUV420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YVU420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +     switch (angle) {
> > +     case DRM_MODE_ROTATE_0:
> > +             *rotation = DPU_LAYER_ROTATION_0;
> > +             break;
> > +     case DRM_MODE_ROTATE_90:
> > +             *rotation = DPU_LAYER_ROTATION_90;
> > +             break;
> > +     case DRM_MODE_ROTATE_180:
> > +             *rotation = DPU_LAYER_ROTATION_180;
> > +             break;
> > +     case DRM_MODE_ROTATE_270:
> > +             *rotation = DPU_LAYER_ROTATION_270;
> > +             break;
> > +     case DRM_MODE_REFLECT_Y:
> > +             *rotation = DPU_LAYER_ROTATION_180_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_90_M;
> > +             break;
> > +     case DRM_MODE_REFLECT_X:
> > +             *rotation = DPU_LAYER_ROTATION_0_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_270_M;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                               struct drm_plane_state *state)
> > +{
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct drm_framebuffer *fb = state->fb;
> > +     struct drm_gem_cma_object *cma_obj;
> > +     int i, ret;
> > +     u32 addr;
> > +
> > +     if (!state->fb || !state->crtc)
> > +             return 0;
> > +
> > +     ret = sprd_plane_format_convert(fb->format->format,
> > +                                     &p->format);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane format\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_plane_rotation_convert(state->rotation,
> > +                                     &p->rotation);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane rotation\n");
> > +             return ret;
> > +     }
> > +
> > +     switch (state->pixel_blend_mode) {
> > +     case DRM_MODE_BLEND_COVERAGE:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Normal mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +             break;
> > +     case DRM_MODE_BLEND_PREMULTI:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Pre-mult mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +             break;
> > +     case DRM_MODE_BLEND_PIXEL_NONE:
> > +     default:
> > +             /* don't do blending, maybe RGBX */
> > +             /* alpha mode select - layer alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +             break;
> > +     }
> > +
> > +     for (i = 0; i < fb->format->num_planes; i++) {
> > +             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +             addr = cma_obj->paddr + fb->offsets[i];
> > +             if (addr % 16) {
> > +                     DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> > +                             i, addr);
> > +                     return -EFAULT;
> > +             }
> > +
> > +             p->addr[i] = addr;
> > +             p->pitch[i] = fb->pitches[i];
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                 struct drm_plane_state *old_state)
> > +{
> > +     struct drm_plane_state *state = plane->state;
> > +     struct drm_framebuffer *fb = plane->state->fb;
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +     struct dpu_layer *layer = &dpu->layers[p->index];
> > +     int i;
> > +
> > +     if (!state->crtc || !state->fb)
> > +             return;
> > +
> > +     layer->index = p->index;
> > +     layer->src_x = state->src_x >> 16;
> > +     layer->src_y = state->src_y >> 16;
> > +     layer->src_w = state->src_w >> 16;
> > +     layer->src_h = state->src_h >> 16;
> > +     layer->dst_x = state->crtc_x;
> > +     layer->dst_y = state->crtc_y;
> > +     layer->dst_w = state->crtc_w;
> > +     layer->dst_h = state->crtc_h;
> > +     layer->alpha = state->alpha;
> > +     layer->format = p->format;
> > +     layer->blending = p->blend_mode;
> > +     layer->rotation = p->rotation;
> > +     layer->planes = fb->format->num_planes;
> > +
> > +     for (i = 0; i < layer->planes; i++) {
> > +             layer->addr[i] = p->addr[i];
> > +             layer->pitch[i] = p->pitch[i];
> > +     }
> > +
> > +     dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> > +{
> > +     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                    BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +     /* create rotation property */
> > +     drm_plane_create_rotation_property(&p->plane,
> > +                                        DRM_MODE_ROTATE_0,
> > +                                        DRM_MODE_ROTATE_MASK |
> > +                                        DRM_MODE_REFLECT_MASK);
> > +
> > +     /* create alpha property */
> > +     drm_plane_create_alpha_property(&p->plane);
> > +
> > +     /* create blend mode property */
> > +     drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +     /* create zpos property */
> > +     drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +     .atomic_check = sprd_plane_atomic_check,
> > +     .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +     .update_plane = drm_atomic_helper_update_plane,
> > +     .disable_plane  = drm_atomic_helper_disable_plane,
> > +     .destroy = drm_plane_cleanup,
> > +     .reset = drm_atomic_helper_plane_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                     struct sprd_dpu *dpu)
> > +{
> > +     struct drm_plane *primary = NULL;
> > +     struct sprd_plane *p = NULL;
> > +     struct dpu_capability cap = {};
> > +     int ret, i;
> > +
> > +     dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +     dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                               sizeof(struct dpu_layer), GFP_KERNEL);
> > +     if (!dpu->layers)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     for (i = 0; i < cap.max_layers; i++) {
> > +
> > +             p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +             if (!p)
> > +                     return ERR_PTR(-ENOMEM);
> > +
> > +             ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                            &sprd_plane_funcs, cap.fmts_ptr,
> > +                                            cap.fmts_cnt, NULL,
> > +                                            DRM_PLANE_TYPE_PRIMARY, NULL);
> > +             if (ret) {
> > +                     DRM_ERROR("fail to init primary plane\n");
> > +                     return ERR_PTR(ret);
> > +             }
> > +
> > +             drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> > +
> > +             sprd_plane_create_properties(p, i);
> > +
> > +             p->index = i;
> > +             if (i == 0)
> > +                     primary = &p->plane;
> > +     }
> > +
> > +     return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                     const struct drm_display_mode *mode)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> > +
> > +     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +             if ((mode->hdisplay == mode->htotal) ||
> > +                 (mode->vdisplay == mode->vtotal))
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +             else
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +     }
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     sprd_dpu_init(dpu);
> > +
> > +     enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     disable_irq(dpu->ctx.irq);
> > +
> > +     sprd_dpu_fini(dpu);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                              struct drm_crtc_state *state)
> > +{
> > +     DRM_DEBUG("%s()\n", __func__);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> > +
> > +     dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     if (dpu->pending_planes)
> > +             dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +     .mode_valid     = sprd_crtc_mode_valid,
> > +     .atomic_check   = sprd_crtc_atomic_check,
> > +     .atomic_begin   = sprd_crtc_atomic_begin,
> > +     .atomic_flush   = sprd_crtc_atomic_flush,
> > +     .atomic_enable  = sprd_crtc_atomic_enable,
> > +     .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +     .destroy        = drm_crtc_cleanup,
> > +     .set_config     = drm_atomic_helper_set_config,
> > +     .page_flip      = drm_atomic_helper_page_flip,
> > +     .reset          = drm_atomic_helper_crtc_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +     .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +     .enable_vblank  = sprd_crtc_enable_vblank,
> > +     .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                      struct drm_plane *primary)
> > +{
> > +     struct device_node *port;
> > +     int ret;
> > +
> > +     /*
> > +      * set crtc port so that drm_of_find_possible_crtcs call works
> > +      */
> > +     port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +     if (!port) {
> > +             DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                       drm->dev->of_node->full_name);
> > +             return -EINVAL;
> > +     }
> > +     of_node_put(port);
> > +     crtc->port = port;
> > +
> > +     ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                     &sprd_crtc_funcs, NULL);
> > +     if (ret) {
> > +             DRM_ERROR("failed to init crtc.\n");
> > +             return ret;
> > +     }
> > +
> > +     drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +     drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->init(ctx);
> > +     dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +     struct sprd_dpu *dpu = data;
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     u32 int_mask = 0;
> > +
> > +     int_mask = dpu->core->isr(ctx);
> > +
> > +     if (int_mask & BIT_DPU_INT_ERR)
> > +             DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +     if (int_mask & BIT_DPU_INT_VSYNC)
> > +             drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +     struct drm_device *drm = data;
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +     struct drm_plane *plane;
> > +     int ret;
> > +
> > +     plane = sprd_plane_init(drm, dpu);
> > +     if (IS_ERR_OR_NULL(plane)) {
> > +             ret = PTR_ERR(plane);
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +     void *data)
> > +{
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +     drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +     .bind = sprd_dpu_bind,
> > +     .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                             struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     struct resource *res;
> > +     int ret;
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +     if (!ctx->base) {
> > +             DRM_ERROR("failed to map dpu registers\n");
> > +             return -EFAULT;
> > +     }
> > +
> > +     ctx->irq = platform_get_irq(pdev, 0);
> > +     if (ctx->irq < 0) {
> > +             DRM_ERROR("failed to get dpu irq\n");
> > +             return ctx->irq;
> > +     }
> > +
> > +     irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +     ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                     0, "DPU", dpu);
> > +     if (ret) {
> > +             DRM_ERROR("failed to register dpu irq handler\n");
> > +             return ret;
> > +     }
> > +
> > +     init_waitqueue_head(&ctx->wait_queue);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +     .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +     { .compatible = "sprd,sharkl3-dpu",
> > +       .data = &sharkl3_dpu },
> > +     { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +     const struct sprd_dpu_ops *pdata;
> > +     struct sprd_dpu *dpu;
> > +     int ret;
> > +
> > +     dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +     if (!dpu)
> > +             return -ENOMEM;
> > +
> > +     pdata = of_device_get_match_data(&pdev->dev);
> > +     if (pdata) {
> > +             dpu->core = pdata->core;
> > +     } else {
> > +             DRM_ERROR("No matching driver data found\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +     if (ret)
> > +             return ret;
> > +
> > +     platform_set_drvdata(pdev, dpu);
> > +
> > +     return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +     component_del(&pdev->dev, &dpu_component_ops);
> > +     return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +     .probe = sprd_dpu_probe,
> > +     .remove = sprd_dpu_remove,
> > +     .driver = {
> > +             .name = "sprd-dpu-drv",
> > +             .of_match_table = dpu_match_table,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_            BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE          BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_WB_DONE          BIT(6)
> > +#define BIT_DPU_INT_WB_ERR           BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
> > +
> > +enum {
> > +     SPRD_DPU_IF_DBI = 0,
> > +     SPRD_DPU_IF_DPI,
> > +     SPRD_DPU_IF_EDPI,
> > +     SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +     DPU_LAYER_ROTATION_0,
> > +     DPU_LAYER_ROTATION_90,
> > +     DPU_LAYER_ROTATION_180,
> > +     DPU_LAYER_ROTATION_270,
> > +     DPU_LAYER_ROTATION_0_M,
> > +     DPU_LAYER_ROTATION_90_M,
> > +     DPU_LAYER_ROTATION_180_M,
> > +     DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +     u8 index;
> > +     u8 planes;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     s16 src_x;
> > +     s16 src_y;
> > +     s16 src_w;
> > +     s16 src_h;
> > +     s16 dst_x;
> > +     s16 dst_y;
> > +     u16 dst_w;
> > +     u16 dst_h;
> > +     u32 format;
> > +     u32 alpha;
> > +     u32 blending;
> > +     u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +     u32 max_layers;
> > +     const u32 *fmts_ptr;
> > +     u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +     void (*init)(struct dpu_context *ctx);
> > +     void (*fini)(struct dpu_context *ctx);
> > +     void (*run)(struct dpu_context *ctx);
> > +     void (*stop)(struct dpu_context *ctx);
> > +     void (*enable_vsync)(struct dpu_context *ctx);
> > +     void (*disable_vsync)(struct dpu_context *ctx);
> > +     u32 (*isr)(struct dpu_context *ctx);
> > +     void (*ifconfig)(struct dpu_context *ctx);
> > +     void (*flip)(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count);
> > +     void (*capability)(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +     void __iomem *base;
> > +     int irq;
> > +     u8 if_type;
> > +     struct videomode vm;
> > +     bool stopped;
> > +     wait_queue_head_t wait_queue;
> > +     bool evt_update;
> > +     bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +     struct drm_crtc crtc;
> > +     struct dpu_context ctx;
> > +     const struct dpu_core_ops *core;
> > +     struct dpu_layer *layers;
> > +     u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +     const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +     return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >       &sprd_drm_driver,
> > +     &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >       struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
@ 2020-08-28 17:08       ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-28 17:08 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Mark Rutland, David Airlie, Chunyan Zhang,
	Linux-Kernel@Vger. Kernel. Org, Rob Herring, ML dri-devel,
	Orson Zhai, Sean Paul

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午5:14写道:
>
> Hi Kevin.
>
> Some feedback in the following.
> I lost track of thing for the atomic modesettting stuff and I hope other
> will review that.
>
>         Sam
>
> On Tue, Jul 28, 2020 at 06:07:57PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> Not needed - drop.
>
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +     sprd_dpu.o
> > +
> > +obj-y += dpu/
>
> Until there are several DPU's there is no need for a separate directory.
> Make it simpler by keeping it all in drm/sprd/*
Ok I will move DPU IP code to drm/sprd
>
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE 0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET   0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +     (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> Use a static inline to get better typecheck.
>
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> I am no fan of macros used like this.
>
> Maybe use static inlines and add struct dpu_context * as first argument.
> Then adding base can be hidden away.

Maybe a good idea, i will try it, like this:
static inline void DPU_REG_CLR(dpu_context *ctx, u32 reg, u32 mask) {
    writel_relaxed(readl_relaxed(ctx->base + reg) & ~mask, ctx->base + reg);
}

>
>
> I had a hard time convincing myself that _relaxed was the right
> variants. If I get it correct we may see writes re-ordered with the
> _relaxed variants wich would be no good when dealign with interrupts.
> But I may miss somethign here.
>
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL 0x04
> > +#define REG_DPU_CFG0 0x08
> > +#define REG_DPU_CFG1 0x0C
> > +#define REG_DPU_CFG2 0x10
> > +#define REG_PANEL_SIZE       0x20
> > +#define REG_BLEND_SIZE       0x24
> > +#define REG_BG_COLOR 0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0   0x30
> > +#define REG_LAY_BASE_ADDR1   0x34
> > +#define REG_LAY_BASE_ADDR2   0x38
> > +#define REG_LAY_CTRL         0x40
> > +#define REG_LAY_SIZE         0x44
> > +#define REG_LAY_PITCH                0x48
> > +#define REG_LAY_POS          0x4C
> > +#define REG_LAY_ALPHA                0x50
> > +#define REG_LAY_PALLETE              0x58
> > +#define REG_LAY_CROP_START   0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN               0x1E0
> > +#define REG_DPU_INT_CLR              0x1E4
> > +#define REG_DPU_INT_STS              0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL         0x1F0
> > +#define REG_DPI_H_TIMING     0x1F4
> > +#define REG_DPI_V_TIMING     0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                   0x800
> > +#define REG_MMU_VPN_RANGE            0x80C
> > +#define REG_MMU_VAOR_ADDR_RD         0x818
> > +#define REG_MMU_VAOR_ADDR_WR         0x81C
> > +#define REG_MMU_INV_ADDR_RD          0x820
> > +#define REG_MMU_INV_ADDR_WR          0x824
> > +#define REG_MMU_PPN1                 0x83C
> > +#define REG_MMU_RANGE1                       0x840
> > +#define REG_MMU_PPN2                 0x844
> > +#define REG_MMU_RANGE2                       0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                  BIT(0)
> > +#define BIT_DPU_STOP                 BIT(1)
> > +#define BIT_DPU_REG_UPDATE           BIT(2)
> > +#define BIT_DPU_IF_EDPI                      BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE            BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD     BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                               BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE             BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR              BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR              BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD              BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR              BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD               BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR               BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN           BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD       BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN          BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +     u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                     BIT_DPU_INT_MMU_VAOR_WR |
> > +                     BIT_DPU_INT_MMU_INV_RD |
> > +                     BIT_DPU_INT_MMU_INV_WR;
> > +     u32 val = reg_val & mmu_mask;
> > +     int i;
> > +
> > +     if (val) {
> > +             DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
>
> General comment, applies for the whole patch.
>
> Use drm_err(drm, ...), drm_info(drm, ...) etc.
>
> Use upclassing to get sprd_dpu - and then you have a drm_device *
> (After drm_device is embedded in sprd_dpu as suggested in the other
> mail)
Ok, i will replace DRM_XXX with drm_xxx.
>
> > +
> > +             if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                     DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                     DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                     DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                     DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> > +
> > +             for (i = 0; i < 8; i++) {
> > +                     reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                     if (reg_val & 0x1)
> > +                             DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +             }
> > +     }
> > +
> > +     return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < 8; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, int_mask = 0;
> > +
> > +     reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +     /* disable err interrupt */
> > +     if (reg_val & BIT_DPU_INT_ERR)
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +
> > +     /* dpu update done isr */
> > +     if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +             ctx->evt_update = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu stop done isr */
> > +     if (reg_val & BIT_DPU_INT_DONE) {
> > +             ctx->evt_stop = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu ifbc payload error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +             DRM_ERROR("dpu ifbc payload error\n");
> > +     }
> > +
> > +     /* dpu ifbc header error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +             DRM_ERROR("dpu ifbc header error\n");
> > +     }
> > +
> > +     int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +     return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     if (ctx->stopped)
> > +             return 0;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> > +                                            msecs_to_jiffies(500));
> > +     ctx->evt_stop = false;
> > +
> > +     ctx->stopped = true;
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for stop done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     ctx->evt_update = false;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> > +                                            msecs_to_jiffies(500));
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for reg update done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +     dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +     ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, size;
> > +
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +     DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +     DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +     reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +     if (ctx->stopped)
> > +             dpu_clean_all(ctx);
> > +
> > +     DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                 struct dpu_layer *hwlayer)
> No linebreak needed here.
>
> > +{
> > +     const struct drm_format_info *info;
> > +     u32 size, offset, ctrl, pitch;
> > +     int i;
> > +
> > +     offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +     if (hwlayer->src_w && hwlayer->src_h)
> > +             size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> > +     else
> > +             size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> > +
> > +     for (i = 0; i < hwlayer->planes; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                             hwlayer->index), hwlayer->addr[i]);
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                     hwlayer->index), offset);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                     hwlayer->index), size);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                     hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                     hwlayer->index), hwlayer->alpha);
> > +
> > +     info = drm_format_info(hwlayer->format);
> > +     if (hwlayer->planes == 3) {
> > +             /* UV pitch is 1/2 of Y pitch*/
> > +             pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                             (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     } else {
> > +             pitch = hwlayer->pitch[0] / info->cpp[0];
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     }
> > +
> > +     ctrl = hwlayer->format |
> > +             hwlayer->blending |
> > +             (hwlayer->rotation & 0x7) << 20;
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), ctrl);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +     DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                             hwlayer->dst_x, hwlayer->dst_y,
> > +                             hwlayer->dst_w, hwlayer->dst_h);
> > +     DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> > +                             hwlayer->src_x, hwlayer->src_y,
> > +                             hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count)
> > +{
> > +     int i;
> > +     u32 reg_val;
> > +
> > +     /*
> > +      * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +      * registers in EDPI mode. So the config registers can only be
> > +      * updated in the rising edge of DPU_RUN bit.
> > +      */
> > +     if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +             dpu_wait_stop_done(ctx);
> > +
> > +     /* reset the bgcolor to black */
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     /* disable all the layers */
> > +     dpu_clean_all(ctx);
> > +
> > +     /* start configure dpu layers */
> > +     for (i = 0; i < count; i++)
> > +             dpu_layer(ctx, &layers[i]);
> > +
> > +     /* update trigger and wait */
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             if (!ctx->stopped) {
> > +                     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> > +                     dpu_wait_update_done(ctx);
> > +             }
> > +
> > +             DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +             ctx->stopped = false;
> > +     }
> > +
> > +     /*
> > +      * If the following interrupt was disabled in isr,
> > +      * re-enable it.
> > +      */
> > +     reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +               BIT_DPU_INT_FBC_HDR_ERR |
> > +               BIT_DPU_INT_MMU_VAOR_RD |
> > +               BIT_DPU_INT_MMU_VAOR_WR |
> > +               BIT_DPU_INT_MMU_INV_RD |
> > +               BIT_DPU_INT_MMU_INV_WR;
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +     u32 int_mask = 0;
> > +     u32 reg_val;
> > +
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             /* use dpi as interface */
> > +             DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* disable Halt function for SPRD DSI */
> > +             DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> > +
> > +             /* select te from external pad */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* set dpi timing */
> > +             reg_val = ctx->vm.hsync_len << 0 |
> > +                       ctx->vm.hback_porch << 8 |
> > +                       ctx->vm.hfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +             reg_val = ctx->vm.vsync_len << 0 |
> > +                       ctx->vm.vback_porch << 8 |
> > +                       ctx->vm.vfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                     DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                             "underflow risk!\n");
> > +
> > +             /* enable dpu update done INT */
> > +             int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +             /* enable dpu DONE  INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable dpu dpi vsync */
> > +             int_mask |= BIT_DPU_INT_VSYNC;
> > +             /* enable dpu TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +             /* enable underflow err INT */
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             /* use edpi as interface */
> > +             DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* use external te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* enable te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> > +
> > +             /* enable stop DONE INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +     }
> > +
> > +     /* enable ifbc payload error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +     /* enable ifbc header error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +     /* enable iommu va out of range read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +     /* enable iommu va out of range write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +     /* enable iommu invalid read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +     /* enable iommu invalid write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +     DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +     DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +     DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +     DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +     DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +     DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +     DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +     DRM_FORMAT_YVU420,
> > +};
> One format per line improves readability a lot.
> Maybe sort alphbetically too.
>
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap)
> Drop linebreak
>
> > +{
> > +     cap->max_layers = 6;
> > +     cap->fmts_ptr = primary_fmts;
> > +     cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +     .init = dpu_init,
> > +     .fini = dpu_fini,
> > +     .run = dpu_run,
> > +     .stop = dpu_stop,
> > +     .isr = dpu_isr,
> > +     .ifconfig = dpu_dpi_init,
> > +     .capability = dpu_capability,
> > +     .flip = dpu_flip,
> > +     .enable_vsync = enable_vsync,
> > +     .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +     struct drm_plane plane;
> > +     u32 index;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     u32 format;
> > +     u32 rotation;
> > +     u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +     return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +     switch (fourcc) {
> > +     case DRM_FORMAT_BGRA8888:
> > +             /* BGRA8888 -> ARGB8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_RGBX8888:
> > +     case DRM_FORMAT_RGBA8888:
> > +             /* RGBA8888 -> ABGR8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ABGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ARGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_XBGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_XRGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_BGR565:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_RGB565:
> > +             *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +             break;
> > +     case DRM_FORMAT_NV12:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV21:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV16:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV61:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YUV420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YVU420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +     switch (angle) {
> > +     case DRM_MODE_ROTATE_0:
> > +             *rotation = DPU_LAYER_ROTATION_0;
> > +             break;
> > +     case DRM_MODE_ROTATE_90:
> > +             *rotation = DPU_LAYER_ROTATION_90;
> > +             break;
> > +     case DRM_MODE_ROTATE_180:
> > +             *rotation = DPU_LAYER_ROTATION_180;
> > +             break;
> > +     case DRM_MODE_ROTATE_270:
> > +             *rotation = DPU_LAYER_ROTATION_270;
> > +             break;
> > +     case DRM_MODE_REFLECT_Y:
> > +             *rotation = DPU_LAYER_ROTATION_180_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_90_M;
> > +             break;
> > +     case DRM_MODE_REFLECT_X:
> > +             *rotation = DPU_LAYER_ROTATION_0_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_270_M;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                               struct drm_plane_state *state)
> > +{
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct drm_framebuffer *fb = state->fb;
> > +     struct drm_gem_cma_object *cma_obj;
> > +     int i, ret;
> > +     u32 addr;
> > +
> > +     if (!state->fb || !state->crtc)
> > +             return 0;
> > +
> > +     ret = sprd_plane_format_convert(fb->format->format,
> > +                                     &p->format);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane format\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_plane_rotation_convert(state->rotation,
> > +                                     &p->rotation);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane rotation\n");
> > +             return ret;
> > +     }
> > +
> > +     switch (state->pixel_blend_mode) {
> > +     case DRM_MODE_BLEND_COVERAGE:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Normal mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +             break;
> > +     case DRM_MODE_BLEND_PREMULTI:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Pre-mult mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +             break;
> > +     case DRM_MODE_BLEND_PIXEL_NONE:
> > +     default:
> > +             /* don't do blending, maybe RGBX */
> > +             /* alpha mode select - layer alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +             break;
> > +     }
> > +
> > +     for (i = 0; i < fb->format->num_planes; i++) {
> > +             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +             addr = cma_obj->paddr + fb->offsets[i];
> > +             if (addr % 16) {
> > +                     DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> > +                             i, addr);
> > +                     return -EFAULT;
> > +             }
> > +
> > +             p->addr[i] = addr;
> > +             p->pitch[i] = fb->pitches[i];
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                 struct drm_plane_state *old_state)
> > +{
> > +     struct drm_plane_state *state = plane->state;
> > +     struct drm_framebuffer *fb = plane->state->fb;
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +     struct dpu_layer *layer = &dpu->layers[p->index];
> > +     int i;
> > +
> > +     if (!state->crtc || !state->fb)
> > +             return;
> > +
> > +     layer->index = p->index;
> > +     layer->src_x = state->src_x >> 16;
> > +     layer->src_y = state->src_y >> 16;
> > +     layer->src_w = state->src_w >> 16;
> > +     layer->src_h = state->src_h >> 16;
> > +     layer->dst_x = state->crtc_x;
> > +     layer->dst_y = state->crtc_y;
> > +     layer->dst_w = state->crtc_w;
> > +     layer->dst_h = state->crtc_h;
> > +     layer->alpha = state->alpha;
> > +     layer->format = p->format;
> > +     layer->blending = p->blend_mode;
> > +     layer->rotation = p->rotation;
> > +     layer->planes = fb->format->num_planes;
> > +
> > +     for (i = 0; i < layer->planes; i++) {
> > +             layer->addr[i] = p->addr[i];
> > +             layer->pitch[i] = p->pitch[i];
> > +     }
> > +
> > +     dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> > +{
> > +     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                    BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +     /* create rotation property */
> > +     drm_plane_create_rotation_property(&p->plane,
> > +                                        DRM_MODE_ROTATE_0,
> > +                                        DRM_MODE_ROTATE_MASK |
> > +                                        DRM_MODE_REFLECT_MASK);
> > +
> > +     /* create alpha property */
> > +     drm_plane_create_alpha_property(&p->plane);
> > +
> > +     /* create blend mode property */
> > +     drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +     /* create zpos property */
> > +     drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +     .atomic_check = sprd_plane_atomic_check,
> > +     .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +     .update_plane = drm_atomic_helper_update_plane,
> > +     .disable_plane  = drm_atomic_helper_disable_plane,
> > +     .destroy = drm_plane_cleanup,
> > +     .reset = drm_atomic_helper_plane_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                     struct sprd_dpu *dpu)
> > +{
> > +     struct drm_plane *primary = NULL;
> > +     struct sprd_plane *p = NULL;
> > +     struct dpu_capability cap = {};
> > +     int ret, i;
> > +
> > +     dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +     dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                               sizeof(struct dpu_layer), GFP_KERNEL);
> > +     if (!dpu->layers)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     for (i = 0; i < cap.max_layers; i++) {
> > +
> > +             p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +             if (!p)
> > +                     return ERR_PTR(-ENOMEM);
> > +
> > +             ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                            &sprd_plane_funcs, cap.fmts_ptr,
> > +                                            cap.fmts_cnt, NULL,
> > +                                            DRM_PLANE_TYPE_PRIMARY, NULL);
> > +             if (ret) {
> > +                     DRM_ERROR("fail to init primary plane\n");
> > +                     return ERR_PTR(ret);
> > +             }
> > +
> > +             drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> > +
> > +             sprd_plane_create_properties(p, i);
> > +
> > +             p->index = i;
> > +             if (i == 0)
> > +                     primary = &p->plane;
> > +     }
> > +
> > +     return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                     const struct drm_display_mode *mode)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> > +
> > +     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +             if ((mode->hdisplay == mode->htotal) ||
> > +                 (mode->vdisplay == mode->vtotal))
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +             else
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +     }
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     sprd_dpu_init(dpu);
> > +
> > +     enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     disable_irq(dpu->ctx.irq);
> > +
> > +     sprd_dpu_fini(dpu);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                              struct drm_crtc_state *state)
> > +{
> > +     DRM_DEBUG("%s()\n", __func__);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> > +
> > +     dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     if (dpu->pending_planes)
> > +             dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +     .mode_valid     = sprd_crtc_mode_valid,
> > +     .atomic_check   = sprd_crtc_atomic_check,
> > +     .atomic_begin   = sprd_crtc_atomic_begin,
> > +     .atomic_flush   = sprd_crtc_atomic_flush,
> > +     .atomic_enable  = sprd_crtc_atomic_enable,
> > +     .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +     .destroy        = drm_crtc_cleanup,
> > +     .set_config     = drm_atomic_helper_set_config,
> > +     .page_flip      = drm_atomic_helper_page_flip,
> > +     .reset          = drm_atomic_helper_crtc_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +     .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +     .enable_vblank  = sprd_crtc_enable_vblank,
> > +     .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                      struct drm_plane *primary)
> > +{
> > +     struct device_node *port;
> > +     int ret;
> > +
> > +     /*
> > +      * set crtc port so that drm_of_find_possible_crtcs call works
> > +      */
> > +     port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +     if (!port) {
> > +             DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                       drm->dev->of_node->full_name);
> > +             return -EINVAL;
> > +     }
> > +     of_node_put(port);
> > +     crtc->port = port;
> > +
> > +     ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                     &sprd_crtc_funcs, NULL);
> > +     if (ret) {
> > +             DRM_ERROR("failed to init crtc.\n");
> > +             return ret;
> > +     }
> > +
> > +     drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +     drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->init(ctx);
> > +     dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +     struct sprd_dpu *dpu = data;
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     u32 int_mask = 0;
> > +
> > +     int_mask = dpu->core->isr(ctx);
> > +
> > +     if (int_mask & BIT_DPU_INT_ERR)
> > +             DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +     if (int_mask & BIT_DPU_INT_VSYNC)
> > +             drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +     struct drm_device *drm = data;
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +     struct drm_plane *plane;
> > +     int ret;
> > +
> > +     plane = sprd_plane_init(drm, dpu);
> > +     if (IS_ERR_OR_NULL(plane)) {
> > +             ret = PTR_ERR(plane);
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +     void *data)
> > +{
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +     drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +     .bind = sprd_dpu_bind,
> > +     .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                             struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     struct resource *res;
> > +     int ret;
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +     if (!ctx->base) {
> > +             DRM_ERROR("failed to map dpu registers\n");
> > +             return -EFAULT;
> > +     }
> > +
> > +     ctx->irq = platform_get_irq(pdev, 0);
> > +     if (ctx->irq < 0) {
> > +             DRM_ERROR("failed to get dpu irq\n");
> > +             return ctx->irq;
> > +     }
> > +
> > +     irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +     ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                     0, "DPU", dpu);
> > +     if (ret) {
> > +             DRM_ERROR("failed to register dpu irq handler\n");
> > +             return ret;
> > +     }
> > +
> > +     init_waitqueue_head(&ctx->wait_queue);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +     .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +     { .compatible = "sprd,sharkl3-dpu",
> > +       .data = &sharkl3_dpu },
> > +     { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +     const struct sprd_dpu_ops *pdata;
> > +     struct sprd_dpu *dpu;
> > +     int ret;
> > +
> > +     dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +     if (!dpu)
> > +             return -ENOMEM;
> > +
> > +     pdata = of_device_get_match_data(&pdev->dev);
> > +     if (pdata) {
> > +             dpu->core = pdata->core;
> > +     } else {
> > +             DRM_ERROR("No matching driver data found\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +     if (ret)
> > +             return ret;
> > +
> > +     platform_set_drvdata(pdev, dpu);
> > +
> > +     return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +     component_del(&pdev->dev, &dpu_component_ops);
> > +     return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +     .probe = sprd_dpu_probe,
> > +     .remove = sprd_dpu_remove,
> > +     .driver = {
> > +             .name = "sprd-dpu-drv",
> > +             .of_match_table = dpu_match_table,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_            BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE          BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_WB_DONE          BIT(6)
> > +#define BIT_DPU_INT_WB_ERR           BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
> > +
> > +enum {
> > +     SPRD_DPU_IF_DBI = 0,
> > +     SPRD_DPU_IF_DPI,
> > +     SPRD_DPU_IF_EDPI,
> > +     SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +     DPU_LAYER_ROTATION_0,
> > +     DPU_LAYER_ROTATION_90,
> > +     DPU_LAYER_ROTATION_180,
> > +     DPU_LAYER_ROTATION_270,
> > +     DPU_LAYER_ROTATION_0_M,
> > +     DPU_LAYER_ROTATION_90_M,
> > +     DPU_LAYER_ROTATION_180_M,
> > +     DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +     u8 index;
> > +     u8 planes;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     s16 src_x;
> > +     s16 src_y;
> > +     s16 src_w;
> > +     s16 src_h;
> > +     s16 dst_x;
> > +     s16 dst_y;
> > +     u16 dst_w;
> > +     u16 dst_h;
> > +     u32 format;
> > +     u32 alpha;
> > +     u32 blending;
> > +     u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +     u32 max_layers;
> > +     const u32 *fmts_ptr;
> > +     u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +     void (*init)(struct dpu_context *ctx);
> > +     void (*fini)(struct dpu_context *ctx);
> > +     void (*run)(struct dpu_context *ctx);
> > +     void (*stop)(struct dpu_context *ctx);
> > +     void (*enable_vsync)(struct dpu_context *ctx);
> > +     void (*disable_vsync)(struct dpu_context *ctx);
> > +     u32 (*isr)(struct dpu_context *ctx);
> > +     void (*ifconfig)(struct dpu_context *ctx);
> > +     void (*flip)(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count);
> > +     void (*capability)(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +     void __iomem *base;
> > +     int irq;
> > +     u8 if_type;
> > +     struct videomode vm;
> > +     bool stopped;
> > +     wait_queue_head_t wait_queue;
> > +     bool evt_update;
> > +     bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +     struct drm_crtc crtc;
> > +     struct dpu_context ctx;
> > +     const struct dpu_core_ops *core;
> > +     struct dpu_layer *layers;
> > +     u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +     const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +     return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >       &sprd_drm_driver,
> > +     &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >       struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
  2020-08-28 16:04       ` Kevin Tang
@ 2020-08-28 17:55         ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-08-28 17:55 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Rob Herring, Mark Rutland, Orson Zhai,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, Chunyan Zhang

Hi Kevin

> >
> > > +
> > > +     drm->dev_private = sprd;
> > dev_private is deprecated. Alwyas use upclassing.
> dev_private is deprecated ? I see everyone is still using it, so it
> will be deprecated in the future?
It is deprecated - and a lot of drivers is in need up an update to get
away from using it.

From the kernel-doc:

        /**
         * @dev_private:
         *
         * DRM driver private data. This is deprecated and should be left set to
         * NULL.
         *
         * Instead of using this pointer it is recommended that drivers use
         * drm_dev_init() and embed struct &drm_device in their larger
         * per-device structure.
         */
        void *dev_private;

> > > +     }
> > > +     /* with irq_enabled = true, we can use the vblank feature. */
> > > +     drm->irq_enabled = true;
> > Can drm_irq_install() be used?
> > Then this flag shall not be set by the driver, And the interrupt numbers on different Soc are not necessarily the same
> >
> We need to set "IRQ_NOAUTOEN" flag for CRTC IRQ, you can see that we
> manually turn the interrupt on and off by crtc
> atomic_enable/atomic_disable
> and the interrupt number on different Soc are not necessarily the
> same. So intall interrupt in kms maybe is not suitable for us...

Maybe drm_irq_install() does not fit the need here. But please take an
extra look as what is does is quite basic. And no need to roll your
own if there is available infrstructure that can be used.

But there is nothing wrong rolloing your own if needed.

	Sam

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master
@ 2020-08-28 17:55         ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-08-28 17:55 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Mark Rutland, David Airlie, Chunyan Zhang,
	Linux-Kernel@Vger. Kernel. Org, Rob Herring, ML dri-devel,
	Orson Zhai, Sean Paul

Hi Kevin

> >
> > > +
> > > +     drm->dev_private = sprd;
> > dev_private is deprecated. Alwyas use upclassing.
> dev_private is deprecated ? I see everyone is still using it, so it
> will be deprecated in the future?
It is deprecated - and a lot of drivers is in need up an update to get
away from using it.

From the kernel-doc:

        /**
         * @dev_private:
         *
         * DRM driver private data. This is deprecated and should be left set to
         * NULL.
         *
         * Instead of using this pointer it is recommended that drivers use
         * drm_dev_init() and embed struct &drm_device in their larger
         * per-device structure.
         */
        void *dev_private;

> > > +     }
> > > +     /* with irq_enabled = true, we can use the vblank feature. */
> > > +     drm->irq_enabled = true;
> > Can drm_irq_install() be used?
> > Then this flag shall not be set by the driver, And the interrupt numbers on different Soc are not necessarily the same
> >
> We need to set "IRQ_NOAUTOEN" flag for CRTC IRQ, you can see that we
> manually turn the interrupt on and off by crtc
> atomic_enable/atomic_disable
> and the interrupt number on different Soc are not necessarily the
> same. So intall interrupt in kms maybe is not suitable for us...

Maybe drm_irq_install() does not fit the need here. But please take an
extra look as what is does is quite basic. And no need to roll your
own if there is available infrstructure that can be used.

But there is nothing wrong rolloing your own if needed.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
  2020-08-26 17:11       ` Kevin Tang
@ 2020-08-28 17:57         ` Sam Ravnborg
  -1 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-08-28 17:57 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Rob Herring, Mark Rutland, Orson Zhai,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, Chunyan Zhang

Hi Kevin.

> >
> > Any specific reason why this is not a ports node like used by many other
> > display bindings?
> > In other words - I think this is too simple.
> We only support one display pipeline now, other interface, like
> DP(DisplayPort), HDMI...will be add later...
> 
>   ports:
>     $ref: /schemas/types.yaml#/definitions/phandle-array
>     description: |
>       Should contain a list of phandles pointing to display interface port
>       of dpu devices.. dpu definitions as defined in
>       Documentation/devicetree/bindings/display/sprd/sprd,dpu.yaml

There is nothing wrong having a ports node that is limited to a single
port node. But please remember the binding describes the HW - so if the
HW supports more than one port the binding should describe this.
What the driver supports is not relevant for the binding.

	Sam

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
@ 2020-08-28 17:57         ` Sam Ravnborg
  0 siblings, 0 replies; 50+ messages in thread
From: Sam Ravnborg @ 2020-08-28 17:57 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Mark Rutland, David Airlie, Chunyan Zhang,
	Linux-Kernel@Vger. Kernel. Org, Rob Herring, ML dri-devel,
	Orson Zhai, Sean Paul

Hi Kevin.

> >
> > Any specific reason why this is not a ports node like used by many other
> > display bindings?
> > In other words - I think this is too simple.
> We only support one display pipeline now, other interface, like
> DP(DisplayPort), HDMI...will be add later...
> 
>   ports:
>     $ref: /schemas/types.yaml#/definitions/phandle-array
>     description: |
>       Should contain a list of phandles pointing to display interface port
>       of dpu devices.. dpu definitions as defined in
>       Documentation/devicetree/bindings/display/sprd/sprd,dpu.yaml

There is nothing wrong having a ports node that is limited to a single
port node. But please remember the binding describes the HW - so if the
HW supports more than one port the binding should describe this.
What the driver supports is not relevant for the binding.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
  2020-07-28 10:07   ` Kevin Tang
@ 2020-08-28 19:39     ` Rob Herring
  -1 siblings, 0 replies; 50+ messages in thread
From: Rob Herring @ 2020-08-28 19:39 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Mark Rutland, Orson Zhai, Lyra Zhang,
	linux-kernel, dri-devel

On Tue, Jul 28, 2020 at 4:08 AM Kevin Tang <kevin3.tang@gmail.com> wrote:
>
> From: Kevin Tang <kevin.tang@unisoc.com>
>
> The Unisoc DRM master device is a virtual device needed to list all
> DPU devices or other display interface nodes that comprise the
> graphics subsystem
>
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml

If you want bindings reviewed, then you need to Cc
devicetree@vger.kernel.org. Otherwise you may be waiting until the 6th
version or later or never.

>
> diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> new file mode 100644
> index 0000000..b5792c0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0

New bindings should be dual licensed:

(GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Unisoc DRM master device

DRM is a Linux thing and shouldn't be part of a binding.

> +
> +maintainers:
> +  - Mark Rutland <mark.rutland@arm.com>

No, this should be you or someone that knows the h/w.

> +
> +description: |
> +  The Unisoc DRM master device is a virtual device needed to list all
> +  DPU devices or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: sprd,display-subsystem
> +
> +  ports:
> +    description:
> +      Should contain a list of phandles pointing to display interface port
> +      of DPU devices.
> +
> +required:
> +  - compatible
> +  - ports
> +
> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "sprd,display-subsystem";
> +        ports = <&dpu_out>;

We generally try to avoid this virtual node as it doesn't represent
any h/w. Can't you bind the driver to the DPU directly?

Rob

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings
@ 2020-08-28 19:39     ` Rob Herring
  0 siblings, 0 replies; 50+ messages in thread
From: Rob Herring @ 2020-08-28 19:39 UTC (permalink / raw)
  To: Kevin Tang
  Cc: Mark Rutland, David Airlie, Lyra Zhang, linux-kernel, dri-devel,
	Orson Zhai, Sean Paul

On Tue, Jul 28, 2020 at 4:08 AM Kevin Tang <kevin3.tang@gmail.com> wrote:
>
> From: Kevin Tang <kevin.tang@unisoc.com>
>
> The Unisoc DRM master device is a virtual device needed to list all
> DPU devices or other display interface nodes that comprise the
> graphics subsystem
>
> Cc: Orson Zhai <orsonzhai@gmail.com>
> Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> ---
>  .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml

If you want bindings reviewed, then you need to Cc
devicetree@vger.kernel.org. Otherwise you may be waiting until the 6th
version or later or never.

>
> diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> new file mode 100644
> index 0000000..b5792c0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml
> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0

New bindings should be dual licensed:

(GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/sprd/drm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Unisoc DRM master device

DRM is a Linux thing and shouldn't be part of a binding.

> +
> +maintainers:
> +  - Mark Rutland <mark.rutland@arm.com>

No, this should be you or someone that knows the h/w.

> +
> +description: |
> +  The Unisoc DRM master device is a virtual device needed to list all
> +  DPU devices or other display interface nodes that comprise the
> +  graphics subsystem.
> +
> +properties:
> +  compatible:
> +    const: sprd,display-subsystem
> +
> +  ports:
> +    description:
> +      Should contain a list of phandles pointing to display interface port
> +      of DPU devices.
> +
> +required:
> +  - compatible
> +  - ports
> +
> +examples:
> +  - |
> +    display-subsystem {
> +        compatible = "sprd,display-subsystem";
> +        ports = <&dpu_out>;

We generally try to avoid this virtual node as it doesn't represent
any h/w. Can't you bind the driver to the DPU directly?

Rob
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 21:51     ` Daniel Vetter
@ 2020-08-29 17:10       ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-29 17:10 UTC (permalink / raw)
  To: Kevin Tang, Maarten Lankhorst, Maxime Ripard, Sean Paul,
	Dave Airlie, Rob Herring, Mark Rutland, Orson Zhai,
	Chunyan Zhang, Linux Kernel Mailing List, dri-devel

Daniel Vetter <daniel@ffwll.ch> 于2020年7月29日周三 上午5:51写道:
>
> On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
> >
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
>
> Quickly scrolled through this, and the entire thing very much leaves a
> midlayer heavy aftertaste. Do we really need stuff like struct dpu_layer
> and struct dpu_core_ops? They only seem to complicate the code base, and
> seem to have no real reason. The indirection with first computing register
> values into a sprd_plane/crtc structure, and then writing it into hardware
> is also a bit much - I recommend to only do that if you have to compute
> values in _check to validate them, so that the computation doesn't have to
> be repeated in the commit phase functions.
Hi daniel,
Maybe some DPU's IP verison, there will be some differences in the
DPU on each Soc. Not only control registers, there will be some differences
in control logic and functions
If access H/W registers all put into crtc or plane driver, how did
others deal with it? Add Soc's macro?
When there are too many H/W IPs, split into different files(dpu_core_ops)
according to different IP, I think the code project will be more
convenient to keeping.

For multiple H/W IP, the popular design method is?

>
> Also, the layer and pending_flips stuff in sprd_dpu don't work with
> atomic, that races. You have to put all that stuff into state objects, or
> if it's some data shared with interrupt handlers (doesn't seem to be the
> case here), it needs its own locking, and any data you need in the
> interrupt handler must be copied over.
the layer and pending_flips, we put into atomic_flush, We do have some
features related to interrupt handlers, but it has not been submitted yet.
So pending_flips lock have not been added.
>
> Also no devm_kzalloc for anything containined a drm_* structure, that's
> the wrong lifetime.
>
> So yeah high level review is that I think this driver would benefit a lot
> from a pile of demidlayer.
>
> Cheers, Daniel
>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +       sprd_dpu.o
> > +
> > +obj-y += dpu/
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE   0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET     0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +       (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> > +
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +       writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +       writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL   0x04
> > +#define REG_DPU_CFG0   0x08
> > +#define REG_DPU_CFG1   0x0C
> > +#define REG_DPU_CFG2   0x10
> > +#define REG_PANEL_SIZE 0x20
> > +#define REG_BLEND_SIZE 0x24
> > +#define REG_BG_COLOR   0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0     0x30
> > +#define REG_LAY_BASE_ADDR1     0x34
> > +#define REG_LAY_BASE_ADDR2     0x38
> > +#define REG_LAY_CTRL           0x40
> > +#define REG_LAY_SIZE           0x44
> > +#define REG_LAY_PITCH          0x48
> > +#define REG_LAY_POS            0x4C
> > +#define REG_LAY_ALPHA          0x50
> > +#define REG_LAY_PALLETE                0x58
> > +#define REG_LAY_CROP_START     0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN         0x1E0
> > +#define REG_DPU_INT_CLR                0x1E4
> > +#define REG_DPU_INT_STS                0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL           0x1F0
> > +#define REG_DPI_H_TIMING       0x1F4
> > +#define REG_DPI_V_TIMING       0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                     0x800
> > +#define REG_MMU_VPN_RANGE              0x80C
> > +#define REG_MMU_VAOR_ADDR_RD           0x818
> > +#define REG_MMU_VAOR_ADDR_WR           0x81C
> > +#define REG_MMU_INV_ADDR_RD            0x820
> > +#define REG_MMU_INV_ADDR_WR            0x824
> > +#define REG_MMU_PPN1                   0x83C
> > +#define REG_MMU_RANGE1                 0x840
> > +#define REG_MMU_PPN2                   0x844
> > +#define REG_MMU_RANGE2                 0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                    BIT(0)
> > +#define BIT_DPU_STOP                   BIT(1)
> > +#define BIT_DPU_REG_UPDATE             BIT(2)
> > +#define BIT_DPU_IF_EDPI                        BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE              BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD       BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                         BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE               BIT(0)
> > +#define BIT_DPU_INT_TE                 BIT(1)
> > +#define BIT_DPU_INT_ERR                        BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> > +#define BIT_DPU_INT_VSYNC              BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR                BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR                BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD                BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR                BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD         BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR         BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN             BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN            BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +       u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                       BIT_DPU_INT_MMU_VAOR_WR |
> > +                       BIT_DPU_INT_MMU_INV_RD |
> > +                       BIT_DPU_INT_MMU_INV_WR;
> > +       u32 val = reg_val & mmu_mask;
> > +       int i;
> > +
> > +       if (val) {
> > +               DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
> > +
> > +               if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                       DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> > +               if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                       DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> > +               if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                       DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> > +               if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                       DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> > +
> > +               for (i = 0; i < 8; i++) {
> > +                       reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                       if (reg_val & 0x1)
> > +                               DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +               }
> > +       }
> > +
> > +       return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < 8; i++)
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +       u32 reg_val, int_mask = 0;
> > +
> > +       reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +       /* disable err interrupt */
> > +       if (reg_val & BIT_DPU_INT_ERR)
> > +               int_mask |= BIT_DPU_INT_ERR;
> > +
> > +       /* dpu update done isr */
> > +       if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +               ctx->evt_update = true;
> > +               wake_up_interruptible_all(&ctx->wait_queue);
> > +       }
> > +
> > +       /* dpu stop done isr */
> > +       if (reg_val & BIT_DPU_INT_DONE) {
> > +               ctx->evt_stop = true;
> > +               wake_up_interruptible_all(&ctx->wait_queue);
> > +       }
> > +
> > +       /* dpu ifbc payload error isr */
> > +       if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +               int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +               DRM_ERROR("dpu ifbc payload error\n");
> > +       }
> > +
> > +       /* dpu ifbc header error isr */
> > +       if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +               int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +               DRM_ERROR("dpu ifbc header error\n");
> > +       }
> > +
> > +       int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +       return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +       int rc;
> > +
> > +       if (ctx->stopped)
> > +               return 0;
> > +
> > +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> > +                                              msecs_to_jiffies(500));
> > +       ctx->evt_stop = false;
> > +
> > +       ctx->stopped = true;
> > +
> > +       if (!rc) {
> > +               DRM_ERROR("dpu wait for stop done time out!\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +       int rc;
> > +
> > +       ctx->evt_update = false;
> > +
> > +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> > +                                              msecs_to_jiffies(500));
> > +
> > +       if (!rc) {
> > +               DRM_ERROR("dpu wait for reg update done time out!\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +       dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +       ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +       u32 reg_val, size;
> > +
> > +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +       size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +       DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +       DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +       reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +       if (ctx->stopped)
> > +               dpu_clean_all(ctx);
> > +
> > +       DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +       DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +       DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                   struct dpu_layer *hwlayer)
> > +{
> > +       const struct drm_format_info *info;
> > +       u32 size, offset, ctrl, pitch;
> > +       int i;
> > +
> > +       offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +       if (hwlayer->src_w && hwlayer->src_h)
> > +               size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> > +       else
> > +               size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> > +
> > +       for (i = 0; i < hwlayer->planes; i++)
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                               hwlayer->index), hwlayer->addr[i]);
> > +
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                       hwlayer->index), offset);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                       hwlayer->index), size);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                       hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                       hwlayer->index), hwlayer->alpha);
> > +
> > +       info = drm_format_info(hwlayer->format);
> > +       if (hwlayer->planes == 3) {
> > +               /* UV pitch is 1/2 of Y pitch*/
> > +               pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                               (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                               hwlayer->index), pitch);
> > +       } else {
> > +               pitch = hwlayer->pitch[0] / info->cpp[0];
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                               hwlayer->index), pitch);
> > +       }
> > +
> > +       ctrl = hwlayer->format |
> > +               hwlayer->blending |
> > +               (hwlayer->rotation & 0x7) << 20;
> > +
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                       hwlayer->index), ctrl);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                       hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +       DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                               hwlayer->dst_x, hwlayer->dst_y,
> > +                               hwlayer->dst_w, hwlayer->dst_h);
> > +       DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> > +                               hwlayer->src_x, hwlayer->src_y,
> > +                               hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                    struct dpu_layer layers[], u8 count)
> > +{
> > +       int i;
> > +       u32 reg_val;
> > +
> > +       /*
> > +        * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +        * registers in EDPI mode. So the config registers can only be
> > +        * updated in the rising edge of DPU_RUN bit.
> > +        */
> > +       if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +               dpu_wait_stop_done(ctx);
> > +
> > +       /* reset the bgcolor to black */
> > +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +       /* disable all the layers */
> > +       dpu_clean_all(ctx);
> > +
> > +       /* start configure dpu layers */
> > +       for (i = 0; i < count; i++)
> > +               dpu_layer(ctx, &layers[i]);
> > +
> > +       /* update trigger and wait */
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +               if (!ctx->stopped) {
> > +                       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> > +                       dpu_wait_update_done(ctx);
> > +               }
> > +
> > +               DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +               ctx->stopped = false;
> > +       }
> > +
> > +       /*
> > +        * If the following interrupt was disabled in isr,
> > +        * re-enable it.
> > +        */
> > +       reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +                 BIT_DPU_INT_FBC_HDR_ERR |
> > +                 BIT_DPU_INT_MMU_VAOR_RD |
> > +                 BIT_DPU_INT_MMU_VAOR_WR |
> > +                 BIT_DPU_INT_MMU_INV_RD |
> > +                 BIT_DPU_INT_MMU_INV_WR;
> > +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +       u32 int_mask = 0;
> > +       u32 reg_val;
> > +
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +               /* use dpi as interface */
> > +               DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +               /* disable Halt function for SPRD DSI */
> > +               DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> > +
> > +               /* select te from external pad */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +               /* set dpi timing */
> > +               reg_val = ctx->vm.hsync_len << 0 |
> > +                         ctx->vm.hback_porch << 8 |
> > +                         ctx->vm.hfront_porch << 20;
> > +               DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +               reg_val = ctx->vm.vsync_len << 0 |
> > +                         ctx->vm.vback_porch << 8 |
> > +                         ctx->vm.vfront_porch << 20;
> > +               DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +               if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                       DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                               "underflow risk!\n");
> > +
> > +               /* enable dpu update done INT */
> > +               int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +               /* enable dpu DONE  INT */
> > +               int_mask |= BIT_DPU_INT_DONE;
> > +               /* enable dpu dpi vsync */
> > +               int_mask |= BIT_DPU_INT_VSYNC;
> > +               /* enable dpu TE INT */
> > +               int_mask |= BIT_DPU_INT_TE;
> > +               /* enable underflow err INT */
> > +               int_mask |= BIT_DPU_INT_ERR;
> > +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +               /* use edpi as interface */
> > +               DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +               /* use external te */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +               /* enable te */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> > +
> > +               /* enable stop DONE INT */
> > +               int_mask |= BIT_DPU_INT_DONE;
> > +               /* enable TE INT */
> > +               int_mask |= BIT_DPU_INT_TE;
> > +       }
> > +
> > +       /* enable ifbc payload error INT */
> > +       int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +       /* enable ifbc header error INT */
> > +       int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +       /* enable iommu va out of range read error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +       /* enable iommu va out of range write error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +       /* enable iommu invalid read error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +       /* enable iommu invalid write error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +       DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +       DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +       DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +       DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +       DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +       DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +       DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +       DRM_FORMAT_YVU420,
> > +};
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                       struct dpu_capability *cap)
> > +{
> > +       cap->max_layers = 6;
> > +       cap->fmts_ptr = primary_fmts;
> > +       cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +       .init = dpu_init,
> > +       .fini = dpu_fini,
> > +       .run = dpu_run,
> > +       .stop = dpu_stop,
> > +       .isr = dpu_isr,
> > +       .ifconfig = dpu_dpi_init,
> > +       .capability = dpu_capability,
> > +       .flip = dpu_flip,
> > +       .enable_vsync = enable_vsync,
> > +       .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +       struct drm_plane plane;
> > +       u32 index;
> > +       u32 addr[4];
> > +       u32 pitch[4];
> > +       u32 format;
> > +       u32 rotation;
> > +       u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +       return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +       switch (fourcc) {
> > +       case DRM_FORMAT_BGRA8888:
> > +               /* BGRA8888 -> ARGB8888 */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_RGBX8888:
> > +       case DRM_FORMAT_RGBA8888:
> > +               /* RGBA8888 -> ABGR8888 */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_ABGR8888:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_ARGB8888:
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_XBGR8888:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_XRGB8888:
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_BGR565:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_RGB565:
> > +               *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +               break;
> > +       case DRM_FORMAT_NV12:
> > +               /* 2-Lane: Yuv420 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV21:
> > +               /* 2-Lane: Yuv420 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV16:
> > +               /* 2-Lane: Yuv422 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV61:
> > +               /* 2-Lane: Yuv422 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_YUV420:
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_YVU420:
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +       switch (angle) {
> > +       case DRM_MODE_ROTATE_0:
> > +               *rotation = DPU_LAYER_ROTATION_0;
> > +               break;
> > +       case DRM_MODE_ROTATE_90:
> > +               *rotation = DPU_LAYER_ROTATION_90;
> > +               break;
> > +       case DRM_MODE_ROTATE_180:
> > +               *rotation = DPU_LAYER_ROTATION_180;
> > +               break;
> > +       case DRM_MODE_ROTATE_270:
> > +               *rotation = DPU_LAYER_ROTATION_270;
> > +               break;
> > +       case DRM_MODE_REFLECT_Y:
> > +               *rotation = DPU_LAYER_ROTATION_180_M;
> > +               break;
> > +       case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +               *rotation = DPU_LAYER_ROTATION_90_M;
> > +               break;
> > +       case DRM_MODE_REFLECT_X:
> > +               *rotation = DPU_LAYER_ROTATION_0_M;
> > +               break;
> > +       case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +               *rotation = DPU_LAYER_ROTATION_270_M;
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                                 struct drm_plane_state *state)
> > +{
> > +       struct sprd_plane *p = to_sprd_plane(plane);
> > +       struct drm_framebuffer *fb = state->fb;
> > +       struct drm_gem_cma_object *cma_obj;
> > +       int i, ret;
> > +       u32 addr;
> > +
> > +       if (!state->fb || !state->crtc)
> > +               return 0;
> > +
> > +       ret = sprd_plane_format_convert(fb->format->format,
> > +                                       &p->format);
> > +       if (ret < 0) {
> > +               DRM_ERROR("Invalid plane format\n");
> > +               return ret;
> > +       }
> > +
> > +       ret = sprd_plane_rotation_convert(state->rotation,
> > +                                       &p->rotation);
> > +       if (ret < 0) {
> > +               DRM_ERROR("Invalid plane rotation\n");
> > +               return ret;
> > +       }
> > +
> > +       switch (state->pixel_blend_mode) {
> > +       case DRM_MODE_BLEND_COVERAGE:
> > +               /* alpha mode select - combo alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +               /* Normal mode */
> > +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +               break;
> > +       case DRM_MODE_BLEND_PREMULTI:
> > +               /* alpha mode select - combo alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +               /* Pre-mult mode */
> > +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +               break;
> > +       case DRM_MODE_BLEND_PIXEL_NONE:
> > +       default:
> > +               /* don't do blending, maybe RGBX */
> > +               /* alpha mode select - layer alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +               break;
> > +       }
> > +
> > +       for (i = 0; i < fb->format->num_planes; i++) {
> > +               cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +               addr = cma_obj->paddr + fb->offsets[i];
> > +               if (addr % 16) {
> > +                       DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> > +                               i, addr);
> > +                       return -EFAULT;
> > +               }
> > +
> > +               p->addr[i] = addr;
> > +               p->pitch[i] = fb->pitches[i];
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                   struct drm_plane_state *old_state)
> > +{
> > +       struct drm_plane_state *state = plane->state;
> > +       struct drm_framebuffer *fb = plane->state->fb;
> > +       struct sprd_plane *p = to_sprd_plane(plane);
> > +       struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +       struct dpu_layer *layer = &dpu->layers[p->index];
> > +       int i;
> > +
> > +       if (!state->crtc || !state->fb)
> > +               return;
> > +
> > +       layer->index = p->index;
> > +       layer->src_x = state->src_x >> 16;
> > +       layer->src_y = state->src_y >> 16;
> > +       layer->src_w = state->src_w >> 16;
> > +       layer->src_h = state->src_h >> 16;
> > +       layer->dst_x = state->crtc_x;
> > +       layer->dst_y = state->crtc_y;
> > +       layer->dst_w = state->crtc_w;
> > +       layer->dst_h = state->crtc_h;
> > +       layer->alpha = state->alpha;
> > +       layer->format = p->format;
> > +       layer->blending = p->blend_mode;
> > +       layer->rotation = p->rotation;
> > +       layer->planes = fb->format->num_planes;
> > +
> > +       for (i = 0; i < layer->planes; i++) {
> > +               layer->addr[i] = p->addr[i];
> > +               layer->pitch[i] = p->pitch[i];
> > +       }
> > +
> > +       dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> > +{
> > +       unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                      BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                      BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +       /* create rotation property */
> > +       drm_plane_create_rotation_property(&p->plane,
> > +                                          DRM_MODE_ROTATE_0,
> > +                                          DRM_MODE_ROTATE_MASK |
> > +                                          DRM_MODE_REFLECT_MASK);
> > +
> > +       /* create alpha property */
> > +       drm_plane_create_alpha_property(&p->plane);
> > +
> > +       /* create blend mode property */
> > +       drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +       /* create zpos property */
> > +       drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +       .atomic_check = sprd_plane_atomic_check,
> > +       .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +       .update_plane = drm_atomic_helper_update_plane,
> > +       .disable_plane  = drm_atomic_helper_disable_plane,
> > +       .destroy = drm_plane_cleanup,
> > +       .reset = drm_atomic_helper_plane_reset,
> > +       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                       struct sprd_dpu *dpu)
> > +{
> > +       struct drm_plane *primary = NULL;
> > +       struct sprd_plane *p = NULL;
> > +       struct dpu_capability cap = {};
> > +       int ret, i;
> > +
> > +       dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +       dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                                 sizeof(struct dpu_layer), GFP_KERNEL);
> > +       if (!dpu->layers)
> > +               return ERR_PTR(-ENOMEM);
> > +
> > +       for (i = 0; i < cap.max_layers; i++) {
> > +
> > +               p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +               if (!p)
> > +                       return ERR_PTR(-ENOMEM);
> > +
> > +               ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                              &sprd_plane_funcs, cap.fmts_ptr,
> > +                                              cap.fmts_cnt, NULL,
> > +                                              DRM_PLANE_TYPE_PRIMARY, NULL);
> > +               if (ret) {
> > +                       DRM_ERROR("fail to init primary plane\n");
> > +                       return ERR_PTR(ret);
> > +               }
> > +
> > +               drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> > +
> > +               sprd_plane_create_properties(p, i);
> > +
> > +               p->index = i;
> > +               if (i == 0)
> > +                       primary = &p->plane;
> > +       }
> > +
> > +       return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                       const struct drm_display_mode *mode)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> > +
> > +       if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +               drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +               if ((mode->hdisplay == mode->htotal) ||
> > +                   (mode->vdisplay == mode->vtotal))
> > +                       dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +               else
> > +                       dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +       }
> > +
> > +       return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                  struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       sprd_dpu_init(dpu);
> > +
> > +       enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                   struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +       struct drm_device *drm = dpu->crtc.dev;
> > +
> > +       disable_irq(dpu->ctx.irq);
> > +
> > +       sprd_dpu_fini(dpu);
> > +
> > +       spin_lock_irq(&drm->event_lock);
> > +       if (crtc->state->event) {
> > +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +               crtc->state->event = NULL;
> > +       }
> > +       spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *state)
> > +{
> > +       DRM_DEBUG("%s()\n", __func__);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> > +
> > +       dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +       struct drm_device *drm = dpu->crtc.dev;
> > +
> > +       if (dpu->pending_planes)
> > +               dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> > +
> > +       spin_lock_irq(&drm->event_lock);
> > +       if (crtc->state->event) {
> > +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +               crtc->state->event = NULL;
> > +       }
> > +       spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +       .mode_valid     = sprd_crtc_mode_valid,
> > +       .atomic_check   = sprd_crtc_atomic_check,
> > +       .atomic_begin   = sprd_crtc_atomic_begin,
> > +       .atomic_flush   = sprd_crtc_atomic_flush,
> > +       .atomic_enable  = sprd_crtc_atomic_enable,
> > +       .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +       .destroy        = drm_crtc_cleanup,
> > +       .set_config     = drm_atomic_helper_set_config,
> > +       .page_flip      = drm_atomic_helper_page_flip,
> > +       .reset          = drm_atomic_helper_crtc_reset,
> > +       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +       .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +       .enable_vblank  = sprd_crtc_enable_vblank,
> > +       .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                        struct drm_plane *primary)
> > +{
> > +       struct device_node *port;
> > +       int ret;
> > +
> > +       /*
> > +        * set crtc port so that drm_of_find_possible_crtcs call works
> > +        */
> > +       port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +       if (!port) {
> > +               DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                         drm->dev->of_node->full_name);
> > +               return -EINVAL;
> > +       }
> > +       of_node_put(port);
> > +       crtc->port = port;
> > +
> > +       ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                       &sprd_crtc_funcs, NULL);
> > +       if (ret) {
> > +               DRM_ERROR("failed to init crtc.\n");
> > +               return ret;
> > +       }
> > +
> > +       drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +       drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +
> > +       dpu->core->init(ctx);
> > +       dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +
> > +       dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +       struct sprd_dpu *dpu = data;
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +       u32 int_mask = 0;
> > +
> > +       int_mask = dpu->core->isr(ctx);
> > +
> > +       if (int_mask & BIT_DPU_INT_ERR)
> > +               DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +       if (int_mask & BIT_DPU_INT_VSYNC)
> > +               drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +       struct drm_device *drm = data;
> > +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +       struct drm_plane *plane;
> > +       int ret;
> > +
> > +       plane = sprd_plane_init(drm, dpu);
> > +       if (IS_ERR_OR_NULL(plane)) {
> > +               ret = PTR_ERR(plane);
> > +               return ret;
> > +       }
> > +
> > +       ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +       if (ret)
> > +               return ret;
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +       void *data)
> > +{
> > +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +       drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +       .bind = sprd_dpu_bind,
> > +       .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                               struct device *dev)
> > +{
> > +       struct platform_device *pdev = to_platform_device(dev);
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +       struct resource *res;
> > +       int ret;
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +       if (!ctx->base) {
> > +               DRM_ERROR("failed to map dpu registers\n");
> > +               return -EFAULT;
> > +       }
> > +
> > +       ctx->irq = platform_get_irq(pdev, 0);
> > +       if (ctx->irq < 0) {
> > +               DRM_ERROR("failed to get dpu irq\n");
> > +               return ctx->irq;
> > +       }
> > +
> > +       irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +       ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                       0, "DPU", dpu);
> > +       if (ret) {
> > +               DRM_ERROR("failed to register dpu irq handler\n");
> > +               return ret;
> > +       }
> > +
> > +       init_waitqueue_head(&ctx->wait_queue);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +       .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +       { .compatible = "sprd,sharkl3-dpu",
> > +         .data = &sharkl3_dpu },
> > +       { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +       const struct sprd_dpu_ops *pdata;
> > +       struct sprd_dpu *dpu;
> > +       int ret;
> > +
> > +       dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +       if (!dpu)
> > +               return -ENOMEM;
> > +
> > +       pdata = of_device_get_match_data(&pdev->dev);
> > +       if (pdata) {
> > +               dpu->core = pdata->core;
> > +       } else {
> > +               DRM_ERROR("No matching driver data found\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       platform_set_drvdata(pdev, dpu);
> > +
> > +       return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +       component_del(&pdev->dev, &dpu_component_ops);
> > +       return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +       .probe = sprd_dpu_probe,
> > +       .remove = sprd_dpu_remove,
> > +       .driver = {
> > +               .name = "sprd-dpu-drv",
> > +               .of_match_table = dpu_match_table,
> > +       },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_              BIT(0)
> > +#define BIT_DPU_INT_TE                 BIT(1)
> > +#define BIT_DPU_INT_ERR                        BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE            BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> > +#define BIT_DPU_INT_VSYNC              BIT(5)
> > +#define BIT_DPU_INT_WB_DONE            BIT(6)
> > +#define BIT_DPU_INT_WB_ERR             BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                        (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                        (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE               (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE               (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE               (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                    (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                      (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3               (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0               (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                  (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH            (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL          (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT         (0x01 << 16)
> > +
> > +enum {
> > +       SPRD_DPU_IF_DBI = 0,
> > +       SPRD_DPU_IF_DPI,
> > +       SPRD_DPU_IF_EDPI,
> > +       SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +       DPU_LAYER_ROTATION_0,
> > +       DPU_LAYER_ROTATION_90,
> > +       DPU_LAYER_ROTATION_180,
> > +       DPU_LAYER_ROTATION_270,
> > +       DPU_LAYER_ROTATION_0_M,
> > +       DPU_LAYER_ROTATION_90_M,
> > +       DPU_LAYER_ROTATION_180_M,
> > +       DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +       u8 index;
> > +       u8 planes;
> > +       u32 addr[4];
> > +       u32 pitch[4];
> > +       s16 src_x;
> > +       s16 src_y;
> > +       s16 src_w;
> > +       s16 src_h;
> > +       s16 dst_x;
> > +       s16 dst_y;
> > +       u16 dst_w;
> > +       u16 dst_h;
> > +       u32 format;
> > +       u32 alpha;
> > +       u32 blending;
> > +       u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +       u32 max_layers;
> > +       const u32 *fmts_ptr;
> > +       u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +       void (*init)(struct dpu_context *ctx);
> > +       void (*fini)(struct dpu_context *ctx);
> > +       void (*run)(struct dpu_context *ctx);
> > +       void (*stop)(struct dpu_context *ctx);
> > +       void (*enable_vsync)(struct dpu_context *ctx);
> > +       void (*disable_vsync)(struct dpu_context *ctx);
> > +       u32 (*isr)(struct dpu_context *ctx);
> > +       void (*ifconfig)(struct dpu_context *ctx);
> > +       void (*flip)(struct dpu_context *ctx,
> > +                    struct dpu_layer layers[], u8 count);
> > +       void (*capability)(struct dpu_context *ctx,
> > +                       struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +       void __iomem *base;
> > +       int irq;
> > +       u8 if_type;
> > +       struct videomode vm;
> > +       bool stopped;
> > +       wait_queue_head_t wait_queue;
> > +       bool evt_update;
> > +       bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +       struct drm_crtc crtc;
> > +       struct dpu_context ctx;
> > +       const struct dpu_core_ops *core;
> > +       struct dpu_layer *layers;
> > +       u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +       const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +       return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >         &sprd_drm_driver,
> > +       &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >         struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
@ 2020-08-29 17:10       ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-08-29 17:10 UTC (permalink / raw)
  To: Kevin Tang, Maarten Lankhorst, Maxime Ripard, Sean Paul,
	Dave Airlie, Rob Herring, Mark Rutland, Orson Zhai,
	Chunyan Zhang, Linux Kernel Mailing List, dri-devel

Daniel Vetter <daniel@ffwll.ch> 于2020年7月29日周三 上午5:51写道:
>
> On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
> >
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
>
> Quickly scrolled through this, and the entire thing very much leaves a
> midlayer heavy aftertaste. Do we really need stuff like struct dpu_layer
> and struct dpu_core_ops? They only seem to complicate the code base, and
> seem to have no real reason. The indirection with first computing register
> values into a sprd_plane/crtc structure, and then writing it into hardware
> is also a bit much - I recommend to only do that if you have to compute
> values in _check to validate them, so that the computation doesn't have to
> be repeated in the commit phase functions.
Hi daniel,
Maybe some DPU's IP verison, there will be some differences in the
DPU on each Soc. Not only control registers, there will be some differences
in control logic and functions
If access H/W registers all put into crtc or plane driver, how did
others deal with it? Add Soc's macro?
When there are too many H/W IPs, split into different files(dpu_core_ops)
according to different IP, I think the code project will be more
convenient to keeping.

For multiple H/W IP, the popular design method is?

>
> Also, the layer and pending_flips stuff in sprd_dpu don't work with
> atomic, that races. You have to put all that stuff into state objects, or
> if it's some data shared with interrupt handlers (doesn't seem to be the
> case here), it needs its own locking, and any data you need in the
> interrupt handler must be copied over.
the layer and pending_flips, we put into atomic_flush, We do have some
features related to interrupt handlers, but it has not been submitted yet.
So pending_flips lock have not been added.
>
> Also no devm_kzalloc for anything containined a drm_* structure, that's
> the wrong lifetime.
>
> So yeah high level review is that I think this driver would benefit a lot
> from a pile of demidlayer.
>
> Cheers, Daniel
>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +       sprd_dpu.o
> > +
> > +obj-y += dpu/
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE   0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET     0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +       (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> > +
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +       writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +       writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL   0x04
> > +#define REG_DPU_CFG0   0x08
> > +#define REG_DPU_CFG1   0x0C
> > +#define REG_DPU_CFG2   0x10
> > +#define REG_PANEL_SIZE 0x20
> > +#define REG_BLEND_SIZE 0x24
> > +#define REG_BG_COLOR   0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0     0x30
> > +#define REG_LAY_BASE_ADDR1     0x34
> > +#define REG_LAY_BASE_ADDR2     0x38
> > +#define REG_LAY_CTRL           0x40
> > +#define REG_LAY_SIZE           0x44
> > +#define REG_LAY_PITCH          0x48
> > +#define REG_LAY_POS            0x4C
> > +#define REG_LAY_ALPHA          0x50
> > +#define REG_LAY_PALLETE                0x58
> > +#define REG_LAY_CROP_START     0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN         0x1E0
> > +#define REG_DPU_INT_CLR                0x1E4
> > +#define REG_DPU_INT_STS                0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL           0x1F0
> > +#define REG_DPI_H_TIMING       0x1F4
> > +#define REG_DPI_V_TIMING       0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                     0x800
> > +#define REG_MMU_VPN_RANGE              0x80C
> > +#define REG_MMU_VAOR_ADDR_RD           0x818
> > +#define REG_MMU_VAOR_ADDR_WR           0x81C
> > +#define REG_MMU_INV_ADDR_RD            0x820
> > +#define REG_MMU_INV_ADDR_WR            0x824
> > +#define REG_MMU_PPN1                   0x83C
> > +#define REG_MMU_RANGE1                 0x840
> > +#define REG_MMU_PPN2                   0x844
> > +#define REG_MMU_RANGE2                 0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                    BIT(0)
> > +#define BIT_DPU_STOP                   BIT(1)
> > +#define BIT_DPU_REG_UPDATE             BIT(2)
> > +#define BIT_DPU_IF_EDPI                        BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE              BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD       BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                         BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE               BIT(0)
> > +#define BIT_DPU_INT_TE                 BIT(1)
> > +#define BIT_DPU_INT_ERR                        BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> > +#define BIT_DPU_INT_VSYNC              BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR                BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR                BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD                BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR                BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD         BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR         BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN             BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN            BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +       u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                       BIT_DPU_INT_MMU_VAOR_WR |
> > +                       BIT_DPU_INT_MMU_INV_RD |
> > +                       BIT_DPU_INT_MMU_INV_WR;
> > +       u32 val = reg_val & mmu_mask;
> > +       int i;
> > +
> > +       if (val) {
> > +               DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
> > +
> > +               if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                       DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> > +               if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                       DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> > +               if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                       DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> > +               if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                       DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> > +
> > +               for (i = 0; i < 8; i++) {
> > +                       reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                       if (reg_val & 0x1)
> > +                               DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                       DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +               }
> > +       }
> > +
> > +       return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < 8; i++)
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +       u32 reg_val, int_mask = 0;
> > +
> > +       reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +       /* disable err interrupt */
> > +       if (reg_val & BIT_DPU_INT_ERR)
> > +               int_mask |= BIT_DPU_INT_ERR;
> > +
> > +       /* dpu update done isr */
> > +       if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +               ctx->evt_update = true;
> > +               wake_up_interruptible_all(&ctx->wait_queue);
> > +       }
> > +
> > +       /* dpu stop done isr */
> > +       if (reg_val & BIT_DPU_INT_DONE) {
> > +               ctx->evt_stop = true;
> > +               wake_up_interruptible_all(&ctx->wait_queue);
> > +       }
> > +
> > +       /* dpu ifbc payload error isr */
> > +       if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +               int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +               DRM_ERROR("dpu ifbc payload error\n");
> > +       }
> > +
> > +       /* dpu ifbc header error isr */
> > +       if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +               int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +               DRM_ERROR("dpu ifbc header error\n");
> > +       }
> > +
> > +       int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +       return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +       int rc;
> > +
> > +       if (ctx->stopped)
> > +               return 0;
> > +
> > +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> > +                                              msecs_to_jiffies(500));
> > +       ctx->evt_stop = false;
> > +
> > +       ctx->stopped = true;
> > +
> > +       if (!rc) {
> > +               DRM_ERROR("dpu wait for stop done time out!\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +       int rc;
> > +
> > +       ctx->evt_update = false;
> > +
> > +       rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> > +                                              msecs_to_jiffies(500));
> > +
> > +       if (!rc) {
> > +               DRM_ERROR("dpu wait for reg update done time out!\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +       dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +       ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +       u32 reg_val, size;
> > +
> > +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +       size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +       DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +       DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +       reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +       if (ctx->stopped)
> > +               dpu_clean_all(ctx);
> > +
> > +       DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +       DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +       DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                   struct dpu_layer *hwlayer)
> > +{
> > +       const struct drm_format_info *info;
> > +       u32 size, offset, ctrl, pitch;
> > +       int i;
> > +
> > +       offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +       if (hwlayer->src_w && hwlayer->src_h)
> > +               size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> > +       else
> > +               size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> > +
> > +       for (i = 0; i < hwlayer->planes; i++)
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                               hwlayer->index), hwlayer->addr[i]);
> > +
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                       hwlayer->index), offset);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                       hwlayer->index), size);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                       hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                       hwlayer->index), hwlayer->alpha);
> > +
> > +       info = drm_format_info(hwlayer->format);
> > +       if (hwlayer->planes == 3) {
> > +               /* UV pitch is 1/2 of Y pitch*/
> > +               pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                               (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                               hwlayer->index), pitch);
> > +       } else {
> > +               pitch = hwlayer->pitch[0] / info->cpp[0];
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                               hwlayer->index), pitch);
> > +       }
> > +
> > +       ctrl = hwlayer->format |
> > +               hwlayer->blending |
> > +               (hwlayer->rotation & 0x7) << 20;
> > +
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                       hwlayer->index), ctrl);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                       hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +       DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                               hwlayer->dst_x, hwlayer->dst_y,
> > +                               hwlayer->dst_w, hwlayer->dst_h);
> > +       DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> > +                               hwlayer->src_x, hwlayer->src_y,
> > +                               hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                    struct dpu_layer layers[], u8 count)
> > +{
> > +       int i;
> > +       u32 reg_val;
> > +
> > +       /*
> > +        * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +        * registers in EDPI mode. So the config registers can only be
> > +        * updated in the rising edge of DPU_RUN bit.
> > +        */
> > +       if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +               dpu_wait_stop_done(ctx);
> > +
> > +       /* reset the bgcolor to black */
> > +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +       /* disable all the layers */
> > +       dpu_clean_all(ctx);
> > +
> > +       /* start configure dpu layers */
> > +       for (i = 0; i < count; i++)
> > +               dpu_layer(ctx, &layers[i]);
> > +
> > +       /* update trigger and wait */
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +               if (!ctx->stopped) {
> > +                       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> > +                       dpu_wait_update_done(ctx);
> > +               }
> > +
> > +               DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +               ctx->stopped = false;
> > +       }
> > +
> > +       /*
> > +        * If the following interrupt was disabled in isr,
> > +        * re-enable it.
> > +        */
> > +       reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +                 BIT_DPU_INT_FBC_HDR_ERR |
> > +                 BIT_DPU_INT_MMU_VAOR_RD |
> > +                 BIT_DPU_INT_MMU_VAOR_WR |
> > +                 BIT_DPU_INT_MMU_INV_RD |
> > +                 BIT_DPU_INT_MMU_INV_WR;
> > +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +       u32 int_mask = 0;
> > +       u32 reg_val;
> > +
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +               /* use dpi as interface */
> > +               DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +               /* disable Halt function for SPRD DSI */
> > +               DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> > +
> > +               /* select te from external pad */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +               /* set dpi timing */
> > +               reg_val = ctx->vm.hsync_len << 0 |
> > +                         ctx->vm.hback_porch << 8 |
> > +                         ctx->vm.hfront_porch << 20;
> > +               DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +               reg_val = ctx->vm.vsync_len << 0 |
> > +                         ctx->vm.vback_porch << 8 |
> > +                         ctx->vm.vfront_porch << 20;
> > +               DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +               if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                       DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                               "underflow risk!\n");
> > +
> > +               /* enable dpu update done INT */
> > +               int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +               /* enable dpu DONE  INT */
> > +               int_mask |= BIT_DPU_INT_DONE;
> > +               /* enable dpu dpi vsync */
> > +               int_mask |= BIT_DPU_INT_VSYNC;
> > +               /* enable dpu TE INT */
> > +               int_mask |= BIT_DPU_INT_TE;
> > +               /* enable underflow err INT */
> > +               int_mask |= BIT_DPU_INT_ERR;
> > +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +               /* use edpi as interface */
> > +               DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +               /* use external te */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +               /* enable te */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> > +
> > +               /* enable stop DONE INT */
> > +               int_mask |= BIT_DPU_INT_DONE;
> > +               /* enable TE INT */
> > +               int_mask |= BIT_DPU_INT_TE;
> > +       }
> > +
> > +       /* enable ifbc payload error INT */
> > +       int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +       /* enable ifbc header error INT */
> > +       int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +       /* enable iommu va out of range read error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +       /* enable iommu va out of range write error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +       /* enable iommu invalid read error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +       /* enable iommu invalid write error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +       DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +       DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +       DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +       DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +       DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +       DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +       DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +       DRM_FORMAT_YVU420,
> > +};
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                       struct dpu_capability *cap)
> > +{
> > +       cap->max_layers = 6;
> > +       cap->fmts_ptr = primary_fmts;
> > +       cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +       .init = dpu_init,
> > +       .fini = dpu_fini,
> > +       .run = dpu_run,
> > +       .stop = dpu_stop,
> > +       .isr = dpu_isr,
> > +       .ifconfig = dpu_dpi_init,
> > +       .capability = dpu_capability,
> > +       .flip = dpu_flip,
> > +       .enable_vsync = enable_vsync,
> > +       .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +       struct drm_plane plane;
> > +       u32 index;
> > +       u32 addr[4];
> > +       u32 pitch[4];
> > +       u32 format;
> > +       u32 rotation;
> > +       u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +       return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +       switch (fourcc) {
> > +       case DRM_FORMAT_BGRA8888:
> > +               /* BGRA8888 -> ARGB8888 */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_RGBX8888:
> > +       case DRM_FORMAT_RGBA8888:
> > +               /* RGBA8888 -> ABGR8888 */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_ABGR8888:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_ARGB8888:
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_XBGR8888:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_XRGB8888:
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_BGR565:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_RGB565:
> > +               *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +               break;
> > +       case DRM_FORMAT_NV12:
> > +               /* 2-Lane: Yuv420 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV21:
> > +               /* 2-Lane: Yuv420 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV16:
> > +               /* 2-Lane: Yuv422 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV61:
> > +               /* 2-Lane: Yuv422 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_YUV420:
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_YVU420:
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +       switch (angle) {
> > +       case DRM_MODE_ROTATE_0:
> > +               *rotation = DPU_LAYER_ROTATION_0;
> > +               break;
> > +       case DRM_MODE_ROTATE_90:
> > +               *rotation = DPU_LAYER_ROTATION_90;
> > +               break;
> > +       case DRM_MODE_ROTATE_180:
> > +               *rotation = DPU_LAYER_ROTATION_180;
> > +               break;
> > +       case DRM_MODE_ROTATE_270:
> > +               *rotation = DPU_LAYER_ROTATION_270;
> > +               break;
> > +       case DRM_MODE_REFLECT_Y:
> > +               *rotation = DPU_LAYER_ROTATION_180_M;
> > +               break;
> > +       case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +               *rotation = DPU_LAYER_ROTATION_90_M;
> > +               break;
> > +       case DRM_MODE_REFLECT_X:
> > +               *rotation = DPU_LAYER_ROTATION_0_M;
> > +               break;
> > +       case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +               *rotation = DPU_LAYER_ROTATION_270_M;
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                                 struct drm_plane_state *state)
> > +{
> > +       struct sprd_plane *p = to_sprd_plane(plane);
> > +       struct drm_framebuffer *fb = state->fb;
> > +       struct drm_gem_cma_object *cma_obj;
> > +       int i, ret;
> > +       u32 addr;
> > +
> > +       if (!state->fb || !state->crtc)
> > +               return 0;
> > +
> > +       ret = sprd_plane_format_convert(fb->format->format,
> > +                                       &p->format);
> > +       if (ret < 0) {
> > +               DRM_ERROR("Invalid plane format\n");
> > +               return ret;
> > +       }
> > +
> > +       ret = sprd_plane_rotation_convert(state->rotation,
> > +                                       &p->rotation);
> > +       if (ret < 0) {
> > +               DRM_ERROR("Invalid plane rotation\n");
> > +               return ret;
> > +       }
> > +
> > +       switch (state->pixel_blend_mode) {
> > +       case DRM_MODE_BLEND_COVERAGE:
> > +               /* alpha mode select - combo alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +               /* Normal mode */
> > +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +               break;
> > +       case DRM_MODE_BLEND_PREMULTI:
> > +               /* alpha mode select - combo alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +               /* Pre-mult mode */
> > +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +               break;
> > +       case DRM_MODE_BLEND_PIXEL_NONE:
> > +       default:
> > +               /* don't do blending, maybe RGBX */
> > +               /* alpha mode select - layer alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +               break;
> > +       }
> > +
> > +       for (i = 0; i < fb->format->num_planes; i++) {
> > +               cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +               addr = cma_obj->paddr + fb->offsets[i];
> > +               if (addr % 16) {
> > +                       DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> > +                               i, addr);
> > +                       return -EFAULT;
> > +               }
> > +
> > +               p->addr[i] = addr;
> > +               p->pitch[i] = fb->pitches[i];
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                   struct drm_plane_state *old_state)
> > +{
> > +       struct drm_plane_state *state = plane->state;
> > +       struct drm_framebuffer *fb = plane->state->fb;
> > +       struct sprd_plane *p = to_sprd_plane(plane);
> > +       struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +       struct dpu_layer *layer = &dpu->layers[p->index];
> > +       int i;
> > +
> > +       if (!state->crtc || !state->fb)
> > +               return;
> > +
> > +       layer->index = p->index;
> > +       layer->src_x = state->src_x >> 16;
> > +       layer->src_y = state->src_y >> 16;
> > +       layer->src_w = state->src_w >> 16;
> > +       layer->src_h = state->src_h >> 16;
> > +       layer->dst_x = state->crtc_x;
> > +       layer->dst_y = state->crtc_y;
> > +       layer->dst_w = state->crtc_w;
> > +       layer->dst_h = state->crtc_h;
> > +       layer->alpha = state->alpha;
> > +       layer->format = p->format;
> > +       layer->blending = p->blend_mode;
> > +       layer->rotation = p->rotation;
> > +       layer->planes = fb->format->num_planes;
> > +
> > +       for (i = 0; i < layer->planes; i++) {
> > +               layer->addr[i] = p->addr[i];
> > +               layer->pitch[i] = p->pitch[i];
> > +       }
> > +
> > +       dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> > +{
> > +       unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                      BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                      BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +       /* create rotation property */
> > +       drm_plane_create_rotation_property(&p->plane,
> > +                                          DRM_MODE_ROTATE_0,
> > +                                          DRM_MODE_ROTATE_MASK |
> > +                                          DRM_MODE_REFLECT_MASK);
> > +
> > +       /* create alpha property */
> > +       drm_plane_create_alpha_property(&p->plane);
> > +
> > +       /* create blend mode property */
> > +       drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +       /* create zpos property */
> > +       drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +       .atomic_check = sprd_plane_atomic_check,
> > +       .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +       .update_plane = drm_atomic_helper_update_plane,
> > +       .disable_plane  = drm_atomic_helper_disable_plane,
> > +       .destroy = drm_plane_cleanup,
> > +       .reset = drm_atomic_helper_plane_reset,
> > +       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                       struct sprd_dpu *dpu)
> > +{
> > +       struct drm_plane *primary = NULL;
> > +       struct sprd_plane *p = NULL;
> > +       struct dpu_capability cap = {};
> > +       int ret, i;
> > +
> > +       dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +       dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                                 sizeof(struct dpu_layer), GFP_KERNEL);
> > +       if (!dpu->layers)
> > +               return ERR_PTR(-ENOMEM);
> > +
> > +       for (i = 0; i < cap.max_layers; i++) {
> > +
> > +               p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +               if (!p)
> > +                       return ERR_PTR(-ENOMEM);
> > +
> > +               ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                              &sprd_plane_funcs, cap.fmts_ptr,
> > +                                              cap.fmts_cnt, NULL,
> > +                                              DRM_PLANE_TYPE_PRIMARY, NULL);
> > +               if (ret) {
> > +                       DRM_ERROR("fail to init primary plane\n");
> > +                       return ERR_PTR(ret);
> > +               }
> > +
> > +               drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> > +
> > +               sprd_plane_create_properties(p, i);
> > +
> > +               p->index = i;
> > +               if (i == 0)
> > +                       primary = &p->plane;
> > +       }
> > +
> > +       return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                       const struct drm_display_mode *mode)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> > +
> > +       if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +               drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +               if ((mode->hdisplay == mode->htotal) ||
> > +                   (mode->vdisplay == mode->vtotal))
> > +                       dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +               else
> > +                       dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +       }
> > +
> > +       return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                  struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       sprd_dpu_init(dpu);
> > +
> > +       enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                   struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +       struct drm_device *drm = dpu->crtc.dev;
> > +
> > +       disable_irq(dpu->ctx.irq);
> > +
> > +       sprd_dpu_fini(dpu);
> > +
> > +       spin_lock_irq(&drm->event_lock);
> > +       if (crtc->state->event) {
> > +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +               crtc->state->event = NULL;
> > +       }
> > +       spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *state)
> > +{
> > +       DRM_DEBUG("%s()\n", __func__);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> > +
> > +       dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +       struct drm_device *drm = dpu->crtc.dev;
> > +
> > +       if (dpu->pending_planes)
> > +               dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> > +
> > +       spin_lock_irq(&drm->event_lock);
> > +       if (crtc->state->event) {
> > +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +               crtc->state->event = NULL;
> > +       }
> > +       spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +       .mode_valid     = sprd_crtc_mode_valid,
> > +       .atomic_check   = sprd_crtc_atomic_check,
> > +       .atomic_begin   = sprd_crtc_atomic_begin,
> > +       .atomic_flush   = sprd_crtc_atomic_flush,
> > +       .atomic_enable  = sprd_crtc_atomic_enable,
> > +       .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +       .destroy        = drm_crtc_cleanup,
> > +       .set_config     = drm_atomic_helper_set_config,
> > +       .page_flip      = drm_atomic_helper_page_flip,
> > +       .reset          = drm_atomic_helper_crtc_reset,
> > +       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +       .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +       .enable_vblank  = sprd_crtc_enable_vblank,
> > +       .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                        struct drm_plane *primary)
> > +{
> > +       struct device_node *port;
> > +       int ret;
> > +
> > +       /*
> > +        * set crtc port so that drm_of_find_possible_crtcs call works
> > +        */
> > +       port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +       if (!port) {
> > +               DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                         drm->dev->of_node->full_name);
> > +               return -EINVAL;
> > +       }
> > +       of_node_put(port);
> > +       crtc->port = port;
> > +
> > +       ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                       &sprd_crtc_funcs, NULL);
> > +       if (ret) {
> > +               DRM_ERROR("failed to init crtc.\n");
> > +               return ret;
> > +       }
> > +
> > +       drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +       drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +
> > +       dpu->core->init(ctx);
> > +       dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +
> > +       dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +       struct sprd_dpu *dpu = data;
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +       u32 int_mask = 0;
> > +
> > +       int_mask = dpu->core->isr(ctx);
> > +
> > +       if (int_mask & BIT_DPU_INT_ERR)
> > +               DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +       if (int_mask & BIT_DPU_INT_VSYNC)
> > +               drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +       struct drm_device *drm = data;
> > +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +       struct drm_plane *plane;
> > +       int ret;
> > +
> > +       plane = sprd_plane_init(drm, dpu);
> > +       if (IS_ERR_OR_NULL(plane)) {
> > +               ret = PTR_ERR(plane);
> > +               return ret;
> > +       }
> > +
> > +       ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +       if (ret)
> > +               return ret;
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +       void *data)
> > +{
> > +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +       drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +       .bind = sprd_dpu_bind,
> > +       .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                               struct device *dev)
> > +{
> > +       struct platform_device *pdev = to_platform_device(dev);
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +       struct resource *res;
> > +       int ret;
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +       if (!ctx->base) {
> > +               DRM_ERROR("failed to map dpu registers\n");
> > +               return -EFAULT;
> > +       }
> > +
> > +       ctx->irq = platform_get_irq(pdev, 0);
> > +       if (ctx->irq < 0) {
> > +               DRM_ERROR("failed to get dpu irq\n");
> > +               return ctx->irq;
> > +       }
> > +
> > +       irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +       ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                       0, "DPU", dpu);
> > +       if (ret) {
> > +               DRM_ERROR("failed to register dpu irq handler\n");
> > +               return ret;
> > +       }
> > +
> > +       init_waitqueue_head(&ctx->wait_queue);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +       .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +       { .compatible = "sprd,sharkl3-dpu",
> > +         .data = &sharkl3_dpu },
> > +       { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +       const struct sprd_dpu_ops *pdata;
> > +       struct sprd_dpu *dpu;
> > +       int ret;
> > +
> > +       dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +       if (!dpu)
> > +               return -ENOMEM;
> > +
> > +       pdata = of_device_get_match_data(&pdev->dev);
> > +       if (pdata) {
> > +               dpu->core = pdata->core;
> > +       } else {
> > +               DRM_ERROR("No matching driver data found\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       platform_set_drvdata(pdev, dpu);
> > +
> > +       return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +       component_del(&pdev->dev, &dpu_component_ops);
> > +       return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +       .probe = sprd_dpu_probe,
> > +       .remove = sprd_dpu_remove,
> > +       .driver = {
> > +               .name = "sprd-dpu-drv",
> > +               .of_match_table = dpu_match_table,
> > +       },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_              BIT(0)
> > +#define BIT_DPU_INT_TE                 BIT(1)
> > +#define BIT_DPU_INT_ERR                        BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE            BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> > +#define BIT_DPU_INT_VSYNC              BIT(5)
> > +#define BIT_DPU_INT_WB_DONE            BIT(6)
> > +#define BIT_DPU_INT_WB_ERR             BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                        (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                        (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE               (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE               (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE               (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                    (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                      (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3               (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0               (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                  (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH            (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL          (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT         (0x01 << 16)
> > +
> > +enum {
> > +       SPRD_DPU_IF_DBI = 0,
> > +       SPRD_DPU_IF_DPI,
> > +       SPRD_DPU_IF_EDPI,
> > +       SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +       DPU_LAYER_ROTATION_0,
> > +       DPU_LAYER_ROTATION_90,
> > +       DPU_LAYER_ROTATION_180,
> > +       DPU_LAYER_ROTATION_270,
> > +       DPU_LAYER_ROTATION_0_M,
> > +       DPU_LAYER_ROTATION_90_M,
> > +       DPU_LAYER_ROTATION_180_M,
> > +       DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +       u8 index;
> > +       u8 planes;
> > +       u32 addr[4];
> > +       u32 pitch[4];
> > +       s16 src_x;
> > +       s16 src_y;
> > +       s16 src_w;
> > +       s16 src_h;
> > +       s16 dst_x;
> > +       s16 dst_y;
> > +       u16 dst_w;
> > +       u16 dst_h;
> > +       u32 format;
> > +       u32 alpha;
> > +       u32 blending;
> > +       u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +       u32 max_layers;
> > +       const u32 *fmts_ptr;
> > +       u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +       void (*init)(struct dpu_context *ctx);
> > +       void (*fini)(struct dpu_context *ctx);
> > +       void (*run)(struct dpu_context *ctx);
> > +       void (*stop)(struct dpu_context *ctx);
> > +       void (*enable_vsync)(struct dpu_context *ctx);
> > +       void (*disable_vsync)(struct dpu_context *ctx);
> > +       u32 (*isr)(struct dpu_context *ctx);
> > +       void (*ifconfig)(struct dpu_context *ctx);
> > +       void (*flip)(struct dpu_context *ctx,
> > +                    struct dpu_layer layers[], u8 count);
> > +       void (*capability)(struct dpu_context *ctx,
> > +                       struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +       void __iomem *base;
> > +       int irq;
> > +       u8 if_type;
> > +       struct videomode vm;
> > +       bool stopped;
> > +       wait_queue_head_t wait_queue;
> > +       bool evt_update;
> > +       bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +       struct drm_crtc crtc;
> > +       struct dpu_context ctx;
> > +       const struct dpu_core_ops *core;
> > +       struct dpu_layer *layers;
> > +       u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +       const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +       return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >         &sprd_drm_driver,
> > +       &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >         struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 21:13     ` Sam Ravnborg
@ 2020-11-17  3:07       ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-11-17  3:07 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Maarten Lankhorst, Maxime Ripard, Sean Paul, David Airlie,
	Daniel Vetter, Rob Herring, Mark Rutland, Orson Zhai,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, Chunyan Zhang

Hi Sam,

Here is feedback of RFC V7

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午5:14写道:
>
> Hi Kevin.
>
> Some feedback in the following.
> I lost track of thing for the atomic modesettting stuff and I hope other
> will review that.
>
>         Sam
>
> On Tue, Jul 28, 2020 at 06:07:57PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> Not needed - drop.
It has been fixed on RFC V7.
>
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +     sprd_dpu.o
> > +
> > +obj-y += dpu/
>
> Until there are several DPU's there is no need for a separate directory.
> Make it simpler by keeping it all in drm/sprd/*
It has been fixed on RFC V7.
>
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE 0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET   0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +     (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> Use a static inline to get better typecheck.
It has been fixed on RFC V7.
>
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> I am no fan of macros used like this.
>
> Maybe use static inlines and add struct dpu_context * as first argument.
> Then adding base can be hidden away.
It has been fixed on RFC V7.
>
>
> I had a hard time convincing myself that _relaxed was the right
> variants. If I get it correct we may see writes re-ordered with the
> _relaxed variants wich would be no good when dealign with interrupts.
> But I may miss somethign here.
I need to think about it
>
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL 0x04
> > +#define REG_DPU_CFG0 0x08
> > +#define REG_DPU_CFG1 0x0C
> > +#define REG_DPU_CFG2 0x10
> > +#define REG_PANEL_SIZE       0x20
> > +#define REG_BLEND_SIZE       0x24
> > +#define REG_BG_COLOR 0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0   0x30
> > +#define REG_LAY_BASE_ADDR1   0x34
> > +#define REG_LAY_BASE_ADDR2   0x38
> > +#define REG_LAY_CTRL         0x40
> > +#define REG_LAY_SIZE         0x44
> > +#define REG_LAY_PITCH                0x48
> > +#define REG_LAY_POS          0x4C
> > +#define REG_LAY_ALPHA                0x50
> > +#define REG_LAY_PALLETE              0x58
> > +#define REG_LAY_CROP_START   0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN               0x1E0
> > +#define REG_DPU_INT_CLR              0x1E4
> > +#define REG_DPU_INT_STS              0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL         0x1F0
> > +#define REG_DPI_H_TIMING     0x1F4
> > +#define REG_DPI_V_TIMING     0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                   0x800
> > +#define REG_MMU_VPN_RANGE            0x80C
> > +#define REG_MMU_VAOR_ADDR_RD         0x818
> > +#define REG_MMU_VAOR_ADDR_WR         0x81C
> > +#define REG_MMU_INV_ADDR_RD          0x820
> > +#define REG_MMU_INV_ADDR_WR          0x824
> > +#define REG_MMU_PPN1                 0x83C
> > +#define REG_MMU_RANGE1                       0x840
> > +#define REG_MMU_PPN2                 0x844
> > +#define REG_MMU_RANGE2                       0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                  BIT(0)
> > +#define BIT_DPU_STOP                 BIT(1)
> > +#define BIT_DPU_REG_UPDATE           BIT(2)
> > +#define BIT_DPU_IF_EDPI                      BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE            BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD     BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                               BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE             BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR              BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR              BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD              BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR              BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD               BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR               BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN           BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD       BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN          BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +     u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                     BIT_DPU_INT_MMU_VAOR_WR |
> > +                     BIT_DPU_INT_MMU_INV_RD |
> > +                     BIT_DPU_INT_MMU_INV_WR;
> > +     u32 val = reg_val & mmu_mask;
> > +     int i;
> > +
> > +     if (val) {
> > +             DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
>
> General comment, applies for the whole patch.
>
> Use drm_err(drm, ...), drm_info(drm, ...) etc.
>
> Use upclassing to get sprd_dpu - and then you have a drm_device *
> (After drm_device is embedded in sprd_dpu as suggested in the other
> mail)
We did not use drm_device in crtc driver, only in kms driver(sprd_drm.c)
So is this still necessary?
>
> > +
> > +             if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                     DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                     DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                     DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                     DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> > +
> > +             for (i = 0; i < 8; i++) {
> > +                     reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                     if (reg_val & 0x1)
> > +                             DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +             }
> > +     }
> > +
> > +     return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < 8; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, int_mask = 0;
> > +
> > +     reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +     /* disable err interrupt */
> > +     if (reg_val & BIT_DPU_INT_ERR)
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +
> > +     /* dpu update done isr */
> > +     if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +             ctx->evt_update = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu stop done isr */
> > +     if (reg_val & BIT_DPU_INT_DONE) {
> > +             ctx->evt_stop = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu ifbc payload error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +             DRM_ERROR("dpu ifbc payload error\n");
> > +     }
> > +
> > +     /* dpu ifbc header error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +             DRM_ERROR("dpu ifbc header error\n");
> > +     }
> > +
> > +     int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +     return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     if (ctx->stopped)
> > +             return 0;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> > +                                            msecs_to_jiffies(500));
> > +     ctx->evt_stop = false;
> > +
> > +     ctx->stopped = true;
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for stop done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     ctx->evt_update = false;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> > +                                            msecs_to_jiffies(500));
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for reg update done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +     dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +     ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, size;
> > +
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +     DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +     DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +     reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +     if (ctx->stopped)
> > +             dpu_clean_all(ctx);
> > +
> > +     DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                 struct dpu_layer *hwlayer)
> No linebreak needed here.
It has been fixed on RFC V7.
>
> > +{
> > +     const struct drm_format_info *info;
> > +     u32 size, offset, ctrl, pitch;
> > +     int i;
> > +
> > +     offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +     if (hwlayer->src_w && hwlayer->src_h)
> > +             size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> > +     else
> > +             size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> > +
> > +     for (i = 0; i < hwlayer->planes; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                             hwlayer->index), hwlayer->addr[i]);
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                     hwlayer->index), offset);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                     hwlayer->index), size);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                     hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                     hwlayer->index), hwlayer->alpha);
> > +
> > +     info = drm_format_info(hwlayer->format);
> > +     if (hwlayer->planes == 3) {
> > +             /* UV pitch is 1/2 of Y pitch*/
> > +             pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                             (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     } else {
> > +             pitch = hwlayer->pitch[0] / info->cpp[0];
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     }
> > +
> > +     ctrl = hwlayer->format |
> > +             hwlayer->blending |
> > +             (hwlayer->rotation & 0x7) << 20;
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), ctrl);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +     DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                             hwlayer->dst_x, hwlayer->dst_y,
> > +                             hwlayer->dst_w, hwlayer->dst_h);
> > +     DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> > +                             hwlayer->src_x, hwlayer->src_y,
> > +                             hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count)
> > +{
> > +     int i;
> > +     u32 reg_val;
> > +
> > +     /*
> > +      * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +      * registers in EDPI mode. So the config registers can only be
> > +      * updated in the rising edge of DPU_RUN bit.
> > +      */
> > +     if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +             dpu_wait_stop_done(ctx);
> > +
> > +     /* reset the bgcolor to black */
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     /* disable all the layers */
> > +     dpu_clean_all(ctx);
> > +
> > +     /* start configure dpu layers */
> > +     for (i = 0; i < count; i++)
> > +             dpu_layer(ctx, &layers[i]);
> > +
> > +     /* update trigger and wait */
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             if (!ctx->stopped) {
> > +                     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> > +                     dpu_wait_update_done(ctx);
> > +             }
> > +
> > +             DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +             ctx->stopped = false;
> > +     }
> > +
> > +     /*
> > +      * If the following interrupt was disabled in isr,
> > +      * re-enable it.
> > +      */
> > +     reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +               BIT_DPU_INT_FBC_HDR_ERR |
> > +               BIT_DPU_INT_MMU_VAOR_RD |
> > +               BIT_DPU_INT_MMU_VAOR_WR |
> > +               BIT_DPU_INT_MMU_INV_RD |
> > +               BIT_DPU_INT_MMU_INV_WR;
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +     u32 int_mask = 0;
> > +     u32 reg_val;
> > +
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             /* use dpi as interface */
> > +             DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* disable Halt function for SPRD DSI */
> > +             DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> > +
> > +             /* select te from external pad */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* set dpi timing */
> > +             reg_val = ctx->vm.hsync_len << 0 |
> > +                       ctx->vm.hback_porch << 8 |
> > +                       ctx->vm.hfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +             reg_val = ctx->vm.vsync_len << 0 |
> > +                       ctx->vm.vback_porch << 8 |
> > +                       ctx->vm.vfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                     DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                             "underflow risk!\n");
> > +
> > +             /* enable dpu update done INT */
> > +             int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +             /* enable dpu DONE  INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable dpu dpi vsync */
> > +             int_mask |= BIT_DPU_INT_VSYNC;
> > +             /* enable dpu TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +             /* enable underflow err INT */
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             /* use edpi as interface */
> > +             DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* use external te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* enable te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> > +
> > +             /* enable stop DONE INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +     }
> > +
> > +     /* enable ifbc payload error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +     /* enable ifbc header error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +     /* enable iommu va out of range read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +     /* enable iommu va out of range write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +     /* enable iommu invalid read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +     /* enable iommu invalid write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +     DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +     DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +     DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +     DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +     DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +     DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +     DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +     DRM_FORMAT_YVU420,
> > +};
> One format per line improves readability a lot.
> Maybe sort alphbetically too.
It has been fixed on RFC V7.
>
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap)
> Drop linebreak
It has been fixed on RFC V7.
>
> > +{
> > +     cap->max_layers = 6;
> > +     cap->fmts_ptr = primary_fmts;
> > +     cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +     .init = dpu_init,
> > +     .fini = dpu_fini,
> > +     .run = dpu_run,
> > +     .stop = dpu_stop,
> > +     .isr = dpu_isr,
> > +     .ifconfig = dpu_dpi_init,
> > +     .capability = dpu_capability,
> > +     .flip = dpu_flip,
> > +     .enable_vsync = enable_vsync,
> > +     .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +     struct drm_plane plane;
> > +     u32 index;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     u32 format;
> > +     u32 rotation;
> > +     u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +     return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +     switch (fourcc) {
> > +     case DRM_FORMAT_BGRA8888:
> > +             /* BGRA8888 -> ARGB8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_RGBX8888:
> > +     case DRM_FORMAT_RGBA8888:
> > +             /* RGBA8888 -> ABGR8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ABGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ARGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_XBGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_XRGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_BGR565:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_RGB565:
> > +             *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +             break;
> > +     case DRM_FORMAT_NV12:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV21:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV16:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV61:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YUV420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YVU420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +     switch (angle) {
> > +     case DRM_MODE_ROTATE_0:
> > +             *rotation = DPU_LAYER_ROTATION_0;
> > +             break;
> > +     case DRM_MODE_ROTATE_90:
> > +             *rotation = DPU_LAYER_ROTATION_90;
> > +             break;
> > +     case DRM_MODE_ROTATE_180:
> > +             *rotation = DPU_LAYER_ROTATION_180;
> > +             break;
> > +     case DRM_MODE_ROTATE_270:
> > +             *rotation = DPU_LAYER_ROTATION_270;
> > +             break;
> > +     case DRM_MODE_REFLECT_Y:
> > +             *rotation = DPU_LAYER_ROTATION_180_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_90_M;
> > +             break;
> > +     case DRM_MODE_REFLECT_X:
> > +             *rotation = DPU_LAYER_ROTATION_0_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_270_M;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                               struct drm_plane_state *state)
> > +{
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct drm_framebuffer *fb = state->fb;
> > +     struct drm_gem_cma_object *cma_obj;
> > +     int i, ret;
> > +     u32 addr;
> > +
> > +     if (!state->fb || !state->crtc)
> > +             return 0;
> > +
> > +     ret = sprd_plane_format_convert(fb->format->format,
> > +                                     &p->format);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane format\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_plane_rotation_convert(state->rotation,
> > +                                     &p->rotation);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane rotation\n");
> > +             return ret;
> > +     }
> > +
> > +     switch (state->pixel_blend_mode) {
> > +     case DRM_MODE_BLEND_COVERAGE:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Normal mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +             break;
> > +     case DRM_MODE_BLEND_PREMULTI:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Pre-mult mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +             break;
> > +     case DRM_MODE_BLEND_PIXEL_NONE:
> > +     default:
> > +             /* don't do blending, maybe RGBX */
> > +             /* alpha mode select - layer alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +             break;
> > +     }
> > +
> > +     for (i = 0; i < fb->format->num_planes; i++) {
> > +             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +             addr = cma_obj->paddr + fb->offsets[i];
> > +             if (addr % 16) {
> > +                     DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> > +                             i, addr);
> > +                     return -EFAULT;
> > +             }
> > +
> > +             p->addr[i] = addr;
> > +             p->pitch[i] = fb->pitches[i];
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                 struct drm_plane_state *old_state)
> > +{
> > +     struct drm_plane_state *state = plane->state;
> > +     struct drm_framebuffer *fb = plane->state->fb;
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +     struct dpu_layer *layer = &dpu->layers[p->index];
> > +     int i;
> > +
> > +     if (!state->crtc || !state->fb)
> > +             return;
> > +
> > +     layer->index = p->index;
> > +     layer->src_x = state->src_x >> 16;
> > +     layer->src_y = state->src_y >> 16;
> > +     layer->src_w = state->src_w >> 16;
> > +     layer->src_h = state->src_h >> 16;
> > +     layer->dst_x = state->crtc_x;
> > +     layer->dst_y = state->crtc_y;
> > +     layer->dst_w = state->crtc_w;
> > +     layer->dst_h = state->crtc_h;
> > +     layer->alpha = state->alpha;
> > +     layer->format = p->format;
> > +     layer->blending = p->blend_mode;
> > +     layer->rotation = p->rotation;
> > +     layer->planes = fb->format->num_planes;
> > +
> > +     for (i = 0; i < layer->planes; i++) {
> > +             layer->addr[i] = p->addr[i];
> > +             layer->pitch[i] = p->pitch[i];
> > +     }
> > +
> > +     dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> > +{
> > +     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                    BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +     /* create rotation property */
> > +     drm_plane_create_rotation_property(&p->plane,
> > +                                        DRM_MODE_ROTATE_0,
> > +                                        DRM_MODE_ROTATE_MASK |
> > +                                        DRM_MODE_REFLECT_MASK);
> > +
> > +     /* create alpha property */
> > +     drm_plane_create_alpha_property(&p->plane);
> > +
> > +     /* create blend mode property */
> > +     drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +     /* create zpos property */
> > +     drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +     .atomic_check = sprd_plane_atomic_check,
> > +     .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +     .update_plane = drm_atomic_helper_update_plane,
> > +     .disable_plane  = drm_atomic_helper_disable_plane,
> > +     .destroy = drm_plane_cleanup,
> > +     .reset = drm_atomic_helper_plane_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                     struct sprd_dpu *dpu)
> > +{
> > +     struct drm_plane *primary = NULL;
> > +     struct sprd_plane *p = NULL;
> > +     struct dpu_capability cap = {};
> > +     int ret, i;
> > +
> > +     dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +     dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                               sizeof(struct dpu_layer), GFP_KERNEL);
> > +     if (!dpu->layers)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     for (i = 0; i < cap.max_layers; i++) {
> > +
> > +             p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +             if (!p)
> > +                     return ERR_PTR(-ENOMEM);
> > +
> > +             ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                            &sprd_plane_funcs, cap.fmts_ptr,
> > +                                            cap.fmts_cnt, NULL,
> > +                                            DRM_PLANE_TYPE_PRIMARY, NULL);
> > +             if (ret) {
> > +                     DRM_ERROR("fail to init primary plane\n");
> > +                     return ERR_PTR(ret);
> > +             }
> > +
> > +             drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> > +
> > +             sprd_plane_create_properties(p, i);
> > +
> > +             p->index = i;
> > +             if (i == 0)
> > +                     primary = &p->plane;
> > +     }
> > +
> > +     return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                     const struct drm_display_mode *mode)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> > +
> > +     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +             if ((mode->hdisplay == mode->htotal) ||
> > +                 (mode->vdisplay == mode->vtotal))
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +             else
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +     }
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     sprd_dpu_init(dpu);
> > +
> > +     enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     disable_irq(dpu->ctx.irq);
> > +
> > +     sprd_dpu_fini(dpu);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                              struct drm_crtc_state *state)
> > +{
> > +     DRM_DEBUG("%s()\n", __func__);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> > +
> > +     dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     if (dpu->pending_planes)
> > +             dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +     .mode_valid     = sprd_crtc_mode_valid,
> > +     .atomic_check   = sprd_crtc_atomic_check,
> > +     .atomic_begin   = sprd_crtc_atomic_begin,
> > +     .atomic_flush   = sprd_crtc_atomic_flush,
> > +     .atomic_enable  = sprd_crtc_atomic_enable,
> > +     .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +     .destroy        = drm_crtc_cleanup,
> > +     .set_config     = drm_atomic_helper_set_config,
> > +     .page_flip      = drm_atomic_helper_page_flip,
> > +     .reset          = drm_atomic_helper_crtc_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +     .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +     .enable_vblank  = sprd_crtc_enable_vblank,
> > +     .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                      struct drm_plane *primary)
> > +{
> > +     struct device_node *port;
> > +     int ret;
> > +
> > +     /*
> > +      * set crtc port so that drm_of_find_possible_crtcs call works
> > +      */
> > +     port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +     if (!port) {
> > +             DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                       drm->dev->of_node->full_name);
> > +             return -EINVAL;
> > +     }
> > +     of_node_put(port);
> > +     crtc->port = port;
> > +
> > +     ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                     &sprd_crtc_funcs, NULL);
> > +     if (ret) {
> > +             DRM_ERROR("failed to init crtc.\n");
> > +             return ret;
> > +     }
> > +
> > +     drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +     drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->init(ctx);
> > +     dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +     struct sprd_dpu *dpu = data;
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     u32 int_mask = 0;
> > +
> > +     int_mask = dpu->core->isr(ctx);
> > +
> > +     if (int_mask & BIT_DPU_INT_ERR)
> > +             DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +     if (int_mask & BIT_DPU_INT_VSYNC)
> > +             drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +     struct drm_device *drm = data;
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +     struct drm_plane *plane;
> > +     int ret;
> > +
> > +     plane = sprd_plane_init(drm, dpu);
> > +     if (IS_ERR_OR_NULL(plane)) {
> > +             ret = PTR_ERR(plane);
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +     void *data)
> > +{
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +     drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +     .bind = sprd_dpu_bind,
> > +     .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                             struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     struct resource *res;
> > +     int ret;
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +     if (!ctx->base) {
> > +             DRM_ERROR("failed to map dpu registers\n");
> > +             return -EFAULT;
> > +     }
> > +
> > +     ctx->irq = platform_get_irq(pdev, 0);
> > +     if (ctx->irq < 0) {
> > +             DRM_ERROR("failed to get dpu irq\n");
> > +             return ctx->irq;
> > +     }
> > +
> > +     irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +     ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                     0, "DPU", dpu);
> > +     if (ret) {
> > +             DRM_ERROR("failed to register dpu irq handler\n");
> > +             return ret;
> > +     }
> > +
> > +     init_waitqueue_head(&ctx->wait_queue);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +     .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +     { .compatible = "sprd,sharkl3-dpu",
> > +       .data = &sharkl3_dpu },
> > +     { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +     const struct sprd_dpu_ops *pdata;
> > +     struct sprd_dpu *dpu;
> > +     int ret;
> > +
> > +     dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +     if (!dpu)
> > +             return -ENOMEM;
> > +
> > +     pdata = of_device_get_match_data(&pdev->dev);
> > +     if (pdata) {
> > +             dpu->core = pdata->core;
> > +     } else {
> > +             DRM_ERROR("No matching driver data found\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +     if (ret)
> > +             return ret;
> > +
> > +     platform_set_drvdata(pdev, dpu);
> > +
> > +     return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +     component_del(&pdev->dev, &dpu_component_ops);
> > +     return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +     .probe = sprd_dpu_probe,
> > +     .remove = sprd_dpu_remove,
> > +     .driver = {
> > +             .name = "sprd-dpu-drv",
> > +             .of_match_table = dpu_match_table,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_            BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE          BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_WB_DONE          BIT(6)
> > +#define BIT_DPU_INT_WB_ERR           BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
> > +
> > +enum {
> > +     SPRD_DPU_IF_DBI = 0,
> > +     SPRD_DPU_IF_DPI,
> > +     SPRD_DPU_IF_EDPI,
> > +     SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +     DPU_LAYER_ROTATION_0,
> > +     DPU_LAYER_ROTATION_90,
> > +     DPU_LAYER_ROTATION_180,
> > +     DPU_LAYER_ROTATION_270,
> > +     DPU_LAYER_ROTATION_0_M,
> > +     DPU_LAYER_ROTATION_90_M,
> > +     DPU_LAYER_ROTATION_180_M,
> > +     DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +     u8 index;
> > +     u8 planes;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     s16 src_x;
> > +     s16 src_y;
> > +     s16 src_w;
> > +     s16 src_h;
> > +     s16 dst_x;
> > +     s16 dst_y;
> > +     u16 dst_w;
> > +     u16 dst_h;
> > +     u32 format;
> > +     u32 alpha;
> > +     u32 blending;
> > +     u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +     u32 max_layers;
> > +     const u32 *fmts_ptr;
> > +     u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +     void (*init)(struct dpu_context *ctx);
> > +     void (*fini)(struct dpu_context *ctx);
> > +     void (*run)(struct dpu_context *ctx);
> > +     void (*stop)(struct dpu_context *ctx);
> > +     void (*enable_vsync)(struct dpu_context *ctx);
> > +     void (*disable_vsync)(struct dpu_context *ctx);
> > +     u32 (*isr)(struct dpu_context *ctx);
> > +     void (*ifconfig)(struct dpu_context *ctx);
> > +     void (*flip)(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count);
> > +     void (*capability)(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +     void __iomem *base;
> > +     int irq;
> > +     u8 if_type;
> > +     struct videomode vm;
> > +     bool stopped;
> > +     wait_queue_head_t wait_queue;
> > +     bool evt_update;
> > +     bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +     struct drm_crtc crtc;
> > +     struct dpu_context ctx;
> > +     const struct dpu_core_ops *core;
> > +     struct dpu_layer *layers;
> > +     u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +     const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +     return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >       &sprd_drm_driver,
> > +     &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >       struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
@ 2020-11-17  3:07       ` Kevin Tang
  0 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2020-11-17  3:07 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Mark Rutland, David Airlie, Chunyan Zhang,
	Linux-Kernel@Vger. Kernel. Org, Rob Herring, ML dri-devel,
	Orson Zhai, Sean Paul

Hi Sam,

Here is feedback of RFC V7

Sam Ravnborg <sam@ravnborg.org> 于2020年7月29日周三 上午5:14写道:
>
> Hi Kevin.
>
> Some feedback in the following.
> I lost track of thing for the atomic modesettting stuff and I hope other
> will review that.
>
>         Sam
>
> On Tue, Jul 28, 2020 at 06:07:57PM +0800, Kevin Tang wrote:
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> Not needed - drop.
It has been fixed on RFC V7.
>
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +     sprd_dpu.o
> > +
> > +obj-y += dpu/
>
> Until there are several DPU's there is no need for a separate directory.
> Make it simpler by keeping it all in drm/sprd/*
It has been fixed on RFC V7.
>
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE 0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET   0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +     (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> Use a static inline to get better typecheck.
It has been fixed on RFC V7.
>
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +     writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> I am no fan of macros used like this.
>
> Maybe use static inlines and add struct dpu_context * as first argument.
> Then adding base can be hidden away.
It has been fixed on RFC V7.
>
>
> I had a hard time convincing myself that _relaxed was the right
> variants. If I get it correct we may see writes re-ordered with the
> _relaxed variants wich would be no good when dealign with interrupts.
> But I may miss somethign here.
I need to think about it
>
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL 0x04
> > +#define REG_DPU_CFG0 0x08
> > +#define REG_DPU_CFG1 0x0C
> > +#define REG_DPU_CFG2 0x10
> > +#define REG_PANEL_SIZE       0x20
> > +#define REG_BLEND_SIZE       0x24
> > +#define REG_BG_COLOR 0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0   0x30
> > +#define REG_LAY_BASE_ADDR1   0x34
> > +#define REG_LAY_BASE_ADDR2   0x38
> > +#define REG_LAY_CTRL         0x40
> > +#define REG_LAY_SIZE         0x44
> > +#define REG_LAY_PITCH                0x48
> > +#define REG_LAY_POS          0x4C
> > +#define REG_LAY_ALPHA                0x50
> > +#define REG_LAY_PALLETE              0x58
> > +#define REG_LAY_CROP_START   0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN               0x1E0
> > +#define REG_DPU_INT_CLR              0x1E4
> > +#define REG_DPU_INT_STS              0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL         0x1F0
> > +#define REG_DPI_H_TIMING     0x1F4
> > +#define REG_DPI_V_TIMING     0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                   0x800
> > +#define REG_MMU_VPN_RANGE            0x80C
> > +#define REG_MMU_VAOR_ADDR_RD         0x818
> > +#define REG_MMU_VAOR_ADDR_WR         0x81C
> > +#define REG_MMU_INV_ADDR_RD          0x820
> > +#define REG_MMU_INV_ADDR_WR          0x824
> > +#define REG_MMU_PPN1                 0x83C
> > +#define REG_MMU_RANGE1                       0x840
> > +#define REG_MMU_PPN2                 0x844
> > +#define REG_MMU_RANGE2                       0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                  BIT(0)
> > +#define BIT_DPU_STOP                 BIT(1)
> > +#define BIT_DPU_REG_UPDATE           BIT(2)
> > +#define BIT_DPU_IF_EDPI                      BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE            BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD     BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                               BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE             BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR              BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR              BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD              BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR              BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD               BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR               BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN           BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD       BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN          BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +     u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                     BIT_DPU_INT_MMU_VAOR_WR |
> > +                     BIT_DPU_INT_MMU_INV_RD |
> > +                     BIT_DPU_INT_MMU_INV_WR;
> > +     u32 val = reg_val & mmu_mask;
> > +     int i;
> > +
> > +     if (val) {
> > +             DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
>
> General comment, applies for the whole patch.
>
> Use drm_err(drm, ...), drm_info(drm, ...) etc.
>
> Use upclassing to get sprd_dpu - and then you have a drm_device *
> (After drm_device is embedded in sprd_dpu as suggested in the other
> mail)
We did not use drm_device in crtc driver, only in kms driver(sprd_drm.c)
So is this still necessary?
>
> > +
> > +             if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                     DRM_ERROR("iommu invalid read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                     DRM_ERROR("iommu invalid write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_INV_ADDR_WR));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                     DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_RD));
> > +             if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                     DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n",
> > +                             DPU_REG_RD(ctx->base + REG_MMU_VAOR_ADDR_WR));
> > +
> > +             for (i = 0; i < 8; i++) {
> > +                     reg_val = DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                     if (reg_val & 0x1)
> > +                             DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i,
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                     DPU_REG_RD(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +             }
> > +     }
> > +
> > +     return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < 8; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i), 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, int_mask = 0;
> > +
> > +     reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +     /* disable err interrupt */
> > +     if (reg_val & BIT_DPU_INT_ERR)
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +
> > +     /* dpu update done isr */
> > +     if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +             ctx->evt_update = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu stop done isr */
> > +     if (reg_val & BIT_DPU_INT_DONE) {
> > +             ctx->evt_stop = true;
> > +             wake_up_interruptible_all(&ctx->wait_queue);
> > +     }
> > +
> > +     /* dpu ifbc payload error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +             DRM_ERROR("dpu ifbc payload error\n");
> > +     }
> > +
> > +     /* dpu ifbc header error isr */
> > +     if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +             int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +             DRM_ERROR("dpu ifbc header error\n");
> > +     }
> > +
> > +     int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +     return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     if (ctx->stopped)
> > +             return 0;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
> > +                                            msecs_to_jiffies(500));
> > +     ctx->evt_stop = false;
> > +
> > +     ctx->stopped = true;
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for stop done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +     int rc;
> > +
> > +     ctx->evt_update = false;
> > +
> > +     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
> > +                                            msecs_to_jiffies(500));
> > +
> > +     if (!rc) {
> > +             DRM_ERROR("dpu wait for reg update done time out!\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +     dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +     ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +     u32 reg_val, size;
> > +
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +     DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +     DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +     reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +     DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +     if (ctx->stopped)
> > +             dpu_clean_all(ctx);
> > +
> > +     DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +     DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                 struct dpu_layer *hwlayer)
> No linebreak needed here.
It has been fixed on RFC V7.
>
> > +{
> > +     const struct drm_format_info *info;
> > +     u32 size, offset, ctrl, pitch;
> > +     int i;
> > +
> > +     offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +     if (hwlayer->src_w && hwlayer->src_h)
> > +             size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16);
> > +     else
> > +             size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16);
> > +
> > +     for (i = 0; i < hwlayer->planes; i++)
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                             hwlayer->index), hwlayer->addr[i]);
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                     hwlayer->index), offset);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                     hwlayer->index), size);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                     hwlayer->index), hwlayer->src_y << 16 | hwlayer->src_x);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                     hwlayer->index), hwlayer->alpha);
> > +
> > +     info = drm_format_info(hwlayer->format);
> > +     if (hwlayer->planes == 3) {
> > +             /* UV pitch is 1/2 of Y pitch*/
> > +             pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                             (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     } else {
> > +             pitch = hwlayer->pitch[0] / info->cpp[0];
> > +             DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                             hwlayer->index), pitch);
> > +     }
> > +
> > +     ctrl = hwlayer->format |
> > +             hwlayer->blending |
> > +             (hwlayer->rotation & 0x7) << 20;
> > +
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), ctrl);
> > +     DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                     hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +     DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                             hwlayer->dst_x, hwlayer->dst_y,
> > +                             hwlayer->dst_w, hwlayer->dst_h);
> > +     DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n",
> > +                             hwlayer->src_x, hwlayer->src_y,
> > +                             hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count)
> > +{
> > +     int i;
> > +     u32 reg_val;
> > +
> > +     /*
> > +      * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +      * registers in EDPI mode. So the config registers can only be
> > +      * updated in the rising edge of DPU_RUN bit.
> > +      */
> > +     if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +             dpu_wait_stop_done(ctx);
> > +
> > +     /* reset the bgcolor to black */
> > +     DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +     /* disable all the layers */
> > +     dpu_clean_all(ctx);
> > +
> > +     /* start configure dpu layers */
> > +     for (i = 0; i < count; i++)
> > +             dpu_layer(ctx, &layers[i]);
> > +
> > +     /* update trigger and wait */
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             if (!ctx->stopped) {
> > +                     DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
> > +                     dpu_wait_update_done(ctx);
> > +             }
> > +
> > +             DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +             ctx->stopped = false;
> > +     }
> > +
> > +     /*
> > +      * If the following interrupt was disabled in isr,
> > +      * re-enable it.
> > +      */
> > +     reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +               BIT_DPU_INT_FBC_HDR_ERR |
> > +               BIT_DPU_INT_MMU_VAOR_RD |
> > +               BIT_DPU_INT_MMU_VAOR_WR |
> > +               BIT_DPU_INT_MMU_INV_RD |
> > +               BIT_DPU_INT_MMU_INV_WR;
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +     u32 int_mask = 0;
> > +     u32 reg_val;
> > +
> > +     if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +             /* use dpi as interface */
> > +             DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* disable Halt function for SPRD DSI */
> > +             DPU_REG_CLR(ctx->base + REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN);
> > +
> > +             /* select te from external pad */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* set dpi timing */
> > +             reg_val = ctx->vm.hsync_len << 0 |
> > +                       ctx->vm.hback_porch << 8 |
> > +                       ctx->vm.hfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +             reg_val = ctx->vm.vsync_len << 0 |
> > +                       ctx->vm.vback_porch << 8 |
> > +                       ctx->vm.vfront_porch << 20;
> > +             DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                     DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                             "underflow risk!\n");
> > +
> > +             /* enable dpu update done INT */
> > +             int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +             /* enable dpu DONE  INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable dpu dpi vsync */
> > +             int_mask |= BIT_DPU_INT_VSYNC;
> > +             /* enable dpu TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +             /* enable underflow err INT */
> > +             int_mask |= BIT_DPU_INT_ERR;
> > +     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +             /* use edpi as interface */
> > +             DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +             /* use external te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +             /* enable te */
> > +             DPU_REG_SET(ctx->base + REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN);
> > +
> > +             /* enable stop DONE INT */
> > +             int_mask |= BIT_DPU_INT_DONE;
> > +             /* enable TE INT */
> > +             int_mask |= BIT_DPU_INT_TE;
> > +     }
> > +
> > +     /* enable ifbc payload error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +     /* enable ifbc header error INT */
> > +     int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +     /* enable iommu va out of range read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +     /* enable iommu va out of range write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +     /* enable iommu invalid read error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +     /* enable iommu invalid write error INT */
> > +     int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +     DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +     DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +     DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +     DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +     DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +     DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +     DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +     DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +     DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +     DRM_FORMAT_YVU420,
> > +};
> One format per line improves readability a lot.
> Maybe sort alphbetically too.
It has been fixed on RFC V7.
>
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap)
> Drop linebreak
It has been fixed on RFC V7.
>
> > +{
> > +     cap->max_layers = 6;
> > +     cap->fmts_ptr = primary_fmts;
> > +     cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +     .init = dpu_init,
> > +     .fini = dpu_fini,
> > +     .run = dpu_run,
> > +     .stop = dpu_stop,
> > +     .isr = dpu_isr,
> > +     .ifconfig = dpu_dpi_init,
> > +     .capability = dpu_capability,
> > +     .flip = dpu_flip,
> > +     .enable_vsync = enable_vsync,
> > +     .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +     struct drm_plane plane;
> > +     u32 index;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     u32 format;
> > +     u32 rotation;
> > +     u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +     return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +     switch (fourcc) {
> > +     case DRM_FORMAT_BGRA8888:
> > +             /* BGRA8888 -> ARGB8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_RGBX8888:
> > +     case DRM_FORMAT_RGBA8888:
> > +             /* RGBA8888 -> ABGR8888 */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ABGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_ARGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_XBGR8888:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_XRGB8888:
> > +             *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +             break;
> > +     case DRM_FORMAT_BGR565:
> > +             /* RB switch */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             /* FALLTHRU */
> > +     case DRM_FORMAT_RGB565:
> > +             *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +             break;
> > +     case DRM_FORMAT_NV12:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV21:
> > +             /* 2-Lane: Yuv420 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV16:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_NV61:
> > +             /* 2-Lane: Yuv422 */
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YUV420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_NO_SWITCH;
> > +             break;
> > +     case DRM_FORMAT_YVU420:
> > +             *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +             /* Y endian */
> > +             *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +             /* UV endian */
> > +             *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +     switch (angle) {
> > +     case DRM_MODE_ROTATE_0:
> > +             *rotation = DPU_LAYER_ROTATION_0;
> > +             break;
> > +     case DRM_MODE_ROTATE_90:
> > +             *rotation = DPU_LAYER_ROTATION_90;
> > +             break;
> > +     case DRM_MODE_ROTATE_180:
> > +             *rotation = DPU_LAYER_ROTATION_180;
> > +             break;
> > +     case DRM_MODE_ROTATE_270:
> > +             *rotation = DPU_LAYER_ROTATION_270;
> > +             break;
> > +     case DRM_MODE_REFLECT_Y:
> > +             *rotation = DPU_LAYER_ROTATION_180_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_90_M;
> > +             break;
> > +     case DRM_MODE_REFLECT_X:
> > +             *rotation = DPU_LAYER_ROTATION_0_M;
> > +             break;
> > +     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +             *rotation = DPU_LAYER_ROTATION_270_M;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                               struct drm_plane_state *state)
> > +{
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct drm_framebuffer *fb = state->fb;
> > +     struct drm_gem_cma_object *cma_obj;
> > +     int i, ret;
> > +     u32 addr;
> > +
> > +     if (!state->fb || !state->crtc)
> > +             return 0;
> > +
> > +     ret = sprd_plane_format_convert(fb->format->format,
> > +                                     &p->format);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane format\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_plane_rotation_convert(state->rotation,
> > +                                     &p->rotation);
> > +     if (ret < 0) {
> > +             DRM_ERROR("Invalid plane rotation\n");
> > +             return ret;
> > +     }
> > +
> > +     switch (state->pixel_blend_mode) {
> > +     case DRM_MODE_BLEND_COVERAGE:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Normal mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +             break;
> > +     case DRM_MODE_BLEND_PREMULTI:
> > +             /* alpha mode select - combo alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +             /* Pre-mult mode */
> > +             p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +             break;
> > +     case DRM_MODE_BLEND_PIXEL_NONE:
> > +     default:
> > +             /* don't do blending, maybe RGBX */
> > +             /* alpha mode select - layer alpha */
> > +             p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +             break;
> > +     }
> > +
> > +     for (i = 0; i < fb->format->num_planes; i++) {
> > +             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +             addr = cma_obj->paddr + fb->offsets[i];
> > +             if (addr % 16) {
> > +                     DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n",
> > +                             i, addr);
> > +                     return -EFAULT;
> > +             }
> > +
> > +             p->addr[i] = addr;
> > +             p->pitch[i] = fb->pitches[i];
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                 struct drm_plane_state *old_state)
> > +{
> > +     struct drm_plane_state *state = plane->state;
> > +     struct drm_framebuffer *fb = plane->state->fb;
> > +     struct sprd_plane *p = to_sprd_plane(plane);
> > +     struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +     struct dpu_layer *layer = &dpu->layers[p->index];
> > +     int i;
> > +
> > +     if (!state->crtc || !state->fb)
> > +             return;
> > +
> > +     layer->index = p->index;
> > +     layer->src_x = state->src_x >> 16;
> > +     layer->src_y = state->src_y >> 16;
> > +     layer->src_w = state->src_w >> 16;
> > +     layer->src_h = state->src_h >> 16;
> > +     layer->dst_x = state->crtc_x;
> > +     layer->dst_y = state->crtc_y;
> > +     layer->dst_w = state->crtc_w;
> > +     layer->dst_h = state->crtc_h;
> > +     layer->alpha = state->alpha;
> > +     layer->format = p->format;
> > +     layer->blending = p->blend_mode;
> > +     layer->rotation = p->rotation;
> > +     layer->planes = fb->format->num_planes;
> > +
> > +     for (i = 0; i < layer->planes; i++) {
> > +             layer->addr[i] = p->addr[i];
> > +             layer->pitch[i] = p->pitch[i];
> > +     }
> > +
> > +     dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int index)
> > +{
> > +     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                    BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                    BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +     /* create rotation property */
> > +     drm_plane_create_rotation_property(&p->plane,
> > +                                        DRM_MODE_ROTATE_0,
> > +                                        DRM_MODE_ROTATE_MASK |
> > +                                        DRM_MODE_REFLECT_MASK);
> > +
> > +     /* create alpha property */
> > +     drm_plane_create_alpha_property(&p->plane);
> > +
> > +     /* create blend mode property */
> > +     drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +     /* create zpos property */
> > +     drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +     .atomic_check = sprd_plane_atomic_check,
> > +     .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +     .update_plane = drm_atomic_helper_update_plane,
> > +     .disable_plane  = drm_atomic_helper_disable_plane,
> > +     .destroy = drm_plane_cleanup,
> > +     .reset = drm_atomic_helper_plane_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                     struct sprd_dpu *dpu)
> > +{
> > +     struct drm_plane *primary = NULL;
> > +     struct sprd_plane *p = NULL;
> > +     struct dpu_capability cap = {};
> > +     int ret, i;
> > +
> > +     dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +     dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                               sizeof(struct dpu_layer), GFP_KERNEL);
> > +     if (!dpu->layers)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     for (i = 0; i < cap.max_layers; i++) {
> > +
> > +             p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +             if (!p)
> > +                     return ERR_PTR(-ENOMEM);
> > +
> > +             ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                            &sprd_plane_funcs, cap.fmts_ptr,
> > +                                            cap.fmts_cnt, NULL,
> > +                                            DRM_PLANE_TYPE_PRIMARY, NULL);
> > +             if (ret) {
> > +                     DRM_ERROR("fail to init primary plane\n");
> > +                     return ERR_PTR(ret);
> > +             }
> > +
> > +             drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs);
> > +
> > +             sprd_plane_create_properties(p, i);
> > +
> > +             p->index = i;
> > +             if (i == 0)
> > +                     primary = &p->plane;
> > +     }
> > +
> > +     return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                     const struct drm_display_mode *mode)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode));
> > +
> > +     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +             if ((mode->hdisplay == mode->htotal) ||
> > +                 (mode->vdisplay == mode->vtotal))
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +             else
> > +                     dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +     }
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     sprd_dpu_init(dpu);
> > +
> > +     enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     disable_irq(dpu->ctx.irq);
> > +
> > +     sprd_dpu_fini(dpu);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                              struct drm_crtc_state *state)
> > +{
> > +     DRM_DEBUG("%s()\n", __func__);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes);
> > +
> > +     dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                               struct drm_crtc_state *old_state)
> > +
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +     struct drm_device *drm = dpu->crtc.dev;
> > +
> > +     if (dpu->pending_planes)
> > +             dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes);
> > +
> > +     spin_lock_irq(&drm->event_lock);
> > +     if (crtc->state->event) {
> > +             drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +             crtc->state->event = NULL;
> > +     }
> > +     spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +     struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +     dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +     .mode_valid     = sprd_crtc_mode_valid,
> > +     .atomic_check   = sprd_crtc_atomic_check,
> > +     .atomic_begin   = sprd_crtc_atomic_begin,
> > +     .atomic_flush   = sprd_crtc_atomic_flush,
> > +     .atomic_enable  = sprd_crtc_atomic_enable,
> > +     .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +     .destroy        = drm_crtc_cleanup,
> > +     .set_config     = drm_atomic_helper_set_config,
> > +     .page_flip      = drm_atomic_helper_page_flip,
> > +     .reset          = drm_atomic_helper_crtc_reset,
> > +     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +     .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +     .enable_vblank  = sprd_crtc_enable_vblank,
> > +     .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                      struct drm_plane *primary)
> > +{
> > +     struct device_node *port;
> > +     int ret;
> > +
> > +     /*
> > +      * set crtc port so that drm_of_find_possible_crtcs call works
> > +      */
> > +     port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +     if (!port) {
> > +             DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                       drm->dev->of_node->full_name);
> > +             return -EINVAL;
> > +     }
> > +     of_node_put(port);
> > +     crtc->port = port;
> > +
> > +     ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                     &sprd_crtc_funcs, NULL);
> > +     if (ret) {
> > +             DRM_ERROR("failed to init crtc.\n");
> > +             return ret;
> > +     }
> > +
> > +     drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +     drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->init(ctx);
> > +     dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +
> > +     dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +     struct sprd_dpu *dpu = data;
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     u32 int_mask = 0;
> > +
> > +     int_mask = dpu->core->isr(ctx);
> > +
> > +     if (int_mask & BIT_DPU_INT_ERR)
> > +             DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +     if (int_mask & BIT_DPU_INT_VSYNC)
> > +             drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +     struct drm_device *drm = data;
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +     struct drm_plane *plane;
> > +     int ret;
> > +
> > +     plane = sprd_plane_init(drm, dpu);
> > +     if (IS_ERR_OR_NULL(plane)) {
> > +             ret = PTR_ERR(plane);
> > +             return ret;
> > +     }
> > +
> > +     ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +     void *data)
> > +{
> > +     struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +     drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +     .bind = sprd_dpu_bind,
> > +     .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                             struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct dpu_context *ctx = &dpu->ctx;
> > +     struct resource *res;
> > +     int ret;
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +     if (!ctx->base) {
> > +             DRM_ERROR("failed to map dpu registers\n");
> > +             return -EFAULT;
> > +     }
> > +
> > +     ctx->irq = platform_get_irq(pdev, 0);
> > +     if (ctx->irq < 0) {
> > +             DRM_ERROR("failed to get dpu irq\n");
> > +             return ctx->irq;
> > +     }
> > +
> > +     irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +     ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                     0, "DPU", dpu);
> > +     if (ret) {
> > +             DRM_ERROR("failed to register dpu irq handler\n");
> > +             return ret;
> > +     }
> > +
> > +     init_waitqueue_head(&ctx->wait_queue);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +     .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +     { .compatible = "sprd,sharkl3-dpu",
> > +       .data = &sharkl3_dpu },
> > +     { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +     const struct sprd_dpu_ops *pdata;
> > +     struct sprd_dpu *dpu;
> > +     int ret;
> > +
> > +     dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +     if (!dpu)
> > +             return -ENOMEM;
> > +
> > +     pdata = of_device_get_match_data(&pdev->dev);
> > +     if (pdata) {
> > +             dpu->core = pdata->core;
> > +     } else {
> > +             DRM_ERROR("No matching driver data found\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +     if (ret)
> > +             return ret;
> > +
> > +     platform_set_drvdata(pdev, dpu);
> > +
> > +     return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +     component_del(&pdev->dev, &dpu_component_ops);
> > +     return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +     .probe = sprd_dpu_probe,
> > +     .remove = sprd_dpu_remove,
> > +     .driver = {
> > +             .name = "sprd-dpu-drv",
> > +             .of_match_table = dpu_match_table,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_            BIT(0)
> > +#define BIT_DPU_INT_TE                       BIT(1)
> > +#define BIT_DPU_INT_ERR                      BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE          BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
> > +#define BIT_DPU_INT_VSYNC            BIT(5)
> > +#define BIT_DPU_INT_WB_DONE          BIT(6)
> > +#define BIT_DPU_INT_WB_ERR           BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
> > +
> > +enum {
> > +     SPRD_DPU_IF_DBI = 0,
> > +     SPRD_DPU_IF_DPI,
> > +     SPRD_DPU_IF_EDPI,
> > +     SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +     DPU_LAYER_ROTATION_0,
> > +     DPU_LAYER_ROTATION_90,
> > +     DPU_LAYER_ROTATION_180,
> > +     DPU_LAYER_ROTATION_270,
> > +     DPU_LAYER_ROTATION_0_M,
> > +     DPU_LAYER_ROTATION_90_M,
> > +     DPU_LAYER_ROTATION_180_M,
> > +     DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +     u8 index;
> > +     u8 planes;
> > +     u32 addr[4];
> > +     u32 pitch[4];
> > +     s16 src_x;
> > +     s16 src_y;
> > +     s16 src_w;
> > +     s16 src_h;
> > +     s16 dst_x;
> > +     s16 dst_y;
> > +     u16 dst_w;
> > +     u16 dst_h;
> > +     u32 format;
> > +     u32 alpha;
> > +     u32 blending;
> > +     u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +     u32 max_layers;
> > +     const u32 *fmts_ptr;
> > +     u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +     void (*init)(struct dpu_context *ctx);
> > +     void (*fini)(struct dpu_context *ctx);
> > +     void (*run)(struct dpu_context *ctx);
> > +     void (*stop)(struct dpu_context *ctx);
> > +     void (*enable_vsync)(struct dpu_context *ctx);
> > +     void (*disable_vsync)(struct dpu_context *ctx);
> > +     u32 (*isr)(struct dpu_context *ctx);
> > +     void (*ifconfig)(struct dpu_context *ctx);
> > +     void (*flip)(struct dpu_context *ctx,
> > +                  struct dpu_layer layers[], u8 count);
> > +     void (*capability)(struct dpu_context *ctx,
> > +                     struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +     void __iomem *base;
> > +     int irq;
> > +     u8 if_type;
> > +     struct videomode vm;
> > +     bool stopped;
> > +     wait_queue_head_t wait_queue;
> > +     bool evt_update;
> > +     bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +     struct drm_crtc crtc;
> > +     struct dpu_context ctx;
> > +     const struct dpu_core_ops *core;
> > +     struct dpu_layer *layers;
> > +     u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +     const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +     return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >       &sprd_drm_driver,
> > +     &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >       struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver
  2020-07-28 21:51     ` Daniel Vetter
  (?)
  (?)
@ 2021-01-05 11:09     ` Kevin Tang
  -1 siblings, 0 replies; 50+ messages in thread
From: Kevin Tang @ 2021-01-05 11:09 UTC (permalink / raw)
  To: Kevin Tang, Maarten Lankhorst, Maxime Ripard, Sean Paul,
	Dave Airlie, Rob Herring, Mark Rutland, Orson Zhai,
	Chunyan Zhang, Linux Kernel Mailing List, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 56593 bytes --]

Daniel Vetter <daniel@ffwll.ch> 于2020年7月29日周三 上午5:51写道:

> On Tue, Jul 28, 2020 at 12:08 PM Kevin Tang <kevin3.tang@gmail.com> wrote:
> >
> > From: Kevin Tang <kevin.tang@unisoc.com>
> >
> > Adds DPU(Display Processor Unit) support for the Unisoc's display
> subsystem.
> > It's support multi planes, scaler, rotation, PQ(Picture Quality) and
> more.
> >
> > RFC v6:
> >   - Access registers via readl/writel
> >   - Checking for unsupported KMS properties (format, rotation,
> blend_mode, etc) on plane_check ops
> >   - Remove always true checks for dpu core ops
> >
> > Cc: Orson Zhai <orsonzhai@gmail.com>
> > Cc: Chunyan Zhang <zhang.lyra@gmail.com>
> > Signed-off-by: Kevin Tang <kevin.tang@unisoc.com>
>
> Quickly scrolled through this, and the entire thing very much leaves a
> midlayer heavy aftertaste. Do we really need stuff like struct dpu_layer
> and struct dpu_core_ops? They only seem to complicate the code base, and
> seem to have no real reason. The indirection with first computing register
> values into a sprd_plane/crtc structure, and then writing it into hardware
> is also a bit much - I recommend to only do that if you have to compute
> values in _check to validate them, so that the computation doesn't have to
> be repeated in the commit phase functions.
>
> Also, the layer and pending_flips stuff in sprd_dpu don't work with
> atomic, that races. You have to put all that stuff into state objects, or
> if it's some data shared with interrupt handlers (doesn't seem to be the
> case here), it needs its own locking, and any data you need in the
> interrupt handler must be copied over.
>
> Also no devm_kzalloc for anything containined a drm_* structure, that's
> the wrong lifetime.
>
> So yeah high level review is that I think this driver would benefit a lot
> from a pile of demidlayer.
>
> Cheers, Daniel
>
Hi Daniel,
After a long time of thinking, I think you are right, the stuff layer of
dpu_layer and dpu_core_ops
maybe no need for us now. So i will delete "dpu_layer" on patch v3, and
commit layer information directly
in atomic_update.

Because only one dpu h/w verison been submitt now, so i have delete
"dpu_core_ops" on patch v2.
After the basic version is submitted, I will start preparing for the
support of multiple h/w versions,
but it will take some time, because our different h/w versions all have
some differences.

Only drm format convert to dpu format need to check on atomic_check, i'm
not sure if drm framework
will help filter out illegal formats, so i add layer format check on
atomic_check,
as for rotation, blend mode, i think it just give a default value,  it
maybe work well.

Best Wishes

>
> > ---
> >  drivers/gpu/drm/sprd/Makefile       |   5 +-
> >  drivers/gpu/drm/sprd/dpu/Makefile   |   3 +
> >  drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 503 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.c     | 646
> ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sprd/sprd_dpu.h     | 187 +++++++++++
> >  drivers/gpu/drm/sprd/sprd_drm.c     |   1 +
> >  drivers/gpu/drm/sprd/sprd_drm.h     |   2 +
> >  7 files changed, 1346 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile
> >  create mode 100644 drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
> >  create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
> >
> > diff --git a/drivers/gpu/drm/sprd/Makefile
> b/drivers/gpu/drm/sprd/Makefile
> > index 86d95d9..88ab32a 100644
> > --- a/drivers/gpu/drm/sprd/Makefile
> > +++ b/drivers/gpu/drm/sprd/Makefile
> > @@ -2,4 +2,7 @@
> >
> >  subdir-ccflags-y += -I$(srctree)/$(src)
> >
> > -obj-y := sprd_drm.o
> > +obj-y := sprd_drm.o \
> > +       sprd_dpu.o
> > +
> > +obj-y += dpu/
> > diff --git a/drivers/gpu/drm/sprd/dpu/Makefile
> b/drivers/gpu/drm/sprd/dpu/Makefile
> > new file mode 100644
> > index 0000000..40278b6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-y += dpu_r2p0.o
> > diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > new file mode 100644
> > index 0000000..4b9521d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c
> > @@ -0,0 +1,503 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/wait.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "sprd_dpu.h"
> > +
> > +/* DPU registers size, 4 Bytes(32 Bits) */
> > +#define DPU_REG_SIZE   0x04
> > +
> > +/* Layer registers offset */
> > +#define DPU_LAY_REG_OFFSET     0x0C
> > +
> > +#define DPU_LAY_REG(reg, index) \
> > +       (reg + index * DPU_LAY_REG_OFFSET * DPU_REG_SIZE)
> > +
> > +#define DPU_REG_RD(reg) readl_relaxed(reg)
> > +
> > +#define DPU_REG_WR(reg, mask) writel_relaxed(mask, reg)
> > +
> > +#define DPU_REG_SET(reg, mask) \
> > +       writel_relaxed(readl_relaxed(reg) | mask, reg)
> > +
> > +#define DPU_REG_CLR(reg, mask) \
> > +       writel_relaxed(readl_relaxed(reg) & ~mask, reg)
> > +
> > +/* Global control registers */
> > +#define REG_DPU_CTRL   0x04
> > +#define REG_DPU_CFG0   0x08
> > +#define REG_DPU_CFG1   0x0C
> > +#define REG_DPU_CFG2   0x10
> > +#define REG_PANEL_SIZE 0x20
> > +#define REG_BLEND_SIZE 0x24
> > +#define REG_BG_COLOR   0x2C
> > +
> > +/* Layer0 control registers */
> > +#define REG_LAY_BASE_ADDR0     0x30
> > +#define REG_LAY_BASE_ADDR1     0x34
> > +#define REG_LAY_BASE_ADDR2     0x38
> > +#define REG_LAY_CTRL           0x40
> > +#define REG_LAY_SIZE           0x44
> > +#define REG_LAY_PITCH          0x48
> > +#define REG_LAY_POS            0x4C
> > +#define REG_LAY_ALPHA          0x50
> > +#define REG_LAY_PALLETE                0x58
> > +#define REG_LAY_CROP_START     0x5C
> > +
> > +/* Interrupt control registers */
> > +#define REG_DPU_INT_EN         0x1E0
> > +#define REG_DPU_INT_CLR                0x1E4
> > +#define REG_DPU_INT_STS                0x1E8
> > +
> > +/* DPI control registers */
> > +#define REG_DPI_CTRL           0x1F0
> > +#define REG_DPI_H_TIMING       0x1F4
> > +#define REG_DPI_V_TIMING       0x1F8
> > +
> > +/* MMU control registers */
> > +#define REG_MMU_EN                     0x800
> > +#define REG_MMU_VPN_RANGE              0x80C
> > +#define REG_MMU_VAOR_ADDR_RD           0x818
> > +#define REG_MMU_VAOR_ADDR_WR           0x81C
> > +#define REG_MMU_INV_ADDR_RD            0x820
> > +#define REG_MMU_INV_ADDR_WR            0x824
> > +#define REG_MMU_PPN1                   0x83C
> > +#define REG_MMU_RANGE1                 0x840
> > +#define REG_MMU_PPN2                   0x844
> > +#define REG_MMU_RANGE2                 0x848
> > +
> > +/* Global control bits */
> > +#define BIT_DPU_RUN                    BIT(0)
> > +#define BIT_DPU_STOP                   BIT(1)
> > +#define BIT_DPU_REG_UPDATE             BIT(2)
> > +#define BIT_DPU_IF_EDPI                        BIT(0)
> > +#define BIT_DPU_COEF_NARROW_RANGE              BIT(4)
> > +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD       BIT(5)
> > +
> > +/* Layer control bits */
> > +#define BIT_DPU_LAY_EN                         BIT(0)
> > +
> > +/* Interrupt control & status bits */
> > +#define BIT_DPU_INT_DONE               BIT(0)
> > +#define BIT_DPU_INT_TE                 BIT(1)
> > +#define BIT_DPU_INT_ERR                        BIT(2)
> > +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> > +#define BIT_DPU_INT_VSYNC              BIT(5)
> > +#define BIT_DPU_INT_FBC_PLD_ERR                BIT(8)
> > +#define BIT_DPU_INT_FBC_HDR_ERR                BIT(9)
> > +#define BIT_DPU_INT_MMU_VAOR_RD                BIT(16)
> > +#define BIT_DPU_INT_MMU_VAOR_WR                BIT(17)
> > +#define BIT_DPU_INT_MMU_INV_RD         BIT(18)
> > +#define BIT_DPU_INT_MMU_INV_WR         BIT(19)
> > +
> > +/* DPI control bits */
> > +#define BIT_DPU_EDPI_TE_EN             BIT(8)
> > +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10)
> > +#define BIT_DPU_DPI_HALT_EN            BIT(16)
> > +
> > +
> > +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val)
> > +{
> > +       u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
> > +                       BIT_DPU_INT_MMU_VAOR_WR |
> > +                       BIT_DPU_INT_MMU_INV_RD |
> > +                       BIT_DPU_INT_MMU_INV_WR;
> > +       u32 val = reg_val & mmu_mask;
> > +       int i;
> > +
> > +       if (val) {
> > +               DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val);
> > +
> > +               if (val & BIT_DPU_INT_MMU_INV_RD)
> > +                       DRM_ERROR("iommu invalid read error, addr:
> 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base +
> REG_MMU_INV_ADDR_RD));
> > +               if (val & BIT_DPU_INT_MMU_INV_WR)
> > +                       DRM_ERROR("iommu invalid write error, addr:
> 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base +
> REG_MMU_INV_ADDR_WR));
> > +               if (val & BIT_DPU_INT_MMU_VAOR_RD)
> > +                       DRM_ERROR("iommu va out of range read error,
> addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base +
> REG_MMU_VAOR_ADDR_RD));
> > +               if (val & BIT_DPU_INT_MMU_VAOR_WR)
> > +                       DRM_ERROR("iommu va out of range write error,
> addr: 0x%08x\n",
> > +                               DPU_REG_RD(ctx->base +
> REG_MMU_VAOR_ADDR_WR));
> > +
> > +               for (i = 0; i < 8; i++) {
> > +                       reg_val = DPU_REG_RD(ctx->base +
> DPU_LAY_REG(REG_LAY_CTRL, i));
> > +                       if (reg_val & 0x1)
> > +                               DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x
> ctrl: 0x%08x\n", i,
> > +                                       DPU_REG_RD(ctx->base +
> DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),
> > +                                       DPU_REG_RD(ctx->base +
> DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),
> > +                                       DPU_REG_RD(ctx->base +
> DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),
> > +                                       DPU_REG_RD(ctx->base +
> DPU_LAY_REG(REG_LAY_CTRL, i)));
> > +               }
> > +       }
> > +
> > +       return val;
> > +}
> > +
> > +static void dpu_clean_all(struct dpu_context *ctx)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < 8; i++)
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL, i),
> 0x00);
> > +}
> > +
> > +static u32 dpu_isr(struct dpu_context *ctx)
> > +{
> > +       u32 reg_val, int_mask = 0;
> > +
> > +       reg_val = DPU_REG_RD(ctx->base + REG_DPU_INT_STS);
> > +
> > +       /* disable err interrupt */
> > +       if (reg_val & BIT_DPU_INT_ERR)
> > +               int_mask |= BIT_DPU_INT_ERR;
> > +
> > +       /* dpu update done isr */
> > +       if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
> > +               ctx->evt_update = true;
> > +               wake_up_interruptible_all(&ctx->wait_queue);
> > +       }
> > +
> > +       /* dpu stop done isr */
> > +       if (reg_val & BIT_DPU_INT_DONE) {
> > +               ctx->evt_stop = true;
> > +               wake_up_interruptible_all(&ctx->wait_queue);
> > +       }
> > +
> > +       /* dpu ifbc payload error isr */
> > +       if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) {
> > +               int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +               DRM_ERROR("dpu ifbc payload error\n");
> > +       }
> > +
> > +       /* dpu ifbc header error isr */
> > +       if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) {
> > +               int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +               DRM_ERROR("dpu ifbc header error\n");
> > +       }
> > +
> > +       int_mask |= check_mmu_isr(ctx, reg_val);
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, reg_val);
> > +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +
> > +       return reg_val;
> > +}
> > +
> > +static int dpu_wait_stop_done(struct dpu_context *ctx)
> > +{
> > +       int rc;
> > +
> > +       if (ctx->stopped)
> > +               return 0;
> > +
> > +       rc = wait_event_interruptible_timeout(ctx->wait_queue,
> ctx->evt_stop,
> > +                                              msecs_to_jiffies(500));
> > +       ctx->evt_stop = false;
> > +
> > +       ctx->stopped = true;
> > +
> > +       if (!rc) {
> > +               DRM_ERROR("dpu wait for stop done time out!\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int dpu_wait_update_done(struct dpu_context *ctx)
> > +{
> > +       int rc;
> > +
> > +       ctx->evt_update = false;
> > +
> > +       rc = wait_event_interruptible_timeout(ctx->wait_queue,
> ctx->evt_update,
> > +                                              msecs_to_jiffies(500));
> > +
> > +       if (!rc) {
> > +               DRM_ERROR("dpu wait for reg update done time out!\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void dpu_stop(struct dpu_context *ctx)
> > +{
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI)
> > +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_STOP);
> > +
> > +       dpu_wait_stop_done(ctx);
> > +}
> > +
> > +static void dpu_run(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +       ctx->stopped = false;
> > +}
> > +
> > +static void dpu_init(struct dpu_context *ctx)
> > +{
> > +       u32 reg_val, size;
> > +
> > +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +       size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
> > +
> > +       DPU_REG_WR(ctx->base + REG_PANEL_SIZE, size);
> > +       DPU_REG_WR(ctx->base + REG_BLEND_SIZE, size);
> > +
> > +       reg_val = BIT_DPU_COEF_NARROW_RANGE |
> BIT_DPU_Y2R_COEF_ITU709_STANDARD;
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG0, reg_val);
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG1, 0x004466da);
> > +       DPU_REG_WR(ctx->base + REG_DPU_CFG2, 0x00);
> > +
> > +       if (ctx->stopped)
> > +               dpu_clean_all(ctx);
> > +
> > +       DPU_REG_WR(ctx->base + REG_MMU_EN, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_PPN1, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_RANGE1, 0xffff);
> > +       DPU_REG_WR(ctx->base + REG_MMU_PPN2, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_MMU_RANGE2, 0xffff);
> > +       DPU_REG_WR(ctx->base + REG_MMU_VPN_RANGE, 0x1ffff);
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xffff);
> > +}
> > +
> > +static void dpu_fini(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, 0x00);
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_CLR, 0xff);
> > +}
> > +
> > +static void dpu_layer(struct dpu_context *ctx,
> > +                   struct dpu_layer *hwlayer)
> > +{
> > +       const struct drm_format_info *info;
> > +       u32 size, offset, ctrl, pitch;
> > +       int i;
> > +
> > +       offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16);
> > +
> > +       if (hwlayer->src_w && hwlayer->src_h)
> > +               size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) <<
> 16);
> > +       else
> > +               size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) <<
> 16);
> > +
> > +       for (i = 0; i < hwlayer->planes; i++)
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_BASE_ADDR0,
> > +                               hwlayer->index), hwlayer->addr[i]);
> > +
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_POS,
> > +                       hwlayer->index), offset);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_SIZE,
> > +                       hwlayer->index), size);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CROP_START,
> > +                       hwlayer->index), hwlayer->src_y << 16 |
> hwlayer->src_x);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_ALPHA,
> > +                       hwlayer->index), hwlayer->alpha);
> > +
> > +       info = drm_format_info(hwlayer->format);
> > +       if (hwlayer->planes == 3) {
> > +               /* UV pitch is 1/2 of Y pitch*/
> > +               pitch = (hwlayer->pitch[0] / info->cpp[0]) |
> > +                               (hwlayer->pitch[0] / info->cpp[0] << 15);
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                               hwlayer->index), pitch);
> > +       } else {
> > +               pitch = hwlayer->pitch[0] / info->cpp[0];
> > +               DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_PITCH,
> > +                               hwlayer->index), pitch);
> > +       }
> > +
> > +       ctrl = hwlayer->format |
> > +               hwlayer->blending |
> > +               (hwlayer->rotation & 0x7) << 20;
> > +
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                       hwlayer->index), ctrl);
> > +       DPU_REG_WR(ctx->base + DPU_LAY_REG(REG_LAY_CTRL,
> > +                       hwlayer->index), BIT_DPU_LAY_EN);
> > +
> > +       DRM_DEBUG("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n",
> > +                               hwlayer->dst_x, hwlayer->dst_y,
> > +                               hwlayer->dst_w, hwlayer->dst_h);
> > +       DRM_DEBUG("start_x = %d, start_y = %d, start_w = %d, start_h =
> %d\n",
> > +                               hwlayer->src_x, hwlayer->src_y,
> > +                               hwlayer->src_w, hwlayer->src_h);
> > +}
> > +
> > +static void dpu_flip(struct dpu_context *ctx,
> > +                    struct dpu_layer layers[], u8 count)
> > +{
> > +       int i;
> > +       u32 reg_val;
> > +
> > +       /*
> > +        * Make sure the dpu is in stop status. DPU_R2P0 has no shadow
> > +        * registers in EDPI mode. So the config registers can only be
> > +        * updated in the rising edge of DPU_RUN bit.
> > +        */
> > +       if (ctx->if_type == SPRD_DPU_IF_EDPI)
> > +               dpu_wait_stop_done(ctx);
> > +
> > +       /* reset the bgcolor to black */
> > +       DPU_REG_WR(ctx->base + REG_BG_COLOR, 0x00);
> > +
> > +       /* disable all the layers */
> > +       dpu_clean_all(ctx);
> > +
> > +       /* start configure dpu layers */
> > +       for (i = 0; i < count; i++)
> > +               dpu_layer(ctx, &layers[i]);
> > +
> > +       /* update trigger and wait */
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +               if (!ctx->stopped) {
> > +                       DPU_REG_SET(ctx->base + REG_DPU_CTRL,
> BIT_DPU_REG_UPDATE);
> > +                       dpu_wait_update_done(ctx);
> > +               }
> > +
> > +               DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_ERR);
> > +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +               DPU_REG_SET(ctx->base + REG_DPU_CTRL, BIT_DPU_RUN);
> > +
> > +               ctx->stopped = false;
> > +       }
> > +
> > +       /*
> > +        * If the following interrupt was disabled in isr,
> > +        * re-enable it.
> > +        */
> > +       reg_val = BIT_DPU_INT_FBC_PLD_ERR |
> > +                 BIT_DPU_INT_FBC_HDR_ERR |
> > +                 BIT_DPU_INT_MMU_VAOR_RD |
> > +                 BIT_DPU_INT_MMU_VAOR_WR |
> > +                 BIT_DPU_INT_MMU_INV_RD |
> > +                 BIT_DPU_INT_MMU_INV_WR;
> > +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, reg_val);
> > +
> > +}
> > +
> > +static void dpu_dpi_init(struct dpu_context *ctx)
> > +{
> > +       u32 int_mask = 0;
> > +       u32 reg_val;
> > +
> > +       if (ctx->if_type == SPRD_DPU_IF_DPI) {
> > +               /* use dpi as interface */
> > +               DPU_REG_CLR(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +               /* disable Halt function for SPRD DSI */
> > +               DPU_REG_CLR(ctx->base + REG_DPI_CTRL,
> BIT_DPU_DPI_HALT_EN);
> > +
> > +               /* select te from external pad */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL,
> BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +               /* set dpi timing */
> > +               reg_val = ctx->vm.hsync_len << 0 |
> > +                         ctx->vm.hback_porch << 8 |
> > +                         ctx->vm.hfront_porch << 20;
> > +               DPU_REG_WR(ctx->base + REG_DPI_H_TIMING, reg_val);
> > +
> > +               reg_val = ctx->vm.vsync_len << 0 |
> > +                         ctx->vm.vback_porch << 8 |
> > +                         ctx->vm.vfront_porch << 20;
> > +               DPU_REG_WR(ctx->base + REG_DPI_V_TIMING, reg_val);
> > +
> > +               if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
> > +                       DRM_WARN("Warning: (vsync + vbp) < 32, "
> > +                               "underflow risk!\n");
> > +
> > +               /* enable dpu update done INT */
> > +               int_mask |= BIT_DPU_INT_UPDATE_DONE;
> > +               /* enable dpu DONE  INT */
> > +               int_mask |= BIT_DPU_INT_DONE;
> > +               /* enable dpu dpi vsync */
> > +               int_mask |= BIT_DPU_INT_VSYNC;
> > +               /* enable dpu TE INT */
> > +               int_mask |= BIT_DPU_INT_TE;
> > +               /* enable underflow err INT */
> > +               int_mask |= BIT_DPU_INT_ERR;
> > +       } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
> > +               /* use edpi as interface */
> > +               DPU_REG_SET(ctx->base + REG_DPU_CFG0, BIT_DPU_IF_EDPI);
> > +
> > +               /* use external te */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL,
> BIT_DPU_EDPI_FROM_EXTERNAL_PAD);
> > +
> > +               /* enable te */
> > +               DPU_REG_SET(ctx->base + REG_DPI_CTRL,
> BIT_DPU_EDPI_TE_EN);
> > +
> > +               /* enable stop DONE INT */
> > +               int_mask |= BIT_DPU_INT_DONE;
> > +               /* enable TE INT */
> > +               int_mask |= BIT_DPU_INT_TE;
> > +       }
> > +
> > +       /* enable ifbc payload error INT */
> > +       int_mask |= BIT_DPU_INT_FBC_PLD_ERR;
> > +       /* enable ifbc header error INT */
> > +       int_mask |= BIT_DPU_INT_FBC_HDR_ERR;
> > +       /* enable iommu va out of range read error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_VAOR_RD;
> > +       /* enable iommu va out of range write error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_VAOR_WR;
> > +       /* enable iommu invalid read error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_INV_RD;
> > +       /* enable iommu invalid write error INT */
> > +       int_mask |= BIT_DPU_INT_MMU_INV_WR;
> > +
> > +       DPU_REG_WR(ctx->base + REG_DPU_INT_EN, int_mask);
> > +}
> > +
> > +static void enable_vsync(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_SET(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static void disable_vsync(struct dpu_context *ctx)
> > +{
> > +       DPU_REG_CLR(ctx->base + REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
> > +}
> > +
> > +static const u32 primary_fmts[] = {
> > +       DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
> > +       DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
> > +       DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888,
> > +       DRM_FORMAT_RGBX8888, DRM_FORMAT_RGB565,
> > +       DRM_FORMAT_BGR565, DRM_FORMAT_NV12,
> > +       DRM_FORMAT_NV21, DRM_FORMAT_NV16,
> > +       DRM_FORMAT_NV61, DRM_FORMAT_YUV420,
> > +       DRM_FORMAT_YVU420,
> > +};
> > +
> > +static void dpu_capability(struct dpu_context *ctx,
> > +                       struct dpu_capability *cap)
> > +{
> > +       cap->max_layers = 6;
> > +       cap->fmts_ptr = primary_fmts;
> > +       cap->fmts_cnt = ARRAY_SIZE(primary_fmts);
> > +}
> > +
> > +const struct dpu_core_ops dpu_r2p0_core_ops = {
> > +       .init = dpu_init,
> > +       .fini = dpu_fini,
> > +       .run = dpu_run,
> > +       .stop = dpu_stop,
> > +       .isr = dpu_isr,
> > +       .ifconfig = dpu_dpi_init,
> > +       .capability = dpu_capability,
> > +       .flip = dpu_flip,
> > +       .enable_vsync = enable_vsync,
> > +       .disable_vsync = disable_vsync,
> > +};
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c
> b/drivers/gpu/drm/sprd/sprd_dpu.c
> > new file mode 100644
> > index 0000000..5ec8e7c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.c
> > @@ -0,0 +1,646 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#include <linux/component.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_fb_cma_helper.h>
> > +#include <drm/drm_gem_cma_helper.h>
> > +#include <drm/drm_gem_framebuffer_helper.h>
> > +#include <drm/drm_plane_helper.h>
> > +
> > +#include "sprd_drm.h"
> > +#include "sprd_dpu.h"
> > +
> > +struct sprd_plane {
> > +       struct drm_plane plane;
> > +       u32 index;
> > +       u32 addr[4];
> > +       u32 pitch[4];
> > +       u32 format;
> > +       u32 rotation;
> > +       u32 blend_mode;
> > +};
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu);
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu);
> > +
> > +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
> > +{
> > +       return container_of(plane, struct sprd_plane, plane);
> > +}
> > +
> > +static int sprd_plane_format_convert(u32 fourcc, u32 *format)
> > +{
> > +       switch (fourcc) {
> > +       case DRM_FORMAT_BGRA8888:
> > +               /* BGRA8888 -> ARGB8888 */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_RGBX8888:
> > +       case DRM_FORMAT_RGBA8888:
> > +               /* RGBA8888 -> ABGR8888 */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_ABGR8888:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_ARGB8888:
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_XBGR8888:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_XRGB8888:
> > +               *format |= BIT_DPU_LAY_FORMAT_ARGB8888;
> > +               break;
> > +       case DRM_FORMAT_BGR565:
> > +               /* RB switch */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               /* FALLTHRU */
> > +       case DRM_FORMAT_RGB565:
> > +               *format |= BIT_DPU_LAY_FORMAT_RGB565;
> > +               break;
> > +       case DRM_FORMAT_NV12:
> > +               /* 2-Lane: Yuv420 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV21:
> > +               /* 2-Lane: Yuv420 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV16:
> > +               /* 2-Lane: Yuv422 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_NV61:
> > +               /* 2-Lane: Yuv422 */
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_YUV420:
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_NO_SWITCH;
> > +               break;
> > +       case DRM_FORMAT_YVU420:
> > +               *format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
> > +               /* Y endian */
> > +               *format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
> > +               /* UV endian */
> > +               *format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sprd_plane_rotation_convert(u32 angle, u32 *rotation)
> > +{
> > +       switch (angle) {
> > +       case DRM_MODE_ROTATE_0:
> > +               *rotation = DPU_LAYER_ROTATION_0;
> > +               break;
> > +       case DRM_MODE_ROTATE_90:
> > +               *rotation = DPU_LAYER_ROTATION_90;
> > +               break;
> > +       case DRM_MODE_ROTATE_180:
> > +               *rotation = DPU_LAYER_ROTATION_180;
> > +               break;
> > +       case DRM_MODE_ROTATE_270:
> > +               *rotation = DPU_LAYER_ROTATION_270;
> > +               break;
> > +       case DRM_MODE_REFLECT_Y:
> > +               *rotation = DPU_LAYER_ROTATION_180_M;
> > +               break;
> > +       case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
> > +               *rotation = DPU_LAYER_ROTATION_90_M;
> > +               break;
> > +       case DRM_MODE_REFLECT_X:
> > +               *rotation = DPU_LAYER_ROTATION_0_M;
> > +               break;
> > +       case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
> > +               *rotation = DPU_LAYER_ROTATION_270_M;
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int sprd_plane_atomic_check(struct drm_plane *plane,
> > +                                 struct drm_plane_state *state)
> > +{
> > +       struct sprd_plane *p = to_sprd_plane(plane);
> > +       struct drm_framebuffer *fb = state->fb;
> > +       struct drm_gem_cma_object *cma_obj;
> > +       int i, ret;
> > +       u32 addr;
> > +
> > +       if (!state->fb || !state->crtc)
> > +               return 0;
> > +
> > +       ret = sprd_plane_format_convert(fb->format->format,
> > +                                       &p->format);
> > +       if (ret < 0) {
> > +               DRM_ERROR("Invalid plane format\n");
> > +               return ret;
> > +       }
> > +
> > +       ret = sprd_plane_rotation_convert(state->rotation,
> > +                                       &p->rotation);
> > +       if (ret < 0) {
> > +               DRM_ERROR("Invalid plane rotation\n");
> > +               return ret;
> > +       }
> > +
> > +       switch (state->pixel_blend_mode) {
> > +       case DRM_MODE_BLEND_COVERAGE:
> > +               /* alpha mode select - combo alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +               /* Normal mode */
> > +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
> > +               break;
> > +       case DRM_MODE_BLEND_PREMULTI:
> > +               /* alpha mode select - combo alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_COMBO_ALPHA;
> > +               /* Pre-mult mode */
> > +               p->blend_mode |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
> > +               break;
> > +       case DRM_MODE_BLEND_PIXEL_NONE:
> > +       default:
> > +               /* don't do blending, maybe RGBX */
> > +               /* alpha mode select - layer alpha */
> > +               p->blend_mode |= BIT_DPU_LAY_LAYER_ALPHA;
> > +               break;
> > +       }
> > +
> > +       for (i = 0; i < fb->format->num_planes; i++) {
> > +               cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> > +               addr = cma_obj->paddr + fb->offsets[i];
> > +               if (addr % 16) {
> > +                       DRM_ERROR("layer addr[%d] is not 16 bytes align,
> it's 0x%08x\n",
> > +                               i, addr);
> > +                       return -EFAULT;
> > +               }
> > +
> > +               p->addr[i] = addr;
> > +               p->pitch[i] = fb->pitches[i];
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_plane_atomic_update(struct drm_plane *plane,
> > +                                   struct drm_plane_state *old_state)
> > +{
> > +       struct drm_plane_state *state = plane->state;
> > +       struct drm_framebuffer *fb = plane->state->fb;
> > +       struct sprd_plane *p = to_sprd_plane(plane);
> > +       struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc);
> > +       struct dpu_layer *layer = &dpu->layers[p->index];
> > +       int i;
> > +
> > +       if (!state->crtc || !state->fb)
> > +               return;
> > +
> > +       layer->index = p->index;
> > +       layer->src_x = state->src_x >> 16;
> > +       layer->src_y = state->src_y >> 16;
> > +       layer->src_w = state->src_w >> 16;
> > +       layer->src_h = state->src_h >> 16;
> > +       layer->dst_x = state->crtc_x;
> > +       layer->dst_y = state->crtc_y;
> > +       layer->dst_w = state->crtc_w;
> > +       layer->dst_h = state->crtc_h;
> > +       layer->alpha = state->alpha;
> > +       layer->format = p->format;
> > +       layer->blending = p->blend_mode;
> > +       layer->rotation = p->rotation;
> > +       layer->planes = fb->format->num_planes;
> > +
> > +       for (i = 0; i < layer->planes; i++) {
> > +               layer->addr[i] = p->addr[i];
> > +               layer->pitch[i] = p->pitch[i];
> > +       }
> > +
> > +       dpu->pending_planes++;
> > +}
> > +
> > +static void sprd_plane_create_properties(struct sprd_plane *p, int
> index)
> > +{
> > +       unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +                                      BIT(DRM_MODE_BLEND_PREMULTI) |
> > +                                      BIT(DRM_MODE_BLEND_COVERAGE);
> > +
> > +       /* create rotation property */
> > +       drm_plane_create_rotation_property(&p->plane,
> > +                                          DRM_MODE_ROTATE_0,
> > +                                          DRM_MODE_ROTATE_MASK |
> > +                                          DRM_MODE_REFLECT_MASK);
> > +
> > +       /* create alpha property */
> > +       drm_plane_create_alpha_property(&p->plane);
> > +
> > +       /* create blend mode property */
> > +       drm_plane_create_blend_mode_property(&p->plane, supported_modes);
> > +
> > +       /* create zpos property */
> > +       drm_plane_create_zpos_immutable_property(&p->plane, index);
> > +}
> > +
> > +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
> > +       .atomic_check = sprd_plane_atomic_check,
> > +       .atomic_update = sprd_plane_atomic_update,
> > +};
> > +
> > +static const struct drm_plane_funcs sprd_plane_funcs = {
> > +       .update_plane = drm_atomic_helper_update_plane,
> > +       .disable_plane  = drm_atomic_helper_disable_plane,
> > +       .destroy = drm_plane_cleanup,
> > +       .reset = drm_atomic_helper_plane_reset,
> > +       .atomic_duplicate_state =
> drm_atomic_helper_plane_duplicate_state,
> > +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +};
> > +
> > +static struct drm_plane *sprd_plane_init(struct drm_device *drm,
> > +                                       struct sprd_dpu *dpu)
> > +{
> > +       struct drm_plane *primary = NULL;
> > +       struct sprd_plane *p = NULL;
> > +       struct dpu_capability cap = {};
> > +       int ret, i;
> > +
> > +       dpu->core->capability(&dpu->ctx, &cap);
> > +
> > +       dpu->layers = devm_kcalloc(drm->dev, cap.max_layers,
> > +                                 sizeof(struct dpu_layer), GFP_KERNEL);
> > +       if (!dpu->layers)
> > +               return ERR_PTR(-ENOMEM);
> > +
> > +       for (i = 0; i < cap.max_layers; i++) {
> > +
> > +               p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL);
> > +               if (!p)
> > +                       return ERR_PTR(-ENOMEM);
> > +
> > +               ret = drm_universal_plane_init(drm, &p->plane, 1,
> > +                                              &sprd_plane_funcs,
> cap.fmts_ptr,
> > +                                              cap.fmts_cnt, NULL,
> > +                                              DRM_PLANE_TYPE_PRIMARY,
> NULL);
> > +               if (ret) {
> > +                       DRM_ERROR("fail to init primary plane\n");
> > +                       return ERR_PTR(ret);
> > +               }
> > +
> > +               drm_plane_helper_add(&p->plane,
> &sprd_plane_helper_funcs);
> > +
> > +               sprd_plane_create_properties(p, i);
> > +
> > +               p->index = i;
> > +               if (i == 0)
> > +                       primary = &p->plane;
> > +       }
> > +
> > +       return primary;
> > +}
> > +
> > +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
> > +                                       const struct drm_display_mode
> *mode)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__,
> DRM_MODE_ARG(mode));
> > +
> > +       if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > +               drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> > +
> > +               if ((mode->hdisplay == mode->htotal) ||
> > +                   (mode->vdisplay == mode->vtotal))
> > +                       dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > +               else
> > +                       dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> > +       }
> > +
> > +       return MODE_OK;
> > +}
> > +
> > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > +                                  struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       sprd_dpu_init(dpu);
> > +
> > +       enable_irq(dpu->ctx.irq);
> > +}
> > +
> > +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
> > +                                   struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +       struct drm_device *drm = dpu->crtc.dev;
> > +
> > +       disable_irq(dpu->ctx.irq);
> > +
> > +       sprd_dpu_fini(dpu);
> > +
> > +       spin_lock_irq(&drm->event_lock);
> > +       if (crtc->state->event) {
> > +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +               crtc->state->event = NULL;
> > +       }
> > +       spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
> > +                                struct drm_crtc_state *state)
> > +{
> > +       DRM_DEBUG("%s()\n", __func__);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_begin(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       memset(dpu->layers, 0, sizeof(*dpu->layers) *
> dpu->pending_planes);
> > +
> > +       dpu->pending_planes = 0;
> > +}
> > +
> > +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
> > +                                 struct drm_crtc_state *old_state)
> > +
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +       struct drm_device *drm = dpu->crtc.dev;
> > +
> > +       if (dpu->pending_planes)
> > +               dpu->core->flip(&dpu->ctx, dpu->layers,
> dpu->pending_planes);
> > +
> > +       spin_lock_irq(&drm->event_lock);
> > +       if (crtc->state->event) {
> > +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +               crtc->state->event = NULL;
> > +       }
> > +       spin_unlock_irq(&drm->event_lock);
> > +}
> > +
> > +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       dpu->core->enable_vsync(&dpu->ctx);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
> > +
> > +       dpu->core->disable_vsync(&dpu->ctx);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
> > +       .mode_valid     = sprd_crtc_mode_valid,
> > +       .atomic_check   = sprd_crtc_atomic_check,
> > +       .atomic_begin   = sprd_crtc_atomic_begin,
> > +       .atomic_flush   = sprd_crtc_atomic_flush,
> > +       .atomic_enable  = sprd_crtc_atomic_enable,
> > +       .atomic_disable = sprd_crtc_atomic_disable,
> > +};
> > +
> > +static const struct drm_crtc_funcs sprd_crtc_funcs = {
> > +       .destroy        = drm_crtc_cleanup,
> > +       .set_config     = drm_atomic_helper_set_config,
> > +       .page_flip      = drm_atomic_helper_page_flip,
> > +       .reset          = drm_atomic_helper_crtc_reset,
> > +       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> > +       .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
> > +       .enable_vblank  = sprd_crtc_enable_vblank,
> > +       .disable_vblank = sprd_crtc_disable_vblank,
> > +};
> > +
> > +static int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc,
> > +                        struct drm_plane *primary)
> > +{
> > +       struct device_node *port;
> > +       int ret;
> > +
> > +       /*
> > +        * set crtc port so that drm_of_find_possible_crtcs call works
> > +        */
> > +       port = of_parse_phandle(drm->dev->of_node, "ports", 0);
> > +       if (!port) {
> > +               DRM_ERROR("find 'ports' phandle of %s failed\n",
> > +                         drm->dev->of_node->full_name);
> > +               return -EINVAL;
> > +       }
> > +       of_node_put(port);
> > +       crtc->port = port;
> > +
> > +       ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
> > +                                       &sprd_crtc_funcs, NULL);
> > +       if (ret) {
> > +               DRM_ERROR("failed to init crtc.\n");
> > +               return ret;
> > +       }
> > +
> > +       drm_mode_crtc_set_gamma_size(crtc, 256);
> > +
> > +       drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs);
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_dpu_init(struct sprd_dpu *dpu)
> > +{
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +
> > +       dpu->core->init(ctx);
> > +       dpu->core->ifconfig(ctx);
> > +}
> > +
> > +static void sprd_dpu_fini(struct sprd_dpu *dpu)
> > +{
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +
> > +       dpu->core->fini(ctx);
> > +}
> > +
> > +static irqreturn_t sprd_dpu_isr(int irq, void *data)
> > +{
> > +       struct sprd_dpu *dpu = data;
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +       u32 int_mask = 0;
> > +
> > +       int_mask = dpu->core->isr(ctx);
> > +
> > +       if (int_mask & BIT_DPU_INT_ERR)
> > +               DRM_WARN("Warning: dpu underflow!\n");
> > +
> > +       if (int_mask & BIT_DPU_INT_VSYNC)
> > +               drm_crtc_handle_vblank(&dpu->crtc);
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static int sprd_dpu_bind(struct device *dev, struct device *master,
> void *data)
> > +{
> > +       struct drm_device *drm = data;
> > +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +       struct drm_plane *plane;
> > +       int ret;
> > +
> > +       plane = sprd_plane_init(drm, dpu);
> > +       if (IS_ERR_OR_NULL(plane)) {
> > +               ret = PTR_ERR(plane);
> > +               return ret;
> > +       }
> > +
> > +       ret = sprd_crtc_init(drm, &dpu->crtc, plane);
> > +       if (ret)
> > +               return ret;
> > +
> > +       return 0;
> > +}
> > +
> > +static void sprd_dpu_unbind(struct device *dev, struct device *master,
> > +       void *data)
> > +{
> > +       struct sprd_dpu *dpu = dev_get_drvdata(dev);
> > +
> > +       drm_crtc_cleanup(&dpu->crtc);
> > +}
> > +
> > +static const struct component_ops dpu_component_ops = {
> > +       .bind = sprd_dpu_bind,
> > +       .unbind = sprd_dpu_unbind,
> > +};
> > +
> > +static int sprd_dpu_context_init(struct sprd_dpu *dpu,
> > +                               struct device *dev)
> > +{
> > +       struct platform_device *pdev = to_platform_device(dev);
> > +       struct dpu_context *ctx = &dpu->ctx;
> > +       struct resource *res;
> > +       int ret;
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       ctx->base = devm_ioremap(dev, res->start, resource_size(res));
> > +       if (!ctx->base) {
> > +               DRM_ERROR("failed to map dpu registers\n");
> > +               return -EFAULT;
> > +       }
> > +
> > +       ctx->irq = platform_get_irq(pdev, 0);
> > +       if (ctx->irq < 0) {
> > +               DRM_ERROR("failed to get dpu irq\n");
> > +               return ctx->irq;
> > +       }
> > +
> > +       irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
> > +       ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
> > +                                       0, "DPU", dpu);
> > +       if (ret) {
> > +               DRM_ERROR("failed to register dpu irq handler\n");
> > +               return ret;
> > +       }
> > +
> > +       init_waitqueue_head(&ctx->wait_queue);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct sprd_dpu_ops sharkl3_dpu = {
> > +       .core = &dpu_r2p0_core_ops,
> > +};
> > +
> > +static const struct of_device_id dpu_match_table[] = {
> > +       { .compatible = "sprd,sharkl3-dpu",
> > +         .data = &sharkl3_dpu },
> > +       { /* sentinel */ },
> > +};
> > +
> > +static int sprd_dpu_probe(struct platform_device *pdev)
> > +{
> > +       const struct sprd_dpu_ops *pdata;
> > +       struct sprd_dpu *dpu;
> > +       int ret;
> > +
> > +       dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL);
> > +       if (!dpu)
> > +               return -ENOMEM;
> > +
> > +       pdata = of_device_get_match_data(&pdev->dev);
> > +       if (pdata) {
> > +               dpu->core = pdata->core;
> > +       } else {
> > +               DRM_ERROR("No matching driver data found\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = sprd_dpu_context_init(dpu, &pdev->dev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       platform_set_drvdata(pdev, dpu);
> > +
> > +       return component_add(&pdev->dev, &dpu_component_ops);
> > +}
> > +
> > +static int sprd_dpu_remove(struct platform_device *pdev)
> > +{
> > +       component_del(&pdev->dev, &dpu_component_ops);
> > +       return 0;
> > +}
> > +
> > +struct platform_driver sprd_dpu_driver = {
> > +       .probe = sprd_dpu_probe,
> > +       .remove = sprd_dpu_remove,
> > +       .driver = {
> > +               .name = "sprd-dpu-drv",
> > +               .of_match_table = dpu_match_table,
> > +       },
> > +};
> > +
> > +MODULE_AUTHOR("Leon He <leon.he@unisoc.com>");
> > +MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>");
> > +MODULE_DESCRIPTION("Unisoc Display Controller Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h
> b/drivers/gpu/drm/sprd/sprd_dpu.h
> > new file mode 100644
> > index 0000000..7d3c5e4
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sprd/sprd_dpu.h
> > @@ -0,0 +1,187 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020 Unisoc Inc.
> > + */
> > +
> > +#ifndef __SPRD_DPU_H__
> > +#define __SPRD_DPU_H__
> > +
> > +#include <linux/bug.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_vblank.h>
> > +#include <uapi/drm/drm_mode.h>
> > +
> > +#define BIT_DPU_INT_DONE_              BIT(0)
> > +#define BIT_DPU_INT_TE                 BIT(1)
> > +#define BIT_DPU_INT_ERR                        BIT(2)
> > +#define BIT_DPU_INT_EDPI_TE            BIT(3)
> > +#define BIT_DPU_INT_UPDATE_DONE                BIT(4)
> > +#define BIT_DPU_INT_VSYNC              BIT(5)
> > +#define BIT_DPU_INT_WB_DONE            BIT(6)
> > +#define BIT_DPU_INT_WB_ERR             BIT(7)
> > +
> > +#define BIT_DPU_LAY_LAYER_ALPHA                        (0x01 << 2)
> > +#define BIT_DPU_LAY_COMBO_ALPHA                        (0x02 << 2)
> > +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE               (0x00 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE               (0x01 << 4)
> > +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE               (0x02 << 4)
> > +#define BIT_DPU_LAY_FORMAT_ARGB8888                    (0x03 << 4)
> > +#define BIT_DPU_LAY_FORMAT_RGB565                      (0x04 << 4)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3               (0x00 << 8)
> > +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0               (0x01 << 8)
> > +#define BIT_DPU_LAY_NO_SWITCH                  (0x00 << 10)
> > +#define BIT_DPU_LAY_RB_OR_UV_SWITCH            (0x01 << 10)
> > +#define BIT_DPU_LAY_MODE_BLEND_NORMAL          (0x00 << 16)
> > +#define BIT_DPU_LAY_MODE_BLEND_PREMULT         (0x01 << 16)
> > +
> > +enum {
> > +       SPRD_DPU_IF_DBI = 0,
> > +       SPRD_DPU_IF_DPI,
> > +       SPRD_DPU_IF_EDPI,
> > +       SPRD_DPU_IF_LIMIT
> > +};
> > +
> > +enum {
> > +       DPU_LAYER_ROTATION_0,
> > +       DPU_LAYER_ROTATION_90,
> > +       DPU_LAYER_ROTATION_180,
> > +       DPU_LAYER_ROTATION_270,
> > +       DPU_LAYER_ROTATION_0_M,
> > +       DPU_LAYER_ROTATION_90_M,
> > +       DPU_LAYER_ROTATION_180_M,
> > +       DPU_LAYER_ROTATION_270_M,
> > +};
> > +
> > +struct dpu_layer {
> > +       u8 index;
> > +       u8 planes;
> > +       u32 addr[4];
> > +       u32 pitch[4];
> > +       s16 src_x;
> > +       s16 src_y;
> > +       s16 src_w;
> > +       s16 src_h;
> > +       s16 dst_x;
> > +       s16 dst_y;
> > +       u16 dst_w;
> > +       u16 dst_h;
> > +       u32 format;
> > +       u32 alpha;
> > +       u32 blending;
> > +       u32 rotation;
> > +};
> > +
> > +/**
> > + * Sprd DPU capability structure
> > + *
> > + * @max_layers: maximum number of layers available
> > + * @fmts_ptr: A pointer to array of supported pixel formats
> > + * @fmts_cnt: the number of format on @fmts_ptr
> > + */
> > +struct dpu_capability {
> > +       u32 max_layers;
> > +       const u32 *fmts_ptr;
> > +       u32 fmts_cnt;
> > +};
> > +
> > +/**
> > + * Sprd DPU core callback ops
> > + *
> > + * This structure decribes the display controller common
> > + * callback ops
> > + *
> > + * @init: initial DPU core
> > + * @fini: cleanup DPU core
> > + * @run: enable DPU output
> > + * @stop: disable DPU output
> > + * @enable_vsync: enable vblank interrupt
> > + * @disable_vsync: disable vblank interrupt
> > + * @isr: function pointer to the isr
> > + * @ifconfig: initial DPI interface
> > + * @flip: commit CRTC planes to DPU
> > + * @capability: callback for DPU capabilities
> > + */
> > +struct dpu_context;
> > +struct dpu_core_ops {
> > +       void (*init)(struct dpu_context *ctx);
> > +       void (*fini)(struct dpu_context *ctx);
> > +       void (*run)(struct dpu_context *ctx);
> > +       void (*stop)(struct dpu_context *ctx);
> > +       void (*enable_vsync)(struct dpu_context *ctx);
> > +       void (*disable_vsync)(struct dpu_context *ctx);
> > +       u32 (*isr)(struct dpu_context *ctx);
> > +       void (*ifconfig)(struct dpu_context *ctx);
> > +       void (*flip)(struct dpu_context *ctx,
> > +                    struct dpu_layer layers[], u8 count);
> > +       void (*capability)(struct dpu_context *ctx,
> > +                       struct dpu_capability *cap);
> > +};
> > +
> > +/**
> > + * Sprd DPU context structure
> > + *
> > + * @base: DPU controller base address
> > + * @irq: IRQ number to install the handler for
> > + * @if_type: The type of DPI interface, default is DPI mode.
> > + * @vm: videomode structure to use for DPU and DPI initialization
> > + * @stopped: indicates whether DPU are stopped
> > + * @wait_queue: wait queue, used to wait for DPU shadow register update
> done and
> > + * DPU stop register done interrupt signal.
> > + * @evt_update: wait queue condition for DPU shadow register
> > + * @evt_stop: wait queue condition for DPU stop register
> > + */
> > +struct dpu_context {
> > +       void __iomem *base;
> > +       int irq;
> > +       u8 if_type;
> > +       struct videomode vm;
> > +       bool stopped;
> > +       wait_queue_head_t wait_queue;
> > +       bool evt_update;
> > +       bool evt_stop;
> > +};
> > +
> > +/**
> > + * Sprd DPU device structure
> > + *
> > + * @crtc: DRM crtc
> > + * @ctx: A pointer to the DPU's implementation specific context
> > + * @core: pointer to callbacks for DPU core functionality
> > + * @layers: active DPU layers ready to commit
> > + * @pending_planes: the number of layers on @layers
> > + */
> > +struct sprd_dpu {
> > +       struct drm_crtc crtc;
> > +       struct dpu_context ctx;
> > +       const struct dpu_core_ops *core;
> > +       struct dpu_layer *layers;
> > +       u8 pending_planes;
> > +};
> > +
> > +/**
> > + * Sprd DPU H/W callback ops match table structure
> > + * The structure used for matching a specific device callback ops
> > + *
> > + * @core: pointer to callbacks for DPU core functionality
> > + */
> > +struct sprd_dpu_ops {
> > +       const struct dpu_core_ops *core;
> > +};
> > +
> > +static inline struct sprd_dpu *crtc_to_dpu(struct drm_crtc *crtc)
> > +{
> > +       return crtc ? container_of(crtc, struct sprd_dpu, crtc) : NULL;
> > +}
> > +
> > +extern const struct dpu_core_ops dpu_r2p0_core_ops;
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.c
> b/drivers/gpu/drm/sprd/sprd_drm.c
> > index 4706185..200020f 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.c
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.c
> > @@ -200,6 +200,7 @@ static struct platform_driver sprd_drm_driver = {
> >
> >  static struct platform_driver *sprd_drm_drivers[]  = {
> >         &sprd_drm_driver,
> > +       &sprd_dpu_driver,
> >  };
> >
> >  static int __init sprd_drm_init(void)
> > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h
> b/drivers/gpu/drm/sprd/sprd_drm.h
> > index edf0881..3c32f3a 100644
> > --- a/drivers/gpu/drm/sprd/sprd_drm.h
> > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
> > @@ -13,4 +13,6 @@ struct sprd_drm {
> >         struct drm_device *drm;
> >  };
> >
> > +extern struct platform_driver sprd_dpu_driver;
> > +
> >  #endif /* _SPRD_DRM_H_ */
> > --
> > 2.7.4
> >
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>

[-- Attachment #1.2: Type: text/html, Size: 77791 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2021-01-06  8:53 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-28 10:07 [PATCH RFC v6 0/6] Add Unisoc's drm kms module Kevin Tang
2020-07-28 10:07 ` Kevin Tang
2020-07-28 10:07 ` [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings Kevin Tang
2020-07-28 10:07   ` Kevin Tang
2020-07-28 20:27   ` Sam Ravnborg
2020-07-28 20:27     ` Sam Ravnborg
2020-08-26 17:11     ` Kevin Tang
2020-08-26 17:11       ` Kevin Tang
2020-08-28 17:57       ` Sam Ravnborg
2020-08-28 17:57         ` Sam Ravnborg
2020-08-28 19:39   ` Rob Herring
2020-08-28 19:39     ` Rob Herring
2020-07-28 10:07 ` [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master Kevin Tang
2020-07-28 10:07   ` Kevin Tang
2020-07-28 12:25   ` Randy Dunlap
2020-07-28 12:25     ` Randy Dunlap
2020-07-28 20:45   ` Sam Ravnborg
2020-07-28 20:45     ` Sam Ravnborg
2020-08-28 16:04     ` Kevin Tang
2020-08-28 16:04       ` Kevin Tang
2020-08-28 17:55       ` Sam Ravnborg
2020-08-28 17:55         ` Sam Ravnborg
2020-07-28 10:07 ` [PATCH RFC v6 3/6] dt-bindings: display: add Unisoc's dpu bindings Kevin Tang
2020-07-28 10:07   ` Kevin Tang
2020-07-28 20:51   ` Sam Ravnborg
2020-07-28 20:51     ` Sam Ravnborg
2020-07-28 10:07 ` [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver Kevin Tang
2020-07-28 10:07   ` Kevin Tang
2020-07-28 21:13   ` Sam Ravnborg
2020-07-28 21:13     ` Sam Ravnborg
2020-08-28 17:08     ` Kevin Tang
2020-08-28 17:08       ` Kevin Tang
2020-11-17  3:07     ` Kevin Tang
2020-11-17  3:07       ` Kevin Tang
2020-07-28 21:51   ` Daniel Vetter
2020-07-28 21:51     ` Daniel Vetter
2020-08-29 17:10     ` Kevin Tang
2020-08-29 17:10       ` Kevin Tang
2021-01-05 11:09     ` Kevin Tang
2020-07-28 10:07 ` [PATCH RFC v6 5/6] dt-bindings: display: add Unisoc's mipi dsi&dphy bindings Kevin Tang
2020-07-28 10:07   ` Kevin Tang
2020-07-28 10:07 ` [PATCH RFC v6 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver Kevin Tang
2020-07-28 10:07   ` Kevin Tang
2020-07-28 16:33   ` kernel test robot
2020-07-28 10:12 ` [PATCH RFC v6 0/6] Add Unisoc's drm kms module Daniel Vetter
2020-07-28 10:12   ` Daniel Vetter
2020-08-04 17:29   ` Rob Herring
2020-08-04 17:29     ` Rob Herring
2020-07-28 21:20 ` Sam Ravnborg
2020-07-28 21:20   ` Sam Ravnborg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.