Skip to contentSkip to conversation

Add Unisoc's drm kms module

RFC PATCH Superseded Kevin Tang 6 participants 26 messages Jul 28, 2020 – Jan 6, 2021

Subsystems (4)

v6 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Kevin Tang

author
#0 lore raw

v6 cover Add Unisoc's drm kms module

From: Kevin Tang <[email protected]>

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
Signature · 1 line
2.7.4
author
#1 lore raw

RFC PATCH v6 1/6 dt-bindings: display: add Unisoc's drm master bindings

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +36 -0
new file mode 100644
index 0000000..b5792c0
@@ -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 <[email protected]>
+
+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>;
+    };
+
Signature · 1 line
2.7.4
#2 lore raw

Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings

Hi Kevin

Documentation/devicetree/bindings/display/sprd/drm.yaml

Quoted 19 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +0 -0
new file mode 100644
index 0000000..b5792c0

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).

Documentation/devicetree/bindings/display/sprd/drm.yaml line 1

Quoted 2 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +1 -0
@@ -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.

Quoted 13 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +13 -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 <[email protected]>
+
+description: |
+  The Unisoc DRM master device is a virtual device needed to list all
+  DPU devices or other display interface nodes that comprise the

Documentation/devicetree/bindings/display/sprd/drm.yaml line 24

Quoted 10 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +10 -0
+  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.

Documentation/devicetree/bindings/display/sprd/drm.yaml line 29

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +5 -0
+
+required:
+  - compatible
+  - ports
+

Add:
additionalProperties: false

so we catch if other properties sneak in.

Quoted 14 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +7 -0
+examples:
+  - |
+    display-subsystem {
+        compatible = "sprd,display-subsystem";
+        ports = <&dpu_out>;
+    };
+
Signature · 6 lines
2.7.4

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#3 lore raw

Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings

Quoted 18 lines · Sam Ravnborg <[email protected]> 于2020年7月29日周三 上午4:27写道:

Hi Kevin

Quoted 14 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../devicetree/bindings/display/sprd/drm.yaml      | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml
Quoted 8 lines

Documentation/devicetree/bindings/display/sprd/drm.yaml

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +0 -0
new file mode 100644
index 0000000..b5792c0

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

Quoted 7 lines

Documentation/devicetree/bindings/display/sprd/drm.yaml line 1

Quoted 3 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +1 -0
@@ -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.

Quoted 27 lines
Quoted 14 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +13 -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 <[email protected]>
+
+description: |
+  The Unisoc DRM master device is a virtual device needed to list all
+  DPU devices or other display interface nodes that comprise the

Documentation/devicetree/bindings/display/sprd/drm.yaml line 24

Quoted 10 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +10 -0
+  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

Quoted 5 lines

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

Quoted 26 lines

Documentation/devicetree/bindings/display/sprd/drm.yaml line 29

Quoted 6 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +5 -0
+
+required:
+  - compatible
+  - ports
+

Add:
additionalProperties: false

so we catch if other properties sneak in.

Quoted 14 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +7 -1
+examples:
+  - |
+    display-subsystem {
+        compatible = "sprd,display-subsystem";
+        ports = <&dpu_out>;
+    };
+
--

2.7.4

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#4 lore raw

Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings

Hi Kevin.

Quoted 7 lines
Quoted 4 lines

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...

Quoted 6 lines

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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#5 lore raw

Re: [PATCH RFC v6 1/6] dt-bindings: display: add Unisoc's drm master bindings

In reply to Kevin Tang
Quoted 7 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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

Quoted 7 lines
Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../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 [email protected]. Otherwise you may be waiting until the 6th version or later or never.

Documentation/devicetree/bindings/display/sprd/drm.yaml line 1

Quoted 8 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +1 -0
new file mode 100644
index 0000000..b5792c0
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0

New bindings should be dual licensed:

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

Documentation/devicetree/bindings/display/sprd/drm.yaml line 7

Quoted 6 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +6 -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

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

Documentation/devicetree/bindings/display/sprd/drm.yaml line 10

Quoted 3 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +3 -0
+
+maintainers:
+  - Mark Rutland <[email protected]>

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

Quoted 14 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +14 -0
+
+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.

Documentation/devicetree/bindings/display/sprd/drm.yaml line 34

Quoted 10 lines
Documentation/devicetree/bindings/display/sprd/drm.yaml new file +10 -0
+
+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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#6 lore raw

RFC PATCH v6 2/6 drm/sprd: add Unisoc's drm kms master

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/Kconfig +2 -0
index c4fd57d..7fe7ab91 100644
@@ -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
drivers/gpu/drm/Makefile +1 -0
index 2c0e5a7..ee2ccd9 100644
@@ -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/
drivers/gpu/drm/sprd/Kconfig new file +12 -0
new file mode 100644
index 0000000..b189a54
@@ -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.
+
drivers/gpu/drm/sprd/Makefile new file +5 -0
new file mode 100644
index 0000000..86d95d9
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +226 -0

226 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_drm.h new file +16 -0
new file mode 100644
index 0000000..edf0881
@@ -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_ */
Signature · 1 line
2.7.4
#7 lore raw

Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master

Quoted 6 lines · Kevin Tang
drivers/gpu/drm/sprd/Kconfig new file +0 -0
new file mode 100644
index 0000000..b189a54

drivers/gpu/drm/sprd/Kconfig line 10

Quoted 10 lines
drivers/gpu/drm/sprd/Kconfig new file +10 -0
@@ -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.

drivers/gpu/drm/sprd/Kconfig line 11

Quoted 1 line
drivers/gpu/drm/sprd/Kconfig new file +1 -0
+	  If M is selected the module will be called sprd-drm.

sprd_drm.

Quoted 12 lines
drivers/gpu/drm/sprd/Kconfig new file +1 -0
+
drivers/gpu/drm/sprd/Makefile new file +5 -0
new file mode 100644
index 0000000..86d95d9
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
Signature · 1 line
~Randy
#8 lore raw

Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master

In reply to Kevin Tang

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

Quoted 73 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/Kconfig +2 -0
index c4fd57d..7fe7ab91 100644
@@ -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
drivers/gpu/drm/Makefile +1 -0
index 2c0e5a7..ee2ccd9 100644
@@ -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/
drivers/gpu/drm/sprd/Kconfig new file +12 -0
new file mode 100644
index 0000000..b189a54
@@ -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.
+
drivers/gpu/drm/sprd/Makefile new file +0 -0
new file mode 100644
index 0000000..86d95d9

drivers/gpu/drm/sprd/Makefile line 3

Quoted 3 lines
drivers/gpu/drm/sprd/Makefile new file +3 -0
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)

subdir-ccflags-y is not needed - drop it.

Quoted 40 lines
drivers/gpu/drm/sprd/Makefile new file +2 -0
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +32 -0
new file mode 100644
index 0000000..4706185
@@ -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,
+};

drivers/gpu/drm/sprd/sprd_drm.c line 42

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+
+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().

Quoted 32 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +32 -0
+
+	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;

drivers/gpu/drm/sprd/sprd_drm.c line 84

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+
+	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.

drivers/gpu/drm/sprd/sprd_drm.c line 86

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +2 -0
+
+	drm->dev_private = sprd;

dev_private is deprecated. Alwyas use upclassing.

Quoted 8 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +8 -0
+
+	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;

drivers/gpu/drm/sprd/sprd_drm.c line 104

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+	}
+
+	/* 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.

Quoted 151 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +122 -0
+
+	/* 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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_drm.h new file +16 -0
new file mode 100644
index 0000000..edf0881
@@ -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_ */
Signature · 6 lines
2.7.4

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#9 lore raw

Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master

Quoted 11 lines · Sam Ravnborg <[email protected]> 于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.

Quoted 183 lines · Kevin Tang
Quoted 73 lines

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/Kconfig +2 -0
index c4fd57d..7fe7ab91 100644
@@ -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
drivers/gpu/drm/Makefile +1 -0
index 2c0e5a7..ee2ccd9 100644
@@ -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/
drivers/gpu/drm/sprd/Kconfig new file +12 -0
new file mode 100644
index 0000000..b189a54
@@ -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.
+
drivers/gpu/drm/sprd/Makefile new file +0 -0
new file mode 100644
index 0000000..86d95d9

drivers/gpu/drm/sprd/Makefile line 3

Quoted 3 lines
drivers/gpu/drm/sprd/Makefile new file +3 -0
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)

subdir-ccflags-y is not needed - drop it.

Quoted 40 lines
drivers/gpu/drm/sprd/Makefile new file +2 -0
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +32 -0
new file mode 100644
index 0000000..4706185
@@ -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,
+};

drivers/gpu/drm/sprd/sprd_drm.c line 42

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+
+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().

Quoted 32 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +32 -0
+
+     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;

drivers/gpu/drm/sprd/sprd_drm.c line 84

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+
+     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.

Quoted 3 lines

drivers/gpu/drm/sprd/sprd_drm.c line 86

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +2 -0
+
+     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?

Quoted 13 lines
Quoted 13 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +12 -0
+
+     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);
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_drm.c line 104

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +6 -0
+     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...

Quoted 151 lines
Quoted 151 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +122 -0
+
+     /* 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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_drm.h new file +16 -1
new file mode 100644
index 0000000..edf0881
@@ -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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#10 lore raw

Re: [PATCH RFC v6 2/6] drm/sprd: add Unisoc's drm kms master

Hi Kevin

Quoted 6 lines
Quoted 4 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+
+     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;

Quoted 11 lines
Quoted 6 lines

drivers/gpu/drm/sprd/sprd_drm.c line 104

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +3 -0
+     }
+     /* 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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#11 lore raw

RFC PATCH v6 3/6 dt-bindings: display: add Unisoc's dpu bindings

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../devicetree/bindings/display/sprd/dpu.yaml      | 82 ++++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +82 -0
new file mode 100644
index 0000000..54ba9b6f
@@ -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 <[email protected]>
+
+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>;
+                  };
+          };
+    };
Signature · 1 line
2.7.4
#12 lore raw

Re: [PATCH RFC v6 3/6] dt-bindings: display: add Unisoc's dpu bindings

Hi Kevin.

Quoted 11 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../devicetree/bindings/display/sprd/dpu.yaml      | 82 ++++++++++++++++++++++

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

Documentation/devicetree/bindings/display/sprd/dpu.yaml line 1

Quoted 10 lines
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +1 -0
new file mode 100644
index 0000000..54ba9b6f
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0

Can this be: (GPL-2.0-only OR BSD-2-Clause)

Quoted 29 lines
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +29 -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 <[email protected]>
+
+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
+

Documentation/devicetree/bindings/display/sprd/dpu.yaml line 40

Quoted 10 lines
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +10 -0
+  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

Documentation/devicetree/bindings/display/sprd/dpu.yaml line 43

Quoted 3 lines
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +3 -0
+
+  iommus:
+    description: A phandle to DPU iommu node.

maxItems: 1

Quoted 29 lines
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +29 -0
+
+  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";

Documentation/devicetree/bindings/display/sprd/dpu.yaml line 82

Quoted 10 lines
Documentation/devicetree/bindings/display/sprd/dpu.yaml new file +10 -0
+
+          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>
...

Quoted 7 lines
Signature · 6 lines
2.7.4

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#13 lore raw

RFC PATCH v6 4/6 drm/sprd: add Unisoc's drm display controller driver

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/sprd/Makefile +4 -1
index 86d95d9..88ab32a 100644
@@ -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/
drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
new file mode 100644
index 0000000..40278b6
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +503 -0

503 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dpu.c new file +646 -0

646 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dpu.h new file +187 -0

187 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index 4706185..200020f 100644
@@ -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)
drivers/gpu/drm/sprd/sprd_drm.h +2 -0
index edf0881..3c32f3a 100644
@@ -13,4 +13,6 @@ struct sprd_drm {
 	struct drm_device *drm;
 };
 
+extern struct platform_driver sprd_dpu_driver;
+
 #endif /* _SPRD_DRM_H_ */
Signature · 1 line
2.7.4
#14 lore raw

Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver

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

Quoted 32 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/sprd/Makefile +0 -0
index 86d95d9..88ab32a 100644

drivers/gpu/drm/sprd/Makefile line 3

Quoted 2 lines
drivers/gpu/drm/sprd/Makefile +0 -0
@@ -2,4 +2,7 @@
 
 subdir-ccflags-y += -I$(srctree)/$(src)

Not needed - drop.

drivers/gpu/drm/sprd/Makefile line 8

Quoted 6 lines
drivers/gpu/drm/sprd/Makefile +4 -1
-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/*

Quoted 25 lines
drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
new file mode 100644
index 0000000..40278b6
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
new file mode 100644
index 0000000..4b9521d
@@ -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>
+

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 20

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+#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.

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 30

Quoted 9 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +9 -0
+#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.

Quoted 75 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +75 -0
+
+/* 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)

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 115

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+{
+	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)

Quoted 158 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +158 -0
+
+		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);

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 283

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+}
+
+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.

Quoted 189 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +189 -0
+{
+	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);
+}
+

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 482

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+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.

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 485

Quoted 3 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +3 -0
+
+static void dpu_capability(struct dpu_context *ctx,
+			struct dpu_capability *cap)

Drop linebreak

Quoted 893 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +18 -0
+{
+	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,
+};
drivers/gpu/drm/sprd/sprd_dpu.c new file +646 -0
new file mode 100644
index 0000000..5ec8e7c
@@ -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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_dpu.h new file +187 -0
new file mode 100644
index 0000000..7d3c5e4
@@ -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
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index 4706185..200020f 100644
@@ -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)
drivers/gpu/drm/sprd/sprd_drm.h +2 -0
index edf0881..3c32f3a 100644
@@ -13,4 +13,6 @@ struct sprd_drm {
 	struct drm_device *drm;
 };
 
+extern struct platform_driver sprd_dpu_driver;
+
 #endif /* _SPRD_DRM_H_ */
Signature · 6 lines
2.7.4

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#15 lore raw

Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver

Quoted 47 lines · Sam Ravnborg <[email protected]> 于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

Quoted 32 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/sprd/Makefile +0 -0
index 86d95d9..88ab32a 100644

drivers/gpu/drm/sprd/Makefile line 3

Quoted 2 lines
drivers/gpu/drm/sprd/Makefile +0 -0
@@ -2,4 +2,7 @@
 
 subdir-ccflags-y += -I$(srctree)/$(src)

Not needed - drop.

Quoted 1 line
Quoted 8 lines

drivers/gpu/drm/sprd/Makefile line 8

Quoted 5 lines
drivers/gpu/drm/sprd/Makefile +4 -1
-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

Quoted 49 lines
Quoted 26 lines
drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
new file mode 100644
index 0000000..40278b6
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
new file mode 100644
index 0000000..4b9521d
@@ -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>
+

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 20

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+#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.

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 30

Quoted 9 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +9 -0
+#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.

Quoted 2 lines

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);
}
Quoted 95 lines

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.

Quoted 75 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +75 -0
+
+/* 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)

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 115

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+{
+     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.

Quoted 5 lines

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.

Quoted 1,271 lines
Quoted 159 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +158 -0
+
+             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);

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 283

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+}
+
+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.

Quoted 189 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +189 -0
+{
+     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);
+}
+

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 482

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+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.

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 485

Quoted 3 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +3 -0
+
+static void dpu_capability(struct dpu_context *ctx,
+                     struct dpu_capability *cap)

Drop linebreak

Quoted 893 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +18 -0
+{
+     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,
+};
drivers/gpu/drm/sprd/sprd_dpu.c new file +646 -0
new file mode 100644
index 0000000..5ec8e7c
@@ -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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_dpu.h new file +187 -0
new file mode 100644
index 0000000..7d3c5e4
@@ -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
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index 4706185..200020f 100644
@@ -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)
drivers/gpu/drm/sprd/sprd_drm.h +2 -1
index edf0881..3c32f3a 100644
@@ -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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#16 lore raw

Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver

In reply to Sam Ravnborg

Hi Sam,

Here is feedback of RFC V7

Quoted 37 lines · Sam Ravnborg <[email protected]> 于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

Quoted 27 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
Quoted 8 lines

drivers/gpu/drm/sprd/Makefile line 3

Quoted 7 lines
drivers/gpu/drm/sprd/Makefile +0 -0
index 86d95d9..88ab32a 100644
@@ -2,4 +2,7 @@
 
 subdir-ccflags-y += -I$(srctree)/$(src)

Not needed - drop.

It has been fixed on RFC V7.

Quoted 10 lines

drivers/gpu/drm/sprd/Makefile line 8

Quoted 7 lines
drivers/gpu/drm/sprd/Makefile +4 -1
-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.

Quoted 29 lines
Quoted 29 lines
drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
new file mode 100644
index 0000000..40278b6
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +13 -0
new file mode 100644
index 0000000..4b9521d
@@ -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) */
Quoted 8 lines

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 20

Quoted 7 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +7 -0
+#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.

Quoted 12 lines

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 30

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +9 -0
+#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.

Quoted 2 lines

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.

Quoted 6 lines

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

Quoted 89 lines
Quoted 76 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +75 -0
+
+/* 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)

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 115

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+{
+     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.

Quoted 5 lines

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?

Quoted 162 lines
Quoted 162 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +161 -0
+
+             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)
Quoted 8 lines

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 283

Quoted 7 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +7 -0
+{
+     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.

Quoted 194 lines
Quoted 194 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +193 -0
+{
+     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,
Quoted 8 lines

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 482

Quoted 6 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +6 -0
+     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.

Quoted 5 lines

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 485

Quoted 4 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +3 -0
+
+static void dpu_capability(struct dpu_context *ctx,
+                     struct dpu_capability *cap)

Drop linebreak

It has been fixed on RFC V7.

Quoted 894 lines
Quoted 894 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +18 -0
+{
+     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,
+};
drivers/gpu/drm/sprd/sprd_dpu.c new file +646 -0
new file mode 100644
index 0000000..5ec8e7c
@@ -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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_dpu.h new file +187 -0
new file mode 100644
index 0000000..7d3c5e4
@@ -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
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index 4706185..200020f 100644
@@ -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)
drivers/gpu/drm/sprd/sprd_drm.h +2 -1
index edf0881..3c32f3a 100644
@@ -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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#17 lore raw

Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver

In reply to Kevin Tang
Quoted 6 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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

Quoted 8 lines

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

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

Quoted 1,416 lines
---
 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
drivers/gpu/drm/sprd/Makefile +4 -1
index 86d95d9..88ab32a 100644
@@ -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/
drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
new file mode 100644
index 0000000..40278b6
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +503 -0
new file mode 100644
index 0000000..4b9521d
@@ -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,
+};
drivers/gpu/drm/sprd/sprd_dpu.c new file +646 -0
new file mode 100644
index 0000000..5ec8e7c
@@ -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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_dpu.h new file +187 -0
new file mode 100644
index 0000000..7d3c5e4
@@ -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
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index 4706185..200020f 100644
@@ -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)
drivers/gpu/drm/sprd/sprd_drm.h +2 -1
index edf0881..3c32f3a 100644
@@ -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

Signature · 7 lines
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
author
#18 lore raw

Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver

Quoted 17 lines · Daniel Vetter <[email protected]> 于2020年7月29日周三 上午5:51写道:
Quoted 6 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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

Quoted 8 lines

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
Quoted 8 lines

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?

Quoted 6 lines

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.

Quoted 1,431 lines

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

Quoted 1,414 lines
---
 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
drivers/gpu/drm/sprd/Makefile +4 -1
index 86d95d9..88ab32a 100644
@@ -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/
drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
new file mode 100644
index 0000000..40278b6
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +503 -0
new file mode 100644
index 0000000..4b9521d
@@ -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,
+};
drivers/gpu/drm/sprd/sprd_dpu.c new file +646 -0
new file mode 100644
index 0000000..5ec8e7c
@@ -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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_dpu.h new file +187 -0
new file mode 100644
index 0000000..7d3c5e4
@@ -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
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index 4706185..200020f 100644
@@ -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)
drivers/gpu/drm/sprd/sprd_drm.h +2 -1
index edf0881..3c32f3a 100644
@@ -13,4 +13,6 @@ struct sprd_drm {
        struct drm_device *drm;
 };
 
+extern struct platform_driver sprd_dpu_driver;
+
 #endif /* _SPRD_DRM_H_ */
--
Quoted 2 lines

2.7.4

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

author
#19 lore raw

Re: [PATCH RFC v6 4/6] drm/sprd: add Unisoc's drm display controller driver

In reply to Daniel Vetter
Quoted 34 lines · Daniel Vetter <[email protected]> 于2020年7月29日周三 上午5:51写道:
Quoted 4 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds DPU(Display Processor Unit) support for the Unisoc's display

subsystem.

Quoted 1 line

It's support multi planes, scaler, rotation, PQ(Picture Quality) and

more.

Quoted 4 lines

RFC v6:
- Access registers via readl/writel
- Checking for unsupported KMS properties (format, rotation,

blend_mode, etc) on plane_check ops

Quoted 5 lines

- Remove always true checks for dpu core ops

Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>

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.

Quoted 8 lines

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

Quoted 1,458 lines
Quoted 6 lines
---
 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

++++++++++++++++++++++++++++++++++++

Review

Quoted 10 lines
 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
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/Makefile

Review

Quoted 13 lines

index 86d95d9..88ab32a 100644

drivers/gpu/drm/sprd/Makefile +4 -1
@@ -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/
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/dpu/Makefile

Review

Quoted 9 lines

new file mode 100644
index 0000000..40278b6

drivers/gpu/drm/sprd/dpu/Makefile new file +3 -0
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dpu_r2p0.o
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 113 lines

new file mode 100644
index 0000000..4b9521d

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +108 -0
@@ -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 |

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 118

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+                       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",

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 119

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                               DPU_REG_RD(ctx->base +

REG_MMU_INV_ADDR_RD));

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 121

Quoted 2 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +2 -0
+               if (val & BIT_DPU_INT_MMU_INV_WR)
+                       DRM_ERROR("iommu invalid write error, addr:

0x%08x\n",

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 122

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                               DPU_REG_RD(ctx->base +

REG_MMU_INV_ADDR_WR));

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 124

Quoted 2 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +2 -0
+               if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                       DRM_ERROR("iommu va out of range read error,

addr: 0x%08x\n",

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 125

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                               DPU_REG_RD(ctx->base +

REG_MMU_VAOR_ADDR_RD));

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 127

Quoted 2 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +2 -0
+               if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                       DRM_ERROR("iommu va out of range write error,

addr: 0x%08x\n",

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 128

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                               DPU_REG_RD(ctx->base +

REG_MMU_VAOR_ADDR_WR));

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 3 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +3 -0
+
+               for (i = 0; i < 8; i++) {
+                       reg_val = DPU_REG_RD(ctx->base +

DPU_LAY_REG(REG_LAY_CTRL, i));

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 133

Quoted 2 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +2 -0
+                       if (reg_val & 0x1)
+                               DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x

ctrl: 0x%08x\n", i,

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 134

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                                       DPU_REG_RD(ctx->base +

DPU_LAY_REG(REG_LAY_BASE_ADDR0, i)),

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 135

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                                       DPU_REG_RD(ctx->base +

DPU_LAY_REG(REG_LAY_BASE_ADDR1, i)),

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 136

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                                       DPU_REG_RD(ctx->base +

DPU_LAY_REG(REG_LAY_BASE_ADDR2, i)),

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 137

Quoted 1 line
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +1 -0
+                                       DPU_REG_RD(ctx->base +

DPU_LAY_REG(REG_LAY_CTRL, i)));

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 12 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +12 -0
+               }
+       }
+
+       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);

Quoted 42 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +42 -0
+}
+
+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;

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+}
+
+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,

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+                                              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;
+       }
+

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+       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,

Quoted 27 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +27 -0
+                                              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)
+{

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+       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;

Quoted 24 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +24 -0
+       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,

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+                   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);

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c line 294

Quoted 2 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +2 -0
+       else
+               size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) <<

16);

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 11 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +11 -0
+
+       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);

Quoted 19 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +19 -0
+       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;

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+
+       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",

Quoted 22 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +22 -0
+                               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 */

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+       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);

Quoted 25 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +25 -0
+                       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)

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+{
+       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);

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 3 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +3 -0
+
+               /* select te from external pad */
+               DPU_REG_SET(ctx->base + REG_DPI_CTRL,

BIT_DPU_EDPI_FROM_EXTERNAL_PAD);

Quoted 22 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +22 -0
+
+               /* 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;

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 10 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +10 -0
+               /* 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);

drivers/gpu/drm/sprd/dpu/dpu_r2p0.c

Quoted 3 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +3 -0
+
+               /* enable te */
+               DPU_REG_SET(ctx->base + REG_DPI_CTRL,

BIT_DPU_EDPI_TE_EN);

Review

Quoted 65 lines
drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file +64 -0
+
+               /* 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,
+};
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 209 lines

new file mode 100644
index 0000000..5ec8e7c

drivers/gpu/drm/sprd/sprd_dpu.c new file +204 -0
@@ -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 */

drivers/gpu/drm/sprd/sprd_dpu.c line 214

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+               /* 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",

Quoted 38 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +38 -0
+                               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;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+       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)

Quoted 22 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +22 -0
+{
+       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 = {

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+       .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,

Quoted 16 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +16 -0
+       .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)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+               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,

drivers/gpu/drm/sprd/sprd_dpu.c line 322

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+                                              cap.fmts_cnt, NULL,
+                                              DRM_PLANE_TYPE_PRIMARY,

NULL);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+               if (ret) {
+                       DRM_ERROR("fail to init primary plane\n");
+                       return ERR_PTR(ret);
+               }
+
+               drm_plane_helper_add(&p->plane,

&sprd_plane_helper_funcs);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 13 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +13 -0
+
+               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)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+{
+       struct sprd_dpu *dpu = crtc_to_dpu(crtc);
+
+       DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__,

DRM_MODE_ARG(mode));

Quoted 46 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +46 -0
+
+       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__);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+       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);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 13 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +13 -0
+
+       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);

Quoted 100 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +100 -0
+
+       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);
+

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+       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)

Review

Quoted 123 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +122 -0
+{
+       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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_dpu.h

Quoted 132 lines

new file mode 100644
index 0000000..7d3c5e4

drivers/gpu/drm/sprd/sprd_dpu.h new file +127 -0
@@ -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);
+};

drivers/gpu/drm/sprd/sprd_dpu.h line 137

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.h new file +10 -0
+
+/**
+ * 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

Review

Quoted 51 lines
drivers/gpu/drm/sprd/sprd_dpu.h new file +50 -0
+ * 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
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.c

Review

Quoted 12 lines

index 4706185..200020f 100644

drivers/gpu/drm/sprd/sprd_drm.c +1 -0
@@ -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)
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.h

Quoted 7 lines

index edf0881..3c32f3a 100644

drivers/gpu/drm/sprd/sprd_drm.h +0 -0
@@ -13,4 +13,6 @@ struct sprd_drm {
        struct drm_device *drm;
 };
 
Quoted 13 lines
Quoted 6 lines
drivers/gpu/drm/sprd/sprd_drm.h +2 -1
+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
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#20 lore raw

RFC PATCH v6 5/6 dt-bindings: display: add Unisoc's mipi dsi&dphy bindings

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

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

Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 .../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
Documentation/devicetree/bindings/display/sprd/dphy.yaml new file +75 -0
new file mode 100644
index 0000000..1b83260
@@ -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 <[email protected]>
+
+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>;
+    	      };
+        };
+    };
Documentation/devicetree/bindings/display/sprd/dsi.yaml new file +98 -0

98 lines not on this page · raw message

Signature · 1 line
2.7.4
author
#21 lore raw

RFC PATCH v6 6/6 drm/sprd: add Unisoc's drm mipi dsi&dphy driver

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>
---
 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
drivers/gpu/drm/sprd/Makefile +6 -1
index 88ab32a..c78565d 100644
@@ -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/
drivers/gpu/drm/sprd/disp_lib.c new file +57 -0

57 lines not on this page · raw message

drivers/gpu/drm/sprd/disp_lib.h new file +16 -0
new file mode 100644
index 0000000..adbd239
@@ -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_ */
drivers/gpu/drm/sprd/dphy/Makefile new file +7 -0
new file mode 100644
index 0000000..6637c27
@@ -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/
drivers/gpu/drm/sprd/dphy/pll/Makefile new file +3 -0
new file mode 100644
index 0000000..59e74ec
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += megacores_sharkle.o
drivers/gpu/drm/sprd/dphy/pll/megacores_sharkle.c new file +473 -0

473 lines not on this page · raw message

drivers/gpu/drm/sprd/dphy/sprd_dphy_api.c new file +201 -0

201 lines not on this page · raw message

drivers/gpu/drm/sprd/dphy/sprd_dphy_api.h new file +22 -0
new file mode 100644
index 0000000..5def380
@@ -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
drivers/gpu/drm/sprd/dsi/Makefile new file +8 -0
new file mode 100644
index 0000000..d95189c
@@ -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/
+
drivers/gpu/drm/sprd/dsi/core/Makefile new file +4 -0
new file mode 100644
index 0000000..41f6477
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += dsi_ctrl_r1p0.o dsi_ctrl_r1p0_ppi.o
+
drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.c new file +964 -0

964 lines not on this page · raw message

drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0.h new file +1477 -0

1,232 lines not on this page · raw message

drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.c new file +328 -0

328 lines not on this page · raw message

drivers/gpu/drm/sprd/dsi/core/dsi_ctrl_r1p0_ppi.h new file +32 -0
new file mode 100644
index 0000000..7477604

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/sprd/dsi/sprd_dsi_api.c new file +590 -0

590 lines not on this page · raw message

drivers/gpu/drm/sprd/dsi/sprd_dsi_api.h new file +26 -0
new file mode 100644
index 0000000..91eef86

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/sprd/sprd_dphy.c new file +209 -0

209 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dphy.h new file +50 -0

50 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dpu.c +22 -0
index 5ec8e7c..ebea25f 100644

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/sprd/sprd_dpu.h +3 -0
index 7d3c5e4..83a2a86 100644

Lines of this file are not shown. View the raw message.

drivers/gpu/drm/sprd/sprd_dsi.c new file +571 -0

571 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dsi.h new file +108 -0

108 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.7.4

Part of the diff is not shown here. View the raw message.

#22 lore raw

Re: [PATCH RFC v6 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver

[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]

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

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

#23 lore raw
In reply to Kevin Tang
Quoted 2 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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

Quoted 120 lines

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

Signature · 7 lines
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
#24 lore raw
Quoted 6 lines · Daniel Vetter
Quoted 2 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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.

Quoted 130 lines

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

Quoted 117 lines

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
Quoted 3 lines

--
2.7.4

Quoted 4 lines

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

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#25 lore raw
In reply to Kevin Tang

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

Quoted 109 lines · Kevin Tang

From: Kevin Tang <[email protected]>

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
Quoted 16 lines
 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
Signature · 6 lines
2.7.4

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel