All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] Initial Allwinner Display Engine 2.0 Support
@ 2017-02-22 15:18 ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng

This patchset is the initial patchset for Allwinner DE2 support.

It contains the support of clocks in DE2 and the mixers in DE2.

The SoC used to develop this patchset is V3s, as V3s is the simplest
one of the SoCs that have DE2.
(Allwinner V3s features only one mixer, although its clock control
unit contains support for second mixer's clock; and its only video
output is RGB LCD, which is already supported in our TCON driver)

The last patch is only a testing patch, it shouldn't be merged; and
for the patch to be really usable, the RFC fix of the TCON driver [1]
is needed.

No HDMI, TV encoder or other internal bridges' support is included
in this patchset, which makes it currently not usable on H3.

Thanks to Jean-Francois Moine and Jernej Skrabec for their efforts
to discover the internal of DE2!

[1] https://lists.freedesktop.org/archives/dri-devel/2016-December/126264.html

Icenowy Zheng (8):
  dt-bindings: add binding for the Allwinner DE2 CCU
  clk: sunxi-ng: add support for DE2 CCU
  dt-bindings: add bindings for DE2 on V3s SoC
  drm/sun4i: add support for sun8i DE2 mixers and display engines
  drm/sun4i: tcon: add support for V3s TCON
  ARM: dts: sun8i: add DE2 nodes for V3s SoC
  ARM: dts: sun8i: add pinmux for LCD pins of V3s SoC
  [DO NOT MERGE] ARM: dts: sun8i: enable LCD panel of Lichee Pi Zero

 .../devicetree/bindings/clock/sunxi-de2.txt        |  31 ++
 .../bindings/display/sunxi/sun4i-drm.txt           |  37 +-
 arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts      |  36 ++
 arch/arm/boot/dts/sun8i-v3s.dtsi                   |  96 +++++
 drivers/clk/sunxi-ng/Kconfig                       |   6 +
 drivers/clk/sunxi-ng/Makefile                      |   1 +
 drivers/clk/sunxi-ng/ccu-sunxi-de2.c               | 204 ++++++++++
 drivers/clk/sunxi-ng/ccu-sunxi-de2.h               |  28 ++
 drivers/gpu/drm/sun4i/Kconfig                      |   8 +
 drivers/gpu/drm/sun4i/Makefile                     |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c                 |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c                  |  41 +-
 drivers/gpu/drm/sun4i/sun4i_drv.h                  |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c                |  92 ++++-
 drivers/gpu/drm/sun4i/sun4i_layer.h                |   1 +
 drivers/gpu/drm/sun4i/sun4i_tcon.c                 |   5 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c                | 417 +++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h                | 133 +++++++
 include/dt-bindings/clock/sunxi-de2.h              |  54 +++
 include/dt-bindings/reset/sunxi-de2.h              |  50 +++
 20 files changed, 1220 insertions(+), 28 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi-de2.txt
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.h
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
 create mode 100644 include/dt-bindings/clock/sunxi-de2.h
 create mode 100644 include/dt-bindings/reset/sunxi-de2.h

-- 
2.11.1

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

* [PATCH 0/8] Initial Allwinner Display Engine 2.0 Support
@ 2017-02-22 15:18 ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, devicetree, linux-kernel, dri-devel,
	linux-sunxi, Icenowy Zheng, linux-clk, linux-arm-kernel

This patchset is the initial patchset for Allwinner DE2 support.

It contains the support of clocks in DE2 and the mixers in DE2.

The SoC used to develop this patchset is V3s, as V3s is the simplest
one of the SoCs that have DE2.
(Allwinner V3s features only one mixer, although its clock control
unit contains support for second mixer's clock; and its only video
output is RGB LCD, which is already supported in our TCON driver)

The last patch is only a testing patch, it shouldn't be merged; and
for the patch to be really usable, the RFC fix of the TCON driver [1]
is needed.

No HDMI, TV encoder or other internal bridges' support is included
in this patchset, which makes it currently not usable on H3.

Thanks to Jean-Francois Moine and Jernej Skrabec for their efforts
to discover the internal of DE2!

[1] https://lists.freedesktop.org/archives/dri-devel/2016-December/126264.html

Icenowy Zheng (8):
  dt-bindings: add binding for the Allwinner DE2 CCU
  clk: sunxi-ng: add support for DE2 CCU
  dt-bindings: add bindings for DE2 on V3s SoC
  drm/sun4i: add support for sun8i DE2 mixers and display engines
  drm/sun4i: tcon: add support for V3s TCON
  ARM: dts: sun8i: add DE2 nodes for V3s SoC
  ARM: dts: sun8i: add pinmux for LCD pins of V3s SoC
  [DO NOT MERGE] ARM: dts: sun8i: enable LCD panel of Lichee Pi Zero

 .../devicetree/bindings/clock/sunxi-de2.txt        |  31 ++
 .../bindings/display/sunxi/sun4i-drm.txt           |  37 +-
 arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts      |  36 ++
 arch/arm/boot/dts/sun8i-v3s.dtsi                   |  96 +++++
 drivers/clk/sunxi-ng/Kconfig                       |   6 +
 drivers/clk/sunxi-ng/Makefile                      |   1 +
 drivers/clk/sunxi-ng/ccu-sunxi-de2.c               | 204 ++++++++++
 drivers/clk/sunxi-ng/ccu-sunxi-de2.h               |  28 ++
 drivers/gpu/drm/sun4i/Kconfig                      |   8 +
 drivers/gpu/drm/sun4i/Makefile                     |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c                 |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c                  |  41 +-
 drivers/gpu/drm/sun4i/sun4i_drv.h                  |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c                |  92 ++++-
 drivers/gpu/drm/sun4i/sun4i_layer.h                |   1 +
 drivers/gpu/drm/sun4i/sun4i_tcon.c                 |   5 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c                | 417 +++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h                | 133 +++++++
 include/dt-bindings/clock/sunxi-de2.h              |  54 +++
 include/dt-bindings/reset/sunxi-de2.h              |  50 +++
 20 files changed, 1220 insertions(+), 28 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi-de2.txt
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.h
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
 create mode 100644 include/dt-bindings/clock/sunxi-de2.h
 create mode 100644 include/dt-bindings/reset/sunxi-de2.h

-- 
2.11.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 0/8] Initial Allwinner Display Engine 2.0 Support
@ 2017-02-22 15:18 ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset is the initial patchset for Allwinner DE2 support.

It contains the support of clocks in DE2 and the mixers in DE2.

The SoC used to develop this patchset is V3s, as V3s is the simplest
one of the SoCs that have DE2.
(Allwinner V3s features only one mixer, although its clock control
unit contains support for second mixer's clock; and its only video
output is RGB LCD, which is already supported in our TCON driver)

The last patch is only a testing patch, it shouldn't be merged; and
for the patch to be really usable, the RFC fix of the TCON driver [1]
is needed.

No HDMI, TV encoder or other internal bridges' support is included
in this patchset, which makes it currently not usable on H3.

Thanks to Jean-Francois Moine and Jernej Skrabec for their efforts
to discover the internal of DE2!

[1] https://lists.freedesktop.org/archives/dri-devel/2016-December/126264.html

Icenowy Zheng (8):
  dt-bindings: add binding for the Allwinner DE2 CCU
  clk: sunxi-ng: add support for DE2 CCU
  dt-bindings: add bindings for DE2 on V3s SoC
  drm/sun4i: add support for sun8i DE2 mixers and display engines
  drm/sun4i: tcon: add support for V3s TCON
  ARM: dts: sun8i: add DE2 nodes for V3s SoC
  ARM: dts: sun8i: add pinmux for LCD pins of V3s SoC
  [DO NOT MERGE] ARM: dts: sun8i: enable LCD panel of Lichee Pi Zero

 .../devicetree/bindings/clock/sunxi-de2.txt        |  31 ++
 .../bindings/display/sunxi/sun4i-drm.txt           |  37 +-
 arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts      |  36 ++
 arch/arm/boot/dts/sun8i-v3s.dtsi                   |  96 +++++
 drivers/clk/sunxi-ng/Kconfig                       |   6 +
 drivers/clk/sunxi-ng/Makefile                      |   1 +
 drivers/clk/sunxi-ng/ccu-sunxi-de2.c               | 204 ++++++++++
 drivers/clk/sunxi-ng/ccu-sunxi-de2.h               |  28 ++
 drivers/gpu/drm/sun4i/Kconfig                      |   8 +
 drivers/gpu/drm/sun4i/Makefile                     |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c                 |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c                  |  41 +-
 drivers/gpu/drm/sun4i/sun4i_drv.h                  |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c                |  92 ++++-
 drivers/gpu/drm/sun4i/sun4i_layer.h                |   1 +
 drivers/gpu/drm/sun4i/sun4i_tcon.c                 |   5 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c                | 417 +++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h                | 133 +++++++
 include/dt-bindings/clock/sunxi-de2.h              |  54 +++
 include/dt-bindings/reset/sunxi-de2.h              |  50 +++
 20 files changed, 1220 insertions(+), 28 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi-de2.txt
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.h
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
 create mode 100644 include/dt-bindings/clock/sunxi-de2.h
 create mode 100644 include/dt-bindings/reset/sunxi-de2.h

-- 
2.11.1

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

* [PATCH 1/8] dt-bindings: add binding for the Allwinner DE2 CCU
  2017-02-22 15:18 ` Icenowy Zheng
  (?)
@ 2017-02-22 15:18     ` Icenowy Zheng
  -1 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng

Allwinner "Display Engine 2.0" contains some clock controls in it.

Add them as a clock driver.

Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
---
 .../devicetree/bindings/clock/sunxi-de2.txt        | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi-de2.txt

diff --git a/Documentation/devicetree/bindings/clock/sunxi-de2.txt b/Documentation/devicetree/bindings/clock/sunxi-de2.txt
new file mode 100644
index 000000000000..c19496f037a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunxi-de2.txt
@@ -0,0 +1,31 @@
+Allwinner Display Engine 2.0 Clock Control Binding
+--------------------------------------------------
+
+Required properties :
+- compatible: must contain one of the following compatibles:
+		- "allwinner,sun8i-a83t-de2-clk"
+		- "allwinner,sun50i-a64-de2-clk"
+		- "allwinner,sun50i-h5-de2-clk"
+
+- reg: Must contain the registers base address and length
+- clocks: phandle to the clocks feeding the display engine subsystem.
+	  Three are needed:
+  - "mod": the display engine module clock
+  - "bus": the bus clock for the whole display engine subsystem
+- clock-names: Must contain the clock names described just above
+- resets: phandle to the reset control for the display engine subsystem.
+- #clock-cells : must contain 1
+- #reset-cells : must contain 1
+
+Example:
+de2_clocks: clock@01000000 {
+	compatible = "allwinner,sun50i-a64-de2-clk";
+	reg = <0x01000000 0x1c>;
+	clocks = <&ccu CLK_DE>,
+		 <&ccu CLK_BUS_DE>;
+	clock-names = "mod",
+		      "bus";
+	resets = <&ccu RST_BUS_DE>;
+	#clock-cells = <1>;
+	#reset-cells = <1>;
+};
-- 
2.11.1

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

* [PATCH 1/8] dt-bindings: add binding for the Allwinner DE2 CCU
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, devicetree, linux-kernel, dri-devel,
	linux-sunxi, Icenowy Zheng, linux-clk, linux-arm-kernel

Allwinner "Display Engine 2.0" contains some clock controls in it.

Add them as a clock driver.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 .../devicetree/bindings/clock/sunxi-de2.txt        | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi-de2.txt

diff --git a/Documentation/devicetree/bindings/clock/sunxi-de2.txt b/Documentation/devicetree/bindings/clock/sunxi-de2.txt
new file mode 100644
index 000000000000..c19496f037a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunxi-de2.txt
@@ -0,0 +1,31 @@
+Allwinner Display Engine 2.0 Clock Control Binding
+--------------------------------------------------
+
+Required properties :
+- compatible: must contain one of the following compatibles:
+		- "allwinner,sun8i-a83t-de2-clk"
+		- "allwinner,sun50i-a64-de2-clk"
+		- "allwinner,sun50i-h5-de2-clk"
+
+- reg: Must contain the registers base address and length
+- clocks: phandle to the clocks feeding the display engine subsystem.
+	  Three are needed:
+  - "mod": the display engine module clock
+  - "bus": the bus clock for the whole display engine subsystem
+- clock-names: Must contain the clock names described just above
+- resets: phandle to the reset control for the display engine subsystem.
+- #clock-cells : must contain 1
+- #reset-cells : must contain 1
+
+Example:
+de2_clocks: clock@01000000 {
+	compatible = "allwinner,sun50i-a64-de2-clk";
+	reg = <0x01000000 0x1c>;
+	clocks = <&ccu CLK_DE>,
+		 <&ccu CLK_BUS_DE>;
+	clock-names = "mod",
+		      "bus";
+	resets = <&ccu RST_BUS_DE>;
+	#clock-cells = <1>;
+	#reset-cells = <1>;
+};
-- 
2.11.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 1/8] dt-bindings: add binding for the Allwinner DE2 CCU
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: linux-arm-kernel

Allwinner "Display Engine 2.0" contains some clock controls in it.

Add them as a clock driver.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 .../devicetree/bindings/clock/sunxi-de2.txt        | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi-de2.txt

diff --git a/Documentation/devicetree/bindings/clock/sunxi-de2.txt b/Documentation/devicetree/bindings/clock/sunxi-de2.txt
new file mode 100644
index 000000000000..c19496f037a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunxi-de2.txt
@@ -0,0 +1,31 @@
+Allwinner Display Engine 2.0 Clock Control Binding
+--------------------------------------------------
+
+Required properties :
+- compatible: must contain one of the following compatibles:
+		- "allwinner,sun8i-a83t-de2-clk"
+		- "allwinner,sun50i-a64-de2-clk"
+		- "allwinner,sun50i-h5-de2-clk"
+
+- reg: Must contain the registers base address and length
+- clocks: phandle to the clocks feeding the display engine subsystem.
+	  Three are needed:
+  - "mod": the display engine module clock
+  - "bus": the bus clock for the whole display engine subsystem
+- clock-names: Must contain the clock names described just above
+- resets: phandle to the reset control for the display engine subsystem.
+- #clock-cells : must contain 1
+- #reset-cells : must contain 1
+
+Example:
+de2_clocks: clock at 01000000 {
+	compatible = "allwinner,sun50i-a64-de2-clk";
+	reg = <0x01000000 0x1c>;
+	clocks = <&ccu CLK_DE>,
+		 <&ccu CLK_BUS_DE>;
+	clock-names = "mod",
+		      "bus";
+	resets = <&ccu RST_BUS_DE>;
+	#clock-cells = <1>;
+	#reset-cells = <1>;
+};
-- 
2.11.1

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

* [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
  2017-02-22 15:18 ` Icenowy Zheng
  (?)
@ 2017-02-22 15:18     ` Icenowy Zheng
  -1 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng

The "Display Engine 2.0" in Allwinner newer SoCs contains a clock
management unit for its subunits, like the DE CCU in A80.

Add a sunxi-ng style driver for it.

Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
---
 drivers/clk/sunxi-ng/Kconfig          |   6 +
 drivers/clk/sunxi-ng/Makefile         |   1 +
 drivers/clk/sunxi-ng/ccu-sunxi-de2.c  | 204 ++++++++++++++++++++++++++++++++++
 drivers/clk/sunxi-ng/ccu-sunxi-de2.h  |  28 +++++
 include/dt-bindings/clock/sunxi-de2.h |  54 +++++++++
 include/dt-bindings/reset/sunxi-de2.h |  50 +++++++++
 6 files changed, 343 insertions(+)
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.h
 create mode 100644 include/dt-bindings/clock/sunxi-de2.h
 create mode 100644 include/dt-bindings/reset/sunxi-de2.h

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 695bbf9ef428..52b88b22ec67 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -141,4 +141,10 @@ config SUN9I_A80_CCU
 	select SUNXI_CCU_PHASE
 	default MACH_SUN9I
 
+config SUNXI_DE2_CCU
+	bool "Support for the Allwinner SoCs DE2 CCU"
+	select SUNXI_CCU_DIV
+	select SUNXI_CCU_GATE
+	default n
+
 endif
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 6feaac0c5600..1d36c40384a6 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
+obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o
diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
new file mode 100644
index 000000000000..4259c145d5da
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "ccu_common.h"
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_reset.h"
+
+#include "ccu-sunxi-de2.h"
+
+static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
+		      0x04, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
+		      0x04, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
+		      0x04, BIT(2), 0);
+
+static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
+		      0x00, BIT(0), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
+		      0x00, BIT(1), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
+		      0x00, BIT(2), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
+		   CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
+		   CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
+		   CLK_SET_RATE_PARENT);
+
+static struct ccu_common *sunxi_de2_clks[] = {
+	&mixer0_clk.common,
+	&mixer1_clk.common,
+	&wb_clk.common,
+
+	&bus_mixer0_clk.common,
+	&bus_mixer1_clk.common,
+	&bus_wb_clk.common,
+
+	&mixer0_div_clk.common,
+	&mixer1_div_clk.common,
+	&wb_div_clk.common,
+};
+
+static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
+	.hws	= {
+		[CLK_MIXER0]		= &mixer0_clk.common.hw,
+		[CLK_MIXER1]		= &mixer1_clk.common.hw,
+		[CLK_WB]		= &wb_clk.common.hw,
+
+		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
+		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
+		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
+
+		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
+		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
+		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
+	},
+	.num	= CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
+	[RST_MIXER0]	= { 0x08, BIT(0) },
+	/*
+	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
+	 * only RST_WB is exported here.
+	 */
+	[RST_WB]	= { 0x08, BIT(2) },
+};
+
+static struct ccu_reset_map sun50i_a64_de2_resets[] = {
+	[RST_MIXER0]	= { 0x08, BIT(0) },
+	[RST_MIXER1]	= { 0x08, BIT(1) },
+	[RST_WB]	= { 0x08, BIT(2) },
+};
+
+static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
+	.ccu_clks	= sunxi_de2_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
+
+	.hw_clks	= &sunxi_de2_hw_clks,
+
+	.resets		= sun8i_a83t_de2_resets,
+	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
+};
+
+static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
+	.ccu_clks	= sunxi_de2_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
+
+	.hw_clks	= &sunxi_de2_hw_clks,
+
+	.resets		= sun50i_a64_de2_resets,
+	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
+};
+
+static int sunxi_de2_clk_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct clk *bus_clk;
+	struct reset_control *rstc;
+	void __iomem *reg;
+	const struct sunxi_ccu_desc *ccu_desc;
+	int ret;
+
+	ccu_desc = of_device_get_match_data(&pdev->dev);
+	if (!ccu_desc)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	bus_clk = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(bus_clk)) {
+		ret = PTR_ERR(bus_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
+		return ret;
+	}
+
+	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+	if (IS_ERR(rstc)) {
+		ret = PTR_ERR(bus_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"Couldn't get reset control: %d\n", ret);
+		return ret;
+	}
+
+	/* The bus clock needs to be enabled for us to access the registers */
+	ret = clk_prepare_enable(bus_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
+		return ret;
+	}
+
+	/* The reset control needs to be asserted for the controls to work */
+	ret = reset_control_deassert(rstc);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Couldn't deassert reset control: %d\n", ret);
+		goto err_disable_clk;
+	}
+
+	ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
+	if (ret)
+		goto err_assert_reset;
+
+	return 0;
+
+err_assert_reset:
+	reset_control_assert(rstc);
+err_disable_clk:
+	clk_disable_unprepare(bus_clk);
+	return ret;
+}
+
+static const struct of_device_id sunxi_de2_clk_ids[] = {
+	{
+		.compatible = "allwinner,sun8i-a83t-de2-clk",
+		.data = &sun8i_a83t_de2_clk_desc,
+	},
+	{
+		.compatible = "allwinner,sun50i-h5-de2-clk",
+		.data = &sun50i_a64_de2_clk_desc,
+	},
+	/*
+	 * The Allwinner A64 SoC needs some bit to be poke in syscon to make
+	 * DE2 really working.
+	 * So there's currently no A64 compatible here.
+	 * H5 shares the same reset line with A64, so here H5 is using the
+	 * clock description of A64.
+	 */
+	{ }
+};
+
+static struct platform_driver sunxi_de2_clk_driver = {
+	.probe	= sunxi_de2_clk_probe,
+	.driver	= {
+		.name	= "sunxi-de2-clks",
+		.of_match_table	= sunxi_de2_clk_ids,
+	},
+};
+builtin_platform_driver(sunxi_de2_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.h b/drivers/clk/sunxi-ng/ccu-sunxi-de2.h
new file mode 100644
index 000000000000..a7b19daa964a
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUNXI_DE2_H_
+#define _CCU_SUNXI_DE2_H_
+
+#include <dt-bindings/clock/sunxi-de2.h>
+#include <dt-bindings/reset/sunxi-de2.h>
+
+/* Intermediary clock dividers are not exported */
+#define CLK_MIXER0_DIV	3
+#define CLK_MIXER1_DIV	4
+#define CLK_WB_DIV	5
+
+#define CLK_NUMBER	(CLK_WB + 1)
+
+#endif /* _CCU_SUNXI_DE2_H_ */
diff --git a/include/dt-bindings/clock/sunxi-de2.h b/include/dt-bindings/clock/sunxi-de2.h
new file mode 100644
index 000000000000..a817bdbda5ef
--- /dev/null
+++ b/include/dt-bindings/clock/sunxi-de2.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_SUNXI_DE2_H_
+#define _DT_BINDINGS_CLOCK_SUNXI_DE2_H_
+
+#define CLK_BUS_MIXER0		0
+#define CLK_BUS_MIXER1		1
+#define CLK_BUS_WB		2
+
+#define CLK_MIXER0		6
+#define CLK_MIXER1		7
+#define CLK_WB			8
+
+#endif /* _DT_BINDINGS_CLOCK_SUNXI_DE2_H_ */
diff --git a/include/dt-bindings/reset/sunxi-de2.h b/include/dt-bindings/reset/sunxi-de2.h
new file mode 100644
index 000000000000..a13113044d70
--- /dev/null
+++ b/include/dt-bindings/reset/sunxi-de2.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_RESET_SUNXI_DE2_H_
+#define _DT_BINDINGS_RESET_SUNXI_DE2_H_
+
+#define RST_MIXER0	0
+#define RST_MIXER1	1
+#define RST_WB		2
+
+#endif /* _DT_BINDINGS_RESET_SUNXI_DE2_H_ */
-- 
2.11.1

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

* [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, devicetree, linux-kernel, dri-devel,
	linux-sunxi, Icenowy Zheng, linux-clk, linux-arm-kernel

The "Display Engine 2.0" in Allwinner newer SoCs contains a clock
management unit for its subunits, like the DE CCU in A80.

Add a sunxi-ng style driver for it.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/clk/sunxi-ng/Kconfig          |   6 +
 drivers/clk/sunxi-ng/Makefile         |   1 +
 drivers/clk/sunxi-ng/ccu-sunxi-de2.c  | 204 ++++++++++++++++++++++++++++++++++
 drivers/clk/sunxi-ng/ccu-sunxi-de2.h  |  28 +++++
 include/dt-bindings/clock/sunxi-de2.h |  54 +++++++++
 include/dt-bindings/reset/sunxi-de2.h |  50 +++++++++
 6 files changed, 343 insertions(+)
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.h
 create mode 100644 include/dt-bindings/clock/sunxi-de2.h
 create mode 100644 include/dt-bindings/reset/sunxi-de2.h

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 695bbf9ef428..52b88b22ec67 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -141,4 +141,10 @@ config SUN9I_A80_CCU
 	select SUNXI_CCU_PHASE
 	default MACH_SUN9I
 
+config SUNXI_DE2_CCU
+	bool "Support for the Allwinner SoCs DE2 CCU"
+	select SUNXI_CCU_DIV
+	select SUNXI_CCU_GATE
+	default n
+
 endif
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 6feaac0c5600..1d36c40384a6 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
+obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o
diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
new file mode 100644
index 000000000000..4259c145d5da
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "ccu_common.h"
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_reset.h"
+
+#include "ccu-sunxi-de2.h"
+
+static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
+		      0x04, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
+		      0x04, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
+		      0x04, BIT(2), 0);
+
+static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
+		      0x00, BIT(0), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
+		      0x00, BIT(1), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
+		      0x00, BIT(2), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
+		   CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
+		   CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
+		   CLK_SET_RATE_PARENT);
+
+static struct ccu_common *sunxi_de2_clks[] = {
+	&mixer0_clk.common,
+	&mixer1_clk.common,
+	&wb_clk.common,
+
+	&bus_mixer0_clk.common,
+	&bus_mixer1_clk.common,
+	&bus_wb_clk.common,
+
+	&mixer0_div_clk.common,
+	&mixer1_div_clk.common,
+	&wb_div_clk.common,
+};
+
+static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
+	.hws	= {
+		[CLK_MIXER0]		= &mixer0_clk.common.hw,
+		[CLK_MIXER1]		= &mixer1_clk.common.hw,
+		[CLK_WB]		= &wb_clk.common.hw,
+
+		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
+		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
+		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
+
+		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
+		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
+		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
+	},
+	.num	= CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
+	[RST_MIXER0]	= { 0x08, BIT(0) },
+	/*
+	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
+	 * only RST_WB is exported here.
+	 */
+	[RST_WB]	= { 0x08, BIT(2) },
+};
+
+static struct ccu_reset_map sun50i_a64_de2_resets[] = {
+	[RST_MIXER0]	= { 0x08, BIT(0) },
+	[RST_MIXER1]	= { 0x08, BIT(1) },
+	[RST_WB]	= { 0x08, BIT(2) },
+};
+
+static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
+	.ccu_clks	= sunxi_de2_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
+
+	.hw_clks	= &sunxi_de2_hw_clks,
+
+	.resets		= sun8i_a83t_de2_resets,
+	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
+};
+
+static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
+	.ccu_clks	= sunxi_de2_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
+
+	.hw_clks	= &sunxi_de2_hw_clks,
+
+	.resets		= sun50i_a64_de2_resets,
+	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
+};
+
+static int sunxi_de2_clk_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct clk *bus_clk;
+	struct reset_control *rstc;
+	void __iomem *reg;
+	const struct sunxi_ccu_desc *ccu_desc;
+	int ret;
+
+	ccu_desc = of_device_get_match_data(&pdev->dev);
+	if (!ccu_desc)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	bus_clk = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(bus_clk)) {
+		ret = PTR_ERR(bus_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
+		return ret;
+	}
+
+	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+	if (IS_ERR(rstc)) {
+		ret = PTR_ERR(bus_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"Couldn't get reset control: %d\n", ret);
+		return ret;
+	}
+
+	/* The bus clock needs to be enabled for us to access the registers */
+	ret = clk_prepare_enable(bus_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
+		return ret;
+	}
+
+	/* The reset control needs to be asserted for the controls to work */
+	ret = reset_control_deassert(rstc);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Couldn't deassert reset control: %d\n", ret);
+		goto err_disable_clk;
+	}
+
+	ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
+	if (ret)
+		goto err_assert_reset;
+
+	return 0;
+
+err_assert_reset:
+	reset_control_assert(rstc);
+err_disable_clk:
+	clk_disable_unprepare(bus_clk);
+	return ret;
+}
+
+static const struct of_device_id sunxi_de2_clk_ids[] = {
+	{
+		.compatible = "allwinner,sun8i-a83t-de2-clk",
+		.data = &sun8i_a83t_de2_clk_desc,
+	},
+	{
+		.compatible = "allwinner,sun50i-h5-de2-clk",
+		.data = &sun50i_a64_de2_clk_desc,
+	},
+	/*
+	 * The Allwinner A64 SoC needs some bit to be poke in syscon to make
+	 * DE2 really working.
+	 * So there's currently no A64 compatible here.
+	 * H5 shares the same reset line with A64, so here H5 is using the
+	 * clock description of A64.
+	 */
+	{ }
+};
+
+static struct platform_driver sunxi_de2_clk_driver = {
+	.probe	= sunxi_de2_clk_probe,
+	.driver	= {
+		.name	= "sunxi-de2-clks",
+		.of_match_table	= sunxi_de2_clk_ids,
+	},
+};
+builtin_platform_driver(sunxi_de2_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.h b/drivers/clk/sunxi-ng/ccu-sunxi-de2.h
new file mode 100644
index 000000000000..a7b19daa964a
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUNXI_DE2_H_
+#define _CCU_SUNXI_DE2_H_
+
+#include <dt-bindings/clock/sunxi-de2.h>
+#include <dt-bindings/reset/sunxi-de2.h>
+
+/* Intermediary clock dividers are not exported */
+#define CLK_MIXER0_DIV	3
+#define CLK_MIXER1_DIV	4
+#define CLK_WB_DIV	5
+
+#define CLK_NUMBER	(CLK_WB + 1)
+
+#endif /* _CCU_SUNXI_DE2_H_ */
diff --git a/include/dt-bindings/clock/sunxi-de2.h b/include/dt-bindings/clock/sunxi-de2.h
new file mode 100644
index 000000000000..a817bdbda5ef
--- /dev/null
+++ b/include/dt-bindings/clock/sunxi-de2.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_SUNXI_DE2_H_
+#define _DT_BINDINGS_CLOCK_SUNXI_DE2_H_
+
+#define CLK_BUS_MIXER0		0
+#define CLK_BUS_MIXER1		1
+#define CLK_BUS_WB		2
+
+#define CLK_MIXER0		6
+#define CLK_MIXER1		7
+#define CLK_WB			8
+
+#endif /* _DT_BINDINGS_CLOCK_SUNXI_DE2_H_ */
diff --git a/include/dt-bindings/reset/sunxi-de2.h b/include/dt-bindings/reset/sunxi-de2.h
new file mode 100644
index 000000000000..a13113044d70
--- /dev/null
+++ b/include/dt-bindings/reset/sunxi-de2.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_RESET_SUNXI_DE2_H_
+#define _DT_BINDINGS_RESET_SUNXI_DE2_H_
+
+#define RST_MIXER0	0
+#define RST_MIXER1	1
+#define RST_WB		2
+
+#endif /* _DT_BINDINGS_RESET_SUNXI_DE2_H_ */
-- 
2.11.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: linux-arm-kernel

The "Display Engine 2.0" in Allwinner newer SoCs contains a clock
management unit for its subunits, like the DE CCU in A80.

Add a sunxi-ng style driver for it.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/clk/sunxi-ng/Kconfig          |   6 +
 drivers/clk/sunxi-ng/Makefile         |   1 +
 drivers/clk/sunxi-ng/ccu-sunxi-de2.c  | 204 ++++++++++++++++++++++++++++++++++
 drivers/clk/sunxi-ng/ccu-sunxi-de2.h  |  28 +++++
 include/dt-bindings/clock/sunxi-de2.h |  54 +++++++++
 include/dt-bindings/reset/sunxi-de2.h |  50 +++++++++
 6 files changed, 343 insertions(+)
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sunxi-de2.h
 create mode 100644 include/dt-bindings/clock/sunxi-de2.h
 create mode 100644 include/dt-bindings/reset/sunxi-de2.h

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 695bbf9ef428..52b88b22ec67 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -141,4 +141,10 @@ config SUN9I_A80_CCU
 	select SUNXI_CCU_PHASE
 	default MACH_SUN9I
 
+config SUNXI_DE2_CCU
+	bool "Support for the Allwinner SoCs DE2 CCU"
+	select SUNXI_CCU_DIV
+	select SUNXI_CCU_GATE
+	default n
+
 endif
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 6feaac0c5600..1d36c40384a6 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
 obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
+obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o
diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
new file mode 100644
index 000000000000..4259c145d5da
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "ccu_common.h"
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_reset.h"
+
+#include "ccu-sunxi-de2.h"
+
+static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
+		      0x04, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
+		      0x04, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
+		      0x04, BIT(2), 0);
+
+static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
+		      0x00, BIT(0), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
+		      0x00, BIT(1), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
+		      0x00, BIT(2), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
+		   CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
+		   CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
+		   CLK_SET_RATE_PARENT);
+
+static struct ccu_common *sunxi_de2_clks[] = {
+	&mixer0_clk.common,
+	&mixer1_clk.common,
+	&wb_clk.common,
+
+	&bus_mixer0_clk.common,
+	&bus_mixer1_clk.common,
+	&bus_wb_clk.common,
+
+	&mixer0_div_clk.common,
+	&mixer1_div_clk.common,
+	&wb_div_clk.common,
+};
+
+static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
+	.hws	= {
+		[CLK_MIXER0]		= &mixer0_clk.common.hw,
+		[CLK_MIXER1]		= &mixer1_clk.common.hw,
+		[CLK_WB]		= &wb_clk.common.hw,
+
+		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
+		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
+		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
+
+		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
+		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
+		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
+	},
+	.num	= CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
+	[RST_MIXER0]	= { 0x08, BIT(0) },
+	/*
+	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
+	 * only RST_WB is exported here.
+	 */
+	[RST_WB]	= { 0x08, BIT(2) },
+};
+
+static struct ccu_reset_map sun50i_a64_de2_resets[] = {
+	[RST_MIXER0]	= { 0x08, BIT(0) },
+	[RST_MIXER1]	= { 0x08, BIT(1) },
+	[RST_WB]	= { 0x08, BIT(2) },
+};
+
+static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
+	.ccu_clks	= sunxi_de2_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
+
+	.hw_clks	= &sunxi_de2_hw_clks,
+
+	.resets		= sun8i_a83t_de2_resets,
+	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
+};
+
+static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
+	.ccu_clks	= sunxi_de2_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
+
+	.hw_clks	= &sunxi_de2_hw_clks,
+
+	.resets		= sun50i_a64_de2_resets,
+	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
+};
+
+static int sunxi_de2_clk_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct clk *bus_clk;
+	struct reset_control *rstc;
+	void __iomem *reg;
+	const struct sunxi_ccu_desc *ccu_desc;
+	int ret;
+
+	ccu_desc = of_device_get_match_data(&pdev->dev);
+	if (!ccu_desc)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	bus_clk = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(bus_clk)) {
+		ret = PTR_ERR(bus_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
+		return ret;
+	}
+
+	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+	if (IS_ERR(rstc)) {
+		ret = PTR_ERR(bus_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"Couldn't get reset control: %d\n", ret);
+		return ret;
+	}
+
+	/* The bus clock needs to be enabled for us to access the registers */
+	ret = clk_prepare_enable(bus_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
+		return ret;
+	}
+
+	/* The reset control needs to be asserted for the controls to work */
+	ret = reset_control_deassert(rstc);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Couldn't deassert reset control: %d\n", ret);
+		goto err_disable_clk;
+	}
+
+	ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
+	if (ret)
+		goto err_assert_reset;
+
+	return 0;
+
+err_assert_reset:
+	reset_control_assert(rstc);
+err_disable_clk:
+	clk_disable_unprepare(bus_clk);
+	return ret;
+}
+
+static const struct of_device_id sunxi_de2_clk_ids[] = {
+	{
+		.compatible = "allwinner,sun8i-a83t-de2-clk",
+		.data = &sun8i_a83t_de2_clk_desc,
+	},
+	{
+		.compatible = "allwinner,sun50i-h5-de2-clk",
+		.data = &sun50i_a64_de2_clk_desc,
+	},
+	/*
+	 * The Allwinner A64 SoC needs some bit to be poke in syscon to make
+	 * DE2 really working.
+	 * So there's currently no A64 compatible here.
+	 * H5 shares the same reset line with A64, so here H5 is using the
+	 * clock description of A64.
+	 */
+	{ }
+};
+
+static struct platform_driver sunxi_de2_clk_driver = {
+	.probe	= sunxi_de2_clk_probe,
+	.driver	= {
+		.name	= "sunxi-de2-clks",
+		.of_match_table	= sunxi_de2_clk_ids,
+	},
+};
+builtin_platform_driver(sunxi_de2_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.h b/drivers/clk/sunxi-ng/ccu-sunxi-de2.h
new file mode 100644
index 000000000000..a7b19daa964a
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUNXI_DE2_H_
+#define _CCU_SUNXI_DE2_H_
+
+#include <dt-bindings/clock/sunxi-de2.h>
+#include <dt-bindings/reset/sunxi-de2.h>
+
+/* Intermediary clock dividers are not exported */
+#define CLK_MIXER0_DIV	3
+#define CLK_MIXER1_DIV	4
+#define CLK_WB_DIV	5
+
+#define CLK_NUMBER	(CLK_WB + 1)
+
+#endif /* _CCU_SUNXI_DE2_H_ */
diff --git a/include/dt-bindings/clock/sunxi-de2.h b/include/dt-bindings/clock/sunxi-de2.h
new file mode 100644
index 000000000000..a817bdbda5ef
--- /dev/null
+++ b/include/dt-bindings/clock/sunxi-de2.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_SUNXI_DE2_H_
+#define _DT_BINDINGS_CLOCK_SUNXI_DE2_H_
+
+#define CLK_BUS_MIXER0		0
+#define CLK_BUS_MIXER1		1
+#define CLK_BUS_WB		2
+
+#define CLK_MIXER0		6
+#define CLK_MIXER1		7
+#define CLK_WB			8
+
+#endif /* _DT_BINDINGS_CLOCK_SUNXI_DE2_H_ */
diff --git a/include/dt-bindings/reset/sunxi-de2.h b/include/dt-bindings/reset/sunxi-de2.h
new file mode 100644
index 000000000000..a13113044d70
--- /dev/null
+++ b/include/dt-bindings/reset/sunxi-de2.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_RESET_SUNXI_DE2_H_
+#define _DT_BINDINGS_RESET_SUNXI_DE2_H_
+
+#define RST_MIXER0	0
+#define RST_MIXER1	1
+#define RST_WB		2
+
+#endif /* _DT_BINDINGS_RESET_SUNXI_DE2_H_ */
-- 
2.11.1

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

* [PATCH 3/8] dt-bindings: add bindings for DE2 on V3s SoC
  2017-02-22 15:18 ` Icenowy Zheng
  (?)
@ 2017-02-22 15:18     ` Icenowy Zheng
  -1 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng

Allwinner V3s SoC have a display engine which have a different pipeline
with older SoCs.

Add document for it (new compatibles and the new "mixer" part).

The paragraph of TCON is also refactored, for furtherly add TCONs in
A83T/H3/A64/H5 that have only a channel 1 (used for HDMI or TV Encoder).

Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
---
 .../bindings/display/sunxi/sun4i-drm.txt           | 37 +++++++++++++++++++---
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index b82c00449468..2c293247c41d 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -31,11 +31,11 @@ Required properties:
    * allwinner,sun6i-a31-tcon
    * allwinner,sun6i-a31s-tcon
    * allwinner,sun8i-a33-tcon
+   * allwinner,sun8i-v3s-tcon
  - reg: base address and size of memory-mapped region
  - interrupts: interrupt associated to this IP
- - clocks: phandles to the clocks feeding the TCON. Three are needed:
+ - clocks: phandles to the clocks feeding the TCON
    - 'ahb': the interface clocks
-   - 'tcon-ch0': The clock driving the TCON channel 0
  - resets: phandles to the reset controllers driving the encoder
    - "lcd": the reset line for the TCON channel 0
 
@@ -52,7 +52,12 @@ Required properties:
   second the block connected to the TCON channel 1 (usually the TV
   encoder)
 
-On SoCs other than the A33, there is one more clock required:
+On TCONs that have a channel 0 (currently all TCONs supported), there
+is one more clock required:
+   - 'tcon-ch0': The clock driving the TCON channel 0
+
+On TCONs that have a channel 1 (currently all TCONs except the ones in
+A33 and V3s), there is one more clock required:
    - 'tcon-ch1': The clock driving the TCON channel 1
 
 DRC
@@ -137,6 +142,26 @@ Required properties:
   Documentation/devicetree/bindings/media/video-interfaces.txt. The
   first port should be the input endpoints, the second one the outputs
 
+Display Engine 2.0 Mixer
+------------------------
+
+The DE2 mixer have many functionalities, currently only layer blending is
+supported.
+
+Required properties:
+  - compatible: value must be one of:
+    * allwinner,sun8i-v3s-de2-mixer
+  - reg: base address and size of the memory-mapped region.
+  - clocks: phandles to the clocks feeding the frontend and backend
+    * bus: the backend interface clock
+    * ram: the backend DRAM clock
+  - clock-names: the clock names mentioned above
+  - resets: phandles to the reset controllers driving the backend
+
+- ports: A ports node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt. The
+  first port should be the input endpoints, the second one the output
+
 
 Display Engine Pipeline
 -----------------------
@@ -151,9 +176,13 @@ Required properties:
     * allwinner,sun6i-a31-display-engine
     * allwinner,sun6i-a31s-display-engine
     * allwinner,sun8i-a33-display-engine
+    * allwinner,sun8i-v3s-display-engine
 
   - allwinner,pipelines: list of phandle to the display engine
-    frontends available.
+    pipeline entry point. For SoCs with original DE (currently
+    all SoCs supported by display engine except V3s), this
+    phandle should be a display frontend or backend; for SoCs
+    with DE2, this phandle should be a mixer.
 
 Example:
 
-- 
2.11.1

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

* [PATCH 3/8] dt-bindings: add bindings for DE2 on V3s SoC
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, devicetree, linux-kernel, dri-devel,
	linux-sunxi, Icenowy Zheng, linux-clk, linux-arm-kernel

Allwinner V3s SoC have a display engine which have a different pipeline
with older SoCs.

Add document for it (new compatibles and the new "mixer" part).

The paragraph of TCON is also refactored, for furtherly add TCONs in
A83T/H3/A64/H5 that have only a channel 1 (used for HDMI or TV Encoder).

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 .../bindings/display/sunxi/sun4i-drm.txt           | 37 +++++++++++++++++++---
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index b82c00449468..2c293247c41d 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -31,11 +31,11 @@ Required properties:
    * allwinner,sun6i-a31-tcon
    * allwinner,sun6i-a31s-tcon
    * allwinner,sun8i-a33-tcon
+   * allwinner,sun8i-v3s-tcon
  - reg: base address and size of memory-mapped region
  - interrupts: interrupt associated to this IP
- - clocks: phandles to the clocks feeding the TCON. Three are needed:
+ - clocks: phandles to the clocks feeding the TCON
    - 'ahb': the interface clocks
-   - 'tcon-ch0': The clock driving the TCON channel 0
  - resets: phandles to the reset controllers driving the encoder
    - "lcd": the reset line for the TCON channel 0
 
@@ -52,7 +52,12 @@ Required properties:
   second the block connected to the TCON channel 1 (usually the TV
   encoder)
 
-On SoCs other than the A33, there is one more clock required:
+On TCONs that have a channel 0 (currently all TCONs supported), there
+is one more clock required:
+   - 'tcon-ch0': The clock driving the TCON channel 0
+
+On TCONs that have a channel 1 (currently all TCONs except the ones in
+A33 and V3s), there is one more clock required:
    - 'tcon-ch1': The clock driving the TCON channel 1
 
 DRC
@@ -137,6 +142,26 @@ Required properties:
   Documentation/devicetree/bindings/media/video-interfaces.txt. The
   first port should be the input endpoints, the second one the outputs
 
+Display Engine 2.0 Mixer
+------------------------
+
+The DE2 mixer have many functionalities, currently only layer blending is
+supported.
+
+Required properties:
+  - compatible: value must be one of:
+    * allwinner,sun8i-v3s-de2-mixer
+  - reg: base address and size of the memory-mapped region.
+  - clocks: phandles to the clocks feeding the frontend and backend
+    * bus: the backend interface clock
+    * ram: the backend DRAM clock
+  - clock-names: the clock names mentioned above
+  - resets: phandles to the reset controllers driving the backend
+
+- ports: A ports node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt. The
+  first port should be the input endpoints, the second one the output
+
 
 Display Engine Pipeline
 -----------------------
@@ -151,9 +176,13 @@ Required properties:
     * allwinner,sun6i-a31-display-engine
     * allwinner,sun6i-a31s-display-engine
     * allwinner,sun8i-a33-display-engine
+    * allwinner,sun8i-v3s-display-engine
 
   - allwinner,pipelines: list of phandle to the display engine
-    frontends available.
+    pipeline entry point. For SoCs with original DE (currently
+    all SoCs supported by display engine except V3s), this
+    phandle should be a display frontend or backend; for SoCs
+    with DE2, this phandle should be a mixer.
 
 Example:
 
-- 
2.11.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/8] dt-bindings: add bindings for DE2 on V3s SoC
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: linux-arm-kernel

Allwinner V3s SoC have a display engine which have a different pipeline
with older SoCs.

Add document for it (new compatibles and the new "mixer" part).

The paragraph of TCON is also refactored, for furtherly add TCONs in
A83T/H3/A64/H5 that have only a channel 1 (used for HDMI or TV Encoder).

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 .../bindings/display/sunxi/sun4i-drm.txt           | 37 +++++++++++++++++++---
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index b82c00449468..2c293247c41d 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -31,11 +31,11 @@ Required properties:
    * allwinner,sun6i-a31-tcon
    * allwinner,sun6i-a31s-tcon
    * allwinner,sun8i-a33-tcon
+   * allwinner,sun8i-v3s-tcon
  - reg: base address and size of memory-mapped region
  - interrupts: interrupt associated to this IP
- - clocks: phandles to the clocks feeding the TCON. Three are needed:
+ - clocks: phandles to the clocks feeding the TCON
    - 'ahb': the interface clocks
-   - 'tcon-ch0': The clock driving the TCON channel 0
  - resets: phandles to the reset controllers driving the encoder
    - "lcd": the reset line for the TCON channel 0
 
@@ -52,7 +52,12 @@ Required properties:
   second the block connected to the TCON channel 1 (usually the TV
   encoder)
 
-On SoCs other than the A33, there is one more clock required:
+On TCONs that have a channel 0 (currently all TCONs supported), there
+is one more clock required:
+   - 'tcon-ch0': The clock driving the TCON channel 0
+
+On TCONs that have a channel 1 (currently all TCONs except the ones in
+A33 and V3s), there is one more clock required:
    - 'tcon-ch1': The clock driving the TCON channel 1
 
 DRC
@@ -137,6 +142,26 @@ Required properties:
   Documentation/devicetree/bindings/media/video-interfaces.txt. The
   first port should be the input endpoints, the second one the outputs
 
+Display Engine 2.0 Mixer
+------------------------
+
+The DE2 mixer have many functionalities, currently only layer blending is
+supported.
+
+Required properties:
+  - compatible: value must be one of:
+    * allwinner,sun8i-v3s-de2-mixer
+  - reg: base address and size of the memory-mapped region.
+  - clocks: phandles to the clocks feeding the frontend and backend
+    * bus: the backend interface clock
+    * ram: the backend DRAM clock
+  - clock-names: the clock names mentioned above
+  - resets: phandles to the reset controllers driving the backend
+
+- ports: A ports node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt. The
+  first port should be the input endpoints, the second one the output
+
 
 Display Engine Pipeline
 -----------------------
@@ -151,9 +176,13 @@ Required properties:
     * allwinner,sun6i-a31-display-engine
     * allwinner,sun6i-a31s-display-engine
     * allwinner,sun8i-a33-display-engine
+    * allwinner,sun8i-v3s-display-engine
 
   - allwinner,pipelines: list of phandle to the display engine
-    frontends available.
+    pipeline entry point. For SoCs with original DE (currently
+    all SoCs supported by display engine except V3s), this
+    phandle should be a display frontend or backend; for SoCs
+    with DE2, this phandle should be a mixer.
 
 Example:
 
-- 
2.11.1

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 15:18 ` Icenowy Zheng
  (?)
@ 2017-02-22 15:18     ` Icenowy Zheng
  -1 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng

Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
in a new "Display Engine" (mixers instead of old backends and
frontends).

Add support for the mixer on Allwinner V3s SoC; it's the simplest one.

Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
---
 drivers/gpu/drm/sun4i/Kconfig       |   8 +
 drivers/gpu/drm/sun4i/Makefile      |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
 drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
 drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
 9 files changed, 674 insertions(+), 23 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..8df401fff145 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,11 @@ config DRM_SUN4I
 	  Choose this option if you have an Allwinner SoC with a
 	  Display Engine. If M is selected the module will be called
 	  sun4i-drm.
+
+config DRM_SUN4I_DE2
+	bool "Support Display Engine 2.0"
+	depends on DRM_SUN4I
+	default MACH_SUN8I
+	help
+	  Choose this option if you have an Allwinner SoC with a
+	  "Display Engine 2.0".
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index d625a82a6e5f..890e6e50dfee 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a192210574f..4d2228454726 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,6 +25,7 @@
 #include <video/videomode.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	DRM_DEBUG_DRIVER("Committing plane changes\n");
 
-	sun4i_backend_commit(drv->backend);
+	if (drv->backend)
+		sun4i_backend_commit(drv->backend);
+	else if (drv->mixer)
+		sun8i_mixer_commit(drv->mixer);
 
 	if (event) {
 		crtc->state->event = NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 4ce665349f6b..58af38f5e833 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -20,12 +20,17 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/of_device.h>
+
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_framebuffer.h"
 #include "sun4i_layer.h"
 #include "sun4i_tcon.h"
 
+#define DE_VER_DE	0
+#define DE_VER_DE2	1
+
 static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
 	struct sun4i_drv *drv = drm->dev_private;
@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
 {
 	struct drm_device *drm;
 	struct sun4i_drv *drv;
-	int ret;
+	int ret, de_ver;
+
+	de_ver = (int)of_device_get_match_data(dev);
 
 	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 	if (IS_ERR(drm))
@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
 	}
 
 	/* Create our layers */
-	drv->layers = sun4i_layers_init(drm);
+	if (de_ver == DE_VER_DE2)
+		drv->layers = sun8i_layers_init(drm);
+	else
+		drv->layers = sun4i_layers_init(drm);
 	if (IS_ERR(drv->layers)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
 		ret = PTR_ERR(drv->layers);
@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_drv_of_table[] = {
-	{ .compatible = "allwinner,sun5i-a13-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
-	{ .compatible = "allwinner,sun8i-a33-display-engine" },
+	{
+		.compatible = "allwinner,sun5i-a13-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-a33-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-display-engine",
+		.data = (void *)DE_VER_DE2,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 597353eab728..cf1da95b85bd 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -18,6 +18,7 @@
 
 struct sun4i_drv {
 	struct sun4i_backend	*backend;
+	struct sun8i_mixer	*mixer;
 	struct sun4i_crtc	*crtc;
 	struct sun4i_tcon	*tcon;
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index 5d53c977bca5..6dcdac38ab69 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,52 +16,66 @@
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
 struct sun4i_plane_desc {
 	       enum drm_plane_type     type;
+	       /* Pipe is not used in sun8i-mixer */
 	       u8                      pipe;
 	       const uint32_t          *formats;
 	       uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+static int sun4i_layer_atomic_check(struct drm_plane *plane,
 					    struct drm_plane_state *state)
 {
 	return 0;
 }
 
-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
+	struct sun8i_mixer *mixer = drv->mixer;
 
-	sun4i_backend_layer_enable(backend, layer->id, false);
+	if (backend)
+		sun4i_backend_layer_enable(backend, layer->id, false);
+	else if (mixer)
+		sun8i_mixer_layer_enable(mixer, layer->id, false);
 }
 
-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+static void sun4i_layer_atomic_update(struct drm_plane *plane,
 					      struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
-
-	sun4i_backend_update_layer_coord(backend, layer->id, plane);
-	sun4i_backend_update_layer_formats(backend, layer->id, plane);
-	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
-	sun4i_backend_layer_enable(backend, layer->id, true);
+	struct sun8i_mixer *mixer = drv->mixer;
+
+	if (backend) {
+		sun4i_backend_update_layer_coord(backend, layer->id, plane);
+		sun4i_backend_update_layer_formats(backend, layer->id, plane);
+		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+		sun4i_backend_layer_enable(backend, layer->id, true);
+	} else if (mixer) {
+		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
+		sun8i_mixer_layer_enable(mixer, layer->id, true);
+	}
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-	.atomic_check	= sun4i_backend_layer_atomic_check,
-	.atomic_disable	= sun4i_backend_layer_atomic_disable,
-	.atomic_update	= sun4i_backend_layer_atomic_update,
+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
+	.atomic_check	= sun4i_layer_atomic_check,
+	.atomic_disable	= sun4i_layer_atomic_disable,
+	.atomic_update	= sun4i_layer_atomic_update,
 };
 
-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+static const struct drm_plane_funcs sun4i_layer_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
 	.destroy		= drm_plane_cleanup,
@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
+static const uint32_t sun8i_mixer_layer_formats[] = {
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XRGB8888,
+};
+
 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	{
 		.type = DRM_PLANE_TYPE_PRIMARY,
@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	},
 };
 
+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
+	{
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+	{
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 						const struct sun4i_plane_desc *plane)
 {
@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 
 	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
-				       &sun4i_backend_layer_funcs,
+				       &sun4i_layer_funcs,
 				       plane->formats, plane->nformats,
 				       plane->type, NULL);
 	if (ret) {
@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 	}
 
 	drm_plane_helper_add(&layer->plane,
-			     &sun4i_backend_layer_helper_funcs);
+			     &sun4i_layer_helper_funcs);
 	layer->drv = drv;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
 
 	return layers;
 }
+
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
+{
+	struct sun4i_layer **layers;
+	int i;
+
+	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
+			      sizeof(**layers), GFP_KERNEL);
+	if (!layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
+		struct sun4i_layer *layer = layers[i];
+
+		layer = sun4i_layer_init_one(drm, plane);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
+
+		layer->id = i;
+	};
+
+	return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index a2f65d7a3f4e..f7b9e5daea50 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
 }
 
 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
 
 #endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..9427b57240d3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * Based on sun4i_backend.c, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun8i_mixer.h"
+#include "sun4i_drv.h"
+
+#define SUN8I_DRAM_OFFSET 0x40000000
+
+#if defined CONFIG_DRM_SUN4I_DE2
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+	DRM_DEBUG_DRIVER("Committing changes\n");
+
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+EXPORT_SYMBOL(sun8i_mixer_commit);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+
+	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	/* Set the alpha configuration */
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
+}
+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+					     u32 format, u32 *mode)
+{
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+	    (format == DRM_FORMAT_ARGB8888))
+		format = DRM_FORMAT_XRGB8888;
+
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
+		break;
+
+	case DRM_FORMAT_RGB888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int i;
+
+	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 state->crtc_w, state->crtc_h);
+		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating blender size\n");
+		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+			regmap_write(mixer->regs,
+				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
+				     SUN8I_MIXER_SIZE(state->crtc_w,
+						      state->crtc_h));
+		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating channel size\n");
+		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+	}
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     fb->pitches[0]);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+			 state->crtc_w, state->crtc_h);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->crtc_x, state->crtc_y);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	bool interlaced = false;
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int ret;
+
+	if (plane->state->crtc)
+		interlaced = plane->state->crtc->state->adjusted_mode.flags
+			& DRM_MODE_FLAG_INTERLACE;
+
+	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+			   interlaced ?
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
+
+	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+			 interlaced ? "on" : "off");
+
+	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
+						&val);
+	if (ret) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return ret;
+	}
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	uint32_t paddr_u32;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+	paddr += (state->src_x >> 16) * bpp;
+	paddr += (state->src_y >> 16) * fb->pitches[0];
+	paddr -= SUN8I_DRAM_OFFSET;
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	paddr_u32 = (uint32_t) paddr;
+
+	regmap_write(mixer->regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     paddr_u32);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0xbffc, /* guessed */
+};
+
+static int sun8i_mixer_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct sun4i_drv *drv = drm->dev_private;
+	struct sun8i_mixer *mixer;
+	struct resource *res;
+	void __iomem *regs;
+	int i, ret;
+
+	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	dev_set_drvdata(dev, mixer);
+	drv->mixer = mixer;
+
+	mixer->cfg = of_device_get_match_data(dev);
+	if (!mixer->cfg)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mixer->regs = devm_regmap_init_mmio(dev, regs,
+					      &sun8i_mixer_regmap_config);
+	if (IS_ERR(mixer->regs)) {
+		dev_err(dev, "Couldn't create the mixer regmap\n");
+		return PTR_ERR(mixer->regs);
+	}
+
+	mixer->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(mixer->reset)) {
+		dev_err(dev, "Couldn't get our reset line\n");
+		return PTR_ERR(mixer->reset);
+	}
+
+	ret = reset_control_deassert(mixer->reset);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert our reset line\n");
+		return ret;
+	}
+
+	mixer->bus_clk = devm_clk_get(dev, "bus");
+	if (IS_ERR(mixer->bus_clk)) {
+		dev_err(dev, "Couldn't get the mixer bus clock\n");
+		ret = PTR_ERR(mixer->bus_clk);
+		goto err_assert_reset;
+	}
+	clk_prepare_enable(mixer->bus_clk);
+
+	mixer->mod_clk = devm_clk_get(dev, "mod");
+	if (IS_ERR(mixer->mod_clk)) {
+		dev_err(dev, "Couldn't get the mixer module clock\n");
+		ret = PTR_ERR(mixer->mod_clk);
+		goto err_disable_bus_clk;
+	}
+	clk_prepare_enable(mixer->mod_clk);
+
+	/* Reset the registers */
+	for (i = 0x0; i < 0x20000; i += 4)
+		regmap_write(mixer->regs, i, 0);
+
+	/* Enable the mixer */
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
+		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+	/* Initialize blender */
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
+		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
+		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+		regmap_write(mixer->regs,
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
+
+	/* Select the first UI channel */
+	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
+			 mixer->cfg->vi_num);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
+		     mixer->cfg->vi_num);
+
+	return 0;
+
+	clk_disable_unprepare(mixer->mod_clk);
+err_disable_bus_clk:
+	clk_disable_unprepare(mixer->bus_clk);
+err_assert_reset:
+	reset_control_assert(mixer->reset);
+	return ret;
+}
+
+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(mixer->mod_clk);
+	clk_disable_unprepare(mixer->bus_clk);
+	reset_control_assert(mixer->reset);
+}
+
+static const struct component_ops sun8i_mixer_ops = {
+	.bind	= sun8i_mixer_bind,
+	.unbind	= sun8i_mixer_unbind,
+};
+
+static int sun8i_mixer_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &sun8i_mixer_ops);
+}
+
+static int sun8i_mixer_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sun8i_mixer_ops);
+
+	return 0;
+}
+
+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
+	.vi_num = 2,
+	.ui_num = 1,
+};
+
+static const struct of_device_id sun8i_mixer_of_table[] = {
+	{
+		.compatible = "allwinner,sun8i-v3s-de2-mixer",
+		.data = &sun8i_v3s_mixer_cfg
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
+
+static struct platform_driver sun8i_mixer_platform_driver = {
+	.probe		= sun8i_mixer_probe,
+	.remove		= sun8i_mixer_remove,
+	.driver		= {
+		.name		= "sun8i-mixer",
+		.of_match_table	= sun8i_mixer_of_table,
+	},
+};
+module_platform_driver(sun8i_mixer_platform_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
+#else /* DRM_SUN4I_DE2 */
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+}
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_DRM_SUN4I_DE2 */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..0bc134b3bc98
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_layer.h"
+
+#define SUN8I_MIXER_MAX_CHAN_COUNT		4
+
+#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
+#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
+
+#define SUN8I_MIXER_GLOBAL_CTL			0x0
+#define SUN8I_MIXER_GLOBAL_STATUS		0x4
+#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
+#define SUN8I_MIXER_GLOBAL_SIZE			0xc
+
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ROUTE			0x1080
+#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
+#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
+#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
+#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
+#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
+#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
+
+/* The following numbers are some still unknown magic numbers */
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
+#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
+#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
+
+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
+
+/*
+ * VI channels are not used now, but the support of them may be introduced in
+ * the future.
+ */
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
+
+/*
+ * These sun-engines are still unknown now, the EN registers are here only to
+ * be used to disable these sub-engines.
+ */
+#define SUN8I_MIXER_VSU_EN			0x20000
+#define SUN8I_MIXER_GSU1_EN			0x30000
+#define SUN8I_MIXER_GSU2_EN			0x40000
+#define SUN8I_MIXER_GSU3_EN			0x50000
+#define SUN8I_MIXER_FCE_EN			0xa0000
+#define SUN8I_MIXER_BWS_EN			0xa2000
+#define SUN8I_MIXER_LTI_EN			0xa4000
+#define SUN8I_MIXER_PEAK_EN			0xa6000
+#define SUN8I_MIXER_ASE_EN			0xa8000
+#define SUN8I_MIXER_FCC_EN			0xaa000
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+
+struct sun8i_mixer_cfg {
+	int		vi_num;
+	int		ui_num;
+};
+
+struct sun8i_mixer {
+	struct regmap			*regs;
+
+	const struct sun8i_mixer_cfg	*cfg;
+
+	struct reset_control		*reset;
+
+	struct clk			*bus_clk;
+	struct clk			*mod_clk;
+};
+
+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane);
+#endif /* _SUN8I_MIXER_H_ */
-- 
2.11.1

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, devicetree, linux-kernel, dri-devel,
	linux-sunxi, Icenowy Zheng, linux-clk, linux-arm-kernel

Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
in a new "Display Engine" (mixers instead of old backends and
frontends).

Add support for the mixer on Allwinner V3s SoC; it's the simplest one.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/gpu/drm/sun4i/Kconfig       |   8 +
 drivers/gpu/drm/sun4i/Makefile      |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
 drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
 drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
 9 files changed, 674 insertions(+), 23 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..8df401fff145 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,11 @@ config DRM_SUN4I
 	  Choose this option if you have an Allwinner SoC with a
 	  Display Engine. If M is selected the module will be called
 	  sun4i-drm.
+
+config DRM_SUN4I_DE2
+	bool "Support Display Engine 2.0"
+	depends on DRM_SUN4I
+	default MACH_SUN8I
+	help
+	  Choose this option if you have an Allwinner SoC with a
+	  "Display Engine 2.0".
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index d625a82a6e5f..890e6e50dfee 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a192210574f..4d2228454726 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,6 +25,7 @@
 #include <video/videomode.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	DRM_DEBUG_DRIVER("Committing plane changes\n");
 
-	sun4i_backend_commit(drv->backend);
+	if (drv->backend)
+		sun4i_backend_commit(drv->backend);
+	else if (drv->mixer)
+		sun8i_mixer_commit(drv->mixer);
 
 	if (event) {
 		crtc->state->event = NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 4ce665349f6b..58af38f5e833 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -20,12 +20,17 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/of_device.h>
+
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_framebuffer.h"
 #include "sun4i_layer.h"
 #include "sun4i_tcon.h"
 
+#define DE_VER_DE	0
+#define DE_VER_DE2	1
+
 static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
 	struct sun4i_drv *drv = drm->dev_private;
@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
 {
 	struct drm_device *drm;
 	struct sun4i_drv *drv;
-	int ret;
+	int ret, de_ver;
+
+	de_ver = (int)of_device_get_match_data(dev);
 
 	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 	if (IS_ERR(drm))
@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
 	}
 
 	/* Create our layers */
-	drv->layers = sun4i_layers_init(drm);
+	if (de_ver == DE_VER_DE2)
+		drv->layers = sun8i_layers_init(drm);
+	else
+		drv->layers = sun4i_layers_init(drm);
 	if (IS_ERR(drv->layers)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
 		ret = PTR_ERR(drv->layers);
@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_drv_of_table[] = {
-	{ .compatible = "allwinner,sun5i-a13-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
-	{ .compatible = "allwinner,sun8i-a33-display-engine" },
+	{
+		.compatible = "allwinner,sun5i-a13-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-a33-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-display-engine",
+		.data = (void *)DE_VER_DE2,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 597353eab728..cf1da95b85bd 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -18,6 +18,7 @@
 
 struct sun4i_drv {
 	struct sun4i_backend	*backend;
+	struct sun8i_mixer	*mixer;
 	struct sun4i_crtc	*crtc;
 	struct sun4i_tcon	*tcon;
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index 5d53c977bca5..6dcdac38ab69 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,52 +16,66 @@
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
 struct sun4i_plane_desc {
 	       enum drm_plane_type     type;
+	       /* Pipe is not used in sun8i-mixer */
 	       u8                      pipe;
 	       const uint32_t          *formats;
 	       uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+static int sun4i_layer_atomic_check(struct drm_plane *plane,
 					    struct drm_plane_state *state)
 {
 	return 0;
 }
 
-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
+	struct sun8i_mixer *mixer = drv->mixer;
 
-	sun4i_backend_layer_enable(backend, layer->id, false);
+	if (backend)
+		sun4i_backend_layer_enable(backend, layer->id, false);
+	else if (mixer)
+		sun8i_mixer_layer_enable(mixer, layer->id, false);
 }
 
-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+static void sun4i_layer_atomic_update(struct drm_plane *plane,
 					      struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
-
-	sun4i_backend_update_layer_coord(backend, layer->id, plane);
-	sun4i_backend_update_layer_formats(backend, layer->id, plane);
-	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
-	sun4i_backend_layer_enable(backend, layer->id, true);
+	struct sun8i_mixer *mixer = drv->mixer;
+
+	if (backend) {
+		sun4i_backend_update_layer_coord(backend, layer->id, plane);
+		sun4i_backend_update_layer_formats(backend, layer->id, plane);
+		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+		sun4i_backend_layer_enable(backend, layer->id, true);
+	} else if (mixer) {
+		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
+		sun8i_mixer_layer_enable(mixer, layer->id, true);
+	}
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-	.atomic_check	= sun4i_backend_layer_atomic_check,
-	.atomic_disable	= sun4i_backend_layer_atomic_disable,
-	.atomic_update	= sun4i_backend_layer_atomic_update,
+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
+	.atomic_check	= sun4i_layer_atomic_check,
+	.atomic_disable	= sun4i_layer_atomic_disable,
+	.atomic_update	= sun4i_layer_atomic_update,
 };
 
-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+static const struct drm_plane_funcs sun4i_layer_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
 	.destroy		= drm_plane_cleanup,
@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
+static const uint32_t sun8i_mixer_layer_formats[] = {
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XRGB8888,
+};
+
 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	{
 		.type = DRM_PLANE_TYPE_PRIMARY,
@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	},
 };
 
+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
+	{
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+	{
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 						const struct sun4i_plane_desc *plane)
 {
@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 
 	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
-				       &sun4i_backend_layer_funcs,
+				       &sun4i_layer_funcs,
 				       plane->formats, plane->nformats,
 				       plane->type, NULL);
 	if (ret) {
@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 	}
 
 	drm_plane_helper_add(&layer->plane,
-			     &sun4i_backend_layer_helper_funcs);
+			     &sun4i_layer_helper_funcs);
 	layer->drv = drv;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
 
 	return layers;
 }
+
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
+{
+	struct sun4i_layer **layers;
+	int i;
+
+	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
+			      sizeof(**layers), GFP_KERNEL);
+	if (!layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
+		struct sun4i_layer *layer = layers[i];
+
+		layer = sun4i_layer_init_one(drm, plane);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
+
+		layer->id = i;
+	};
+
+	return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index a2f65d7a3f4e..f7b9e5daea50 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
 }
 
 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
 
 #endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..9427b57240d3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * Based on sun4i_backend.c, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun8i_mixer.h"
+#include "sun4i_drv.h"
+
+#define SUN8I_DRAM_OFFSET 0x40000000
+
+#if defined CONFIG_DRM_SUN4I_DE2
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+	DRM_DEBUG_DRIVER("Committing changes\n");
+
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+EXPORT_SYMBOL(sun8i_mixer_commit);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+
+	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	/* Set the alpha configuration */
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
+}
+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+					     u32 format, u32 *mode)
+{
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+	    (format == DRM_FORMAT_ARGB8888))
+		format = DRM_FORMAT_XRGB8888;
+
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
+		break;
+
+	case DRM_FORMAT_RGB888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int i;
+
+	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 state->crtc_w, state->crtc_h);
+		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating blender size\n");
+		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+			regmap_write(mixer->regs,
+				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
+				     SUN8I_MIXER_SIZE(state->crtc_w,
+						      state->crtc_h));
+		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating channel size\n");
+		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+	}
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     fb->pitches[0]);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+			 state->crtc_w, state->crtc_h);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->crtc_x, state->crtc_y);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	bool interlaced = false;
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int ret;
+
+	if (plane->state->crtc)
+		interlaced = plane->state->crtc->state->adjusted_mode.flags
+			& DRM_MODE_FLAG_INTERLACE;
+
+	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+			   interlaced ?
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
+
+	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+			 interlaced ? "on" : "off");
+
+	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
+						&val);
+	if (ret) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return ret;
+	}
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	uint32_t paddr_u32;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+	paddr += (state->src_x >> 16) * bpp;
+	paddr += (state->src_y >> 16) * fb->pitches[0];
+	paddr -= SUN8I_DRAM_OFFSET;
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	paddr_u32 = (uint32_t) paddr;
+
+	regmap_write(mixer->regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     paddr_u32);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0xbffc, /* guessed */
+};
+
+static int sun8i_mixer_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct sun4i_drv *drv = drm->dev_private;
+	struct sun8i_mixer *mixer;
+	struct resource *res;
+	void __iomem *regs;
+	int i, ret;
+
+	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	dev_set_drvdata(dev, mixer);
+	drv->mixer = mixer;
+
+	mixer->cfg = of_device_get_match_data(dev);
+	if (!mixer->cfg)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mixer->regs = devm_regmap_init_mmio(dev, regs,
+					      &sun8i_mixer_regmap_config);
+	if (IS_ERR(mixer->regs)) {
+		dev_err(dev, "Couldn't create the mixer regmap\n");
+		return PTR_ERR(mixer->regs);
+	}
+
+	mixer->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(mixer->reset)) {
+		dev_err(dev, "Couldn't get our reset line\n");
+		return PTR_ERR(mixer->reset);
+	}
+
+	ret = reset_control_deassert(mixer->reset);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert our reset line\n");
+		return ret;
+	}
+
+	mixer->bus_clk = devm_clk_get(dev, "bus");
+	if (IS_ERR(mixer->bus_clk)) {
+		dev_err(dev, "Couldn't get the mixer bus clock\n");
+		ret = PTR_ERR(mixer->bus_clk);
+		goto err_assert_reset;
+	}
+	clk_prepare_enable(mixer->bus_clk);
+
+	mixer->mod_clk = devm_clk_get(dev, "mod");
+	if (IS_ERR(mixer->mod_clk)) {
+		dev_err(dev, "Couldn't get the mixer module clock\n");
+		ret = PTR_ERR(mixer->mod_clk);
+		goto err_disable_bus_clk;
+	}
+	clk_prepare_enable(mixer->mod_clk);
+
+	/* Reset the registers */
+	for (i = 0x0; i < 0x20000; i += 4)
+		regmap_write(mixer->regs, i, 0);
+
+	/* Enable the mixer */
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
+		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+	/* Initialize blender */
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
+		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
+		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+		regmap_write(mixer->regs,
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
+
+	/* Select the first UI channel */
+	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
+			 mixer->cfg->vi_num);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
+		     mixer->cfg->vi_num);
+
+	return 0;
+
+	clk_disable_unprepare(mixer->mod_clk);
+err_disable_bus_clk:
+	clk_disable_unprepare(mixer->bus_clk);
+err_assert_reset:
+	reset_control_assert(mixer->reset);
+	return ret;
+}
+
+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(mixer->mod_clk);
+	clk_disable_unprepare(mixer->bus_clk);
+	reset_control_assert(mixer->reset);
+}
+
+static const struct component_ops sun8i_mixer_ops = {
+	.bind	= sun8i_mixer_bind,
+	.unbind	= sun8i_mixer_unbind,
+};
+
+static int sun8i_mixer_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &sun8i_mixer_ops);
+}
+
+static int sun8i_mixer_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sun8i_mixer_ops);
+
+	return 0;
+}
+
+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
+	.vi_num = 2,
+	.ui_num = 1,
+};
+
+static const struct of_device_id sun8i_mixer_of_table[] = {
+	{
+		.compatible = "allwinner,sun8i-v3s-de2-mixer",
+		.data = &sun8i_v3s_mixer_cfg
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
+
+static struct platform_driver sun8i_mixer_platform_driver = {
+	.probe		= sun8i_mixer_probe,
+	.remove		= sun8i_mixer_remove,
+	.driver		= {
+		.name		= "sun8i-mixer",
+		.of_match_table	= sun8i_mixer_of_table,
+	},
+};
+module_platform_driver(sun8i_mixer_platform_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
+#else /* DRM_SUN4I_DE2 */
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+}
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_DRM_SUN4I_DE2 */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..0bc134b3bc98
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_layer.h"
+
+#define SUN8I_MIXER_MAX_CHAN_COUNT		4
+
+#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
+#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
+
+#define SUN8I_MIXER_GLOBAL_CTL			0x0
+#define SUN8I_MIXER_GLOBAL_STATUS		0x4
+#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
+#define SUN8I_MIXER_GLOBAL_SIZE			0xc
+
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ROUTE			0x1080
+#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
+#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
+#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
+#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
+#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
+#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
+
+/* The following numbers are some still unknown magic numbers */
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
+#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
+#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
+
+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
+
+/*
+ * VI channels are not used now, but the support of them may be introduced in
+ * the future.
+ */
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
+
+/*
+ * These sun-engines are still unknown now, the EN registers are here only to
+ * be used to disable these sub-engines.
+ */
+#define SUN8I_MIXER_VSU_EN			0x20000
+#define SUN8I_MIXER_GSU1_EN			0x30000
+#define SUN8I_MIXER_GSU2_EN			0x40000
+#define SUN8I_MIXER_GSU3_EN			0x50000
+#define SUN8I_MIXER_FCE_EN			0xa0000
+#define SUN8I_MIXER_BWS_EN			0xa2000
+#define SUN8I_MIXER_LTI_EN			0xa4000
+#define SUN8I_MIXER_PEAK_EN			0xa6000
+#define SUN8I_MIXER_ASE_EN			0xa8000
+#define SUN8I_MIXER_FCC_EN			0xaa000
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+
+struct sun8i_mixer_cfg {
+	int		vi_num;
+	int		ui_num;
+};
+
+struct sun8i_mixer {
+	struct regmap			*regs;
+
+	const struct sun8i_mixer_cfg	*cfg;
+
+	struct reset_control		*reset;
+
+	struct clk			*bus_clk;
+	struct clk			*mod_clk;
+};
+
+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane);
+#endif /* _SUN8I_MIXER_H_ */
-- 
2.11.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 15:18     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:18 UTC (permalink / raw)
  To: linux-arm-kernel

Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
in a new "Display Engine" (mixers instead of old backends and
frontends).

Add support for the mixer on Allwinner V3s SoC; it's the simplest one.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/gpu/drm/sun4i/Kconfig       |   8 +
 drivers/gpu/drm/sun4i/Makefile      |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
 drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
 drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
 9 files changed, 674 insertions(+), 23 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..8df401fff145 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,11 @@ config DRM_SUN4I
 	  Choose this option if you have an Allwinner SoC with a
 	  Display Engine. If M is selected the module will be called
 	  sun4i-drm.
+
+config DRM_SUN4I_DE2
+	bool "Support Display Engine 2.0"
+	depends on DRM_SUN4I
+	default MACH_SUN8I
+	help
+	  Choose this option if you have an Allwinner SoC with a
+	  "Display Engine 2.0".
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index d625a82a6e5f..890e6e50dfee 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a192210574f..4d2228454726 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,6 +25,7 @@
 #include <video/videomode.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	DRM_DEBUG_DRIVER("Committing plane changes\n");
 
-	sun4i_backend_commit(drv->backend);
+	if (drv->backend)
+		sun4i_backend_commit(drv->backend);
+	else if (drv->mixer)
+		sun8i_mixer_commit(drv->mixer);
 
 	if (event) {
 		crtc->state->event = NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 4ce665349f6b..58af38f5e833 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -20,12 +20,17 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/of_device.h>
+
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_framebuffer.h"
 #include "sun4i_layer.h"
 #include "sun4i_tcon.h"
 
+#define DE_VER_DE	0
+#define DE_VER_DE2	1
+
 static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
 	struct sun4i_drv *drv = drm->dev_private;
@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
 {
 	struct drm_device *drm;
 	struct sun4i_drv *drv;
-	int ret;
+	int ret, de_ver;
+
+	de_ver = (int)of_device_get_match_data(dev);
 
 	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 	if (IS_ERR(drm))
@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
 	}
 
 	/* Create our layers */
-	drv->layers = sun4i_layers_init(drm);
+	if (de_ver == DE_VER_DE2)
+		drv->layers = sun8i_layers_init(drm);
+	else
+		drv->layers = sun4i_layers_init(drm);
 	if (IS_ERR(drv->layers)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
 		ret = PTR_ERR(drv->layers);
@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_drv_of_table[] = {
-	{ .compatible = "allwinner,sun5i-a13-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
-	{ .compatible = "allwinner,sun8i-a33-display-engine" },
+	{
+		.compatible = "allwinner,sun5i-a13-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-a33-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-display-engine",
+		.data = (void *)DE_VER_DE2,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 597353eab728..cf1da95b85bd 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -18,6 +18,7 @@
 
 struct sun4i_drv {
 	struct sun4i_backend	*backend;
+	struct sun8i_mixer	*mixer;
 	struct sun4i_crtc	*crtc;
 	struct sun4i_tcon	*tcon;
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index 5d53c977bca5..6dcdac38ab69 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,52 +16,66 @@
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
 struct sun4i_plane_desc {
 	       enum drm_plane_type     type;
+	       /* Pipe is not used in sun8i-mixer */
 	       u8                      pipe;
 	       const uint32_t          *formats;
 	       uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+static int sun4i_layer_atomic_check(struct drm_plane *plane,
 					    struct drm_plane_state *state)
 {
 	return 0;
 }
 
-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
+	struct sun8i_mixer *mixer = drv->mixer;
 
-	sun4i_backend_layer_enable(backend, layer->id, false);
+	if (backend)
+		sun4i_backend_layer_enable(backend, layer->id, false);
+	else if (mixer)
+		sun8i_mixer_layer_enable(mixer, layer->id, false);
 }
 
-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+static void sun4i_layer_atomic_update(struct drm_plane *plane,
 					      struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
-
-	sun4i_backend_update_layer_coord(backend, layer->id, plane);
-	sun4i_backend_update_layer_formats(backend, layer->id, plane);
-	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
-	sun4i_backend_layer_enable(backend, layer->id, true);
+	struct sun8i_mixer *mixer = drv->mixer;
+
+	if (backend) {
+		sun4i_backend_update_layer_coord(backend, layer->id, plane);
+		sun4i_backend_update_layer_formats(backend, layer->id, plane);
+		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+		sun4i_backend_layer_enable(backend, layer->id, true);
+	} else if (mixer) {
+		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
+		sun8i_mixer_layer_enable(mixer, layer->id, true);
+	}
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-	.atomic_check	= sun4i_backend_layer_atomic_check,
-	.atomic_disable	= sun4i_backend_layer_atomic_disable,
-	.atomic_update	= sun4i_backend_layer_atomic_update,
+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
+	.atomic_check	= sun4i_layer_atomic_check,
+	.atomic_disable	= sun4i_layer_atomic_disable,
+	.atomic_update	= sun4i_layer_atomic_update,
 };
 
-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+static const struct drm_plane_funcs sun4i_layer_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
 	.destroy		= drm_plane_cleanup,
@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
+static const uint32_t sun8i_mixer_layer_formats[] = {
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XRGB8888,
+};
+
 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	{
 		.type = DRM_PLANE_TYPE_PRIMARY,
@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	},
 };
 
+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
+	{
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+	{
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 						const struct sun4i_plane_desc *plane)
 {
@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 
 	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
-				       &sun4i_backend_layer_funcs,
+				       &sun4i_layer_funcs,
 				       plane->formats, plane->nformats,
 				       plane->type, NULL);
 	if (ret) {
@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 	}
 
 	drm_plane_helper_add(&layer->plane,
-			     &sun4i_backend_layer_helper_funcs);
+			     &sun4i_layer_helper_funcs);
 	layer->drv = drv;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
 
 	return layers;
 }
+
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
+{
+	struct sun4i_layer **layers;
+	int i;
+
+	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
+			      sizeof(**layers), GFP_KERNEL);
+	if (!layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
+		struct sun4i_layer *layer = layers[i];
+
+		layer = sun4i_layer_init_one(drm, plane);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
+
+		layer->id = i;
+	};
+
+	return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index a2f65d7a3f4e..f7b9e5daea50 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
 }
 
 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
 
 #endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..9427b57240d3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * Based on sun4i_backend.c, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun8i_mixer.h"
+#include "sun4i_drv.h"
+
+#define SUN8I_DRAM_OFFSET 0x40000000
+
+#if defined CONFIG_DRM_SUN4I_DE2
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+	DRM_DEBUG_DRIVER("Committing changes\n");
+
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+EXPORT_SYMBOL(sun8i_mixer_commit);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+
+	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	/* Set the alpha configuration */
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
+}
+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+					     u32 format, u32 *mode)
+{
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+	    (format == DRM_FORMAT_ARGB8888))
+		format = DRM_FORMAT_XRGB8888;
+
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
+		break;
+
+	case DRM_FORMAT_RGB888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int i;
+
+	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 state->crtc_w, state->crtc_h);
+		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating blender size\n");
+		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+			regmap_write(mixer->regs,
+				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
+				     SUN8I_MIXER_SIZE(state->crtc_w,
+						      state->crtc_h));
+		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating channel size\n");
+		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+	}
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     fb->pitches[0]);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+			 state->crtc_w, state->crtc_h);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->crtc_x, state->crtc_y);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	bool interlaced = false;
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int ret;
+
+	if (plane->state->crtc)
+		interlaced = plane->state->crtc->state->adjusted_mode.flags
+			& DRM_MODE_FLAG_INTERLACE;
+
+	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+			   interlaced ?
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
+
+	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+			 interlaced ? "on" : "off");
+
+	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
+						&val);
+	if (ret) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return ret;
+	}
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	uint32_t paddr_u32;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+	paddr += (state->src_x >> 16) * bpp;
+	paddr += (state->src_y >> 16) * fb->pitches[0];
+	paddr -= SUN8I_DRAM_OFFSET;
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	paddr_u32 = (uint32_t) paddr;
+
+	regmap_write(mixer->regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     paddr_u32);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0xbffc, /* guessed */
+};
+
+static int sun8i_mixer_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct sun4i_drv *drv = drm->dev_private;
+	struct sun8i_mixer *mixer;
+	struct resource *res;
+	void __iomem *regs;
+	int i, ret;
+
+	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	dev_set_drvdata(dev, mixer);
+	drv->mixer = mixer;
+
+	mixer->cfg = of_device_get_match_data(dev);
+	if (!mixer->cfg)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mixer->regs = devm_regmap_init_mmio(dev, regs,
+					      &sun8i_mixer_regmap_config);
+	if (IS_ERR(mixer->regs)) {
+		dev_err(dev, "Couldn't create the mixer regmap\n");
+		return PTR_ERR(mixer->regs);
+	}
+
+	mixer->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(mixer->reset)) {
+		dev_err(dev, "Couldn't get our reset line\n");
+		return PTR_ERR(mixer->reset);
+	}
+
+	ret = reset_control_deassert(mixer->reset);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert our reset line\n");
+		return ret;
+	}
+
+	mixer->bus_clk = devm_clk_get(dev, "bus");
+	if (IS_ERR(mixer->bus_clk)) {
+		dev_err(dev, "Couldn't get the mixer bus clock\n");
+		ret = PTR_ERR(mixer->bus_clk);
+		goto err_assert_reset;
+	}
+	clk_prepare_enable(mixer->bus_clk);
+
+	mixer->mod_clk = devm_clk_get(dev, "mod");
+	if (IS_ERR(mixer->mod_clk)) {
+		dev_err(dev, "Couldn't get the mixer module clock\n");
+		ret = PTR_ERR(mixer->mod_clk);
+		goto err_disable_bus_clk;
+	}
+	clk_prepare_enable(mixer->mod_clk);
+
+	/* Reset the registers */
+	for (i = 0x0; i < 0x20000; i += 4)
+		regmap_write(mixer->regs, i, 0);
+
+	/* Enable the mixer */
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
+		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+	/* Initialize blender */
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
+		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
+		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+		regmap_write(mixer->regs,
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
+
+	/* Select the first UI channel */
+	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
+			 mixer->cfg->vi_num);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
+		     mixer->cfg->vi_num);
+
+	return 0;
+
+	clk_disable_unprepare(mixer->mod_clk);
+err_disable_bus_clk:
+	clk_disable_unprepare(mixer->bus_clk);
+err_assert_reset:
+	reset_control_assert(mixer->reset);
+	return ret;
+}
+
+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(mixer->mod_clk);
+	clk_disable_unprepare(mixer->bus_clk);
+	reset_control_assert(mixer->reset);
+}
+
+static const struct component_ops sun8i_mixer_ops = {
+	.bind	= sun8i_mixer_bind,
+	.unbind	= sun8i_mixer_unbind,
+};
+
+static int sun8i_mixer_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &sun8i_mixer_ops);
+}
+
+static int sun8i_mixer_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sun8i_mixer_ops);
+
+	return 0;
+}
+
+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
+	.vi_num = 2,
+	.ui_num = 1,
+};
+
+static const struct of_device_id sun8i_mixer_of_table[] = {
+	{
+		.compatible = "allwinner,sun8i-v3s-de2-mixer",
+		.data = &sun8i_v3s_mixer_cfg
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
+
+static struct platform_driver sun8i_mixer_platform_driver = {
+	.probe		= sun8i_mixer_probe,
+	.remove		= sun8i_mixer_remove,
+	.driver		= {
+		.name		= "sun8i-mixer",
+		.of_match_table	= sun8i_mixer_of_table,
+	},
+};
+module_platform_driver(sun8i_mixer_platform_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
+#else /* DRM_SUN4I_DE2 */
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+}
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_DRM_SUN4I_DE2 */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..0bc134b3bc98
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_layer.h"
+
+#define SUN8I_MIXER_MAX_CHAN_COUNT		4
+
+#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
+#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
+
+#define SUN8I_MIXER_GLOBAL_CTL			0x0
+#define SUN8I_MIXER_GLOBAL_STATUS		0x4
+#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
+#define SUN8I_MIXER_GLOBAL_SIZE			0xc
+
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ROUTE			0x1080
+#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
+#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
+#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
+#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
+#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
+#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
+
+/* The following numbers are some still unknown magic numbers */
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
+#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
+#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
+
+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
+
+/*
+ * VI channels are not used now, but the support of them may be introduced in
+ * the future.
+ */
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
+
+/*
+ * These sun-engines are still unknown now, the EN registers are here only to
+ * be used to disable these sub-engines.
+ */
+#define SUN8I_MIXER_VSU_EN			0x20000
+#define SUN8I_MIXER_GSU1_EN			0x30000
+#define SUN8I_MIXER_GSU2_EN			0x40000
+#define SUN8I_MIXER_GSU3_EN			0x50000
+#define SUN8I_MIXER_FCE_EN			0xa0000
+#define SUN8I_MIXER_BWS_EN			0xa2000
+#define SUN8I_MIXER_LTI_EN			0xa4000
+#define SUN8I_MIXER_PEAK_EN			0xa6000
+#define SUN8I_MIXER_ASE_EN			0xa8000
+#define SUN8I_MIXER_FCC_EN			0xaa000
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+
+struct sun8i_mixer_cfg {
+	int		vi_num;
+	int		ui_num;
+};
+
+struct sun8i_mixer {
+	struct regmap			*regs;
+
+	const struct sun8i_mixer_cfg	*cfg;
+
+	struct reset_control		*reset;
+
+	struct clk			*bus_clk;
+	struct clk			*mod_clk;
+};
+
+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane);
+#endif /* _SUN8I_MIXER_H_ */
-- 
2.11.1

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

* Re: [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
@ 2017-02-22 19:09       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 19:09 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk, devicetree, linux-arm-kernel,
	linux-kernel, dri-devel, linux-sunxi

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

Hi,

On Wed, Feb 22, 2017 at 11:18:48PM +0800, Icenowy Zheng wrote:
> +config SUNXI_DE2_CCU
> +	bool "Support for the Allwinner SoCs DE2 CCU"
> +	select SUNXI_CCU_DIV
> +	select SUNXI_CCU_GATE
> +	default n

This is already the default.

> +
>  endif
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index 6feaac0c5600..1d36c40384a6 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
> +obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o

sun8i-de2 please

> diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> new file mode 100644
> index 000000000000..4259c145d5da
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> @@ -0,0 +1,204 @@
> +/*
> + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.

Wrong author?

> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_div.h"
> +#include "ccu_gate.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu-sunxi-de2.h"
> +
> +static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
> +		      0x04, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
> +		      0x04, BIT(1), 0);
> +static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
> +		      0x04, BIT(2), 0);
> +
> +static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
> +		      0x00, BIT(0), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
> +		      0x00, BIT(1), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
> +		      0x00, BIT(2), CLK_SET_RATE_PARENT);
> +
> +static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
> +		   CLK_SET_RATE_PARENT);
> +
> +static struct ccu_common *sunxi_de2_clks[] = {
> +	&mixer0_clk.common,
> +	&mixer1_clk.common,
> +	&wb_clk.common,
> +
> +	&bus_mixer0_clk.common,
> +	&bus_mixer1_clk.common,
> +	&bus_wb_clk.common,
> +
> +	&mixer0_div_clk.common,
> +	&mixer1_div_clk.common,
> +	&wb_div_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
> +	.hws	= {
> +		[CLK_MIXER0]		= &mixer0_clk.common.hw,
> +		[CLK_MIXER1]		= &mixer1_clk.common.hw,
> +		[CLK_WB]		= &wb_clk.common.hw,
> +
> +		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
> +		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
> +		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
> +
> +		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
> +		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
> +		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
> +	},
> +	.num	= CLK_NUMBER,
> +};
> +
> +static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	/*
> +	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
> +	 * only RST_WB is exported here.
> +	 */
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static struct ccu_reset_map sun50i_a64_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	[RST_MIXER1]	= { 0x08, BIT(1) },
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun8i_a83t_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
> +};
> +
> +static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun50i_a64_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
> +};
> +
> +static int sunxi_de2_clk_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct clk *bus_clk;
> +	struct reset_control *rstc;
> +	void __iomem *reg;
> +	const struct sunxi_ccu_desc *ccu_desc;
> +	int ret;
> +
> +	ccu_desc = of_device_get_match_data(&pdev->dev);
> +	if (!ccu_desc)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	reg = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(reg))
> +		return PTR_ERR(reg);
> +
> +	bus_clk = devm_clk_get(&pdev->dev, "bus");
> +	if (IS_ERR(bus_clk)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
> +	if (IS_ERR(rstc)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev,
> +				"Couldn't get reset control: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* The bus clock needs to be enabled for us to access the registers */
> +	ret = clk_prepare_enable(bus_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
> +		return ret;
> +	}

That looks inefficient if none of the clocks are enabled. Maybe you
can use CLK_OPS_PARENT_ENABLE?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
@ 2017-02-22 19:09       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 19:09 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

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

Hi,

On Wed, Feb 22, 2017 at 11:18:48PM +0800, Icenowy Zheng wrote:
> +config SUNXI_DE2_CCU
> +	bool "Support for the Allwinner SoCs DE2 CCU"
> +	select SUNXI_CCU_DIV
> +	select SUNXI_CCU_GATE
> +	default n

This is already the default.

> +
>  endif
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index 6feaac0c5600..1d36c40384a6 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
> +obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o

sun8i-de2 please

> diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> new file mode 100644
> index 000000000000..4259c145d5da
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> @@ -0,0 +1,204 @@
> +/*
> + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.

Wrong author?

> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_div.h"
> +#include "ccu_gate.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu-sunxi-de2.h"
> +
> +static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
> +		      0x04, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
> +		      0x04, BIT(1), 0);
> +static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
> +		      0x04, BIT(2), 0);
> +
> +static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
> +		      0x00, BIT(0), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
> +		      0x00, BIT(1), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
> +		      0x00, BIT(2), CLK_SET_RATE_PARENT);
> +
> +static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
> +		   CLK_SET_RATE_PARENT);
> +
> +static struct ccu_common *sunxi_de2_clks[] = {
> +	&mixer0_clk.common,
> +	&mixer1_clk.common,
> +	&wb_clk.common,
> +
> +	&bus_mixer0_clk.common,
> +	&bus_mixer1_clk.common,
> +	&bus_wb_clk.common,
> +
> +	&mixer0_div_clk.common,
> +	&mixer1_div_clk.common,
> +	&wb_div_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
> +	.hws	= {
> +		[CLK_MIXER0]		= &mixer0_clk.common.hw,
> +		[CLK_MIXER1]		= &mixer1_clk.common.hw,
> +		[CLK_WB]		= &wb_clk.common.hw,
> +
> +		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
> +		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
> +		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
> +
> +		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
> +		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
> +		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
> +	},
> +	.num	= CLK_NUMBER,
> +};
> +
> +static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	/*
> +	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
> +	 * only RST_WB is exported here.
> +	 */
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static struct ccu_reset_map sun50i_a64_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	[RST_MIXER1]	= { 0x08, BIT(1) },
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun8i_a83t_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
> +};
> +
> +static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun50i_a64_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
> +};
> +
> +static int sunxi_de2_clk_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct clk *bus_clk;
> +	struct reset_control *rstc;
> +	void __iomem *reg;
> +	const struct sunxi_ccu_desc *ccu_desc;
> +	int ret;
> +
> +	ccu_desc = of_device_get_match_data(&pdev->dev);
> +	if (!ccu_desc)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	reg = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(reg))
> +		return PTR_ERR(reg);
> +
> +	bus_clk = devm_clk_get(&pdev->dev, "bus");
> +	if (IS_ERR(bus_clk)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
> +	if (IS_ERR(rstc)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev,
> +				"Couldn't get reset control: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* The bus clock needs to be enabled for us to access the registers */
> +	ret = clk_prepare_enable(bus_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
> +		return ret;
> +	}

That looks inefficient if none of the clocks are enabled. Maybe you
can use CLK_OPS_PARENT_ENABLE?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* Re: [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
@ 2017-02-22 19:09       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 19:09 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, David Airlie,
	linux-sunxi, linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
	linux-clk, linux-arm-kernel


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

Hi,

On Wed, Feb 22, 2017 at 11:18:48PM +0800, Icenowy Zheng wrote:
> +config SUNXI_DE2_CCU
> +	bool "Support for the Allwinner SoCs DE2 CCU"
> +	select SUNXI_CCU_DIV
> +	select SUNXI_CCU_GATE
> +	default n

This is already the default.

> +
>  endif
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index 6feaac0c5600..1d36c40384a6 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
> +obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o

sun8i-de2 please

> diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> new file mode 100644
> index 000000000000..4259c145d5da
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> @@ -0,0 +1,204 @@
> +/*
> + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.

Wrong author?

> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_div.h"
> +#include "ccu_gate.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu-sunxi-de2.h"
> +
> +static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
> +		      0x04, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
> +		      0x04, BIT(1), 0);
> +static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
> +		      0x04, BIT(2), 0);
> +
> +static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
> +		      0x00, BIT(0), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
> +		      0x00, BIT(1), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
> +		      0x00, BIT(2), CLK_SET_RATE_PARENT);
> +
> +static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
> +		   CLK_SET_RATE_PARENT);
> +
> +static struct ccu_common *sunxi_de2_clks[] = {
> +	&mixer0_clk.common,
> +	&mixer1_clk.common,
> +	&wb_clk.common,
> +
> +	&bus_mixer0_clk.common,
> +	&bus_mixer1_clk.common,
> +	&bus_wb_clk.common,
> +
> +	&mixer0_div_clk.common,
> +	&mixer1_div_clk.common,
> +	&wb_div_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
> +	.hws	= {
> +		[CLK_MIXER0]		= &mixer0_clk.common.hw,
> +		[CLK_MIXER1]		= &mixer1_clk.common.hw,
> +		[CLK_WB]		= &wb_clk.common.hw,
> +
> +		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
> +		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
> +		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
> +
> +		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
> +		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
> +		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
> +	},
> +	.num	= CLK_NUMBER,
> +};
> +
> +static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	/*
> +	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
> +	 * only RST_WB is exported here.
> +	 */
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static struct ccu_reset_map sun50i_a64_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	[RST_MIXER1]	= { 0x08, BIT(1) },
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun8i_a83t_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
> +};
> +
> +static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun50i_a64_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
> +};
> +
> +static int sunxi_de2_clk_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct clk *bus_clk;
> +	struct reset_control *rstc;
> +	void __iomem *reg;
> +	const struct sunxi_ccu_desc *ccu_desc;
> +	int ret;
> +
> +	ccu_desc = of_device_get_match_data(&pdev->dev);
> +	if (!ccu_desc)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	reg = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(reg))
> +		return PTR_ERR(reg);
> +
> +	bus_clk = devm_clk_get(&pdev->dev, "bus");
> +	if (IS_ERR(bus_clk)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
> +	if (IS_ERR(rstc)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev,
> +				"Couldn't get reset control: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* The bus clock needs to be enabled for us to access the registers */
> +	ret = clk_prepare_enable(bus_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
> +		return ret;
> +	}

That looks inefficient if none of the clocks are enabled. Maybe you
can use CLK_OPS_PARENT_ENABLE?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 2/8] clk: sunxi-ng: add support for DE2 CCU
@ 2017-02-22 19:09       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 19:09 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Wed, Feb 22, 2017 at 11:18:48PM +0800, Icenowy Zheng wrote:
> +config SUNXI_DE2_CCU
> +	bool "Support for the Allwinner SoCs DE2 CCU"
> +	select SUNXI_CCU_DIV
> +	select SUNXI_CCU_GATE
> +	default n

This is already the default.

> +
>  endif
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index 6feaac0c5600..1d36c40384a6 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -28,3 +28,4 @@ obj-$(CONFIG_SUN8I_V3S_CCU)	+= ccu-sun8i-v3s.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-de.o
>  obj-$(CONFIG_SUN9I_A80_CCU)	+= ccu-sun9i-a80-usb.o
> +obj-$(CONFIG_SUNXI_DE2_CCU)	+= ccu-sunxi-de2.o

sun8i-de2 please

> diff --git a/drivers/clk/sunxi-ng/ccu-sunxi-de2.c b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> new file mode 100644
> index 000000000000..4259c145d5da
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sunxi-de2.c
> @@ -0,0 +1,204 @@
> +/*
> + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.

Wrong author?

> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_div.h"
> +#include "ccu_gate.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu-sunxi-de2.h"
> +
> +static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
> +		      0x04, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
> +		      0x04, BIT(1), 0);
> +static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
> +		      0x04, BIT(2), 0);
> +
> +static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
> +		      0x00, BIT(0), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
> +		      0x00, BIT(1), CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
> +		      0x00, BIT(2), CLK_SET_RATE_PARENT);
> +
> +static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
> +		   CLK_SET_RATE_PARENT);
> +static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
> +		   CLK_SET_RATE_PARENT);
> +
> +static struct ccu_common *sunxi_de2_clks[] = {
> +	&mixer0_clk.common,
> +	&mixer1_clk.common,
> +	&wb_clk.common,
> +
> +	&bus_mixer0_clk.common,
> +	&bus_mixer1_clk.common,
> +	&bus_wb_clk.common,
> +
> +	&mixer0_div_clk.common,
> +	&mixer1_div_clk.common,
> +	&wb_div_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
> +	.hws	= {
> +		[CLK_MIXER0]		= &mixer0_clk.common.hw,
> +		[CLK_MIXER1]		= &mixer1_clk.common.hw,
> +		[CLK_WB]		= &wb_clk.common.hw,
> +
> +		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
> +		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
> +		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
> +
> +		[CLK_MIXER0_DIV]	= &mixer1_div_clk.common.hw,
> +		[CLK_MIXER1_DIV]	= &mixer0_div_clk.common.hw,
> +		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
> +	},
> +	.num	= CLK_NUMBER,
> +};
> +
> +static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	/*
> +	 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
> +	 * only RST_WB is exported here.
> +	 */
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static struct ccu_reset_map sun50i_a64_de2_resets[] = {
> +	[RST_MIXER0]	= { 0x08, BIT(0) },
> +	[RST_MIXER1]	= { 0x08, BIT(1) },
> +	[RST_WB]	= { 0x08, BIT(2) },
> +};
> +
> +static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun8i_a83t_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
> +};
> +
> +static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
> +	.ccu_clks	= sunxi_de2_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sunxi_de2_clks),
> +
> +	.hw_clks	= &sunxi_de2_hw_clks,
> +
> +	.resets		= sun50i_a64_de2_resets,
> +	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
> +};
> +
> +static int sunxi_de2_clk_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct clk *bus_clk;
> +	struct reset_control *rstc;
> +	void __iomem *reg;
> +	const struct sunxi_ccu_desc *ccu_desc;
> +	int ret;
> +
> +	ccu_desc = of_device_get_match_data(&pdev->dev);
> +	if (!ccu_desc)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	reg = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(reg))
> +		return PTR_ERR(reg);
> +
> +	bus_clk = devm_clk_get(&pdev->dev, "bus");
> +	if (IS_ERR(bus_clk)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
> +	if (IS_ERR(rstc)) {
> +		ret = PTR_ERR(bus_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(&pdev->dev,
> +				"Couldn't get reset control: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* The bus clock needs to be enabled for us to access the registers */
> +	ret = clk_prepare_enable(bus_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
> +		return ret;
> +	}

That looks inefficient if none of the clocks are enabled. Maybe you
can use CLK_OPS_PARENT_ENABLE?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170222/f064153a/attachment.sig>

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 15:18     ` Icenowy Zheng
  (?)
  (?)
@ 2017-02-22 22:01       ` Jernej Škrabec
  -1 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-22 22:01 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Maxime Ripard, Chen-Yu Tsai, David Airlie,
	Jean-Francois Moine, linux-clk, devicetree, linux-arm-kernel,
	linux-kernel, dri-devel, linux-sunxi

Hi,

Dne sreda, 22. februar 2017 ob 16:18:50 CET je Icenowy Zheng napisal(a):
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417
> ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h |
> 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
> 
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4a192210574f..4d2228454726
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc
> *crtc,
> 
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
> 
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);

Does it make sense to have everything in sun4i_{crtc,drv} files? Looking 
further it seems that there is a lot of differentiation based on DE1 vs DE2. I 
guess having separate sun8i_{crtc,drv} files would be more clean approach. It 
would enable easier extensions later as well.

Regards,
Jernej Skrabec

> 
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
> b/drivers/gpu/drm/sun4i/sun4i_drv.c index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
> 
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
> 
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int
> pipe) {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
> 
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
> 
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device
> *pdev) }
> 
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h
> b/drivers/gpu/drm/sun4i/sun4i_drv.h index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
> 
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
> b/drivers/gpu/drm/sun4i/sun4i_layer.c index 5d53c977bca5..6dcdac38ab69
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
> 
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
> 
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
> 
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
> 
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }
> 
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
> 
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
> 
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t
> sun4i_backend_layer_formats_overlay[] = { DRM_FORMAT_XRGB8888,
>  };
> 
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc
> sun4i_backend_planes[] = { },
>  };
> 
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, return ERR_PTR(-ENOMEM);
> 
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, }
> 
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
> 
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct
> drm_device *drm)
> 
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
> b/drivers/gpu/drm/sun4i/sun4i_layer.h index a2f65d7a3f4e..f7b9e5daea50
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
> 
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> 
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000
> +
> +#if defined CONFIG_DRM_SUN4I_DE2
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;
> +
> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);
> +
> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);
> +
> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h
> b/drivers/gpu/drm/sun4i/sun8i_mixer.h new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced
> in + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only
> to + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> --
> 2.11.1

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:01       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-22 22:01 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Jean-Francois Moine, devicetree, David Airlie, linux-sunxi,
	linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
	Maxime Ripard, linux-clk, linux-arm-kernel

Hi,

Dne sreda, 22. februar 2017 ob 16:18:50 CET je Icenowy Zheng napisal(a):
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417
> ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h |
> 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
> 
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4a192210574f..4d2228454726
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc
> *crtc,
> 
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
> 
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);

Does it make sense to have everything in sun4i_{crtc,drv} files? Looking 
further it seems that there is a lot of differentiation based on DE1 vs DE2. I 
guess having separate sun8i_{crtc,drv} files would be more clean approach. It 
would enable easier extensions later as well.

Regards,
Jernej Skrabec

> 
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
> b/drivers/gpu/drm/sun4i/sun4i_drv.c index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
> 
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
> 
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int
> pipe) {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
> 
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
> 
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device
> *pdev) }
> 
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h
> b/drivers/gpu/drm/sun4i/sun4i_drv.h index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
> 
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
> b/drivers/gpu/drm/sun4i/sun4i_layer.c index 5d53c977bca5..6dcdac38ab69
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
> 
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
> 
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
> 
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
> 
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }
> 
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
> 
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
> 
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t
> sun4i_backend_layer_formats_overlay[] = { DRM_FORMAT_XRGB8888,
>  };
> 
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc
> sun4i_backend_planes[] = { },
>  };
> 
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, return ERR_PTR(-ENOMEM);
> 
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, }
> 
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
> 
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct
> drm_device *drm)
> 
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
> b/drivers/gpu/drm/sun4i/sun4i_layer.h index a2f65d7a3f4e..f7b9e5daea50
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
> 
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> 
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000
> +
> +#if defined CONFIG_DRM_SUN4I_DE2
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;
> +
> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);
> +
> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);
> +
> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h
> b/drivers/gpu/drm/sun4i/sun8i_mixer.h new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced
> in + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only
> to + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> --
> 2.11.1

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:01       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-22 22:01 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Jean-Francois Moine, devicetree, David Airlie, linux-sunxi,
	linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
	Maxime Ripard, linux-clk, linux-arm-kernel

Hi,

Dne sreda, 22. februar 2017 ob 16:18:50 CET je Icenowy Zheng napisal(a):
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417
> ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h |
> 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
> 
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4a192210574f..4d2228454726
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc
> *crtc,
> 
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
> 
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);

Does it make sense to have everything in sun4i_{crtc,drv} files? Looking 
further it seems that there is a lot of differentiation based on DE1 vs DE2. I 
guess having separate sun8i_{crtc,drv} files would be more clean approach. It 
would enable easier extensions later as well.

Regards,
Jernej Skrabec

> 
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
> b/drivers/gpu/drm/sun4i/sun4i_drv.c index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
> 
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
> 
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int
> pipe) {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
> 
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
> 
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device
> *pdev) }
> 
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h
> b/drivers/gpu/drm/sun4i/sun4i_drv.h index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
> 
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
> b/drivers/gpu/drm/sun4i/sun4i_layer.c index 5d53c977bca5..6dcdac38ab69
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
> 
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
> 
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
> 
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
> 
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }
> 
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
> 
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
> 
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t
> sun4i_backend_layer_formats_overlay[] = { DRM_FORMAT_XRGB8888,
>  };
> 
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc
> sun4i_backend_planes[] = { },
>  };
> 
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, return ERR_PTR(-ENOMEM);
> 
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, }
> 
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
> 
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct
> drm_device *drm)
> 
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
> b/drivers/gpu/drm/sun4i/sun4i_layer.h index a2f65d7a3f4e..f7b9e5daea50
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
> 
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> 
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000
> +
> +#if defined CONFIG_DRM_SUN4I_DE2
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;
> +
> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);
> +
> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);
> +
> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h
> b/drivers/gpu/drm/sun4i/sun8i_mixer.h new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced
> in + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only
> to + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> --
> 2.11.1



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:01       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-22 22:01 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Dne sreda, 22. februar 2017 ob 16:18:50 CET je Icenowy Zheng napisal(a):
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417
> ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h |
> 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
> 
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4a192210574f..4d2228454726
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc
> *crtc,
> 
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
> 
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);

Does it make sense to have everything in sun4i_{crtc,drv} files? Looking 
further it seems that there is a lot of differentiation based on DE1 vs DE2. I 
guess having separate sun8i_{crtc,drv} files would be more clean approach. It 
would enable easier extensions later as well.

Regards,
Jernej Skrabec

> 
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
> b/drivers/gpu/drm/sun4i/sun4i_drv.c index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
> 
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
> 
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int
> pipe) {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
> 
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
> 
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device
> *pdev) }
> 
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h
> b/drivers/gpu/drm/sun4i/sun4i_drv.h index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
> 
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
> b/drivers/gpu/drm/sun4i/sun4i_layer.c index 5d53c977bca5..6dcdac38ab69
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
> 
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
> 
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
> 
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
> 
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
> 
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }
> 
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
> 
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
> 
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t
> sun4i_backend_layer_formats_overlay[] = { DRM_FORMAT_XRGB8888,
>  };
> 
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc
> sun4i_backend_planes[] = { },
>  };
> 
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, return ERR_PTR(-ENOMEM);
> 
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
> drm_device *drm, }
> 
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
> 
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct
> drm_device *drm)
> 
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
> b/drivers/gpu/drm/sun4i/sun4i_layer.h index a2f65d7a3f4e..f7b9e5daea50
> 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
> 
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> 
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000
> +
> +#if defined CONFIG_DRM_SUN4I_DE2
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;
> +
> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);
> +
> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);
> +
> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h
> b/drivers/gpu/drm/sun4i/sun8i_mixer.h new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced
> in + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only
> to + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> --
> 2.11.1

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 22:01       ` Jernej Škrabec
  (?)
@ 2017-02-22 22:04         ` Icenowy Zheng
  -1 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 22:04 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Rob Herring, Maxime Ripard, Chen-Yu Tsai, David Airlie,
	Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw



23.02.2017, 06:01, "Jernej Škrabec" <jernej.skrabec-gGgVlfcn5nU@public.gmane.org>:
> Hi,
>
> Dne sreda, 22. februar 2017 ob 16:18:50 CET je Icenowy Zheng napisal(a):
>>  Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>>  in a new "Display Engine" (mixers instead of old backends and
>>  frontends).
>>
>>  Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>>  Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  ---
>>   drivers/gpu/drm/sun4i/Kconfig | 8 +
>>   drivers/gpu/drm/sun4i/Makefile | 1 +
>>   drivers/gpu/drm/sun4i/sun4i_crtc.c | 6 +-
>>   drivers/gpu/drm/sun4i/sun4i_drv.c | 38 +++-
>>   drivers/gpu/drm/sun4i/sun4i_drv.h | 1 +
>>   drivers/gpu/drm/sun4i/sun4i_layer.c | 92 ++++++--
>>   drivers/gpu/drm/sun4i/sun4i_layer.h | 1 +
>>   drivers/gpu/drm/sun4i/sun8i_mixer.c | 417
>>  ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h |
>>  133 ++++++++++++
>>   9 files changed, 674 insertions(+), 23 deletions(-)
>>   create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>>   create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
>>
>>  diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>>  index a4b357db8856..8df401fff145 100644
>>  --- a/drivers/gpu/drm/sun4i/Kconfig
>>  +++ b/drivers/gpu/drm/sun4i/Kconfig
>>  @@ -12,3 +12,11 @@ config DRM_SUN4I
>>             Choose this option if you have an Allwinner SoC with a
>>             Display Engine. If M is selected the module will be called
>>             sun4i-drm.
>>  +
>>  +config DRM_SUN4I_DE2
>>  + bool "Support Display Engine 2.0"
>>  + depends on DRM_SUN4I
>>  + default MACH_SUN8I
>>  + help
>>  + Choose this option if you have an Allwinner SoC with a
>>  + "Display Engine 2.0".
>>  diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>>  index d625a82a6e5f..890e6e50dfee 100644
>>  --- a/drivers/gpu/drm/sun4i/Makefile
>>  +++ b/drivers/gpu/drm/sun4i/Makefile
>>  @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>>
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
>>  +obj-$(CONFIG_DRM_SUN4I) += sun8i_mixer.o
>>   obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>  b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4a192210574f..4d2228454726
>>  100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>  @@ -25,6 +25,7 @@
>>   #include <video/videomode.h>
>>
>>   #include "sun4i_backend.h"
>>  +#include "sun8i_mixer.h"
>>   #include "sun4i_crtc.h"
>>   #include "sun4i_drv.h"
>>   #include "sun4i_tcon.h"
>>  @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc
>>  *crtc,
>>
>>           DRM_DEBUG_DRIVER("Committing plane changes\n");
>>
>>  - sun4i_backend_commit(drv->backend);
>>  + if (drv->backend)
>>  + sun4i_backend_commit(drv->backend);
>>  + else if (drv->mixer)
>>  + sun8i_mixer_commit(drv->mixer);
>
> Does it make sense to have everything in sun4i_{crtc,drv} files? Looking
> further it seems that there is a lot of differentiation based on DE1 vs DE2. I
> guess having separate sun8i_{crtc,drv} files would be more clean approach. It
> would enable easier extensions later as well.

I think they have few changes here -- sharing code path is very valuable in these
two files.

>
> Regards,
> Jernej Skrabec
>
>>           if (event) {
>>                   crtc->state->event = NULL;
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
>>  b/drivers/gpu/drm/sun4i/sun4i_drv.c index 4ce665349f6b..58af38f5e833 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
>>  @@ -20,12 +20,17 @@
>>   #include <drm/drm_fb_helper.h>
>>   #include <drm/drm_of.h>
>>
>>  +#include <linux/of_device.h>
>>  +
>>   #include "sun4i_crtc.h"
>>   #include "sun4i_drv.h"
>>   #include "sun4i_framebuffer.h"
>>   #include "sun4i_layer.h"
>>   #include "sun4i_tcon.h"
>>
>>  +#define DE_VER_DE 0
>>  +#define DE_VER_DE2 1
>>  +
>>   static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int
>>  pipe) {
>>           struct sun4i_drv *drv = drm->dev_private;
>>  @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>>   {
>>           struct drm_device *drm;
>>           struct sun4i_drv *drv;
>>  - int ret;
>>  + int ret, de_ver;
>>  +
>>  + de_ver = (int)of_device_get_match_data(dev);
>>
>>           drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>>           if (IS_ERR(drm))
>>  @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>>           }
>>
>>           /* Create our layers */
>>  - drv->layers = sun4i_layers_init(drm);
>>  + if (de_ver == DE_VER_DE2)
>>  + drv->layers = sun8i_layers_init(drm);
>>  + else
>>  + drv->layers = sun4i_layers_init(drm);
>>           if (IS_ERR(drv->layers)) {
>>                   dev_err(drm->dev, "Couldn't create the planes\n");
>>                   ret = PTR_ERR(drv->layers);
>>  @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device
>>  *pdev) }
>>
>>   static const struct of_device_id sun4i_drv_of_table[] = {
>>  - { .compatible = "allwinner,sun5i-a13-display-engine" },
>>  - { .compatible = "allwinner,sun6i-a31-display-engine" },
>>  - { .compatible = "allwinner,sun6i-a31s-display-engine" },
>>  - { .compatible = "allwinner,sun8i-a33-display-engine" },
>>  + {
>>  + .compatible = "allwinner,sun5i-a13-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun6i-a31-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun6i-a31s-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun8i-a33-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun8i-v3s-display-engine",
>>  + .data = (void *)DE_VER_DE2,
>>  + },
>>           { }
>>   };
>>   MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h
>>  b/drivers/gpu/drm/sun4i/sun4i_drv.h index 597353eab728..cf1da95b85bd 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
>>  @@ -18,6 +18,7 @@
>>
>>   struct sun4i_drv {
>>           struct sun4i_backend *backend;
>>  + struct sun8i_mixer *mixer;
>>           struct sun4i_crtc *crtc;
>>           struct sun4i_tcon *tcon;
>>
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>  b/drivers/gpu/drm/sun4i/sun4i_layer.c index 5d53c977bca5..6dcdac38ab69
>>  100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>  @@ -16,52 +16,66 @@
>>   #include <drm/drmP.h>
>>
>>   #include "sun4i_backend.h"
>>  +#include "sun8i_mixer.h"
>>   #include "sun4i_drv.h"
>>   #include "sun4i_layer.h"
>>
>>   struct sun4i_plane_desc {
>>                  enum drm_plane_type type;
>>  + /* Pipe is not used in sun8i-mixer */
>>                  u8 pipe;
>>                  const uint32_t *formats;
>>                  uint32_t nformats;
>>   };
>>
>>  -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
>>  +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>>                                               struct drm_plane_state *state)
>>   {
>>           return 0;
>>   }
>>
>>  -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
>>  +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>>                                                  struct drm_plane_state *old_state)
>>   {
>>           struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>>           struct sun4i_drv *drv = layer->drv;
>>           struct sun4i_backend *backend = drv->backend;
>>  + struct sun8i_mixer *mixer = drv->mixer;
>>
>>  - sun4i_backend_layer_enable(backend, layer->id, false);
>>  + if (backend)
>>  + sun4i_backend_layer_enable(backend, layer->id, false);
>>  + else if (mixer)
>>  + sun8i_mixer_layer_enable(mixer, layer->id, false);
>>   }
>>
>>  -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
>>  +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>>                                                 struct drm_plane_state *old_state)
>>   {
>>           struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>>           struct sun4i_drv *drv = layer->drv;
>>           struct sun4i_backend *backend = drv->backend;
>>  -
>>  - sun4i_backend_update_layer_coord(backend, layer->id, plane);
>>  - sun4i_backend_update_layer_formats(backend, layer->id, plane);
>>  - sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>>  - sun4i_backend_layer_enable(backend, layer->id, true);
>>  + struct sun8i_mixer *mixer = drv->mixer;
>>  +
>>  + if (backend) {
>>  + sun4i_backend_update_layer_coord(backend, layer->id, plane);
>>  + sun4i_backend_update_layer_formats(backend, layer->id, plane);
>>  + sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>>  + sun4i_backend_layer_enable(backend, layer->id, true);
>>  + } else if (mixer) {
>>  + sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
>>  + sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
>>  + sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
>>  + sun8i_mixer_layer_enable(mixer, layer->id, true);
>>  + }
>>   }
>>
>>  -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
>>  - .atomic_check = sun4i_backend_layer_atomic_check,
>>  - .atomic_disable = sun4i_backend_layer_atomic_disable,
>>  - .atomic_update = sun4i_backend_layer_atomic_update,
>>  +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
>>  + .atomic_check = sun4i_layer_atomic_check,
>>  + .atomic_disable = sun4i_layer_atomic_disable,
>>  + .atomic_update = sun4i_layer_atomic_update,
>>   };
>>
>>  -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
>>  +static const struct drm_plane_funcs sun4i_layer_funcs = {
>>           .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>>           .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>>           .destroy = drm_plane_cleanup,
>>  @@ -88,6 +102,12 @@ static const uint32_t
>>  sun4i_backend_layer_formats_overlay[] = { DRM_FORMAT_XRGB8888,
>>   };
>>
>>  +static const uint32_t sun8i_mixer_layer_formats[] = {
>>  + DRM_FORMAT_ARGB8888,
>>  + DRM_FORMAT_RGB888,
>>  + DRM_FORMAT_XRGB8888,
>>  +};
>>  +
>>   static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>>           {
>>                   .type = DRM_PLANE_TYPE_PRIMARY,
>>  @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc
>>  sun4i_backend_planes[] = { },
>>   };
>>
>>  +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
>>  + {
>>  + .type = DRM_PLANE_TYPE_PRIMARY,
>>  + .formats = sun8i_mixer_layer_formats,
>>  + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>>  + },
>>  + {
>>  + .type = DRM_PLANE_TYPE_OVERLAY,
>>  + .formats = sun8i_mixer_layer_formats,
>>  + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>>  + },
>>  +};
>>  +
>>   static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>>                                                   const struct sun4i_plane_desc *plane)
>>   {
>>  @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
>>  drm_device *drm, return ERR_PTR(-ENOMEM);
>>
>>           ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
>>  - &sun4i_backend_layer_funcs,
>>  + &sun4i_layer_funcs,
>>                                          plane->formats, plane->nformats,
>>                                          plane->type, NULL);
>>           if (ret) {
>>  @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
>>  drm_device *drm, }
>>
>>           drm_plane_helper_add(&layer->plane,
>>  - &sun4i_backend_layer_helper_funcs);
>>  + &sun4i_layer_helper_funcs);
>>           layer->drv = drv;
>>
>>           if (plane->type == DRM_PLANE_TYPE_PRIMARY)
>>  @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct
>>  drm_device *drm)
>>
>>           return layers;
>>   }
>>  +
>>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
>>  +{
>>  + struct sun4i_layer **layers;
>>  + int i;
>>  +
>>  + layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
>>  + sizeof(**layers), GFP_KERNEL);
>>  + if (!layers)
>>  + return ERR_PTR(-ENOMEM);
>>  +
>>  + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>>  + const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
>>  + struct sun4i_layer *layer = layers[i];
>>  +
>>  + layer = sun4i_layer_init_one(drm, plane);
>>  + if (IS_ERR(layer)) {
>>  + dev_err(drm->dev, "Couldn't initialize %s plane\n",
>>  + i ? "overlay" : "primary");
>>  + return ERR_CAST(layer);
>>  + };
>>  +
>>  + layer->id = i;
>>  + };
>>  +
>>  + return layers;
>>  +}
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>  b/drivers/gpu/drm/sun4i/sun4i_layer.h index a2f65d7a3f4e..f7b9e5daea50
>>  100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>  @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>>   }
>>
>>   struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
>>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>>
>>   #endif /* _SUN4I_LAYER_H_ */
>>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>  b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
>>  index 000000000000..9427b57240d3
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>  @@ -0,0 +1,417 @@
>>  +/*
>>  + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  + *
>>  + * Based on sun4i_backend.c, which is:
>>  + * Copyright (C) 2015 Free Electrons
>>  + * Copyright (C) 2015 NextThing Co
>>  + *
>>  + * This program is free software; you can redistribute it and/or
>>  + * modify it under the terms of the GNU General Public License as
>>  + * published by the Free Software Foundation; either version 2 of
>>  + * the License, or (at your option) any later version.
>>  + */
>>  +
>>  +#include <drm/drmP.h>
>>  +#include <drm/drm_atomic_helper.h>
>>  +#include <drm/drm_crtc.h>
>>  +#include <drm/drm_crtc_helper.h>
>>  +#include <drm/drm_fb_cma_helper.h>
>>  +#include <drm/drm_gem_cma_helper.h>
>>  +#include <drm/drm_plane_helper.h>
>>  +
>>  +#include <linux/component.h>
>>  +#include <linux/reset.h>
>>  +#include <linux/of_device.h>
>>  +
>>  +#include "sun8i_mixer.h"
>>  +#include "sun4i_drv.h"
>>  +
>>  +#define SUN8I_DRAM_OFFSET 0x40000000
>>  +
>>  +#if defined CONFIG_DRM_SUN4I_DE2
>>  +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>>  +{
>>  + DRM_DEBUG_DRIVER("Committing changes\n");
>>  +
>>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>>  + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_commit);
>>  +
>>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>>  + int layer, bool enable)
>>  +{
>>  + u32 val;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  +
>>  + DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
>>  +
>>  + if (enable)
>>  + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
>>  + else
>>  + val = 0;
>>  +
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
>>  +
>>  + /* Set the alpha configuration */
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
>>  +
>>  +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>>  + u32 format, u32 *mode)
>>  +{
>>  + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
>>  + (format == DRM_FORMAT_ARGB8888))
>>  + format = DRM_FORMAT_XRGB8888;
>>  +
>>  + switch (format) {
>>  + case DRM_FORMAT_ARGB8888:
>>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
>>  + break;
>>  +
>>  + case DRM_FORMAT_XRGB8888:
>>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
>>  + break;
>>  +
>>  + case DRM_FORMAT_RGB888:
>>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
>>  + break;
>>  +
>>  + default:
>>  + return -EINVAL;
>>  + }
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + struct drm_plane_state *state = plane->state;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  + int i;
>>  +
>>  + DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
>>  +
>>  + if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
>>  + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
>>  + state->crtc_w, state->crtc_h);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + DRM_DEBUG_DRIVER("Updating blender size\n");
>>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>>  + regmap_write(mixer->regs,
>>  + SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + DRM_DEBUG_DRIVER("Updating channel size\n");
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + }
>>  +
>>  + /* Set the line width */
>>  + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
>>  + fb->pitches[0]);
>>  +
>>  + /* Set height and width */
>>  + DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
>>  + state->crtc_w, state->crtc_h);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
>>  + SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
>>  +
>>  + /* Set base coordinates */
>>  + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
>>  + state->crtc_x, state->crtc_y);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>>  + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>>  +
>>  + return 0;
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
>>  +
>>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + struct drm_plane_state *state = plane->state;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + bool interlaced = false;
>>  + u32 val;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  + int ret;
>>  +
>>  + if (plane->state->crtc)
>>  + interlaced = plane->state->crtc->state->adjusted_mode.flags
>>  + & DRM_MODE_FLAG_INTERLACE;
>>  +
>>  + regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
>>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
>>  + interlaced ?
>>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
>>  +
>>  + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
>>  + interlaced ? "on" : "off");
>>  +
>>  + ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
>>  + &val);
>>  + if (ret) {
>>  + DRM_DEBUG_DRIVER("Invalid format\n");
>>  + return ret;
>>  + }
>>  +
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>>  +
>>  + return 0;
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
>>  +
>>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + struct drm_plane_state *state = plane->state;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + struct drm_gem_cma_object *gem;
>>  + dma_addr_t paddr;
>>  + uint32_t paddr_u32;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  + int bpp;
>>  +
>>  + /* Get the physical address of the buffer in memory */
>>  + gem = drm_fb_cma_get_gem_obj(fb, 0);
>>  +
>>  + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
>>  +
>>  + /* Compute the start of the displayed memory */
>>  + bpp = fb->format->cpp[0];
>>  + paddr = gem->paddr + fb->offsets[0];
>>  + paddr += (state->src_x >> 16) * bpp;
>>  + paddr += (state->src_y >> 16) * fb->pitches[0];
>>  + paddr -= SUN8I_DRAM_OFFSET;
>>  +
>>  + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>>  +
>>  + paddr_u32 = (uint32_t) paddr;
>>  +
>>  + regmap_write(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>>  + paddr_u32);
>>  +
>>  + return 0;
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
>>  +
>>  +static struct regmap_config sun8i_mixer_regmap_config = {
>>  + .reg_bits = 32,
>>  + .val_bits = 32,
>>  + .reg_stride = 4,
>>  + .max_register = 0xbffc, /* guessed */
>>  +};
>>  +
>>  +static int sun8i_mixer_bind(struct device *dev, struct device *master,
>>  + void *data)
>>  +{
>>  + struct platform_device *pdev = to_platform_device(dev);
>>  + struct drm_device *drm = data;
>>  + struct sun4i_drv *drv = drm->dev_private;
>>  + struct sun8i_mixer *mixer;
>>  + struct resource *res;
>>  + void __iomem *regs;
>>  + int i, ret;
>>  +
>>  + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>>  + if (!mixer)
>>  + return -ENOMEM;
>>  + dev_set_drvdata(dev, mixer);
>>  + drv->mixer = mixer;
>>  +
>>  + mixer->cfg = of_device_get_match_data(dev);
>>  + if (!mixer->cfg)
>>  + return -EINVAL;
>>  +
>>  + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>  + regs = devm_ioremap_resource(dev, res);
>>  + if (IS_ERR(regs))
>>  + return PTR_ERR(regs);
>>  +
>>  + mixer->regs = devm_regmap_init_mmio(dev, regs,
>>  + &sun8i_mixer_regmap_config);
>>  + if (IS_ERR(mixer->regs)) {
>>  + dev_err(dev, "Couldn't create the mixer regmap\n");
>>  + return PTR_ERR(mixer->regs);
>>  + }
>>  +
>>  + mixer->reset = devm_reset_control_get(dev, NULL);
>>  + if (IS_ERR(mixer->reset)) {
>>  + dev_err(dev, "Couldn't get our reset line\n");
>>  + return PTR_ERR(mixer->reset);
>>  + }
>>  +
>>  + ret = reset_control_deassert(mixer->reset);
>>  + if (ret) {
>>  + dev_err(dev, "Couldn't deassert our reset line\n");
>>  + return ret;
>>  + }
>>  +
>>  + mixer->bus_clk = devm_clk_get(dev, "bus");
>>  + if (IS_ERR(mixer->bus_clk)) {
>>  + dev_err(dev, "Couldn't get the mixer bus clock\n");
>>  + ret = PTR_ERR(mixer->bus_clk);
>>  + goto err_assert_reset;
>>  + }
>>  + clk_prepare_enable(mixer->bus_clk);
>>  +
>>  + mixer->mod_clk = devm_clk_get(dev, "mod");
>>  + if (IS_ERR(mixer->mod_clk)) {
>>  + dev_err(dev, "Couldn't get the mixer module clock\n");
>>  + ret = PTR_ERR(mixer->mod_clk);
>>  + goto err_disable_bus_clk;
>>  + }
>>  + clk_prepare_enable(mixer->mod_clk);
>>  +
>>  + /* Reset the registers */
>>  + for (i = 0x0; i < 0x20000; i += 4)
>>  + regmap_write(mixer->regs, i, 0);
>>  +
>>  + /* Enable the mixer */
>>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
>>  + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>>  +
>>  + /* Initialize blender */
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>>  + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>>  + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
>>  + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
>>  + SUN8I_MIXER_BLEND_MODE_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
>>  + SUN8I_MIXER_BLEND_MODE_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
>>  + SUN8I_MIXER_BLEND_CK_CTL_DEF);
>>  +
>>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>>  + regmap_write(mixer->regs,
>>  + SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
>>  + SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
>>  +
>>  + /* Select the first UI channel */
>>  + DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
>>  + mixer->cfg->vi_num);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
>>  + mixer->cfg->vi_num);
>>  +
>>  + return 0;
>>  +
>>  + clk_disable_unprepare(mixer->mod_clk);
>>  +err_disable_bus_clk:
>>  + clk_disable_unprepare(mixer->bus_clk);
>>  +err_assert_reset:
>>  + reset_control_assert(mixer->reset);
>>  + return ret;
>>  +}
>>  +
>>  +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
>>  + void *data)
>>  +{
>>  + struct sun8i_mixer *mixer = dev_get_drvdata(dev);
>>  +
>>  + clk_disable_unprepare(mixer->mod_clk);
>>  + clk_disable_unprepare(mixer->bus_clk);
>>  + reset_control_assert(mixer->reset);
>>  +}
>>  +
>>  +static const struct component_ops sun8i_mixer_ops = {
>>  + .bind = sun8i_mixer_bind,
>>  + .unbind = sun8i_mixer_unbind,
>>  +};
>>  +
>>  +static int sun8i_mixer_probe(struct platform_device *pdev)
>>  +{
>>  + return component_add(&pdev->dev, &sun8i_mixer_ops);
>>  +}
>>  +
>>  +static int sun8i_mixer_remove(struct platform_device *pdev)
>>  +{
>>  + component_del(&pdev->dev, &sun8i_mixer_ops);
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
>>  + .vi_num = 2,
>>  + .ui_num = 1,
>>  +};
>>  +
>>  +static const struct of_device_id sun8i_mixer_of_table[] = {
>>  + {
>>  + .compatible = "allwinner,sun8i-v3s-de2-mixer",
>>  + .data = &sun8i_v3s_mixer_cfg
>>  + },
>>  + { }
>>  +};
>>  +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
>>  +
>>  +static struct platform_driver sun8i_mixer_platform_driver = {
>>  + .probe = sun8i_mixer_probe,
>>  + .remove = sun8i_mixer_remove,
>>  + .driver = {
>>  + .name = "sun8i-mixer",
>>  + .of_match_table = sun8i_mixer_of_table,
>>  + },
>>  +};
>>  +module_platform_driver(sun8i_mixer_platform_driver);
>>  +
>>  +MODULE_AUTHOR("Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>");
>>  +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
>>  +MODULE_LICENSE("GPL");
>>  +#else /* DRM_SUN4I_DE2 */
>>  +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>>  +{
>>  +}
>>  +
>>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>>  + int layer, bool enable)
>>  +{
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + return -ENOENT;
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + return -ENOENT;
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + return -ENOENT;
>>  +}
>>  +#endif /* CONFIG_DRM_SUN4I_DE2 */
>>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h
>>  b/drivers/gpu/drm/sun4i/sun8i_mixer.h new file mode 100644
>>  index 000000000000..0bc134b3bc98
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
>>  @@ -0,0 +1,133 @@
>>  +/*
>>  + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  + *
>>  + * This program is free software; you can redistribute it and/or
>>  + * modify it under the terms of the GNU General Public License as
>>  + * published by the Free Software Foundation; either version 2 of
>>  + * the License, or (at your option) any later version.
>>  + */
>>  +
>>  +#ifndef _SUN8I_MIXER_H_
>>  +#define _SUN8I_MIXER_H_
>>  +
>>  +#include <linux/clk.h>
>>  +#include <linux/regmap.h>
>>  +#include <linux/reset.h>
>>  +
>>  +#include "sun4i_layer.h"
>>  +
>>  +#define SUN8I_MIXER_MAX_CHAN_COUNT 4
>>  +
>>  +#define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
>>  +#define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x))
>>  +
>>  +#define SUN8I_MIXER_GLOBAL_CTL 0x0
>>  +#define SUN8I_MIXER_GLOBAL_STATUS 0x4
>>  +#define SUN8I_MIXER_GLOBAL_DBUFF 0x8
>>  +#define SUN8I_MIXER_GLOBAL_SIZE 0xc
>>  +
>>  +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1
>>  +
>>  +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1
>>  +
>>  +#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000
>>  +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0)
>>  +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4)
>>  +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8)
>>  +#define SUN8I_MIXER_BLEND_ROUTE 0x1080
>>  +#define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084
>>  +#define SUN8I_MIXER_BLEND_BKCOLOR 0x1088
>>  +#define SUN8I_MIXER_BLEND_OUTSIZE 0x108c
>>  +#define SUN8I_MIXER_BLEND_MODE(x) (0x1090 + 0x04 * (x))
>>  +#define SUN8I_MIXER_BLEND_CK_CTL 0x10b0
>>  +#define SUN8I_MIXER_BLEND_CK_CFG 0x10b4
>>  +#define SUN8I_MIXER_BLEND_CK_MAX(x) (0x10c0 + 0x04 * (x))
>>  +#define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x))
>>  +#define SUN8I_MIXER_BLEND_OUTCTL 0x10fc
>>  +
>>  +/* The following numbers are some still unknown magic numbers */
>>  +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000
>>  +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101
>>  +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0
>>  +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000
>>  +#define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301
>>  +#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0
>>  +
>>  +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1)
>>  +
>>  +/*
>>  + * VI channels are not used now, but the support of them may be introduced
>>  in + * the future.
>>  + */
>>  +
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
>>  +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80)
>>  +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84)
>>  +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88)
>>  +
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24)
>>  +
>>  +/*
>>  + * These sun-engines are still unknown now, the EN registers are here only
>>  to + * be used to disable these sub-engines.
>>  + */
>>  +#define SUN8I_MIXER_VSU_EN 0x20000
>>  +#define SUN8I_MIXER_GSU1_EN 0x30000
>>  +#define SUN8I_MIXER_GSU2_EN 0x40000
>>  +#define SUN8I_MIXER_GSU3_EN 0x50000
>>  +#define SUN8I_MIXER_FCE_EN 0xa0000
>>  +#define SUN8I_MIXER_BWS_EN 0xa2000
>>  +#define SUN8I_MIXER_LTI_EN 0xa4000
>>  +#define SUN8I_MIXER_PEAK_EN 0xa6000
>>  +#define SUN8I_MIXER_ASE_EN 0xa8000
>>  +#define SUN8I_MIXER_FCC_EN 0xaa000
>>  +#define SUN8I_MIXER_DCSC_EN 0xb0000
>>  +
>>  +struct sun8i_mixer_cfg {
>>  + int vi_num;
>>  + int ui_num;
>>  +};
>>  +
>>  +struct sun8i_mixer {
>>  + struct regmap *regs;
>>  +
>>  + const struct sun8i_mixer_cfg *cfg;
>>  +
>>  + struct reset_control *reset;
>>  +
>>  + struct clk *bus_clk;
>>  + struct clk *mod_clk;
>>  +};
>>  +
>>  +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
>>  +
>>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>>  + int layer, bool enable);
>>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane);
>>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane);
>>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane);
>>  +#endif /* _SUN8I_MIXER_H_ */
>>  --
>>  2.11.1

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:04         ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 22:04 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Jean-Francois Moine, devicetree, David Airlie, linux-sunxi,
	linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
	Maxime Ripard, linux-clk, linux-arm-kernel

CgoyMy4wMi4yMDE3LCAwNjowMSwgIkplcm5laiDFoGtyYWJlYyIgPGplcm5lai5za3JhYmVjQHNp
b2wubmV0PjoKPiBIaSwKPgo+IERuZSBzcmVkYSwgMjIuIGZlYnJ1YXIgMjAxNyBvYiAxNjoxODo1
MCBDRVQgamUgSWNlbm93eSBaaGVuZyBuYXBpc2FsKGEpOgo+PiDCoEFsbHdpbm5lciBoYXZlIGEg
bmV3ICJEaXNwbGF5IEVuZ2luZSAyLjAiIGluIHRoZXJlIG5ldyBTb0NzLCB3aGljaCBjb21lcwo+
PiDCoGluIGEgbmV3ICJEaXNwbGF5IEVuZ2luZSIgKG1peGVycyBpbnN0ZWFkIG9mIG9sZCBiYWNr
ZW5kcyBhbmQKPj4gwqBmcm9udGVuZHMpLgo+Pgo+PiDCoEFkZCBzdXBwb3J0IGZvciB0aGUgbWl4
ZXIgb24gQWxsd2lubmVyIFYzcyBTb0M7IGl0J3MgdGhlIHNpbXBsZXN0IG9uZS4KPj4KPj4gwqBT
aWduZWQtb2ZmLWJ5OiBJY2Vub3d5IFpoZW5nIDxpY2Vub3d5QGFvc2MueHl6Pgo+PiDCoC0tLQo+
PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL0tjb25maWcgfCA4ICsKPj4gwqDCoGRyaXZlcnMv
Z3B1L2RybS9zdW40aS9NYWtlZmlsZSB8IDEgKwo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRp
L3N1bjRpX2NydGMuYyB8IDYgKy0KPj4gwqDCoGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9k
cnYuYyB8IDM4ICsrKy0KPj4gwqDCoGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9kcnYuaCB8
IDEgKwo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmMgfCA5MiArKysr
KystLQo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmggfCAxICsKPj4g
wqDCoGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW44aV9taXhlci5jIHwgNDE3Cj4+IMKgKysrKysr
KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrIGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW44
aV9taXhlci5oIHwKPj4gwqAxMzMgKysrKysrKysrKysrCj4+IMKgwqA5IGZpbGVzIGNoYW5nZWQs
IDY3NCBpbnNlcnRpb25zKCspLCAyMyBkZWxldGlvbnMoLSkKPj4gwqDCoGNyZWF0ZSBtb2RlIDEw
MDY0NCBkcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuOGlfbWl4ZXIuYwo+PiDCoMKgY3JlYXRlIG1v
ZGUgMTAwNjQ0IGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW44aV9taXhlci5oCj4+Cj4+IMKgZGlm
ZiAtLWdpdCBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9LY29uZmlnIGIvZHJpdmVycy9ncHUvZHJt
L3N1bjRpL0tjb25maWcKPj4gwqBpbmRleCBhNGIzNTdkYjg4NTYuLjhkZjQwMWZmZjE0NSAxMDA2
NDQKPj4gwqAtLS0gYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvS2NvbmZpZwo+PiDCoCsrKyBiL2Ry
aXZlcnMvZ3B1L2RybS9zdW40aS9LY29uZmlnCj4+IMKgQEAgLTEyLDMgKzEyLDExIEBAIGNvbmZp
ZyBEUk1fU1VONEkKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgQ2hvb3NlIHRoaXMgb3B0aW9u
IGlmIHlvdSBoYXZlIGFuIEFsbHdpbm5lciBTb0Mgd2l0aCBhCj4+IMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoERpc3BsYXkgRW5naW5lLiBJZiBNIGlzIHNlbGVjdGVkIHRoZSBtb2R1bGUgd2lsbCBi
ZSBjYWxsZWQKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgc3VuNGktZHJtLgo+PiDCoCsKPj4g
wqArY29uZmlnIERSTV9TVU40SV9ERTIKPj4gwqArIGJvb2wgIlN1cHBvcnQgRGlzcGxheSBFbmdp
bmUgMi4wIgo+PiDCoCsgZGVwZW5kcyBvbiBEUk1fU1VONEkKPj4gwqArIGRlZmF1bHQgTUFDSF9T
VU44SQo+PiDCoCsgaGVscAo+PiDCoCsgQ2hvb3NlIHRoaXMgb3B0aW9uIGlmIHlvdSBoYXZlIGFu
IEFsbHdpbm5lciBTb0Mgd2l0aCBhCj4+IMKgKyAiRGlzcGxheSBFbmdpbmUgMi4wIi4KPj4gwqBk
aWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL01ha2VmaWxlIGIvZHJpdmVycy9ncHUv
ZHJtL3N1bjRpL01ha2VmaWxlCj4+IMKgaW5kZXggZDYyNWE4MmE2ZTVmLi44OTBlNmU1MGRmZWUg
MTAwNjQ0Cj4+IMKgLS0tIGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL01ha2VmaWxlCj4+IMKgKysr
IGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL01ha2VmaWxlCj4+IMKgQEAgLTksNSArOSw2IEBAIHN1
bjRpLXRjb24teSArPSBzdW40aV9kb3RjbG9jay5vCj4+Cj4+IMKgwqBvYmotJChDT05GSUdfRFJN
X1NVTjRJKSArPSBzdW40aS1kcm0ubyBzdW40aS10Y29uLm8KPj4gwqDCoG9iai0kKENPTkZJR19E
Uk1fU1VONEkpICs9IHN1bjRpX2JhY2tlbmQubwo+PiDCoCtvYmotJChDT05GSUdfRFJNX1NVTjRJ
KSArPSBzdW44aV9taXhlci5vCj4+IMKgwqBvYmotJChDT05GSUdfRFJNX1NVTjRJKSArPSBzdW42
aV9kcmMubwo+PiDCoMKgb2JqLSQoQ09ORklHX0RSTV9TVU40SSkgKz0gc3VuNGlfdHYubwo+PiDC
oGRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfY3J0Yy5jCj4+IMKgYi9k
cml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfY3J0Yy5jIGluZGV4IDRhMTkyMjEwNTc0Zi4uNGQy
MjI4NDU0NzI2Cj4+IMKgMTAwNjQ0Cj4+IMKgLS0tIGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1
bjRpX2NydGMuYwo+PiDCoCsrKyBiL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9jcnRjLmMK
Pj4gwqBAQCAtMjUsNiArMjUsNyBAQAo+PiDCoMKgI2luY2x1ZGUgPHZpZGVvL3ZpZGVvbW9kZS5o
Pgo+Pgo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX2JhY2tlbmQuaCIKPj4gwqArI2luY2x1ZGUgInN1
bjhpX21peGVyLmgiCj4+IMKgwqAjaW5jbHVkZSAic3VuNGlfY3J0Yy5oIgo+PiDCoMKgI2luY2x1
ZGUgInN1bjRpX2Rydi5oIgo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX3Rjb24uaCIKPj4gwqBAQCAt
NTUsNyArNTYsMTAgQEAgc3RhdGljIHZvaWQgc3VuNGlfY3J0Y19hdG9taWNfZmx1c2goc3RydWN0
IGRybV9jcnRjCj4+IMKgKmNydGMsCj4+Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgRFJNX0RFQlVH
X0RSSVZFUigiQ29tbWl0dGluZyBwbGFuZSBjaGFuZ2VzXG4iKTsKPj4KPj4gwqAtIHN1bjRpX2Jh
Y2tlbmRfY29tbWl0KGRydi0+YmFja2VuZCk7Cj4+IMKgKyBpZiAoZHJ2LT5iYWNrZW5kKQo+PiDC
oCsgc3VuNGlfYmFja2VuZF9jb21taXQoZHJ2LT5iYWNrZW5kKTsKPj4gwqArIGVsc2UgaWYgKGRy
di0+bWl4ZXIpCj4+IMKgKyBzdW44aV9taXhlcl9jb21taXQoZHJ2LT5taXhlcik7Cj4KPiBEb2Vz
IGl0IG1ha2Ugc2Vuc2UgdG8gaGF2ZSBldmVyeXRoaW5nIGluIHN1bjRpX3tjcnRjLGRydn0gZmls
ZXM/IExvb2tpbmcKPiBmdXJ0aGVyIGl0IHNlZW1zIHRoYXQgdGhlcmUgaXMgYSBsb3Qgb2YgZGlm
ZmVyZW50aWF0aW9uIGJhc2VkIG9uIERFMSB2cyBERTIuIEkKPiBndWVzcyBoYXZpbmcgc2VwYXJh
dGUgc3VuOGlfe2NydGMsZHJ2fSBmaWxlcyB3b3VsZCBiZSBtb3JlIGNsZWFuIGFwcHJvYWNoLiBJ
dAo+IHdvdWxkIGVuYWJsZSBlYXNpZXIgZXh0ZW5zaW9ucyBsYXRlciBhcyB3ZWxsLgoKSSB0aGlu
ayB0aGV5IGhhdmUgZmV3IGNoYW5nZXMgaGVyZSAtLSBzaGFyaW5nIGNvZGUgcGF0aCBpcyB2ZXJ5
IHZhbHVhYmxlIGluIHRoZXNlCnR3byBmaWxlcy4KCj4KPiBSZWdhcmRzLAo+IEplcm5laiBTa3Jh
YmVjCj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBpZiAoZXZlbnQpIHsKPj4gwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgY3J0Yy0+c3RhdGUtPmV2ZW50ID0gTlVMTDsKPj4gwqBk
aWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2Rydi5jCj4+IMKgYi9kcml2
ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfZHJ2LmMgaW5kZXggNGNlNjY1MzQ5ZjZiLi41OGFmMzhm
NWU4MzMgMTAwNjQ0Cj4+IMKgLS0tIGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2Rydi5j
Cj4+IMKgKysrIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2Rydi5jCj4+IMKgQEAgLTIw
LDEyICsyMCwxNyBAQAo+PiDCoMKgI2luY2x1ZGUgPGRybS9kcm1fZmJfaGVscGVyLmg+Cj4+IMKg
wqAjaW5jbHVkZSA8ZHJtL2RybV9vZi5oPgo+Pgo+PiDCoCsjaW5jbHVkZSA8bGludXgvb2ZfZGV2
aWNlLmg+Cj4+IMKgKwo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX2NydGMuaCIKPj4gwqDCoCNpbmNs
dWRlICJzdW40aV9kcnYuaCIKPj4gwqDCoCNpbmNsdWRlICJzdW40aV9mcmFtZWJ1ZmZlci5oIgo+
PiDCoMKgI2luY2x1ZGUgInN1bjRpX2xheWVyLmgiCj4+IMKgwqAjaW5jbHVkZSAic3VuNGlfdGNv
bi5oIgo+Pgo+PiDCoCsjZGVmaW5lIERFX1ZFUl9ERSAwCj4+IMKgKyNkZWZpbmUgREVfVkVSX0RF
MiAxCj4+IMKgKwo+PiDCoMKgc3RhdGljIGludCBzdW40aV9kcnZfZW5hYmxlX3ZibGFuayhzdHJ1
Y3QgZHJtX2RldmljZSAqZHJtLCB1bnNpZ25lZCBpbnQKPj4gwqBwaXBlKSB7Cj4+IMKgwqDCoMKg
wqDCoMKgwqDCoMKgc3RydWN0IHN1bjRpX2RydiAqZHJ2ID0gZHJtLT5kZXZfcHJpdmF0ZTsKPj4g
wqBAQCAtMTE3LDcgKzEyMiw5IEBAIHN0YXRpYyBpbnQgc3VuNGlfZHJ2X2JpbmQoc3RydWN0IGRl
dmljZSAqZGV2KQo+PiDCoMKgewo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVjdCBkcm1fZGV2
aWNlICpkcm07Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IHN1bjRpX2RydiAqZHJ2Owo+
PiDCoC0gaW50IHJldDsKPj4gwqArIGludCByZXQsIGRlX3ZlcjsKPj4gwqArCj4+IMKgKyBkZV92
ZXIgPSAoaW50KW9mX2RldmljZV9nZXRfbWF0Y2hfZGF0YShkZXYpOwo+Pgo+PiDCoMKgwqDCoMKg
wqDCoMKgwqDCoGRybSA9IGRybV9kZXZfYWxsb2MoJnN1bjRpX2Rydl9kcml2ZXIsIGRldik7Cj4+
IMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKElTX0VSUihkcm0pKQo+PiDCoEBAIC0xNDAsNyArMTQ3
LDEwIEBAIHN0YXRpYyBpbnQgc3VuNGlfZHJ2X2JpbmQoc3RydWN0IGRldmljZSAqZGV2KQo+PiDC
oMKgwqDCoMKgwqDCoMKgwqDCoH0KPj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqAvKiBDcmVhdGUg
b3VyIGxheWVycyAqLwo+PiDCoC0gZHJ2LT5sYXllcnMgPSBzdW40aV9sYXllcnNfaW5pdChkcm0p
Owo+PiDCoCsgaWYgKGRlX3ZlciA9PSBERV9WRVJfREUyKQo+PiDCoCsgZHJ2LT5sYXllcnMgPSBz
dW44aV9sYXllcnNfaW5pdChkcm0pOwo+PiDCoCsgZWxzZQo+PiDCoCsgZHJ2LT5sYXllcnMgPSBz
dW40aV9sYXllcnNfaW5pdChkcm0pOwo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoGlmIChJU19FUlIo
ZHJ2LT5sYXllcnMpKSB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGRl
dl9lcnIoZHJtLT5kZXYsICJDb3VsZG4ndCBjcmVhdGUgdGhlIHBsYW5lc1xuIik7Cj4+IMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJldCA9IFBUUl9FUlIoZHJ2LT5sYXllcnMp
Owo+PiDCoEBAIC0zMjMsMTAgKzMzMywyNiBAQCBzdGF0aWMgaW50IHN1bjRpX2Rydl9yZW1vdmUo
c3RydWN0IHBsYXRmb3JtX2RldmljZQo+PiDCoCpwZGV2KSB9Cj4+Cj4+IMKgwqBzdGF0aWMgY29u
c3Qgc3RydWN0IG9mX2RldmljZV9pZCBzdW40aV9kcnZfb2ZfdGFibGVbXSA9IHsKPj4gwqAtIHsg
LmNvbXBhdGlibGUgPSAiYWxsd2lubmVyLHN1bjVpLWExMy1kaXNwbGF5LWVuZ2luZSIgfSwKPj4g
wqAtIHsgLmNvbXBhdGlibGUgPSAiYWxsd2lubmVyLHN1bjZpLWEzMS1kaXNwbGF5LWVuZ2luZSIg
fSwKPj4gwqAtIHsgLmNvbXBhdGlibGUgPSAiYWxsd2lubmVyLHN1bjZpLWEzMXMtZGlzcGxheS1l
bmdpbmUiIH0sCj4+IMKgLSB7IC5jb21wYXRpYmxlID0gImFsbHdpbm5lcixzdW44aS1hMzMtZGlz
cGxheS1lbmdpbmUiIH0sCj4+IMKgKyB7Cj4+IMKgKyAuY29tcGF0aWJsZSA9ICJhbGx3aW5uZXIs
c3VuNWktYTEzLWRpc3BsYXktZW5naW5lIiwKPj4gwqArIC5kYXRhID0gKHZvaWQgKilERV9WRVJf
REUsCj4+IMKgKyB9LAo+PiDCoCsgewo+PiDCoCsgLmNvbXBhdGlibGUgPSAiYWxsd2lubmVyLHN1
bjZpLWEzMS1kaXNwbGF5LWVuZ2luZSIsCj4+IMKgKyAuZGF0YSA9ICh2b2lkICopREVfVkVSX0RF
LAo+PiDCoCsgfSwKPj4gwqArIHsKPj4gwqArIC5jb21wYXRpYmxlID0gImFsbHdpbm5lcixzdW42
aS1hMzFzLWRpc3BsYXktZW5naW5lIiwKPj4gwqArIC5kYXRhID0gKHZvaWQgKilERV9WRVJfREUs
Cj4+IMKgKyB9LAo+PiDCoCsgewo+PiDCoCsgLmNvbXBhdGlibGUgPSAiYWxsd2lubmVyLHN1bjhp
LWEzMy1kaXNwbGF5LWVuZ2luZSIsCj4+IMKgKyAuZGF0YSA9ICh2b2lkICopREVfVkVSX0RFLAo+
PiDCoCsgfSwKPj4gwqArIHsKPj4gwqArIC5jb21wYXRpYmxlID0gImFsbHdpbm5lcixzdW44aS12
M3MtZGlzcGxheS1lbmdpbmUiLAo+PiDCoCsgLmRhdGEgPSAodm9pZCAqKURFX1ZFUl9ERTIsCj4+
IMKgKyB9LAo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHsgfQo+PiDCoMKgfTsKPj4gwqDCoE1PRFVM
RV9ERVZJQ0VfVEFCTEUob2YsIHN1bjRpX2Rydl9vZl90YWJsZSk7Cj4+IMKgZGlmZiAtLWdpdCBh
L2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9kcnYuaAo+PiDCoGIvZHJpdmVycy9ncHUvZHJt
L3N1bjRpL3N1bjRpX2Rydi5oIGluZGV4IDU5NzM1M2VhYjcyOC4uY2YxZGE5NWI4NWJkIDEwMDY0
NAo+PiDCoC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9kcnYuaAo+PiDCoCsrKyBi
L2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9kcnYuaAo+PiDCoEBAIC0xOCw2ICsxOCw3IEBA
Cj4+Cj4+IMKgwqBzdHJ1Y3Qgc3VuNGlfZHJ2IHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1
Y3Qgc3VuNGlfYmFja2VuZCAqYmFja2VuZDsKPj4gwqArIHN0cnVjdCBzdW44aV9taXhlciAqbWl4
ZXI7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IHN1bjRpX2NydGMgKmNydGM7Cj4+IMKg
wqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IHN1bjRpX3Rjb24gKnRjb247Cj4+Cj4+IMKgZGlmZiAt
LWdpdCBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9sYXllci5jCj4+IMKgYi9kcml2ZXJz
L2dwdS9kcm0vc3VuNGkvc3VuNGlfbGF5ZXIuYyBpbmRleCA1ZDUzYzk3N2JjYTUuLjZkY2RhYzM4
YWI2OQo+PiDCoDEwMDY0NAo+PiDCoC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9s
YXllci5jCj4+IMKgKysrIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmMKPj4g
wqBAQCAtMTYsNTIgKzE2LDY2IEBACj4+IMKgwqAjaW5jbHVkZSA8ZHJtL2RybVAuaD4KPj4KPj4g
wqDCoCNpbmNsdWRlICJzdW40aV9iYWNrZW5kLmgiCj4+IMKgKyNpbmNsdWRlICJzdW44aV9taXhl
ci5oIgo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX2Rydi5oIgo+PiDCoMKgI2luY2x1ZGUgInN1bjRp
X2xheWVyLmgiCj4+Cj4+IMKgwqBzdHJ1Y3Qgc3VuNGlfcGxhbmVfZGVzYyB7Cj4+IMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBlbnVtIGRybV9wbGFuZV90eXBlIHR5cGU7Cj4+IMKg
KyAvKiBQaXBlIGlzIG5vdCB1c2VkIGluIHN1bjhpLW1peGVyICovCj4+IMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqB1OCBwaXBlOwo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgY29uc3QgdWludDMyX3QgKmZvcm1hdHM7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqB1aW50MzJfdCBuZm9ybWF0czsKPj4gwqDCoH07Cj4+Cj4+IMKgLXN0YXRp
YyBpbnQgc3VuNGlfYmFja2VuZF9sYXllcl9hdG9taWNfY2hlY2soc3RydWN0IGRybV9wbGFuZSAq
cGxhbmUsCj4+IMKgK3N0YXRpYyBpbnQgc3VuNGlfbGF5ZXJfYXRvbWljX2NoZWNrKHN0cnVjdCBk
cm1fcGxhbmUgKnBsYW5lLAo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0
cnVjdCBkcm1fcGxhbmVfc3RhdGUgKnN0YXRlKQo+PiDCoMKgewo+PiDCoMKgwqDCoMKgwqDCoMKg
wqDCoHJldHVybiAwOwo+PiDCoMKgfQo+Pgo+PiDCoC1zdGF0aWMgdm9pZCBzdW40aV9iYWNrZW5k
X2xheWVyX2F0b21pY19kaXNhYmxlKHN0cnVjdCBkcm1fcGxhbmUgKnBsYW5lLAo+PiDCoCtzdGF0
aWMgdm9pZCBzdW40aV9sYXllcl9hdG9taWNfZGlzYWJsZShzdHJ1Y3QgZHJtX3BsYW5lICpwbGFu
ZSwKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1Y3QgZHJt
X3BsYW5lX3N0YXRlICpvbGRfc3RhdGUpCj4+IMKgwqB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKg
c3RydWN0IHN1bjRpX2xheWVyICpsYXllciA9IHBsYW5lX3RvX3N1bjRpX2xheWVyKHBsYW5lKTsK
Pj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1Y3Qgc3VuNGlfZHJ2ICpkcnYgPSBsYXllci0+ZHJ2
Owo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVjdCBzdW40aV9iYWNrZW5kICpiYWNrZW5kID0g
ZHJ2LT5iYWNrZW5kOwo+PiDCoCsgc3RydWN0IHN1bjhpX21peGVyICptaXhlciA9IGRydi0+bWl4
ZXI7Cj4+Cj4+IMKgLSBzdW40aV9iYWNrZW5kX2xheWVyX2VuYWJsZShiYWNrZW5kLCBsYXllci0+
aWQsIGZhbHNlKTsKPj4gwqArIGlmIChiYWNrZW5kKQo+PiDCoCsgc3VuNGlfYmFja2VuZF9sYXll
cl9lbmFibGUoYmFja2VuZCwgbGF5ZXItPmlkLCBmYWxzZSk7Cj4+IMKgKyBlbHNlIGlmIChtaXhl
cikKPj4gwqArIHN1bjhpX21peGVyX2xheWVyX2VuYWJsZShtaXhlciwgbGF5ZXItPmlkLCBmYWxz
ZSk7Cj4+IMKgwqB9Cj4+Cj4+IMKgLXN0YXRpYyB2b2lkIHN1bjRpX2JhY2tlbmRfbGF5ZXJfYXRv
bWljX3VwZGF0ZShzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSwKPj4gwqArc3RhdGljIHZvaWQgc3Vu
NGlfbGF5ZXJfYXRvbWljX3VwZGF0ZShzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSwKPj4gwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IGRybV9wbGFuZV9zdGF0ZSAq
b2xkX3N0YXRlKQo+PiDCoMKgewo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVjdCBzdW40aV9s
YXllciAqbGF5ZXIgPSBwbGFuZV90b19zdW40aV9sYXllcihwbGFuZSk7Cj4+IMKgwqDCoMKgwqDC
oMKgwqDCoMKgc3RydWN0IHN1bjRpX2RydiAqZHJ2ID0gbGF5ZXItPmRydjsKPj4gwqDCoMKgwqDC
oMKgwqDCoMKgwqBzdHJ1Y3Qgc3VuNGlfYmFja2VuZCAqYmFja2VuZCA9IGRydi0+YmFja2VuZDsK
Pj4gwqAtCj4+IMKgLSBzdW40aV9iYWNrZW5kX3VwZGF0ZV9sYXllcl9jb29yZChiYWNrZW5kLCBs
YXllci0+aWQsIHBsYW5lKTsKPj4gwqAtIHN1bjRpX2JhY2tlbmRfdXBkYXRlX2xheWVyX2Zvcm1h
dHMoYmFja2VuZCwgbGF5ZXItPmlkLCBwbGFuZSk7Cj4+IMKgLSBzdW40aV9iYWNrZW5kX3VwZGF0
ZV9sYXllcl9idWZmZXIoYmFja2VuZCwgbGF5ZXItPmlkLCBwbGFuZSk7Cj4+IMKgLSBzdW40aV9i
YWNrZW5kX2xheWVyX2VuYWJsZShiYWNrZW5kLCBsYXllci0+aWQsIHRydWUpOwo+PiDCoCsgc3Ry
dWN0IHN1bjhpX21peGVyICptaXhlciA9IGRydi0+bWl4ZXI7Cj4+IMKgKwo+PiDCoCsgaWYgKGJh
Y2tlbmQpIHsKPj4gwqArIHN1bjRpX2JhY2tlbmRfdXBkYXRlX2xheWVyX2Nvb3JkKGJhY2tlbmQs
IGxheWVyLT5pZCwgcGxhbmUpOwo+PiDCoCsgc3VuNGlfYmFja2VuZF91cGRhdGVfbGF5ZXJfZm9y
bWF0cyhiYWNrZW5kLCBsYXllci0+aWQsIHBsYW5lKTsKPj4gwqArIHN1bjRpX2JhY2tlbmRfdXBk
YXRlX2xheWVyX2J1ZmZlcihiYWNrZW5kLCBsYXllci0+aWQsIHBsYW5lKTsKPj4gwqArIHN1bjRp
X2JhY2tlbmRfbGF5ZXJfZW5hYmxlKGJhY2tlbmQsIGxheWVyLT5pZCwgdHJ1ZSk7Cj4+IMKgKyB9
IGVsc2UgaWYgKG1peGVyKSB7Cj4+IMKgKyBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfY29vcmQo
bWl4ZXIsIGxheWVyLT5pZCwgcGxhbmUpOwo+PiDCoCsgc3VuOGlfbWl4ZXJfdXBkYXRlX2xheWVy
X2Zvcm1hdHMobWl4ZXIsIGxheWVyLT5pZCwgcGxhbmUpOwo+PiDCoCsgc3VuOGlfbWl4ZXJfdXBk
YXRlX2xheWVyX2J1ZmZlcihtaXhlciwgbGF5ZXItPmlkLCBwbGFuZSk7Cj4+IMKgKyBzdW44aV9t
aXhlcl9sYXllcl9lbmFibGUobWl4ZXIsIGxheWVyLT5pZCwgdHJ1ZSk7Cj4+IMKgKyB9Cj4+IMKg
wqB9Cj4+Cj4+IMKgLXN0YXRpYyBzdHJ1Y3QgZHJtX3BsYW5lX2hlbHBlcl9mdW5jcyBzdW40aV9i
YWNrZW5kX2xheWVyX2hlbHBlcl9mdW5jcyA9IHsKPj4gwqAtIC5hdG9taWNfY2hlY2sgPSBzdW40
aV9iYWNrZW5kX2xheWVyX2F0b21pY19jaGVjaywKPj4gwqAtIC5hdG9taWNfZGlzYWJsZSA9IHN1
bjRpX2JhY2tlbmRfbGF5ZXJfYXRvbWljX2Rpc2FibGUsCj4+IMKgLSAuYXRvbWljX3VwZGF0ZSA9
IHN1bjRpX2JhY2tlbmRfbGF5ZXJfYXRvbWljX3VwZGF0ZSwKPj4gwqArc3RhdGljIHN0cnVjdCBk
cm1fcGxhbmVfaGVscGVyX2Z1bmNzIHN1bjRpX2xheWVyX2hlbHBlcl9mdW5jcyA9IHsKPj4gwqAr
IC5hdG9taWNfY2hlY2sgPSBzdW40aV9sYXllcl9hdG9taWNfY2hlY2ssCj4+IMKgKyAuYXRvbWlj
X2Rpc2FibGUgPSBzdW40aV9sYXllcl9hdG9taWNfZGlzYWJsZSwKPj4gwqArIC5hdG9taWNfdXBk
YXRlID0gc3VuNGlfbGF5ZXJfYXRvbWljX3VwZGF0ZSwKPj4gwqDCoH07Cj4+Cj4+IMKgLXN0YXRp
YyBjb25zdCBzdHJ1Y3QgZHJtX3BsYW5lX2Z1bmNzIHN1bjRpX2JhY2tlbmRfbGF5ZXJfZnVuY3Mg
PSB7Cj4+IMKgK3N0YXRpYyBjb25zdCBzdHJ1Y3QgZHJtX3BsYW5lX2Z1bmNzIHN1bjRpX2xheWVy
X2Z1bmNzID0gewo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoC5hdG9taWNfZGVzdHJveV9zdGF0ZSA9
IGRybV9hdG9taWNfaGVscGVyX3BsYW5lX2Rlc3Ryb3lfc3RhdGUsCj4+IMKgwqDCoMKgwqDCoMKg
wqDCoMKgLmF0b21pY19kdXBsaWNhdGVfc3RhdGUgPSBkcm1fYXRvbWljX2hlbHBlcl9wbGFuZV9k
dXBsaWNhdGVfc3RhdGUsCj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgLmRlc3Ryb3kgPSBkcm1fcGxh
bmVfY2xlYW51cCwKPj4gwqBAQCAtODgsNiArMTAyLDEyIEBAIHN0YXRpYyBjb25zdCB1aW50MzJf
dAo+PiDCoHN1bjRpX2JhY2tlbmRfbGF5ZXJfZm9ybWF0c19vdmVybGF5W10gPSB7IERSTV9GT1JN
QVRfWFJHQjg4ODgsCj4+IMKgwqB9Owo+Pgo+PiDCoCtzdGF0aWMgY29uc3QgdWludDMyX3Qgc3Vu
OGlfbWl4ZXJfbGF5ZXJfZm9ybWF0c1tdID0gewo+PiDCoCsgRFJNX0ZPUk1BVF9BUkdCODg4OCwK
Pj4gwqArIERSTV9GT1JNQVRfUkdCODg4LAo+PiDCoCsgRFJNX0ZPUk1BVF9YUkdCODg4OCwKPj4g
wqArfTsKPj4gwqArCj4+IMKgwqBzdGF0aWMgY29uc3Qgc3RydWN0IHN1bjRpX3BsYW5lX2Rlc2Mg
c3VuNGlfYmFja2VuZF9wbGFuZXNbXSA9IHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqB7Cj4+IMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoC50eXBlID0gRFJNX1BMQU5FX1RZUEVf
UFJJTUFSWSwKPj4gwqBAQCAtMTAzLDYgKzEyMywxOSBAQCBzdGF0aWMgY29uc3Qgc3RydWN0IHN1
bjRpX3BsYW5lX2Rlc2MKPj4gwqBzdW40aV9iYWNrZW5kX3BsYW5lc1tdID0geyB9LAo+PiDCoMKg
fTsKPj4KPj4gwqArc3RhdGljIGNvbnN0IHN0cnVjdCBzdW40aV9wbGFuZV9kZXNjIHN1bjhpX21p
eGVyX3BsYW5lc1tdID0gewo+PiDCoCsgewo+PiDCoCsgLnR5cGUgPSBEUk1fUExBTkVfVFlQRV9Q
UklNQVJZLAo+PiDCoCsgLmZvcm1hdHMgPSBzdW44aV9taXhlcl9sYXllcl9mb3JtYXRzLAo+PiDC
oCsgLm5mb3JtYXRzID0gQVJSQVlfU0laRShzdW44aV9taXhlcl9sYXllcl9mb3JtYXRzKSwKPj4g
wqArIH0sCj4+IMKgKyB7Cj4+IMKgKyAudHlwZSA9IERSTV9QTEFORV9UWVBFX09WRVJMQVksCj4+
IMKgKyAuZm9ybWF0cyA9IHN1bjhpX21peGVyX2xheWVyX2Zvcm1hdHMsCj4+IMKgKyAubmZvcm1h
dHMgPSBBUlJBWV9TSVpFKHN1bjhpX21peGVyX2xheWVyX2Zvcm1hdHMpLAo+PiDCoCsgfSwKPj4g
wqArfTsKPj4gwqArCj4+IMKgwqBzdGF0aWMgc3RydWN0IHN1bjRpX2xheWVyICpzdW40aV9sYXll
cl9pbml0X29uZShzdHJ1Y3QgZHJtX2RldmljZSAqZHJtLAo+PiDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgY29uc3Qgc3RydWN0IHN1bjRpX3BsYW5lX2Rlc2MgKnBs
YW5lKQo+PiDCoMKgewo+PiDCoEBAIC0xMTUsNyArMTQ4LDcgQEAgc3RhdGljIHN0cnVjdCBzdW40
aV9sYXllciAqc3VuNGlfbGF5ZXJfaW5pdF9vbmUoc3RydWN0Cj4+IMKgZHJtX2RldmljZSAqZHJt
LCByZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKPj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqByZXQg
PSBkcm1fdW5pdmVyc2FsX3BsYW5lX2luaXQoZHJtLCAmbGF5ZXItPnBsYW5lLCBCSVQoMCksCj4+
IMKgLSAmc3VuNGlfYmFja2VuZF9sYXllcl9mdW5jcywKPj4gwqArICZzdW40aV9sYXllcl9mdW5j
cywKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHBsYW5lLT5mb3JtYXRzLCBwbGFuZS0+bmZv
cm1hdHMsCj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBwbGFuZS0+dHlwZSwgTlVMTCk7Cj4+
IMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKHJldCkgewo+PiDCoEBAIC0xMjQsNyArMTU3LDcgQEAg
c3RhdGljIHN0cnVjdCBzdW40aV9sYXllciAqc3VuNGlfbGF5ZXJfaW5pdF9vbmUoc3RydWN0Cj4+
IMKgZHJtX2RldmljZSAqZHJtLCB9Cj4+Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgZHJtX3BsYW5l
X2hlbHBlcl9hZGQoJmxheWVyLT5wbGFuZSwKPj4gwqAtICZzdW40aV9iYWNrZW5kX2xheWVyX2hl
bHBlcl9mdW5jcyk7Cj4+IMKgKyAmc3VuNGlfbGF5ZXJfaGVscGVyX2Z1bmNzKTsKPj4gwqDCoMKg
wqDCoMKgwqDCoMKgwqBsYXllci0+ZHJ2ID0gZHJ2Owo+Pgo+PiDCoMKgwqDCoMKgwqDCoMKgwqDC
oGlmIChwbGFuZS0+dHlwZSA9PSBEUk1fUExBTkVfVFlQRV9QUklNQVJZKQo+PiDCoEBAIC0xODcs
MyArMjIwLDMwIEBAIHN0cnVjdCBzdW40aV9sYXllciAqKnN1bjRpX2xheWVyc19pbml0KHN0cnVj
dAo+PiDCoGRybV9kZXZpY2UgKmRybSkKPj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqByZXR1cm4g
bGF5ZXJzOwo+PiDCoMKgfQo+PiDCoCsKPj4gwqArc3RydWN0IHN1bjRpX2xheWVyICoqc3VuOGlf
bGF5ZXJzX2luaXQoc3RydWN0IGRybV9kZXZpY2UgKmRybSkKPj4gwqArewo+PiDCoCsgc3RydWN0
IHN1bjRpX2xheWVyICoqbGF5ZXJzOwo+PiDCoCsgaW50IGk7Cj4+IMKgKwo+PiDCoCsgbGF5ZXJz
ID0gZGV2bV9rY2FsbG9jKGRybS0+ZGV2LCBBUlJBWV9TSVpFKHN1bjhpX21peGVyX3BsYW5lcyks
Cj4+IMKgKyBzaXplb2YoKipsYXllcnMpLCBHRlBfS0VSTkVMKTsKPj4gwqArIGlmICghbGF5ZXJz
KQo+PiDCoCsgcmV0dXJuIEVSUl9QVFIoLUVOT01FTSk7Cj4+IMKgKwo+PiDCoCsgZm9yIChpID0g
MDsgaSA8IEFSUkFZX1NJWkUoc3VuOGlfbWl4ZXJfcGxhbmVzKTsgaSsrKSB7Cj4+IMKgKyBjb25z
dCBzdHJ1Y3Qgc3VuNGlfcGxhbmVfZGVzYyAqcGxhbmUgPSAmc3VuOGlfbWl4ZXJfcGxhbmVzW2ld
Owo+PiDCoCsgc3RydWN0IHN1bjRpX2xheWVyICpsYXllciA9IGxheWVyc1tpXTsKPj4gwqArCj4+
IMKgKyBsYXllciA9IHN1bjRpX2xheWVyX2luaXRfb25lKGRybSwgcGxhbmUpOwo+PiDCoCsgaWYg
KElTX0VSUihsYXllcikpIHsKPj4gwqArIGRldl9lcnIoZHJtLT5kZXYsICJDb3VsZG4ndCBpbml0
aWFsaXplICVzIHBsYW5lXG4iLAo+PiDCoCsgaSA/ICJvdmVybGF5IiA6ICJwcmltYXJ5Iik7Cj4+
IMKgKyByZXR1cm4gRVJSX0NBU1QobGF5ZXIpOwo+PiDCoCsgfTsKPj4gwqArCj4+IMKgKyBsYXll
ci0+aWQgPSBpOwo+PiDCoCsgfTsKPj4gwqArCj4+IMKgKyByZXR1cm4gbGF5ZXJzOwo+PiDCoCt9
Cj4+IMKgZGlmZiAtLWdpdCBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9sYXllci5oCj4+
IMKgYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfbGF5ZXIuaCBpbmRleCBhMmY2NWQ3YTNm
NGUuLmY3YjllNWRhZWE1MAo+PiDCoDEwMDY0NAo+PiDCoC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9z
dW40aS9zdW40aV9sYXllci5oCj4+IMKgKysrIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRp
X2xheWVyLmgKPj4gwqBAQCAtMjYsNSArMjYsNiBAQCBwbGFuZV90b19zdW40aV9sYXllcihzdHJ1
Y3QgZHJtX3BsYW5lICpwbGFuZSkKPj4gwqDCoH0KPj4KPj4gwqDCoHN0cnVjdCBzdW40aV9sYXll
ciAqKnN1bjRpX2xheWVyc19pbml0KHN0cnVjdCBkcm1fZGV2aWNlICpkcm0pOwo+PiDCoCtzdHJ1
Y3Qgc3VuNGlfbGF5ZXIgKipzdW44aV9sYXllcnNfaW5pdChzdHJ1Y3QgZHJtX2RldmljZSAqZHJt
KTsKPj4KPj4gwqDCoCNlbmRpZiAvKiBfU1VONElfTEFZRVJfSF8gKi8KPj4gwqBkaWZmIC0tZ2l0
IGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjhpX21peGVyLmMKPj4gwqBiL2RyaXZlcnMvZ3B1
L2RybS9zdW40aS9zdW44aV9taXhlci5jIG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4+IMKgaW5kZXgg
MDAwMDAwMDAwMDAwLi45NDI3YjU3MjQwZDMKPj4gwqAtLS0gL2Rldi9udWxsCj4+IMKgKysrIGIv
ZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjhpX21peGVyLmMKPj4gwqBAQCAtMCwwICsxLDQxNyBA
QAo+PiDCoCsvKgo+PiDCoCsgKiBDb3B5cmlnaHQgKEMpIDIwMTcgSWNlbm93eSBaaGVuZyA8aWNl
bm93eUBhb3NjLnh5ej4KPj4gwqArICoKPj4gwqArICogQmFzZWQgb24gc3VuNGlfYmFja2VuZC5j
LCB3aGljaCBpczoKPj4gwqArICogQ29weXJpZ2h0IChDKSAyMDE1IEZyZWUgRWxlY3Ryb25zCj4+
IMKgKyAqIENvcHlyaWdodCAoQykgMjAxNSBOZXh0VGhpbmcgQ28KPj4gwqArICoKPj4gwqArICog
VGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFu
ZC9vcgo+PiDCoCsgKiBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJh
bCBQdWJsaWMgTGljZW5zZSBhcwo+PiDCoCsgKiBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdh
cmUgRm91bmRhdGlvbjsgZWl0aGVyIHZlcnNpb24gMiBvZgo+PiDCoCsgKiB0aGUgTGljZW5zZSwg
b3IgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4KPj4gwqArICovCj4+IMKgKwo+
PiDCoCsjaW5jbHVkZSA8ZHJtL2RybVAuaD4KPj4gwqArI2luY2x1ZGUgPGRybS9kcm1fYXRvbWlj
X2hlbHBlci5oPgo+PiDCoCsjaW5jbHVkZSA8ZHJtL2RybV9jcnRjLmg+Cj4+IMKgKyNpbmNsdWRl
IDxkcm0vZHJtX2NydGNfaGVscGVyLmg+Cj4+IMKgKyNpbmNsdWRlIDxkcm0vZHJtX2ZiX2NtYV9o
ZWxwZXIuaD4KPj4gwqArI2luY2x1ZGUgPGRybS9kcm1fZ2VtX2NtYV9oZWxwZXIuaD4KPj4gwqAr
I2luY2x1ZGUgPGRybS9kcm1fcGxhbmVfaGVscGVyLmg+Cj4+IMKgKwo+PiDCoCsjaW5jbHVkZSA8
bGludXgvY29tcG9uZW50Lmg+Cj4+IMKgKyNpbmNsdWRlIDxsaW51eC9yZXNldC5oPgo+PiDCoCsj
aW5jbHVkZSA8bGludXgvb2ZfZGV2aWNlLmg+Cj4+IMKgKwo+PiDCoCsjaW5jbHVkZSAic3VuOGlf
bWl4ZXIuaCIKPj4gwqArI2luY2x1ZGUgInN1bjRpX2Rydi5oIgo+PiDCoCsKPj4gwqArI2RlZmlu
ZSBTVU44SV9EUkFNX09GRlNFVCAweDQwMDAwMDAwCj4+IMKgKwo+PiDCoCsjaWYgZGVmaW5lZCBD
T05GSUdfRFJNX1NVTjRJX0RFMgo+PiDCoCt2b2lkIHN1bjhpX21peGVyX2NvbW1pdChzdHJ1Y3Qg
c3VuOGlfbWl4ZXIgKm1peGVyKQo+PiDCoCt7Cj4+IMKgKyBEUk1fREVCVUdfRFJJVkVSKCJDb21t
aXR0aW5nIGNoYW5nZXNcbiIpOwo+PiDCoCsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVn
cywgU1VOOElfTUlYRVJfR0xPQkFMX0RCVUZGLAo+PiDCoCsgU1VOOElfTUlYRVJfR0xPQkFMX0RC
VUZGX0VOQUJMRSk7Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJPTChzdW44aV9taXhlcl9jb21t
aXQpOwo+PiDCoCsKPj4gwqArdm9pZCBzdW44aV9taXhlcl9sYXllcl9lbmFibGUoc3RydWN0IHN1
bjhpX21peGVyICptaXhlciwKPj4gwqArIGludCBsYXllciwgYm9vbCBlbmFibGUpCj4+IMKgK3sK
Pj4gwqArIHUzMiB2YWw7Cj4+IMKgKyAvKiBDdXJyZW50bHkgdGhlIGZpcnN0IFVJIGNoYW5uZWwg
aXMgdXNlZCAqLwo+PiDCoCsgaW50IGNoYW4gPSBtaXhlci0+Y2ZnLT52aV9udW07Cj4+IMKgKwo+
PiDCoCsgRFJNX0RFQlVHX0RSSVZFUigiRW5hYmxpbmcgbGF5ZXIgJWQgaW4gY2hhbm5lbCAlZFxu
IiwgbGF5ZXIsIGNoYW4pOwo+PiDCoCsKPj4gwqArIGlmIChlbmFibGUpCj4+IMKgKyB2YWwgPSBT
VU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFJfRU47Cj4+IMKgKyBlbHNlCj4+IMKgKyB2YWwg
PSAwOwo+PiDCoCsKPj4gwqArIHJlZ21hcF91cGRhdGVfYml0cyhtaXhlci0+cmVncywKPj4gwqAr
IFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUihjaGFuLCBsYXllciksCj4+IMKgKyBTVU44
SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFJfRU4sIHZhbCk7Cj4+IMKgKwo+PiDCoCsgLyogU2V0
IHRoZSBhbHBoYSBjb25maWd1cmF0aW9uICovCj4+IMKgKyByZWdtYXBfdXBkYXRlX2JpdHMobWl4
ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFIoY2hhbiwgbGF5
ZXIpLAo+PiDCoCsgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0FMUEhBX01PREVfTUFT
SywKPj4gwqArIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9BTFBIQV9NT0RFX0RFRik7
Cj4+IMKgKyByZWdtYXBfdXBkYXRlX2JpdHMobWl4ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9NSVhF
Ul9DSEFOX1VJX0xBWUVSX0FUVFIoY2hhbiwgbGF5ZXIpLAo+PiDCoCsgU1VOOElfTUlYRVJfQ0hB
Tl9VSV9MQVlFUl9BVFRSX0FMUEhBX01BU0ssCj4+IMKgKyBTVU44SV9NSVhFUl9DSEFOX1VJX0xB
WUVSX0FUVFJfQUxQSEFfREVGKTsKPj4gwqArfQo+PiDCoCtFWFBPUlRfU1lNQk9MKHN1bjhpX21p
eGVyX2xheWVyX2VuYWJsZSk7Cj4+IMKgKwo+PiDCoCtzdGF0aWMgaW50IHN1bjhpX21peGVyX2Ry
bV9mb3JtYXRfdG9fbGF5ZXIoc3RydWN0IGRybV9wbGFuZSAqcGxhbmUsCj4+IMKgKyB1MzIgZm9y
bWF0LCB1MzIgKm1vZGUpCj4+IMKgK3sKPj4gwqArIGlmICgocGxhbmUtPnR5cGUgPT0gRFJNX1BM
QU5FX1RZUEVfUFJJTUFSWSkgJiYKPj4gwqArIChmb3JtYXQgPT0gRFJNX0ZPUk1BVF9BUkdCODg4
OCkpCj4+IMKgKyBmb3JtYXQgPSBEUk1fRk9STUFUX1hSR0I4ODg4Owo+PiDCoCsKPj4gwqArIHN3
aXRjaCAoZm9ybWF0KSB7Cj4+IMKgKyBjYXNlIERSTV9GT1JNQVRfQVJHQjg4ODg6Cj4+IMKgKyAq
bW9kZSA9IFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9GQkZNVF9BUkdCODg4ODsKPj4g
wqArIGJyZWFrOwo+PiDCoCsKPj4gwqArIGNhc2UgRFJNX0ZPUk1BVF9YUkdCODg4ODoKPj4gwqAr
ICptb2RlID0gU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX1hSR0I4ODg4Owo+
PiDCoCsgYnJlYWs7Cj4+IMKgKwo+PiDCoCsgY2FzZSBEUk1fRk9STUFUX1JHQjg4ODoKPj4gwqAr
ICptb2RlID0gU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX1JHQjg4ODsKPj4g
wqArIGJyZWFrOwo+PiDCoCsKPj4gwqArIGRlZmF1bHQ6Cj4+IMKgKyByZXR1cm4gLUVJTlZBTDsK
Pj4gwqArIH0KPj4gwqArCj4+IMKgKyByZXR1cm4gMDsKPj4gwqArfQo+PiDCoCsKPj4gwqAraW50
IHN1bjhpX21peGVyX3VwZGF0ZV9sYXllcl9jb29yZChzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVy
LAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDC
oCsgc3RydWN0IGRybV9wbGFuZV9zdGF0ZSAqc3RhdGUgPSBwbGFuZS0+c3RhdGU7Cj4+IMKgKyBz
dHJ1Y3QgZHJtX2ZyYW1lYnVmZmVyICpmYiA9IHN0YXRlLT5mYjsKPj4gwqArIC8qIEN1cnJlbnRs
eSB0aGUgZmlyc3QgVUkgY2hhbm5lbCBpcyB1c2VkICovCj4+IMKgKyBpbnQgY2hhbiA9IG1peGVy
LT5jZmctPnZpX251bTsKPj4gwqArIGludCBpOwo+PiDCoCsKPj4gwqArIERSTV9ERUJVR19EUklW
RVIoIlVwZGF0aW5nIGxheWVyICVkXG4iLCBsYXllcik7Cj4+IMKgKwo+PiDCoCsgaWYgKHBsYW5l
LT50eXBlID09IERSTV9QTEFORV9UWVBFX1BSSU1BUlkpIHsKPj4gwqArIERSTV9ERUJVR19EUklW
RVIoIlByaW1hcnkgbGF5ZXIsIHVwZGF0aW5nIGdsb2JhbCBzaXplIFc6ICV1IEg6ICV1XG4iLAo+
PiDCoCsgc3RhdGUtPmNydGNfdywgc3RhdGUtPmNydGNfaCk7Cj4+IMKgKyByZWdtYXBfd3JpdGUo
bWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0dMT0JBTF9TSVpFLAo+PiDCoCsgU1VOOElfTUlYRVJf
U0laRShzdGF0ZS0+Y3J0Y193LAo+PiDCoCsgc3RhdGUtPmNydGNfaCkpOwo+PiDCoCsgRFJNX0RF
QlVHX0RSSVZFUigiVXBkYXRpbmcgYmxlbmRlciBzaXplXG4iKTsKPj4gwqArIGZvciAoaSA9IDA7
IGkgPCBTVU44SV9NSVhFUl9NQVhfQ0hBTl9DT1VOVDsgaSsrKQo+PiDCoCsgcmVnbWFwX3dyaXRl
KG1peGVyLT5yZWdzLAo+PiDCoCsgU1VOOElfTUlYRVJfQkxFTkRfQVRUUl9JTlNJWkUoaSksCj4+
IMKgKyBTVU44SV9NSVhFUl9TSVpFKHN0YXRlLT5jcnRjX3csCj4+IMKgKyBzdGF0ZS0+Y3J0Y19o
KSk7Cj4+IMKgKyByZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0JMRU5EX09V
VFNJWkUsCj4+IMKgKyBTVU44SV9NSVhFUl9TSVpFKHN0YXRlLT5jcnRjX3csCj4+IMKgKyBzdGF0
ZS0+Y3J0Y19oKSk7Cj4+IMKgKyBEUk1fREVCVUdfRFJJVkVSKCJVcGRhdGluZyBjaGFubmVsIHNp
emVcbiIpOwo+PiDCoCsgcmVnbWFwX3dyaXRlKG1peGVyLT5yZWdzLCBTVU44SV9NSVhFUl9DSEFO
X1VJX09WTF9TSVpFKGNoYW4pLAo+PiDCoCsgU1VOOElfTUlYRVJfU0laRShzdGF0ZS0+Y3J0Y193
LAo+PiDCoCsgc3RhdGUtPmNydGNfaCkpOwo+PiDCoCsgfQo+PiDCoCsKPj4gwqArIC8qIFNldCB0
aGUgbGluZSB3aWR0aCAqLwo+PiDCoCsgRFJNX0RFQlVHX0RSSVZFUigiTGF5ZXIgbGluZSB3aWR0
aDogJWQgYnl0ZXNcbiIsIGZiLT5waXRjaGVzWzBdKTsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhl
ci0+cmVncywgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9QSVRDSChjaGFuLCBsYXllciksCj4+
IMKgKyBmYi0+cGl0Y2hlc1swXSk7Cj4+IMKgKwo+PiDCoCsgLyogU2V0IGhlaWdodCBhbmQgd2lk
dGggKi8KPj4gwqArIERSTV9ERUJVR19EUklWRVIoIkxheWVyIHNpemUgVzogJXUgSDogJXVcbiIs
Cj4+IMKgKyBzdGF0ZS0+Y3J0Y193LCBzdGF0ZS0+Y3J0Y19oKTsKPj4gwqArIHJlZ21hcF93cml0
ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9TSVpFKGNoYW4sIGxheWVy
KSwKPj4gwqArIFNVTjhJX01JWEVSX1NJWkUoc3RhdGUtPmNydGNfdywgc3RhdGUtPmNydGNfaCkp
Owo+PiDCoCsKPj4gwqArIC8qIFNldCBiYXNlIGNvb3JkaW5hdGVzICovCj4+IMKgKyBEUk1fREVC
VUdfRFJJVkVSKCJMYXllciBjb29yZGluYXRlcyBYOiAlZCBZOiAlZFxuIiwKPj4gwqArIHN0YXRl
LT5jcnRjX3gsIHN0YXRlLT5jcnRjX3kpOwo+PiDCoCsgcmVnbWFwX3dyaXRlKG1peGVyLT5yZWdz
LCBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0NPT1JEKGNoYW4sIGxheWVyKSwKPj4gwqArIFNV
TjhJX01JWEVSX0NPT1JEKHN0YXRlLT5jcnRjX3gsIHN0YXRlLT5jcnRjX3kpKTsKPj4gwqArCj4+
IMKgKyByZXR1cm4gMDsKPj4gwqArfQo+PiDCoCtFWFBPUlRfU1lNQk9MKHN1bjhpX21peGVyX3Vw
ZGF0ZV9sYXllcl9jb29yZCk7Cj4+IMKgKwo+PiDCoCtpbnQgc3VuOGlfbWl4ZXJfdXBkYXRlX2xh
eWVyX2Zvcm1hdHMoc3RydWN0IHN1bjhpX21peGVyICptaXhlciwKPj4gwqArIGludCBsYXllciwg
c3RydWN0IGRybV9wbGFuZSAqcGxhbmUpCj4+IMKgK3sKPj4gwqArIHN0cnVjdCBkcm1fcGxhbmVf
c3RhdGUgKnN0YXRlID0gcGxhbmUtPnN0YXRlOwo+PiDCoCsgc3RydWN0IGRybV9mcmFtZWJ1ZmZl
ciAqZmIgPSBzdGF0ZS0+ZmI7Cj4+IMKgKyBib29sIGludGVybGFjZWQgPSBmYWxzZTsKPj4gwqAr
IHUzMiB2YWw7Cj4+IMKgKyAvKiBDdXJyZW50bHkgdGhlIGZpcnN0IFVJIGNoYW5uZWwgaXMgdXNl
ZCAqLwo+PiDCoCsgaW50IGNoYW4gPSBtaXhlci0+Y2ZnLT52aV9udW07Cj4+IMKgKyBpbnQgcmV0
Owo+PiDCoCsKPj4gwqArIGlmIChwbGFuZS0+c3RhdGUtPmNydGMpCj4+IMKgKyBpbnRlcmxhY2Vk
ID0gcGxhbmUtPnN0YXRlLT5jcnRjLT5zdGF0ZS0+YWRqdXN0ZWRfbW9kZS5mbGFncwo+PiDCoCsg
JiBEUk1fTU9ERV9GTEFHX0lOVEVSTEFDRTsKPj4gwqArCj4+IMKgKyByZWdtYXBfdXBkYXRlX2Jp
dHMobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0JMRU5EX09VVENUTCwKPj4gwqArIFNVTjhJX01J
WEVSX0JMRU5EX09VVENUTF9JTlRFUkxBQ0VELAo+PiDCoCsgaW50ZXJsYWNlZCA/Cj4+IMKgKyBT
VU44SV9NSVhFUl9CTEVORF9PVVRDVExfSU5URVJMQUNFRCA6IDApOwo+PiDCoCsKPj4gwqArIERS
TV9ERUJVR19EUklWRVIoIlN3aXRjaGluZyBkaXNwbGF5IG1peGVyIGludGVybGFjZWQgbW9kZSAl
c1xuIiwKPj4gwqArIGludGVybGFjZWQgPyAib24iIDogIm9mZiIpOwo+PiDCoCsKPj4gwqArIHJl
dCA9IHN1bjhpX21peGVyX2RybV9mb3JtYXRfdG9fbGF5ZXIocGxhbmUsIGZiLT5mb3JtYXQtPmZv
cm1hdCwKPj4gwqArICZ2YWwpOwo+PiDCoCsgaWYgKHJldCkgewo+PiDCoCsgRFJNX0RFQlVHX0RS
SVZFUigiSW52YWxpZCBmb3JtYXRcbiIpOwo+PiDCoCsgcmV0dXJuIHJldDsKPj4gwqArIH0KPj4g
wqArCj4+IMKgKyByZWdtYXBfdXBkYXRlX2JpdHMobWl4ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9N
SVhFUl9DSEFOX1VJX0xBWUVSX0FUVFIoY2hhbiwgbGF5ZXIpLAo+PiDCoCsgU1VOOElfTUlYRVJf
Q0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX01BU0ssIHZhbCk7Cj4+IMKgKwo+PiDCoCsgcmV0dXJu
IDA7Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJPTChzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJf
Zm9ybWF0cyk7Cj4+IMKgKwo+PiDCoCtpbnQgc3VuOGlfbWl4ZXJfdXBkYXRlX2xheWVyX2J1ZmZl
cihzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJt
X3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDCoCsgc3RydWN0IGRybV9wbGFuZV9zdGF0ZSAqc3Rh
dGUgPSBwbGFuZS0+c3RhdGU7Cj4+IMKgKyBzdHJ1Y3QgZHJtX2ZyYW1lYnVmZmVyICpmYiA9IHN0
YXRlLT5mYjsKPj4gwqArIHN0cnVjdCBkcm1fZ2VtX2NtYV9vYmplY3QgKmdlbTsKPj4gwqArIGRt
YV9hZGRyX3QgcGFkZHI7Cj4+IMKgKyB1aW50MzJfdCBwYWRkcl91MzI7Cj4+IMKgKyAvKiBDdXJy
ZW50bHkgdGhlIGZpcnN0IFVJIGNoYW5uZWwgaXMgdXNlZCAqLwo+PiDCoCsgaW50IGNoYW4gPSBt
aXhlci0+Y2ZnLT52aV9udW07Cj4+IMKgKyBpbnQgYnBwOwo+PiDCoCsKPj4gwqArIC8qIEdldCB0
aGUgcGh5c2ljYWwgYWRkcmVzcyBvZiB0aGUgYnVmZmVyIGluIG1lbW9yeSAqLwo+PiDCoCsgZ2Vt
ID0gZHJtX2ZiX2NtYV9nZXRfZ2VtX29iaihmYiwgMCk7Cj4+IMKgKwo+PiDCoCsgRFJNX0RFQlVH
X0RSSVZFUigiVXNpbmcgR0VNIEAgJXBhZFxuIiwgJmdlbS0+cGFkZHIpOwo+PiDCoCsKPj4gwqAr
IC8qIENvbXB1dGUgdGhlIHN0YXJ0IG9mIHRoZSBkaXNwbGF5ZWQgbWVtb3J5ICovCj4+IMKgKyBi
cHAgPSBmYi0+Zm9ybWF0LT5jcHBbMF07Cj4+IMKgKyBwYWRkciA9IGdlbS0+cGFkZHIgKyBmYi0+
b2Zmc2V0c1swXTsKPj4gwqArIHBhZGRyICs9IChzdGF0ZS0+c3JjX3ggPj4gMTYpICogYnBwOwo+
PiDCoCsgcGFkZHIgKz0gKHN0YXRlLT5zcmNfeSA+PiAxNikgKiBmYi0+cGl0Y2hlc1swXTsKPj4g
wqArIHBhZGRyIC09IFNVTjhJX0RSQU1fT0ZGU0VUOwo+PiDCoCsKPj4gwqArIERSTV9ERUJVR19E
UklWRVIoIlNldHRpbmcgYnVmZmVyIGFkZHJlc3MgdG8gJXBhZFxuIiwgJnBhZGRyKTsKPj4gwqAr
Cj4+IMKgKyBwYWRkcl91MzIgPSAodWludDMyX3QpIHBhZGRyOwo+PiDCoCsKPj4gwqArIHJlZ21h
cF93cml0ZShtaXhlci0+cmVncywKPj4gwqArIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfVE9Q
X0xBRERSKGNoYW4sIGxheWVyKSwKPj4gwqArIHBhZGRyX3UzMik7Cj4+IMKgKwo+PiDCoCsgcmV0
dXJuIDA7Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJPTChzdW44aV9taXhlcl91cGRhdGVfbGF5
ZXJfYnVmZmVyKTsKPj4gwqArCj4+IMKgK3N0YXRpYyBzdHJ1Y3QgcmVnbWFwX2NvbmZpZyBzdW44
aV9taXhlcl9yZWdtYXBfY29uZmlnID0gewo+PiDCoCsgLnJlZ19iaXRzID0gMzIsCj4+IMKgKyAu
dmFsX2JpdHMgPSAzMiwKPj4gwqArIC5yZWdfc3RyaWRlID0gNCwKPj4gwqArIC5tYXhfcmVnaXN0
ZXIgPSAweGJmZmMsIC8qIGd1ZXNzZWQgKi8KPj4gwqArfTsKPj4gwqArCj4+IMKgK3N0YXRpYyBp
bnQgc3VuOGlfbWl4ZXJfYmluZChzdHJ1Y3QgZGV2aWNlICpkZXYsIHN0cnVjdCBkZXZpY2UgKm1h
c3RlciwKPj4gwqArIHZvaWQgKmRhdGEpCj4+IMKgK3sKPj4gwqArIHN0cnVjdCBwbGF0Zm9ybV9k
ZXZpY2UgKnBkZXYgPSB0b19wbGF0Zm9ybV9kZXZpY2UoZGV2KTsKPj4gwqArIHN0cnVjdCBkcm1f
ZGV2aWNlICpkcm0gPSBkYXRhOwo+PiDCoCsgc3RydWN0IHN1bjRpX2RydiAqZHJ2ID0gZHJtLT5k
ZXZfcHJpdmF0ZTsKPj4gwqArIHN0cnVjdCBzdW44aV9taXhlciAqbWl4ZXI7Cj4+IMKgKyBzdHJ1
Y3QgcmVzb3VyY2UgKnJlczsKPj4gwqArIHZvaWQgX19pb21lbSAqcmVnczsKPj4gwqArIGludCBp
LCByZXQ7Cj4+IMKgKwo+PiDCoCsgbWl4ZXIgPSBkZXZtX2t6YWxsb2MoZGV2LCBzaXplb2YoKm1p
eGVyKSwgR0ZQX0tFUk5FTCk7Cj4+IMKgKyBpZiAoIW1peGVyKQo+PiDCoCsgcmV0dXJuIC1FTk9N
RU07Cj4+IMKgKyBkZXZfc2V0X2RydmRhdGEoZGV2LCBtaXhlcik7Cj4+IMKgKyBkcnYtPm1peGVy
ID0gbWl4ZXI7Cj4+IMKgKwo+PiDCoCsgbWl4ZXItPmNmZyA9IG9mX2RldmljZV9nZXRfbWF0Y2hf
ZGF0YShkZXYpOwo+PiDCoCsgaWYgKCFtaXhlci0+Y2ZnKQo+PiDCoCsgcmV0dXJuIC1FSU5WQUw7
Cj4+IMKgKwo+PiDCoCsgcmVzID0gcGxhdGZvcm1fZ2V0X3Jlc291cmNlKHBkZXYsIElPUkVTT1VS
Q0VfTUVNLCAwKTsKPj4gwqArIHJlZ3MgPSBkZXZtX2lvcmVtYXBfcmVzb3VyY2UoZGV2LCByZXMp
Owo+PiDCoCsgaWYgKElTX0VSUihyZWdzKSkKPj4gwqArIHJldHVybiBQVFJfRVJSKHJlZ3MpOwo+
PiDCoCsKPj4gwqArIG1peGVyLT5yZWdzID0gZGV2bV9yZWdtYXBfaW5pdF9tbWlvKGRldiwgcmVn
cywKPj4gwqArICZzdW44aV9taXhlcl9yZWdtYXBfY29uZmlnKTsKPj4gwqArIGlmIChJU19FUlIo
bWl4ZXItPnJlZ3MpKSB7Cj4+IMKgKyBkZXZfZXJyKGRldiwgIkNvdWxkbid0IGNyZWF0ZSB0aGUg
bWl4ZXIgcmVnbWFwXG4iKTsKPj4gwqArIHJldHVybiBQVFJfRVJSKG1peGVyLT5yZWdzKTsKPj4g
wqArIH0KPj4gwqArCj4+IMKgKyBtaXhlci0+cmVzZXQgPSBkZXZtX3Jlc2V0X2NvbnRyb2xfZ2V0
KGRldiwgTlVMTCk7Cj4+IMKgKyBpZiAoSVNfRVJSKG1peGVyLT5yZXNldCkpIHsKPj4gwqArIGRl
dl9lcnIoZGV2LCAiQ291bGRuJ3QgZ2V0IG91ciByZXNldCBsaW5lXG4iKTsKPj4gwqArIHJldHVy
biBQVFJfRVJSKG1peGVyLT5yZXNldCk7Cj4+IMKgKyB9Cj4+IMKgKwo+PiDCoCsgcmV0ID0gcmVz
ZXRfY29udHJvbF9kZWFzc2VydChtaXhlci0+cmVzZXQpOwo+PiDCoCsgaWYgKHJldCkgewo+PiDC
oCsgZGV2X2VycihkZXYsICJDb3VsZG4ndCBkZWFzc2VydCBvdXIgcmVzZXQgbGluZVxuIik7Cj4+
IMKgKyByZXR1cm4gcmV0Owo+PiDCoCsgfQo+PiDCoCsKPj4gwqArIG1peGVyLT5idXNfY2xrID0g
ZGV2bV9jbGtfZ2V0KGRldiwgImJ1cyIpOwo+PiDCoCsgaWYgKElTX0VSUihtaXhlci0+YnVzX2Ns
aykpIHsKPj4gwqArIGRldl9lcnIoZGV2LCAiQ291bGRuJ3QgZ2V0IHRoZSBtaXhlciBidXMgY2xv
Y2tcbiIpOwo+PiDCoCsgcmV0ID0gUFRSX0VSUihtaXhlci0+YnVzX2Nsayk7Cj4+IMKgKyBnb3Rv
IGVycl9hc3NlcnRfcmVzZXQ7Cj4+IMKgKyB9Cj4+IMKgKyBjbGtfcHJlcGFyZV9lbmFibGUobWl4
ZXItPmJ1c19jbGspOwo+PiDCoCsKPj4gwqArIG1peGVyLT5tb2RfY2xrID0gZGV2bV9jbGtfZ2V0
KGRldiwgIm1vZCIpOwo+PiDCoCsgaWYgKElTX0VSUihtaXhlci0+bW9kX2NsaykpIHsKPj4gwqAr
IGRldl9lcnIoZGV2LCAiQ291bGRuJ3QgZ2V0IHRoZSBtaXhlciBtb2R1bGUgY2xvY2tcbiIpOwo+
PiDCoCsgcmV0ID0gUFRSX0VSUihtaXhlci0+bW9kX2Nsayk7Cj4+IMKgKyBnb3RvIGVycl9kaXNh
YmxlX2J1c19jbGs7Cj4+IMKgKyB9Cj4+IMKgKyBjbGtfcHJlcGFyZV9lbmFibGUobWl4ZXItPm1v
ZF9jbGspOwo+PiDCoCsKPj4gwqArIC8qIFJlc2V0IHRoZSByZWdpc3RlcnMgKi8KPj4gwqArIGZv
ciAoaSA9IDB4MDsgaSA8IDB4MjAwMDA7IGkgKz0gNCkKPj4gwqArIHJlZ21hcF93cml0ZShtaXhl
ci0+cmVncywgaSwgMCk7Cj4+IMKgKwo+PiDCoCsgLyogRW5hYmxlIHRoZSBtaXhlciAqLwo+PiDC
oCsgcmVnbWFwX3dyaXRlKG1peGVyLT5yZWdzLCBTVU44SV9NSVhFUl9HTE9CQUxfQ1RMLAo+PiDC
oCsgU1VOOElfTUlYRVJfR0xPQkFMX0NUTF9SVF9FTik7Cj4+IMKgKwo+PiDCoCsgLyogSW5pdGlh
bGl6ZSBibGVuZGVyICovCj4+IMKgKyByZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsIFNVTjhJX01J
WEVSX0JMRU5EX0ZDT0xPUl9DVEwsCj4+IMKgKyBTVU44SV9NSVhFUl9CTEVORF9GQ09MT1JfQ1RM
X0RFRik7Cj4+IMKgKyByZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0JMRU5E
X1BSRU1VTFRJUExZLAo+PiDCoCsgU1VOOElfTUlYRVJfQkxFTkRfUFJFTVVMVElQTFlfREVGKTsK
Pj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQkxFTkRfQktDT0xP
UiwKPj4gwqArIFNVTjhJX01JWEVSX0JMRU5EX0JLQ09MT1JfREVGKTsKPj4gwqArIHJlZ21hcF93
cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQkxFTkRfTU9ERSgwKSwKPj4gwqArIFNVTjhJ
X01JWEVSX0JMRU5EX01PREVfREVGKTsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywg
U1VOOElfTUlYRVJfQkxFTkRfTU9ERSgxKSwKPj4gwqArIFNVTjhJX01JWEVSX0JMRU5EX01PREVf
REVGKTsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQkxFTkRf
Q0tfQ1RMLAo+PiDCoCsgU1VOOElfTUlYRVJfQkxFTkRfQ0tfQ1RMX0RFRik7Cj4+IMKgKwo+PiDC
oCsgZm9yIChpID0gMDsgaSA8IFNVTjhJX01JWEVSX01BWF9DSEFOX0NPVU5UOyBpKyspCj4+IMKg
KyByZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9NSVhFUl9CTEVORF9BVFRS
X0ZDT0xPUihpKSwKPj4gwqArIFNVTjhJX01JWEVSX0JMRU5EX0FUVFJfRkNPTE9SX0RFRik7Cj4+
IMKgKwo+PiDCoCsgLyogU2VsZWN0IHRoZSBmaXJzdCBVSSBjaGFubmVsICovCj4+IMKgKyBEUk1f
REVCVUdfRFJJVkVSKCJTZWxlY3RpbmcgY2hhbm5lbCAlZCAoZmlyc3QgVUkgY2hhbm5lbClcbiIs
Cj4+IMKgKyBtaXhlci0+Y2ZnLT52aV9udW0pOwo+PiDCoCsgcmVnbWFwX3dyaXRlKG1peGVyLT5y
ZWdzLCBTVU44SV9NSVhFUl9CTEVORF9ST1VURSwKPj4gwqArIG1peGVyLT5jZmctPnZpX251bSk7
Cj4+IMKgKwo+PiDCoCsgcmV0dXJuIDA7Cj4+IMKgKwo+PiDCoCsgY2xrX2Rpc2FibGVfdW5wcmVw
YXJlKG1peGVyLT5tb2RfY2xrKTsKPj4gwqArZXJyX2Rpc2FibGVfYnVzX2NsazoKPj4gwqArIGNs
a19kaXNhYmxlX3VucHJlcGFyZShtaXhlci0+YnVzX2Nsayk7Cj4+IMKgK2Vycl9hc3NlcnRfcmVz
ZXQ6Cj4+IMKgKyByZXNldF9jb250cm9sX2Fzc2VydChtaXhlci0+cmVzZXQpOwo+PiDCoCsgcmV0
dXJuIHJldDsKPj4gwqArfQo+PiDCoCsKPj4gwqArc3RhdGljIHZvaWQgc3VuOGlfbWl4ZXJfdW5i
aW5kKHN0cnVjdCBkZXZpY2UgKmRldiwgc3RydWN0IGRldmljZSAqbWFzdGVyLAo+PiDCoCsgdm9p
ZCAqZGF0YSkKPj4gwqArewo+PiDCoCsgc3RydWN0IHN1bjhpX21peGVyICptaXhlciA9IGRldl9n
ZXRfZHJ2ZGF0YShkZXYpOwo+PiDCoCsKPj4gwqArIGNsa19kaXNhYmxlX3VucHJlcGFyZShtaXhl
ci0+bW9kX2Nsayk7Cj4+IMKgKyBjbGtfZGlzYWJsZV91bnByZXBhcmUobWl4ZXItPmJ1c19jbGsp
Owo+PiDCoCsgcmVzZXRfY29udHJvbF9hc3NlcnQobWl4ZXItPnJlc2V0KTsKPj4gwqArfQo+PiDC
oCsKPj4gwqArc3RhdGljIGNvbnN0IHN0cnVjdCBjb21wb25lbnRfb3BzIHN1bjhpX21peGVyX29w
cyA9IHsKPj4gwqArIC5iaW5kID0gc3VuOGlfbWl4ZXJfYmluZCwKPj4gwqArIC51bmJpbmQgPSBz
dW44aV9taXhlcl91bmJpbmQsCj4+IMKgK307Cj4+IMKgKwo+PiDCoCtzdGF0aWMgaW50IHN1bjhp
X21peGVyX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpCj4+IMKgK3sKPj4gwqAr
IHJldHVybiBjb21wb25lbnRfYWRkKCZwZGV2LT5kZXYsICZzdW44aV9taXhlcl9vcHMpOwo+PiDC
oCt9Cj4+IMKgKwo+PiDCoCtzdGF0aWMgaW50IHN1bjhpX21peGVyX3JlbW92ZShzdHJ1Y3QgcGxh
dGZvcm1fZGV2aWNlICpwZGV2KQo+PiDCoCt7Cj4+IMKgKyBjb21wb25lbnRfZGVsKCZwZGV2LT5k
ZXYsICZzdW44aV9taXhlcl9vcHMpOwo+PiDCoCsKPj4gwqArIHJldHVybiAwOwo+PiDCoCt9Cj4+
IMKgKwo+PiDCoCtzdGF0aWMgY29uc3Qgc3RydWN0IHN1bjhpX21peGVyX2NmZyBzdW44aV92M3Nf
bWl4ZXJfY2ZnID0gewo+PiDCoCsgLnZpX251bSA9IDIsCj4+IMKgKyAudWlfbnVtID0gMSwKPj4g
wqArfTsKPj4gwqArCj4+IMKgK3N0YXRpYyBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkIHN1bjhp
X21peGVyX29mX3RhYmxlW10gPSB7Cj4+IMKgKyB7Cj4+IMKgKyAuY29tcGF0aWJsZSA9ICJhbGx3
aW5uZXIsc3VuOGktdjNzLWRlMi1taXhlciIsCj4+IMKgKyAuZGF0YSA9ICZzdW44aV92M3NfbWl4
ZXJfY2ZnCj4+IMKgKyB9LAo+PiDCoCsgeyB9Cj4+IMKgK307Cj4+IMKgK01PRFVMRV9ERVZJQ0Vf
VEFCTEUob2YsIHN1bjhpX21peGVyX29mX3RhYmxlKTsKPj4gwqArCj4+IMKgK3N0YXRpYyBzdHJ1
Y3QgcGxhdGZvcm1fZHJpdmVyIHN1bjhpX21peGVyX3BsYXRmb3JtX2RyaXZlciA9IHsKPj4gwqAr
IC5wcm9iZSA9IHN1bjhpX21peGVyX3Byb2JlLAo+PiDCoCsgLnJlbW92ZSA9IHN1bjhpX21peGVy
X3JlbW92ZSwKPj4gwqArIC5kcml2ZXIgPSB7Cj4+IMKgKyAubmFtZSA9ICJzdW44aS1taXhlciIs
Cj4+IMKgKyAub2ZfbWF0Y2hfdGFibGUgPSBzdW44aV9taXhlcl9vZl90YWJsZSwKPj4gwqArIH0s
Cj4+IMKgK307Cj4+IMKgK21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIoc3VuOGlfbWl4ZXJfcGxhdGZv
cm1fZHJpdmVyKTsKPj4gwqArCj4+IMKgK01PRFVMRV9BVVRIT1IoIkljZW5vd3kgWmhlbmcgPGlj
ZW5vd3lAYW9zYy54eXo+Iik7Cj4+IMKgK01PRFVMRV9ERVNDUklQVElPTigiQWxsd2lubmVyIERF
MiBNaXhlciBkcml2ZXIiKTsKPj4gwqArTU9EVUxFX0xJQ0VOU0UoIkdQTCIpOwo+PiDCoCsjZWxz
ZSAvKiBEUk1fU1VONElfREUyICovCj4+IMKgK3ZvaWQgc3VuOGlfbWl4ZXJfY29tbWl0KHN0cnVj
dCBzdW44aV9taXhlciAqbWl4ZXIpCj4+IMKgK3sKPj4gwqArfQo+PiDCoCsKPj4gwqArdm9pZCBz
dW44aV9taXhlcl9sYXllcl9lbmFibGUoc3RydWN0IHN1bjhpX21peGVyICptaXhlciwKPj4gwqAr
IGludCBsYXllciwgYm9vbCBlbmFibGUpCj4+IMKgK3sKPj4gwqArfQo+PiDCoCsKPj4gwqAraW50
IHN1bjhpX21peGVyX3VwZGF0ZV9sYXllcl9jb29yZChzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVy
LAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDC
oCsgcmV0dXJuIC1FTk9FTlQ7Cj4+IMKgK30KPj4gwqArCj4+IMKgK2ludCBzdW44aV9taXhlcl91
cGRhdGVfbGF5ZXJfZm9ybWF0cyhzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50
IGxheWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDCoCsgcmV0dXJuIC1F
Tk9FTlQ7Cj4+IMKgK30KPj4gwqArCj4+IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJf
YnVmZmVyKHN0cnVjdCBzdW44aV9taXhlciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIsIHN0cnVj
dCBkcm1fcGxhbmUgKnBsYW5lKQo+PiDCoCt7Cj4+IMKgKyByZXR1cm4gLUVOT0VOVDsKPj4gwqAr
fQo+PiDCoCsjZW5kaWYgLyogQ09ORklHX0RSTV9TVU40SV9ERTIgKi8KPj4gwqBkaWZmIC0tZ2l0
IGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjhpX21peGVyLmgKPj4gwqBiL2RyaXZlcnMvZ3B1
L2RybS9zdW40aS9zdW44aV9taXhlci5oIG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4+IMKgaW5kZXgg
MDAwMDAwMDAwMDAwLi4wYmMxMzRiM2JjOTgKPj4gwqAtLS0gL2Rldi9udWxsCj4+IMKgKysrIGIv
ZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjhpX21peGVyLmgKPj4gwqBAQCAtMCwwICsxLDEzMyBA
QAo+PiDCoCsvKgo+PiDCoCsgKiBDb3B5cmlnaHQgKEMpIDIwMTcgSWNlbm93eSBaaGVuZyA8aWNl
bm93eUBhb3NjLnh5ej4KPj4gwqArICoKPj4gwqArICogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29m
dHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgo+PiDCoCsgKiBtb2RpZnkgaXQg
dW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcwo+PiDC
oCsgKiBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgZWl0aGVyIHZl
cnNpb24gMiBvZgo+PiDCoCsgKiB0aGUgTGljZW5zZSwgb3IgKGF0IHlvdXIgb3B0aW9uKSBhbnkg
bGF0ZXIgdmVyc2lvbi4KPj4gwqArICovCj4+IMKgKwo+PiDCoCsjaWZuZGVmIF9TVU44SV9NSVhF
Ul9IXwo+PiDCoCsjZGVmaW5lIF9TVU44SV9NSVhFUl9IXwo+PiDCoCsKPj4gwqArI2luY2x1ZGUg
PGxpbnV4L2Nsay5oPgo+PiDCoCsjaW5jbHVkZSA8bGludXgvcmVnbWFwLmg+Cj4+IMKgKyNpbmNs
dWRlIDxsaW51eC9yZXNldC5oPgo+PiDCoCsKPj4gwqArI2luY2x1ZGUgInN1bjRpX2xheWVyLmgi
Cj4+IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX01BWF9DSEFOX0NPVU5UIDQKPj4gwqAr
Cj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfU0laRSh3LCBoKSAoKChoKSAtIDEpIDw8IDE2IHwg
KCh3KSAtIDEpKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0NPT1JEKHgsIHkpICgoeSkgPDwg
MTYgfCAoeCkpCj4+IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0dMT0JBTF9DVEwgMHgw
Cj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfR0xPQkFMX1NUQVRVUyAweDQKPj4gwqArI2RlZmlu
ZSBTVU44SV9NSVhFUl9HTE9CQUxfREJVRkYgMHg4Cj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
R0xPQkFMX1NJWkUgMHhjCj4+IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0dMT0JBTF9D
VExfUlRfRU4gMHgxCj4+IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0dMT0JBTF9EQlVG
Rl9FTkFCTEUgMHgxCj4+IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX0ZDT0xP
Ul9DVEwgMHgxMDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQVRUUl9GQ09MT1Io
eCkgKDB4MTAwNCArIDB4MTAgKiAoeCkgKyAweDApCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
QkxFTkRfQVRUUl9JTlNJWkUoeCkgKDB4MTAwNCArIDB4MTAgKiAoeCkgKyAweDQpCj4+IMKgKyNk
ZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQVRUUl9PRkZTRVQoeCkgKDB4MTAwNCArIDB4MTAgKiAo
eCkgKyAweDgpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfUk9VVEUgMHgxMDgwCj4+
IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfUFJFTVVMVElQTFkgMHgxMDg0Cj4+IMKgKyNk
ZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQktDT0xPUiAweDEwODgKPj4gwqArI2RlZmluZSBTVU44
SV9NSVhFUl9CTEVORF9PVVRTSVpFIDB4MTA4Ywo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JM
RU5EX01PREUoeCkgKDB4MTA5MCArIDB4MDQgKiAoeCkpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlY
RVJfQkxFTkRfQ0tfQ1RMIDB4MTBiMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX0NL
X0NGRyAweDEwYjQKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9DS19NQVgoeCkgKDB4
MTBjMCArIDB4MDQgKiAoeCkpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQ0tfTUlO
KHgpICgweDEwZTAgKyAweDA0ICogKHgpKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5E
X09VVENUTCAweDEwZmMKPj4gwqArCj4+IMKgKy8qIFRoZSBmb2xsb3dpbmcgbnVtYmVycyBhcmUg
c29tZSBzdGlsbCB1bmtub3duIG1hZ2ljIG51bWJlcnMgKi8KPj4gwqArI2RlZmluZSBTVU44SV9N
SVhFUl9CTEVORF9BVFRSX0ZDT0xPUl9ERUYgMHhmZjAwMDAwMAo+PiDCoCsjZGVmaW5lIFNVTjhJ
X01JWEVSX0JMRU5EX0ZDT0xPUl9DVExfREVGIDB4MDAwMDAxMDEKPj4gwqArI2RlZmluZSBTVU44
SV9NSVhFUl9CTEVORF9QUkVNVUxUSVBMWV9ERUYgMHgwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlY
RVJfQkxFTkRfQktDT0xPUl9ERUYgMHhmZjAwMDAwMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVS
X0JMRU5EX01PREVfREVGIDB4MDMwMTAzMDEKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVO
RF9DS19DVExfREVGIDB4MAo+PiDCoCsKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9P
VVRDVExfSU5URVJMQUNFRCBCSVQoMSkKPj4gwqArCj4+IMKgKy8qCj4+IMKgKyAqIFZJIGNoYW5u
ZWxzIGFyZSBub3QgdXNlZCBub3csIGJ1dCB0aGUgc3VwcG9ydCBvZiB0aGVtIG1heSBiZSBpbnRy
b2R1Y2VkCj4+IMKgaW4gKyAqIHRoZSBmdXR1cmUuCj4+IMKgKyAqLwo+PiDCoCsKPj4gwqArI2Rl
ZmluZSBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFIoY2gsIGxheWVyKSBcCj4+IMKgKyAo
MHgyMDAwICsgMHgxMDAwICogKGNoKSArIDB4MjAgKiAobGF5ZXIpICsgMHgwKQo+PiDCoCsjZGVm
aW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfU0laRShjaCwgbGF5ZXIpIFwKPj4gwqArICgw
eDIwMDAgKyAweDEwMDAgKiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweDQpCj4+IMKgKyNkZWZp
bmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9DT09SRChjaCwgbGF5ZXIpIFwKPj4gwqArICgw
eDIwMDAgKyAweDEwMDAgKiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweDgpCj4+IMKgKyNkZWZp
bmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9QSVRDSChjaCwgbGF5ZXIpIFwKPj4gwqArICgw
eDIwMDAgKyAweDEwMDAgKiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweGMpCj4+IMKgKyNkZWZp
bmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9UT1BfTEFERFIoY2gsIGxheWVyKSBcCj4+IMKg
KyAoMHgyMDAwICsgMHgxMDAwICogKGNoKSArIDB4MjAgKiAobGF5ZXIpICsgMHgxMCkKPj4gwqAr
I2RlZmluZSBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0JPVF9MQUREUihjaCwgbGF5ZXIpIFwK
Pj4gwqArICgweDIwMDAgKyAweDEwMDAgKiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweDE0KQo+
PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfRkNPTE9SKGNoLCBsYXllcikg
XAo+PiDCoCsgKDB4MjAwMCArIDB4MTAwMCAqIChjaCkgKyAweDIwICogKGxheWVyKSArIDB4MTgp
Cj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9UT1BfSEFERFIoY2gpICgweDIwMDAg
KyAweDEwMDAgKiAoY2gpICsgMHg4MCkKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9DSEFOX1VJ
X0JPVF9IQUREUihjaCkgKDB4MjAwMCArIDB4MTAwMCAqIChjaCkgKyAweDg0KQo+PiDCoCsjZGVm
aW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfT1ZMX1NJWkUoY2gpICgweDIwMDAgKyAweDEwMDAgKiAo
Y2gpICsgMHg4OCkKPj4gwqArCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlF
Ul9BVFRSX0VOIEJJVCgwKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJf
QVRUUl9BTFBIQV9NT0RFX01BU0sgR0VOTUFTSygyLCAxKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01J
WEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9GQkZNVF9NQVNLIEdFTk1BU0soMTEsIDgpCj4+IMKgKyNk
ZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0FMUEhBX01BU0sgR0VOTUFTSygz
MSwgMjQpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0FMUEhB
X01PREVfREVGICgxIDw8IDEpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlF
Ul9BVFRSX0ZCRk1UX0FSR0I4ODg4ICgwIDw8IDgpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
Q0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX1hSR0I4ODg4ICg0IDw8IDgpCj4+IMKgKyNkZWZpbmUg
U1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX1JHQjg4OCAoOCA8PCA4KQo+PiDC
oCsjZGVmaW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9BTFBIQV9ERUYgKDB4ZmYg
PDwgMjQpCj4+IMKgKwo+PiDCoCsvKgo+PiDCoCsgKiBUaGVzZSBzdW4tZW5naW5lcyBhcmUgc3Rp
bGwgdW5rbm93biBub3csIHRoZSBFTiByZWdpc3RlcnMgYXJlIGhlcmUgb25seQo+PiDCoHRvICsg
KiBiZSB1c2VkIHRvIGRpc2FibGUgdGhlc2Ugc3ViLWVuZ2luZXMuCj4+IMKgKyAqLwo+PiDCoCsj
ZGVmaW5lIFNVTjhJX01JWEVSX1ZTVV9FTiAweDIwMDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlY
RVJfR1NVMV9FTiAweDMwMDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfR1NVMl9FTiAweDQw
MDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfR1NVM19FTiAweDUwMDAwCj4+IMKgKyNkZWZp
bmUgU1VOOElfTUlYRVJfRkNFX0VOIDB4YTAwMDAKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9C
V1NfRU4gMHhhMjAwMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0xUSV9FTiAweGE0MDAwCj4+
IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfUEVBS19FTiAweGE2MDAwCj4+IMKgKyNkZWZpbmUgU1VO
OElfTUlYRVJfQVNFX0VOIDB4YTgwMDAKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9GQ0NfRU4g
MHhhYTAwMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0RDU0NfRU4gMHhiMDAwMAo+PiDCoCsK
Pj4gwqArc3RydWN0IHN1bjhpX21peGVyX2NmZyB7Cj4+IMKgKyBpbnQgdmlfbnVtOwo+PiDCoCsg
aW50IHVpX251bTsKPj4gwqArfTsKPj4gwqArCj4+IMKgK3N0cnVjdCBzdW44aV9taXhlciB7Cj4+
IMKgKyBzdHJ1Y3QgcmVnbWFwICpyZWdzOwo+PiDCoCsKPj4gwqArIGNvbnN0IHN0cnVjdCBzdW44
aV9taXhlcl9jZmcgKmNmZzsKPj4gwqArCj4+IMKgKyBzdHJ1Y3QgcmVzZXRfY29udHJvbCAqcmVz
ZXQ7Cj4+IMKgKwo+PiDCoCsgc3RydWN0IGNsayAqYnVzX2NsazsKPj4gwqArIHN0cnVjdCBjbGsg
Km1vZF9jbGs7Cj4+IMKgK307Cj4+IMKgKwo+PiDCoCt2b2lkIHN1bjhpX21peGVyX2NvbW1pdChz
dHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyKTsKPj4gwqArCj4+IMKgK3ZvaWQgc3VuOGlfbWl4ZXJf
bGF5ZXJfZW5hYmxlKHN0cnVjdCBzdW44aV9taXhlciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIs
IGJvb2wgZW5hYmxlKTsKPj4gwqAraW50IHN1bjhpX21peGVyX3VwZGF0ZV9sYXllcl9jb29yZChz
dHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3Bs
YW5lICpwbGFuZSk7Cj4+IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfZm9ybWF0cyhz
dHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3Bs
YW5lICpwbGFuZSk7Cj4+IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfYnVmZmVyKHN0
cnVjdCBzdW44aV9taXhlciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIsIHN0cnVjdCBkcm1fcGxh
bmUgKnBsYW5lKTsKPj4gwqArI2VuZGlmIC8qIF9TVU44SV9NSVhFUl9IXyAqLwo+PiDCoC0tCj4+
IMKgMi4xMS4xCgpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
XwpsaW51eC1hcm0ta2VybmVsIG1haWxpbmcgbGlzdApsaW51eC1hcm0ta2VybmVsQGxpc3RzLmlu
ZnJhZGVhZC5vcmcKaHR0cDovL2xpc3RzLmluZnJhZGVhZC5vcmcvbWFpbG1hbi9saXN0aW5mby9s
aW51eC1hcm0ta2VybmVsCg==

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:04         ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 22:04 UTC (permalink / raw)
  To: linux-arm-kernel



23.02.2017, 06:01, "Jernej ?krabec" <jernej.skrabec@siol.net>:
> Hi,
>
> Dne sreda, 22. februar 2017 ob 16:18:50 CET je Icenowy Zheng napisal(a):
>> ?Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>> ?in a new "Display Engine" (mixers instead of old backends and
>> ?frontends).
>>
>> ?Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> ?Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ?---
>> ??drivers/gpu/drm/sun4i/Kconfig | 8 +
>> ??drivers/gpu/drm/sun4i/Makefile | 1 +
>> ??drivers/gpu/drm/sun4i/sun4i_crtc.c | 6 +-
>> ??drivers/gpu/drm/sun4i/sun4i_drv.c | 38 +++-
>> ??drivers/gpu/drm/sun4i/sun4i_drv.h | 1 +
>> ??drivers/gpu/drm/sun4i/sun4i_layer.c | 92 ++++++--
>> ??drivers/gpu/drm/sun4i/sun4i_layer.h | 1 +
>> ??drivers/gpu/drm/sun4i/sun8i_mixer.c | 417
>> ?++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h |
>> ?133 ++++++++++++
>> ??9 files changed, 674 insertions(+), 23 deletions(-)
>> ??create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>> ??create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
>>
>> ?diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>> ?index a4b357db8856..8df401fff145 100644
>> ?--- a/drivers/gpu/drm/sun4i/Kconfig
>> ?+++ b/drivers/gpu/drm/sun4i/Kconfig
>> ?@@ -12,3 +12,11 @@ config DRM_SUN4I
>> ????????????Choose this option if you have an Allwinner SoC with a
>> ????????????Display Engine. If M is selected the module will be called
>> ????????????sun4i-drm.
>> ?+
>> ?+config DRM_SUN4I_DE2
>> ?+ bool "Support Display Engine 2.0"
>> ?+ depends on DRM_SUN4I
>> ?+ default MACH_SUN8I
>> ?+ help
>> ?+ Choose this option if you have an Allwinner SoC with a
>> ?+ "Display Engine 2.0".
>> ?diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>> ?index d625a82a6e5f..890e6e50dfee 100644
>> ?--- a/drivers/gpu/drm/sun4i/Makefile
>> ?+++ b/drivers/gpu/drm/sun4i/Makefile
>> ?@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>>
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
>> ?+obj-$(CONFIG_DRM_SUN4I) += sun8i_mixer.o
>> ??obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>> ?b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4a192210574f..4d2228454726
>> ?100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>> ?@@ -25,6 +25,7 @@
>> ??#include <video/videomode.h>
>>
>> ??#include "sun4i_backend.h"
>> ?+#include "sun8i_mixer.h"
>> ??#include "sun4i_crtc.h"
>> ??#include "sun4i_drv.h"
>> ??#include "sun4i_tcon.h"
>> ?@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc
>> ?*crtc,
>>
>> ??????????DRM_DEBUG_DRIVER("Committing plane changes\n");
>>
>> ?- sun4i_backend_commit(drv->backend);
>> ?+ if (drv->backend)
>> ?+ sun4i_backend_commit(drv->backend);
>> ?+ else if (drv->mixer)
>> ?+ sun8i_mixer_commit(drv->mixer);
>
> Does it make sense to have everything in sun4i_{crtc,drv} files? Looking
> further it seems that there is a lot of differentiation based on DE1 vs DE2. I
> guess having separate sun8i_{crtc,drv} files would be more clean approach. It
> would enable easier extensions later as well.

I think they have few changes here -- sharing code path is very valuable in these
two files.

>
> Regards,
> Jernej Skrabec
>
>> ??????????if (event) {
>> ??????????????????crtc->state->event = NULL;
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c
>> ?b/drivers/gpu/drm/sun4i/sun4i_drv.c index 4ce665349f6b..58af38f5e833 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
>> ?@@ -20,12 +20,17 @@
>> ??#include <drm/drm_fb_helper.h>
>> ??#include <drm/drm_of.h>
>>
>> ?+#include <linux/of_device.h>
>> ?+
>> ??#include "sun4i_crtc.h"
>> ??#include "sun4i_drv.h"
>> ??#include "sun4i_framebuffer.h"
>> ??#include "sun4i_layer.h"
>> ??#include "sun4i_tcon.h"
>>
>> ?+#define DE_VER_DE 0
>> ?+#define DE_VER_DE2 1
>> ?+
>> ??static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int
>> ?pipe) {
>> ??????????struct sun4i_drv *drv = drm->dev_private;
>> ?@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>> ??{
>> ??????????struct drm_device *drm;
>> ??????????struct sun4i_drv *drv;
>> ?- int ret;
>> ?+ int ret, de_ver;
>> ?+
>> ?+ de_ver = (int)of_device_get_match_data(dev);
>>
>> ??????????drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>> ??????????if (IS_ERR(drm))
>> ?@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>> ??????????}
>>
>> ??????????/* Create our layers */
>> ?- drv->layers = sun4i_layers_init(drm);
>> ?+ if (de_ver == DE_VER_DE2)
>> ?+ drv->layers = sun8i_layers_init(drm);
>> ?+ else
>> ?+ drv->layers = sun4i_layers_init(drm);
>> ??????????if (IS_ERR(drv->layers)) {
>> ??????????????????dev_err(drm->dev, "Couldn't create the planes\n");
>> ??????????????????ret = PTR_ERR(drv->layers);
>> ?@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device
>> ?*pdev) }
>>
>> ??static const struct of_device_id sun4i_drv_of_table[] = {
>> ?- { .compatible = "allwinner,sun5i-a13-display-engine" },
>> ?- { .compatible = "allwinner,sun6i-a31-display-engine" },
>> ?- { .compatible = "allwinner,sun6i-a31s-display-engine" },
>> ?- { .compatible = "allwinner,sun8i-a33-display-engine" },
>> ?+ {
>> ?+ .compatible = "allwinner,sun5i-a13-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun6i-a31-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun6i-a31s-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun8i-a33-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun8i-v3s-display-engine",
>> ?+ .data = (void *)DE_VER_DE2,
>> ?+ },
>> ??????????{ }
>> ??};
>> ??MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h
>> ?b/drivers/gpu/drm/sun4i/sun4i_drv.h index 597353eab728..cf1da95b85bd 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
>> ?@@ -18,6 +18,7 @@
>>
>> ??struct sun4i_drv {
>> ??????????struct sun4i_backend *backend;
>> ?+ struct sun8i_mixer *mixer;
>> ??????????struct sun4i_crtc *crtc;
>> ??????????struct sun4i_tcon *tcon;
>>
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
>> ?b/drivers/gpu/drm/sun4i/sun4i_layer.c index 5d53c977bca5..6dcdac38ab69
>> ?100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
>> ?@@ -16,52 +16,66 @@
>> ??#include <drm/drmP.h>
>>
>> ??#include "sun4i_backend.h"
>> ?+#include "sun8i_mixer.h"
>> ??#include "sun4i_drv.h"
>> ??#include "sun4i_layer.h"
>>
>> ??struct sun4i_plane_desc {
>> ?????????????????enum drm_plane_type type;
>> ?+ /* Pipe is not used in sun8i-mixer */
>> ?????????????????u8 pipe;
>> ?????????????????const uint32_t *formats;
>> ?????????????????uint32_t nformats;
>> ??};
>>
>> ?-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
>> ?+static int sun4i_layer_atomic_check(struct drm_plane *plane,
>> ??????????????????????????????????????????????struct drm_plane_state *state)
>> ??{
>> ??????????return 0;
>> ??}
>>
>> ?-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
>> ?+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>> ?????????????????????????????????????????????????struct drm_plane_state *old_state)
>> ??{
>> ??????????struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>> ??????????struct sun4i_drv *drv = layer->drv;
>> ??????????struct sun4i_backend *backend = drv->backend;
>> ?+ struct sun8i_mixer *mixer = drv->mixer;
>>
>> ?- sun4i_backend_layer_enable(backend, layer->id, false);
>> ?+ if (backend)
>> ?+ sun4i_backend_layer_enable(backend, layer->id, false);
>> ?+ else if (mixer)
>> ?+ sun8i_mixer_layer_enable(mixer, layer->id, false);
>> ??}
>>
>> ?-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
>> ?+static void sun4i_layer_atomic_update(struct drm_plane *plane,
>> ????????????????????????????????????????????????struct drm_plane_state *old_state)
>> ??{
>> ??????????struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>> ??????????struct sun4i_drv *drv = layer->drv;
>> ??????????struct sun4i_backend *backend = drv->backend;
>> ?-
>> ?- sun4i_backend_update_layer_coord(backend, layer->id, plane);
>> ?- sun4i_backend_update_layer_formats(backend, layer->id, plane);
>> ?- sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>> ?- sun4i_backend_layer_enable(backend, layer->id, true);
>> ?+ struct sun8i_mixer *mixer = drv->mixer;
>> ?+
>> ?+ if (backend) {
>> ?+ sun4i_backend_update_layer_coord(backend, layer->id, plane);
>> ?+ sun4i_backend_update_layer_formats(backend, layer->id, plane);
>> ?+ sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>> ?+ sun4i_backend_layer_enable(backend, layer->id, true);
>> ?+ } else if (mixer) {
>> ?+ sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
>> ?+ sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
>> ?+ sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
>> ?+ sun8i_mixer_layer_enable(mixer, layer->id, true);
>> ?+ }
>> ??}
>>
>> ?-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
>> ?- .atomic_check = sun4i_backend_layer_atomic_check,
>> ?- .atomic_disable = sun4i_backend_layer_atomic_disable,
>> ?- .atomic_update = sun4i_backend_layer_atomic_update,
>> ?+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
>> ?+ .atomic_check = sun4i_layer_atomic_check,
>> ?+ .atomic_disable = sun4i_layer_atomic_disable,
>> ?+ .atomic_update = sun4i_layer_atomic_update,
>> ??};
>>
>> ?-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
>> ?+static const struct drm_plane_funcs sun4i_layer_funcs = {
>> ??????????.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>> ??????????.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>> ??????????.destroy = drm_plane_cleanup,
>> ?@@ -88,6 +102,12 @@ static const uint32_t
>> ?sun4i_backend_layer_formats_overlay[] = { DRM_FORMAT_XRGB8888,
>> ??};
>>
>> ?+static const uint32_t sun8i_mixer_layer_formats[] = {
>> ?+ DRM_FORMAT_ARGB8888,
>> ?+ DRM_FORMAT_RGB888,
>> ?+ DRM_FORMAT_XRGB8888,
>> ?+};
>> ?+
>> ??static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>> ??????????{
>> ??????????????????.type = DRM_PLANE_TYPE_PRIMARY,
>> ?@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc
>> ?sun4i_backend_planes[] = { },
>> ??};
>>
>> ?+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
>> ?+ {
>> ?+ .type = DRM_PLANE_TYPE_PRIMARY,
>> ?+ .formats = sun8i_mixer_layer_formats,
>> ?+ .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>> ?+ },
>> ?+ {
>> ?+ .type = DRM_PLANE_TYPE_OVERLAY,
>> ?+ .formats = sun8i_mixer_layer_formats,
>> ?+ .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>> ?+ },
>> ?+};
>> ?+
>> ??static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>> ??????????????????????????????????????????????????const struct sun4i_plane_desc *plane)
>> ??{
>> ?@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
>> ?drm_device *drm, return ERR_PTR(-ENOMEM);
>>
>> ??????????ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
>> ?- &sun4i_backend_layer_funcs,
>> ?+ &sun4i_layer_funcs,
>> ?????????????????????????????????????????plane->formats, plane->nformats,
>> ?????????????????????????????????????????plane->type, NULL);
>> ??????????if (ret) {
>> ?@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct
>> ?drm_device *drm, }
>>
>> ??????????drm_plane_helper_add(&layer->plane,
>> ?- &sun4i_backend_layer_helper_funcs);
>> ?+ &sun4i_layer_helper_funcs);
>> ??????????layer->drv = drv;
>>
>> ??????????if (plane->type == DRM_PLANE_TYPE_PRIMARY)
>> ?@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct
>> ?drm_device *drm)
>>
>> ??????????return layers;
>> ??}
>> ?+
>> ?+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
>> ?+{
>> ?+ struct sun4i_layer **layers;
>> ?+ int i;
>> ?+
>> ?+ layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
>> ?+ sizeof(**layers), GFP_KERNEL);
>> ?+ if (!layers)
>> ?+ return ERR_PTR(-ENOMEM);
>> ?+
>> ?+ for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>> ?+ const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
>> ?+ struct sun4i_layer *layer = layers[i];
>> ?+
>> ?+ layer = sun4i_layer_init_one(drm, plane);
>> ?+ if (IS_ERR(layer)) {
>> ?+ dev_err(drm->dev, "Couldn't initialize %s plane\n",
>> ?+ i ? "overlay" : "primary");
>> ?+ return ERR_CAST(layer);
>> ?+ };
>> ?+
>> ?+ layer->id = i;
>> ?+ };
>> ?+
>> ?+ return layers;
>> ?+}
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
>> ?b/drivers/gpu/drm/sun4i/sun4i_layer.h index a2f65d7a3f4e..f7b9e5daea50
>> ?100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
>> ?@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>> ??}
>>
>> ??struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
>> ?+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>>
>> ??#endif /* _SUN4I_LAYER_H_ */
>> ?diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> ?b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
>> ?index 000000000000..9427b57240d3
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> ?@@ -0,0 +1,417 @@
>> ?+/*
>> ?+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> ?+ *
>> ?+ * Based on sun4i_backend.c, which is:
>> ?+ * Copyright (C) 2015 Free Electrons
>> ?+ * Copyright (C) 2015 NextThing Co
>> ?+ *
>> ?+ * This program is free software; you can redistribute it and/or
>> ?+ * modify it under the terms of the GNU General Public License as
>> ?+ * published by the Free Software Foundation; either version 2 of
>> ?+ * the License, or (at your option) any later version.
>> ?+ */
>> ?+
>> ?+#include <drm/drmP.h>
>> ?+#include <drm/drm_atomic_helper.h>
>> ?+#include <drm/drm_crtc.h>
>> ?+#include <drm/drm_crtc_helper.h>
>> ?+#include <drm/drm_fb_cma_helper.h>
>> ?+#include <drm/drm_gem_cma_helper.h>
>> ?+#include <drm/drm_plane_helper.h>
>> ?+
>> ?+#include <linux/component.h>
>> ?+#include <linux/reset.h>
>> ?+#include <linux/of_device.h>
>> ?+
>> ?+#include "sun8i_mixer.h"
>> ?+#include "sun4i_drv.h"
>> ?+
>> ?+#define SUN8I_DRAM_OFFSET 0x40000000
>> ?+
>> ?+#if defined CONFIG_DRM_SUN4I_DE2
>> ?+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>> ?+{
>> ?+ DRM_DEBUG_DRIVER("Committing changes\n");
>> ?+
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>> ?+ SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_commit);
>> ?+
>> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>> ?+ int layer, bool enable)
>> ?+{
>> ?+ u32 val;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
>> ?+
>> ?+ if (enable)
>> ?+ val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
>> ?+ else
>> ?+ val = 0;
>> ?+
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
>> ?+
>> ?+ /* Set the alpha configuration */
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
>> ?+
>> ?+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>> ?+ u32 format, u32 *mode)
>> ?+{
>> ?+ if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
>> ?+ (format == DRM_FORMAT_ARGB8888))
>> ?+ format = DRM_FORMAT_XRGB8888;
>> ?+
>> ?+ switch (format) {
>> ?+ case DRM_FORMAT_ARGB8888:
>> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
>> ?+ break;
>> ?+
>> ?+ case DRM_FORMAT_XRGB8888:
>> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
>> ?+ break;
>> ?+
>> ?+ case DRM_FORMAT_RGB888:
>> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
>> ?+ break;
>> ?+
>> ?+ default:
>> ?+ return -EINVAL;
>> ?+ }
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ struct drm_plane_state *state = plane->state;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+ int i;
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
>> ?+
>> ?+ if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
>> ?+ DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
>> ?+ state->crtc_w, state->crtc_h);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ DRM_DEBUG_DRIVER("Updating blender size\n");
>> ?+ for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>> ?+ regmap_write(mixer->regs,
>> ?+ SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ DRM_DEBUG_DRIVER("Updating channel size\n");
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ }
>> ?+
>> ?+ /* Set the line width */
>> ?+ DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
>> ?+ fb->pitches[0]);
>> ?+
>> ?+ /* Set height and width */
>> ?+ DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
>> ?+ state->crtc_w, state->crtc_h);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
>> ?+
>> ?+ /* Set base coordinates */
>> ?+ DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
>> ?+ state->crtc_x, state->crtc_y);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>> ?+ SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
>> ?+
>> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ struct drm_plane_state *state = plane->state;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ bool interlaced = false;
>> ?+ u32 val;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+ int ret;
>> ?+
>> ?+ if (plane->state->crtc)
>> ?+ interlaced = plane->state->crtc->state->adjusted_mode.flags
>> ?+ & DRM_MODE_FLAG_INTERLACE;
>> ?+
>> ?+ regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
>> ?+ SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
>> ?+ interlaced ?
>> ?+ SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
>> ?+ interlaced ? "on" : "off");
>> ?+
>> ?+ ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
>> ?+ &val);
>> ?+ if (ret) {
>> ?+ DRM_DEBUG_DRIVER("Invalid format\n");
>> ?+ return ret;
>> ?+ }
>> ?+
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
>> ?+
>> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ struct drm_plane_state *state = plane->state;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ struct drm_gem_cma_object *gem;
>> ?+ dma_addr_t paddr;
>> ?+ uint32_t paddr_u32;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+ int bpp;
>> ?+
>> ?+ /* Get the physical address of the buffer in memory */
>> ?+ gem = drm_fb_cma_get_gem_obj(fb, 0);
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
>> ?+
>> ?+ /* Compute the start of the displayed memory */
>> ?+ bpp = fb->format->cpp[0];
>> ?+ paddr = gem->paddr + fb->offsets[0];
>> ?+ paddr += (state->src_x >> 16) * bpp;
>> ?+ paddr += (state->src_y >> 16) * fb->pitches[0];
>> ?+ paddr -= SUN8I_DRAM_OFFSET;
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>> ?+
>> ?+ paddr_u32 = (uint32_t) paddr;
>> ?+
>> ?+ regmap_write(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>> ?+ paddr_u32);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
>> ?+
>> ?+static struct regmap_config sun8i_mixer_regmap_config = {
>> ?+ .reg_bits = 32,
>> ?+ .val_bits = 32,
>> ?+ .reg_stride = 4,
>> ?+ .max_register = 0xbffc, /* guessed */
>> ?+};
>> ?+
>> ?+static int sun8i_mixer_bind(struct device *dev, struct device *master,
>> ?+ void *data)
>> ?+{
>> ?+ struct platform_device *pdev = to_platform_device(dev);
>> ?+ struct drm_device *drm = data;
>> ?+ struct sun4i_drv *drv = drm->dev_private;
>> ?+ struct sun8i_mixer *mixer;
>> ?+ struct resource *res;
>> ?+ void __iomem *regs;
>> ?+ int i, ret;
>> ?+
>> ?+ mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>> ?+ if (!mixer)
>> ?+ return -ENOMEM;
>> ?+ dev_set_drvdata(dev, mixer);
>> ?+ drv->mixer = mixer;
>> ?+
>> ?+ mixer->cfg = of_device_get_match_data(dev);
>> ?+ if (!mixer->cfg)
>> ?+ return -EINVAL;
>> ?+
>> ?+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> ?+ regs = devm_ioremap_resource(dev, res);
>> ?+ if (IS_ERR(regs))
>> ?+ return PTR_ERR(regs);
>> ?+
>> ?+ mixer->regs = devm_regmap_init_mmio(dev, regs,
>> ?+ &sun8i_mixer_regmap_config);
>> ?+ if (IS_ERR(mixer->regs)) {
>> ?+ dev_err(dev, "Couldn't create the mixer regmap\n");
>> ?+ return PTR_ERR(mixer->regs);
>> ?+ }
>> ?+
>> ?+ mixer->reset = devm_reset_control_get(dev, NULL);
>> ?+ if (IS_ERR(mixer->reset)) {
>> ?+ dev_err(dev, "Couldn't get our reset line\n");
>> ?+ return PTR_ERR(mixer->reset);
>> ?+ }
>> ?+
>> ?+ ret = reset_control_deassert(mixer->reset);
>> ?+ if (ret) {
>> ?+ dev_err(dev, "Couldn't deassert our reset line\n");
>> ?+ return ret;
>> ?+ }
>> ?+
>> ?+ mixer->bus_clk = devm_clk_get(dev, "bus");
>> ?+ if (IS_ERR(mixer->bus_clk)) {
>> ?+ dev_err(dev, "Couldn't get the mixer bus clock\n");
>> ?+ ret = PTR_ERR(mixer->bus_clk);
>> ?+ goto err_assert_reset;
>> ?+ }
>> ?+ clk_prepare_enable(mixer->bus_clk);
>> ?+
>> ?+ mixer->mod_clk = devm_clk_get(dev, "mod");
>> ?+ if (IS_ERR(mixer->mod_clk)) {
>> ?+ dev_err(dev, "Couldn't get the mixer module clock\n");
>> ?+ ret = PTR_ERR(mixer->mod_clk);
>> ?+ goto err_disable_bus_clk;
>> ?+ }
>> ?+ clk_prepare_enable(mixer->mod_clk);
>> ?+
>> ?+ /* Reset the registers */
>> ?+ for (i = 0x0; i < 0x20000; i += 4)
>> ?+ regmap_write(mixer->regs, i, 0);
>> ?+
>> ?+ /* Enable the mixer */
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
>> ?+ SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>> ?+
>> ?+ /* Initialize blender */
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>> ?+ SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>> ?+ SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
>> ?+ SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
>> ?+ SUN8I_MIXER_BLEND_MODE_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
>> ?+ SUN8I_MIXER_BLEND_MODE_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
>> ?+ SUN8I_MIXER_BLEND_CK_CTL_DEF);
>> ?+
>> ?+ for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>> ?+ regmap_write(mixer->regs,
>> ?+ SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
>> ?+ SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
>> ?+
>> ?+ /* Select the first UI channel */
>> ?+ DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
>> ?+ mixer->cfg->vi_num);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
>> ?+ mixer->cfg->vi_num);
>> ?+
>> ?+ return 0;
>> ?+
>> ?+ clk_disable_unprepare(mixer->mod_clk);
>> ?+err_disable_bus_clk:
>> ?+ clk_disable_unprepare(mixer->bus_clk);
>> ?+err_assert_reset:
>> ?+ reset_control_assert(mixer->reset);
>> ?+ return ret;
>> ?+}
>> ?+
>> ?+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
>> ?+ void *data)
>> ?+{
>> ?+ struct sun8i_mixer *mixer = dev_get_drvdata(dev);
>> ?+
>> ?+ clk_disable_unprepare(mixer->mod_clk);
>> ?+ clk_disable_unprepare(mixer->bus_clk);
>> ?+ reset_control_assert(mixer->reset);
>> ?+}
>> ?+
>> ?+static const struct component_ops sun8i_mixer_ops = {
>> ?+ .bind = sun8i_mixer_bind,
>> ?+ .unbind = sun8i_mixer_unbind,
>> ?+};
>> ?+
>> ?+static int sun8i_mixer_probe(struct platform_device *pdev)
>> ?+{
>> ?+ return component_add(&pdev->dev, &sun8i_mixer_ops);
>> ?+}
>> ?+
>> ?+static int sun8i_mixer_remove(struct platform_device *pdev)
>> ?+{
>> ?+ component_del(&pdev->dev, &sun8i_mixer_ops);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
>> ?+ .vi_num = 2,
>> ?+ .ui_num = 1,
>> ?+};
>> ?+
>> ?+static const struct of_device_id sun8i_mixer_of_table[] = {
>> ?+ {
>> ?+ .compatible = "allwinner,sun8i-v3s-de2-mixer",
>> ?+ .data = &sun8i_v3s_mixer_cfg
>> ?+ },
>> ?+ { }
>> ?+};
>> ?+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
>> ?+
>> ?+static struct platform_driver sun8i_mixer_platform_driver = {
>> ?+ .probe = sun8i_mixer_probe,
>> ?+ .remove = sun8i_mixer_remove,
>> ?+ .driver = {
>> ?+ .name = "sun8i-mixer",
>> ?+ .of_match_table = sun8i_mixer_of_table,
>> ?+ },
>> ?+};
>> ?+module_platform_driver(sun8i_mixer_platform_driver);
>> ?+
>> ?+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
>> ?+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
>> ?+MODULE_LICENSE("GPL");
>> ?+#else /* DRM_SUN4I_DE2 */
>> ?+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>> ?+{
>> ?+}
>> ?+
>> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>> ?+ int layer, bool enable)
>> ?+{
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ return -ENOENT;
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ return -ENOENT;
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ return -ENOENT;
>> ?+}
>> ?+#endif /* CONFIG_DRM_SUN4I_DE2 */
>> ?diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h
>> ?b/drivers/gpu/drm/sun4i/sun8i_mixer.h new file mode 100644
>> ?index 000000000000..0bc134b3bc98
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
>> ?@@ -0,0 +1,133 @@
>> ?+/*
>> ?+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> ?+ *
>> ?+ * This program is free software; you can redistribute it and/or
>> ?+ * modify it under the terms of the GNU General Public License as
>> ?+ * published by the Free Software Foundation; either version 2 of
>> ?+ * the License, or (at your option) any later version.
>> ?+ */
>> ?+
>> ?+#ifndef _SUN8I_MIXER_H_
>> ?+#define _SUN8I_MIXER_H_
>> ?+
>> ?+#include <linux/clk.h>
>> ?+#include <linux/regmap.h>
>> ?+#include <linux/reset.h>
>> ?+
>> ?+#include "sun4i_layer.h"
>> ?+
>> ?+#define SUN8I_MIXER_MAX_CHAN_COUNT 4
>> ?+
>> ?+#define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
>> ?+#define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x))
>> ?+
>> ?+#define SUN8I_MIXER_GLOBAL_CTL 0x0
>> ?+#define SUN8I_MIXER_GLOBAL_STATUS 0x4
>> ?+#define SUN8I_MIXER_GLOBAL_DBUFF 0x8
>> ?+#define SUN8I_MIXER_GLOBAL_SIZE 0xc
>> ?+
>> ?+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1
>> ?+
>> ?+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1
>> ?+
>> ?+#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000
>> ?+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0)
>> ?+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4)
>> ?+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8)
>> ?+#define SUN8I_MIXER_BLEND_ROUTE 0x1080
>> ?+#define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084
>> ?+#define SUN8I_MIXER_BLEND_BKCOLOR 0x1088
>> ?+#define SUN8I_MIXER_BLEND_OUTSIZE 0x108c
>> ?+#define SUN8I_MIXER_BLEND_MODE(x) (0x1090 + 0x04 * (x))
>> ?+#define SUN8I_MIXER_BLEND_CK_CTL 0x10b0
>> ?+#define SUN8I_MIXER_BLEND_CK_CFG 0x10b4
>> ?+#define SUN8I_MIXER_BLEND_CK_MAX(x) (0x10c0 + 0x04 * (x))
>> ?+#define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x))
>> ?+#define SUN8I_MIXER_BLEND_OUTCTL 0x10fc
>> ?+
>> ?+/* The following numbers are some still unknown magic numbers */
>> ?+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000
>> ?+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101
>> ?+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0
>> ?+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000
>> ?+#define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301
>> ?+#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0
>> ?+
>> ?+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1)
>> ?+
>> ?+/*
>> ?+ * VI channels are not used now, but the support of them may be introduced
>> ?in + * the future.
>> ?+ */
>> ?+
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
>> ?+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80)
>> ?+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84)
>> ?+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88)
>> ?+
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24)
>> ?+
>> ?+/*
>> ?+ * These sun-engines are still unknown now, the EN registers are here only
>> ?to + * be used to disable these sub-engines.
>> ?+ */
>> ?+#define SUN8I_MIXER_VSU_EN 0x20000
>> ?+#define SUN8I_MIXER_GSU1_EN 0x30000
>> ?+#define SUN8I_MIXER_GSU2_EN 0x40000
>> ?+#define SUN8I_MIXER_GSU3_EN 0x50000
>> ?+#define SUN8I_MIXER_FCE_EN 0xa0000
>> ?+#define SUN8I_MIXER_BWS_EN 0xa2000
>> ?+#define SUN8I_MIXER_LTI_EN 0xa4000
>> ?+#define SUN8I_MIXER_PEAK_EN 0xa6000
>> ?+#define SUN8I_MIXER_ASE_EN 0xa8000
>> ?+#define SUN8I_MIXER_FCC_EN 0xaa000
>> ?+#define SUN8I_MIXER_DCSC_EN 0xb0000
>> ?+
>> ?+struct sun8i_mixer_cfg {
>> ?+ int vi_num;
>> ?+ int ui_num;
>> ?+};
>> ?+
>> ?+struct sun8i_mixer {
>> ?+ struct regmap *regs;
>> ?+
>> ?+ const struct sun8i_mixer_cfg *cfg;
>> ?+
>> ?+ struct reset_control *reset;
>> ?+
>> ?+ struct clk *bus_clk;
>> ?+ struct clk *mod_clk;
>> ?+};
>> ?+
>> ?+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
>> ?+
>> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>> ?+ int layer, bool enable);
>> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane);
>> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane);
>> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane);
>> ?+#endif /* _SUN8I_MIXER_H_ */
>> ?--
>> ?2.11.1

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 15:18     ` Icenowy Zheng
@ 2017-02-23 14:27       ` Stefan Monnier
  -1 siblings, 0 replies; 57+ messages in thread
From: Stefan Monnier @ 2017-02-23 14:27 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
  Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++

Should the drm/sun4i directory be renamed to drm/sunxi then?


        Stefan

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-23 14:27       ` Stefan Monnier
  0 siblings, 0 replies; 57+ messages in thread
From: Stefan Monnier @ 2017-02-23 14:27 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-sunxi, devicetree, linux-arm-kernel, linux-kernel, dri-devel

>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++

Should the drm/sun4i directory be renamed to drm/sunxi then?


        Stefan


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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-23 17:44       ` Emil Velikov
  0 siblings, 0 replies; 57+ messages in thread
From: Emil Velikov @ 2017-02-23 17:44 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec,
	David Airlie, Jean-Francois Moine, devicetree,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, linux-sunxi,
	linux-clk, LAKML

On 22 February 2017 at 15:18, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
>
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
Silly question:

Does this series cover the same functionality/hardware as the separate
sun8i driver proposed a few months back ?
The diff stat looks quite different (~1.6k vs ~0.6k), although that
could be due to reusing the existing sun4i code.

Thanks
Emil

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-23 17:44       ` Emil Velikov
  0 siblings, 0 replies; 57+ messages in thread
From: Emil Velikov @ 2017-02-23 17:44 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec,
	David Airlie, Jean-Francois Moine, devicetree,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, LAKML

On 22 February 2017 at 15:18, Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org> wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
>
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>
> Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
Silly question:

Does this series cover the same functionality/hardware as the separate
sun8i driver proposed a few months back ?
The diff stat looks quite different (~1.6k vs ~0.6k), although that
could be due to reusing the existing sun4i code.

Thanks
Emil

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-23 17:44       ` Emil Velikov
  0 siblings, 0 replies; 57+ messages in thread
From: Emil Velikov @ 2017-02-23 17:44 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, David Airlie,
	linux-sunxi, Linux-Kernel@Vger. Kernel. Org, ML dri-devel,
	Chen-Yu Tsai, Rob Herring, Maxime Ripard, linux-clk, LAKML

On 22 February 2017 at 15:18, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
>
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
Silly question:

Does this series cover the same functionality/hardware as the separate
sun8i driver proposed a few months back ?
The diff stat looks quite different (~1.6k vs ~0.6k), although that
could be due to reusing the existing sun4i code.

Thanks
Emil

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-23 17:44       ` Emil Velikov
  0 siblings, 0 replies; 57+ messages in thread
From: Emil Velikov @ 2017-02-23 17:44 UTC (permalink / raw)
  To: linux-arm-kernel

On 22 February 2017 at 15:18, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
>
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
Silly question:

Does this series cover the same functionality/hardware as the separate
sun8i driver proposed a few months back ?
The diff stat looks quite different (~1.6k vs ~0.6k), although that
could be due to reusing the existing sun4i code.

Thanks
Emil

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24  0:46         ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-24  0:46 UTC (permalink / raw)
  To: Emil Velikov
  Cc: Icenowy Zheng, Rob Herring, Chen-Yu Tsai, Jernej Skrabec,
	David Airlie, Jean-Francois Moine, devicetree,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel, linux-sunxi,
	linux-clk, LAKML

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

Hi Emil,

On Thu, Feb 23, 2017 at 05:44:54PM +0000, Emil Velikov wrote:
> On 22 February 2017 at 15:18, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> > Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> > in a new "Display Engine" (mixers instead of old backends and
> > frontends).
> >
> > Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >
> > Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> > ---
> >  drivers/gpu/drm/sun4i/Kconfig       |   8 +
> >  drivers/gpu/drm/sun4i/Makefile      |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
> >  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
> >  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
> >  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
> >  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
> >  9 files changed, 674 insertions(+), 23 deletions(-)
> Silly question:
> 
> Does this series cover the same functionality/hardware as the separate
> sun8i driver proposed a few months back ?
> The diff stat looks quite different (~1.6k vs ~0.6k), although that
> could be due to reusing the existing sun4i code.

Since those aren't feature equivalent, this is a bit complicated to
answer, but most of the changes introduced by the old driver was
duplication of code that was already existing (Allwinner TCON, TV
Encoder, Designware HDMI, etc).

This patchset superseeds it by using what is already supported without
the duplication. It also has a bit less features (for example the HDMI
support is missing for now), but it's a much better code base to base
our future work on.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24  0:46         ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-24  0:46 UTC (permalink / raw)
  To: Emil Velikov
  Cc: Icenowy Zheng, Rob Herring, Chen-Yu Tsai, Jernej Skrabec,
	David Airlie, Jean-Francois Moine, devicetree,
	Linux-Kernel@Vger. Kernel. Org, ML dri-devel,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, LAKML

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

Hi Emil,

On Thu, Feb 23, 2017 at 05:44:54PM +0000, Emil Velikov wrote:
> On 22 February 2017 at 15:18, Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org> wrote:
> > Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> > in a new "Display Engine" (mixers instead of old backends and
> > frontends).
> >
> > Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >
> > Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
> > ---
> >  drivers/gpu/drm/sun4i/Kconfig       |   8 +
> >  drivers/gpu/drm/sun4i/Makefile      |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
> >  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
> >  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
> >  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
> >  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
> >  9 files changed, 674 insertions(+), 23 deletions(-)
> Silly question:
> 
> Does this series cover the same functionality/hardware as the separate
> sun8i driver proposed a few months back ?
> The diff stat looks quite different (~1.6k vs ~0.6k), although that
> could be due to reusing the existing sun4i code.

Since those aren't feature equivalent, this is a bit complicated to
answer, but most of the changes introduced by the old driver was
duplication of code that was already existing (Allwinner TCON, TV
Encoder, Designware HDMI, etc).

This patchset superseeds it by using what is already supported without
the duplication. It also has a bit less features (for example the HDMI
support is missing for now), but it's a much better code base to base
our future work on.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24  0:46         ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-24  0:46 UTC (permalink / raw)
  To: Emil Velikov
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, David Airlie,
	linux-sunxi, Linux-Kernel@Vger. Kernel. Org, ML dri-devel,
	Chen-Yu Tsai, Rob Herring, Icenowy Zheng, linux-clk, LAKML


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

Hi Emil,

On Thu, Feb 23, 2017 at 05:44:54PM +0000, Emil Velikov wrote:
> On 22 February 2017 at 15:18, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> > Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> > in a new "Display Engine" (mixers instead of old backends and
> > frontends).
> >
> > Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >
> > Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> > ---
> >  drivers/gpu/drm/sun4i/Kconfig       |   8 +
> >  drivers/gpu/drm/sun4i/Makefile      |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
> >  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
> >  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
> >  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
> >  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
> >  9 files changed, 674 insertions(+), 23 deletions(-)
> Silly question:
> 
> Does this series cover the same functionality/hardware as the separate
> sun8i driver proposed a few months back ?
> The diff stat looks quite different (~1.6k vs ~0.6k), although that
> could be due to reusing the existing sun4i code.

Since those aren't feature equivalent, this is a bit complicated to
answer, but most of the changes introduced by the old driver was
duplication of code that was already existing (Allwinner TCON, TV
Encoder, Designware HDMI, etc).

This patchset superseeds it by using what is already supported without
the duplication. It also has a bit less features (for example the HDMI
support is missing for now), but it's a much better code base to base
our future work on.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24  0:46         ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-24  0:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emil,

On Thu, Feb 23, 2017 at 05:44:54PM +0000, Emil Velikov wrote:
> On 22 February 2017 at 15:18, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> > Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> > in a new "Display Engine" (mixers instead of old backends and
> > frontends).
> >
> > Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >
> > Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> > ---
> >  drivers/gpu/drm/sun4i/Kconfig       |   8 +
> >  drivers/gpu/drm/sun4i/Makefile      |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
> >  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
> >  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
> >  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
> >  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
> >  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
> >  9 files changed, 674 insertions(+), 23 deletions(-)
> Silly question:
> 
> Does this series cover the same functionality/hardware as the separate
> sun8i driver proposed a few months back ?
> The diff stat looks quite different (~1.6k vs ~0.6k), although that
> could be due to reusing the existing sun4i code.

Since those aren't feature equivalent, this is a bit complicated to
answer, but most of the changes introduced by the old driver was
duplication of code that was already existing (Allwinner TCON, TV
Encoder, Designware HDMI, etc).

This patchset superseeds it by using what is already supported without
the duplication. It also has a bit less features (for example the HDMI
support is missing for now), but it's a much better code base to base
our future work on.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170223/1ffbfd57/attachment-0001.sig>

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 15:30       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-24 15:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Maxime Ripard, Icenowy Zheng, Chen-Yu Tsai, David Airlie,
	Jean-Francois Moine, linux-clk, devicetree, linux-arm-kernel,
	linux-kernel, dri-devel, linux-sunxi

Hi,

Dne petek, 24. februar 2017 ob 14:30:36 CET je Rob Herring napisal(a):
> On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
> 
> <maxime.ripard@free-electrons.com> wrote:
> > Hi,
> > 
> > On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> >> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> >> in a new "Display Engine" (mixers instead of old backends and
> >> frontends).
> >> 
> >> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >> 
> >> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> >> ---
> 
> [...]
> 
> >> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> >> index 000000000000..9427b57240d3
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> @@ -0,0 +1,417 @@
> >> +/*
> >> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> >> + *
> >> + * Based on sun4i_backend.c, which is:
> >> + *   Copyright (C) 2015 Free Electrons
> >> + *   Copyright (C) 2015 NextThing Co
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License as
> >> + * published by the Free Software Foundation; either version 2 of
> >> + * the License, or (at your option) any later version.
> >> + */
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +#include <drm/drm_fb_cma_helper.h>
> >> +#include <drm/drm_gem_cma_helper.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/reset.h>
> >> +#include <linux/of_device.h>
> >> +
> >> +#include "sun8i_mixer.h"
> >> +#include "sun4i_drv.h"
> >> +
> >> +#define SUN8I_DRAM_OFFSET 0x40000000
> > 
> > PHYS_OFFSET?
> 
> PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.
> 
> If your DMA addresses are different from CPU addresses (i.e. have some
> offset), then use "dma-ranges" in DT.
> 
> Rob

That adjustment is not needed and should be removed. Such adjusment breaks DE 
on boards with 2 GiB of RAM.

Regards,
Jernej

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 15:30       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-24 15:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Maxime Ripard, Icenowy Zheng, Chen-Yu Tsai, David Airlie,
	Jean-Francois Moine, linux-clk,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, dri-devel, linux-sunxi

Hi,

Dne petek, 24. februar 2017 ob 14:30:36 CET je Rob Herring napisal(a):
> On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
> 
> <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
> > Hi,
> > 
> > On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> >> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> >> in a new "Display Engine" (mixers instead of old backends and
> >> frontends).
> >> 
> >> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >> 
> >> Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
> >> ---
> 
> [...]
> 
> >> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> >> index 000000000000..9427b57240d3
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> @@ -0,0 +1,417 @@
> >> +/*
> >> + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
> >> + *
> >> + * Based on sun4i_backend.c, which is:
> >> + *   Copyright (C) 2015 Free Electrons
> >> + *   Copyright (C) 2015 NextThing Co
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License as
> >> + * published by the Free Software Foundation; either version 2 of
> >> + * the License, or (at your option) any later version.
> >> + */
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +#include <drm/drm_fb_cma_helper.h>
> >> +#include <drm/drm_gem_cma_helper.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/reset.h>
> >> +#include <linux/of_device.h>
> >> +
> >> +#include "sun8i_mixer.h"
> >> +#include "sun4i_drv.h"
> >> +
> >> +#define SUN8I_DRAM_OFFSET 0x40000000
> > 
> > PHYS_OFFSET?
> 
> PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.
> 
> If your DMA addresses are different from CPU addresses (i.e. have some
> offset), then use "dma-ranges" in DT.
> 
> Rob

That adjustment is not needed and should be removed. Such adjusment breaks DE 
on boards with 2 GiB of RAM.

Regards,
Jernej

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 15:30       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-24 15:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Jean-Francois Moine, devicetree, David Airlie, linux-sunxi,
	linux-kernel, dri-devel, Chen-Yu Tsai, Icenowy Zheng,
	Maxime Ripard, linux-clk, linux-arm-kernel

Hi,

Dne petek, 24. februar 2017 ob 14:30:36 CET je Rob Herring napisal(a):
> On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
> 
> <maxime.ripard@free-electrons.com> wrote:
> > Hi,
> > 
> > On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> >> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> >> in a new "Display Engine" (mixers instead of old backends and
> >> frontends).
> >> 
> >> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >> 
> >> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> >> ---
> 
> [...]
> 
> >> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> >> index 000000000000..9427b57240d3
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> @@ -0,0 +1,417 @@
> >> +/*
> >> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> >> + *
> >> + * Based on sun4i_backend.c, which is:
> >> + *   Copyright (C) 2015 Free Electrons
> >> + *   Copyright (C) 2015 NextThing Co
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License as
> >> + * published by the Free Software Foundation; either version 2 of
> >> + * the License, or (at your option) any later version.
> >> + */
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +#include <drm/drm_fb_cma_helper.h>
> >> +#include <drm/drm_gem_cma_helper.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/reset.h>
> >> +#include <linux/of_device.h>
> >> +
> >> +#include "sun8i_mixer.h"
> >> +#include "sun4i_drv.h"
> >> +
> >> +#define SUN8I_DRAM_OFFSET 0x40000000
> > 
> > PHYS_OFFSET?
> 
> PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.
> 
> If your DMA addresses are different from CPU addresses (i.e. have some
> offset), then use "dma-ranges" in DT.
> 
> Rob

That adjustment is not needed and should be removed. Such adjusment breaks DE 
on boards with 2 GiB of RAM.

Regards,
Jernej


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 15:30       ` Jernej Škrabec
  0 siblings, 0 replies; 57+ messages in thread
From: Jernej Škrabec @ 2017-02-24 15:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Dne petek, 24. februar 2017 ob 14:30:36 CET je Rob Herring napisal(a):
> On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
> 
> <maxime.ripard@free-electrons.com> wrote:
> > Hi,
> > 
> > On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> >> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> >> in a new "Display Engine" (mixers instead of old backends and
> >> frontends).
> >> 
> >> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> >> 
> >> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> >> ---
> 
> [...]
> 
> >> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644
> >> index 000000000000..9427b57240d3
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> @@ -0,0 +1,417 @@
> >> +/*
> >> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> >> + *
> >> + * Based on sun4i_backend.c, which is:
> >> + *   Copyright (C) 2015 Free Electrons
> >> + *   Copyright (C) 2015 NextThing Co
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License as
> >> + * published by the Free Software Foundation; either version 2 of
> >> + * the License, or (at your option) any later version.
> >> + */
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_crtc.h>
> >> +#include <drm/drm_crtc_helper.h>
> >> +#include <drm/drm_fb_cma_helper.h>
> >> +#include <drm/drm_gem_cma_helper.h>
> >> +#include <drm/drm_plane_helper.h>
> >> +
> >> +#include <linux/component.h>
> >> +#include <linux/reset.h>
> >> +#include <linux/of_device.h>
> >> +
> >> +#include "sun8i_mixer.h"
> >> +#include "sun4i_drv.h"
> >> +
> >> +#define SUN8I_DRAM_OFFSET 0x40000000
> > 
> > PHYS_OFFSET?
> 
> PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.
> 
> If your DMA addresses are different from CPU addresses (i.e. have some
> offset), then use "dma-ranges" in DT.
> 
> Rob

That adjustment is not needed and should be removed. Such adjusment breaks DE 
on boards with 2 GiB of RAM.

Regards,
Jernej

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 20:09   ` Maxime Ripard
  (?)
  (?)
@ 2017-02-24 13:30     ` Rob Herring
  -1 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2017-02-24 13:30 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Icenowy Zheng, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk, devicetree, linux-arm-kernel,
	linux-kernel, dri-devel, linux-sunxi

On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
>> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>> in a new "Display Engine" (mixers instead of old backends and
>> frontends).
>>
>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ---

[...]

>> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> new file mode 100644
>> index 000000000000..9427b57240d3
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> @@ -0,0 +1,417 @@
>> +/*
>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> + *
>> + * Based on sun4i_backend.c, which is:
>> + *   Copyright (C) 2015 Free Electrons
>> + *   Copyright (C) 2015 NextThing Co
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_fb_cma_helper.h>
>> +#include <drm/drm_gem_cma_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <linux/component.h>
>> +#include <linux/reset.h>
>> +#include <linux/of_device.h>
>> +
>> +#include "sun8i_mixer.h"
>> +#include "sun4i_drv.h"
>> +
>> +#define SUN8I_DRAM_OFFSET 0x40000000
>
> PHYS_OFFSET?

PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.

If your DMA addresses are different from CPU addresses (i.e. have some
offset), then use "dma-ranges" in DT.

Rob

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 13:30     ` Rob Herring
  0 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2017-02-24 13:30 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, linux-sunxi,
	linux-kernel, dri-devel, Chen-Yu Tsai, Icenowy Zheng, linux-clk,
	linux-arm-kernel

On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
>> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>> in a new "Display Engine" (mixers instead of old backends and
>> frontends).
>>
>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ---

[...]

>> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> new file mode 100644
>> index 000000000000..9427b57240d3
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> @@ -0,0 +1,417 @@
>> +/*
>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> + *
>> + * Based on sun4i_backend.c, which is:
>> + *   Copyright (C) 2015 Free Electrons
>> + *   Copyright (C) 2015 NextThing Co
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_fb_cma_helper.h>
>> +#include <drm/drm_gem_cma_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <linux/component.h>
>> +#include <linux/reset.h>
>> +#include <linux/of_device.h>
>> +
>> +#include "sun8i_mixer.h"
>> +#include "sun4i_drv.h"
>> +
>> +#define SUN8I_DRAM_OFFSET 0x40000000
>
> PHYS_OFFSET?

PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.

If your DMA addresses are different from CPU addresses (i.e. have some
offset), then use "dma-ranges" in DT.

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

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 13:30     ` Rob Herring
  0 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2017-02-24 13:30 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, David Airlie,
	linux-sunxi, linux-kernel, dri-devel, Chen-Yu Tsai,
	Icenowy Zheng, linux-clk, linux-arm-kernel

On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
>> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>> in a new "Display Engine" (mixers instead of old backends and
>> frontends).
>>
>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ---

[...]

>> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> new file mode 100644
>> index 000000000000..9427b57240d3
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> @@ -0,0 +1,417 @@
>> +/*
>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> + *
>> + * Based on sun4i_backend.c, which is:
>> + *   Copyright (C) 2015 Free Electrons
>> + *   Copyright (C) 2015 NextThing Co
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_fb_cma_helper.h>
>> +#include <drm/drm_gem_cma_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <linux/component.h>
>> +#include <linux/reset.h>
>> +#include <linux/of_device.h>
>> +
>> +#include "sun8i_mixer.h"
>> +#include "sun4i_drv.h"
>> +
>> +#define SUN8I_DRAM_OFFSET 0x40000000
>
> PHYS_OFFSET?

PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.

If your DMA addresses are different from CPU addresses (i.e. have some
offset), then use "dma-ranges" in DT.

Rob

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-24 13:30     ` Rob Herring
  0 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2017-02-24 13:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Feb 22, 2017 at 2:09 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
>> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>> in a new "Display Engine" (mixers instead of old backends and
>> frontends).
>>
>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ---

[...]

>> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> new file mode 100644
>> index 000000000000..9427b57240d3
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> @@ -0,0 +1,417 @@
>> +/*
>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> + *
>> + * Based on sun4i_backend.c, which is:
>> + *   Copyright (C) 2015 Free Electrons
>> + *   Copyright (C) 2015 NextThing Co
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_fb_cma_helper.h>
>> +#include <drm/drm_gem_cma_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +
>> +#include <linux/component.h>
>> +#include <linux/reset.h>
>> +#include <linux/of_device.h>
>> +
>> +#include "sun8i_mixer.h"
>> +#include "sun4i_drv.h"
>> +
>> +#define SUN8I_DRAM_OFFSET 0x40000000
>
> PHYS_OFFSET?

PHYS_OFFSET is not portable. __pa(PAGE_OFFSET) instead.

If your DMA addresses are different from CPU addresses (i.e. have some
offset), then use "dma-ranges" in DT.

Rob

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:54       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 22:54 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk, devicetree, linux-arm-kernel,
	linux-kernel, dri-devel, linux-sunxi

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

Hi Icenowy,

(Please fix your mailer, its quotation is broken and mangles all the
indentation)

On Thu, Feb 23, 2017 at 04:28:42AM +0800, Icenowy Zheng wrote:
> >>  @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
> >>
> >>           return layers;
> >>   }
> >>  +
> >>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> >
> > And store this (and sun4i_layers_init) in an structure holding the
> > function pointers for those.
> 
> How should I do it?
> If I create a ops struct, where should I reference it?

I'm not sure I get your question. You can create that structure, fill
a field in the sun4i_drv structure at bind time, and call those
function pointers directly where you need to.

> >>  +{
> >>  + struct sun4i_layer **layers;
> >>  + int i;
> >>  +
> >>  + layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> >>  + sizeof(**layers), GFP_KERNEL);
> >>  + if (!layers)
> >>  + return ERR_PTR(-ENOMEM);
> >>  +
> >>  + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> >>  + const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> >>  + struct sun4i_layer *layer = layers[i];
> >>  +
> >>  + layer = sun4i_layer_init_one(drm, plane);
> >>  + if (IS_ERR(layer)) {
> >>  + dev_err(drm->dev, "Couldn't initialize %s plane\n",
> >>  + i ? "overlay" : "primary");
> >>  + return ERR_CAST(layer);
> >>  + };
> >>  +
> >>  + layer->id = i;
> >>  + };
> >>  +
> >>  + return layers;
> >>  +}
> >>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  index a2f65d7a3f4e..f7b9e5daea50 100644
> >>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
> >>   }
> >>
> >>   struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> >>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> >>
> >>   #endif /* _SUN4I_LAYER_H_ */
> >>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >>  new file mode 100644
> >>  index 000000000000..9427b57240d3
> >>  --- /dev/null
> >>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >>  @@ -0,0 +1,417 @@
> >>  +/*
> >>  + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> >>  + *
> >>  + * Based on sun4i_backend.c, which is:
> >>  + * Copyright (C) 2015 Free Electrons
> >>  + * Copyright (C) 2015 NextThing Co
> >>  + *
> >>  + * This program is free software; you can redistribute it and/or
> >>  + * modify it under the terms of the GNU General Public License as
> >>  + * published by the Free Software Foundation; either version 2 of
> >>  + * the License, or (at your option) any later version.
> >>  + */
> >>  +
> >>  +#include <drm/drmP.h>
> >>  +#include <drm/drm_atomic_helper.h>
> >>  +#include <drm/drm_crtc.h>
> >>  +#include <drm/drm_crtc_helper.h>
> >>  +#include <drm/drm_fb_cma_helper.h>
> >>  +#include <drm/drm_gem_cma_helper.h>
> >>  +#include <drm/drm_plane_helper.h>
> >>  +
> >>  +#include <linux/component.h>
> >>  +#include <linux/reset.h>
> >>  +#include <linux/of_device.h>
> >>  +
> >>  +#include "sun8i_mixer.h"
> >>  +#include "sun4i_drv.h"
> >>  +
> >>  +#define SUN8I_DRAM_OFFSET 0x40000000
> >
> > PHYS_OFFSET?
> 
> Any name is OK.

What I meant is that there is already a variable holding that value
that is PHYS_OFFSET.

> (P.S. this seems also needed for some DE1s)

Did you encounter any issue on it?

> >>  +#if defined CONFIG_DRM_SUN4I_DE2
> >
> > That ifdef should be in the header
> 
> So the file only compile if this option is enabled?

Yes.

> And if this option is disabled, inlined null stubs should be
> made in header?

Yes.

> >>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> >>  + int layer, bool enable)
> >>  +{
> >>  + u32 val;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> >>  +
> >>  + if (enable)
> >>  + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> >>  + else
> >>  + val = 0;
> >
> > So you only support the UI channel?
> >
> > Why do you expose several planes then?
> 
> Currently I didn't find any way to enable more than one channel
> at the same time, so only the first UI channel is used.
> 
> After more knowledges are gained for DE2 mixers we can implement
> more functions (for example, Jernejsk have already discovered how
> to do color space correlation in DE2 for TVE).

Then please expose only the primary plane. You also expose an overlay
here that is not functional from what you tell me.

> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> >>  +
> >>  + /* Set the alpha configuration */
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> >
> > This should be in a property
> 
> What property?

Alpha?

> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> >>  +
> >>  +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> >>  + u32 format, u32 *mode)
> >>  +{
> >>  + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> >>  + (format == DRM_FORMAT_ARGB8888))
> >>  + format = DRM_FORMAT_XRGB8888;
> >
> > Do you actually have that issue.
> 
> Yes, it really do, at least screen go black when I set this to ARGB8888 in
> U-Boot. (U-Boot is a good experiement area ;-) )

Ok. And you have some color when you set the background to some colour ?

> >>  + switch (format) {
> >>  + case DRM_FORMAT_ARGB8888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> >>  + break;
> >>  +
> >>  + case DRM_FORMAT_XRGB8888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> >>  + break;
> >>  +
> >>  + case DRM_FORMAT_RGB888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> >>  + break;
> >>  +
> >>  + default:
> >>  + return -EINVAL;
> >>  + }
> >>  +
> >>  + return 0;
> >>  +}
> >>  +
> >>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int i;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> >>  +
> >>  + if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> >>  + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> >>  + state->crtc_w, state->crtc_h);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + DRM_DEBUG_DRIVER("Updating blender size\n");
> >>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> >>  + regmap_write(mixer->regs,
> >>  + SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + DRM_DEBUG_DRIVER("Updating channel size\n");
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + }
> >>  +
> >>  + /* Set the line width */
> >>  + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> >>  + fb->pitches[0]);
> >>  +
> >>  + /* Set height and width */
> >>  + DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> >>  + state->crtc_w, state->crtc_h);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> >>  +
> >>  + /* Set base coordinates */
> >>  + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> >>  + state->crtc_x, state->crtc_y);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> >>  + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> >>  +
> >>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + bool interlaced = false;
> >>  + u32 val;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int ret;
> >>  +
> >>  + if (plane->state->crtc)
> >>  + interlaced = plane->state->crtc->state->adjusted_mode.flags
> >>  + & DRM_MODE_FLAG_INTERLACE;
> >>  +
> >>  + regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> >>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> >>  + interlaced ?
> >>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> >>  +
> >>  + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> >>  + interlaced ? "on" : "off");
> >>  +
> >>  + ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> >>  + &val);
> >>  + if (ret) {
> >>  + DRM_DEBUG_DRIVER("Invalid format\n");
> >>  + return ret;
> >>  + }
> >>  +
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> >>  +
> >>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + struct drm_gem_cma_object *gem;
> >>  + dma_addr_t paddr;
> >>  + uint32_t paddr_u32;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int bpp;
> >>  +
> >>  + /* Get the physical address of the buffer in memory */
> >>  + gem = drm_fb_cma_get_gem_obj(fb, 0);
> >>  +
> >>  + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> >>  +
> >>  + /* Compute the start of the displayed memory */
> >>  + bpp = fb->format->cpp[0];
> >>  + paddr = gem->paddr + fb->offsets[0];
> >>  + paddr += (state->src_x >> 16) * bpp;
> >>  + paddr += (state->src_y >> 16) * fb->pitches[0];
> >>  + paddr -= SUN8I_DRAM_OFFSET;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> >>  +
> >>  + paddr_u32 = (uint32_t) paddr;
> >>  +
> >>  + regmap_write(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> >>  + paddr_u32);
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> >>  +
> >>  +static struct regmap_config sun8i_mixer_regmap_config = {
> >>  + .reg_bits = 32,
> >>  + .val_bits = 32,
> >>  + .reg_stride = 4,
> >>  + .max_register = 0xbffc, /* guessed */
> >>  +};
> >>  +
> >>  +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> >>  + void *data)
> >>  +{
> >>  + struct platform_device *pdev = to_platform_device(dev);
> >>  + struct drm_device *drm = data;
> >>  + struct sun4i_drv *drv = drm->dev_private;
> >>  + struct sun8i_mixer *mixer;
> >>  + struct resource *res;
> >>  + void __iomem *regs;
> >>  + int i, ret;
> >>  +
> >>  + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> >>  + if (!mixer)
> >>  + return -ENOMEM;
> >>  + dev_set_drvdata(dev, mixer);
> >>  + drv->mixer = mixer;
> >>  +
> >>  + mixer->cfg = of_device_get_match_data(dev);
> >>  + if (!mixer->cfg)
> >>  + return -EINVAL;
> >>  +
> >>  + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>  + regs = devm_ioremap_resource(dev, res);
> >>  + if (IS_ERR(regs))
> >>  + return PTR_ERR(regs);
> >>  +
> >>  + mixer->regs = devm_regmap_init_mmio(dev, regs,
> >>  + &sun8i_mixer_regmap_config);
> >>  + if (IS_ERR(mixer->regs)) {
> >>  + dev_err(dev, "Couldn't create the mixer regmap\n");
> >>  + return PTR_ERR(mixer->regs);
> >>  + }
> >>  +
> >>  + mixer->reset = devm_reset_control_get(dev, NULL);
> >>  + if (IS_ERR(mixer->reset)) {
> >>  + dev_err(dev, "Couldn't get our reset line\n");
> >>  + return PTR_ERR(mixer->reset);
> >>  + }
> >>  +
> >>  + ret = reset_control_deassert(mixer->reset);
> >>  + if (ret) {
> >>  + dev_err(dev, "Couldn't deassert our reset line\n");
> >>  + return ret;
> >>  + }
> >>  +
> >>  + mixer->bus_clk = devm_clk_get(dev, "bus");
> >>  + if (IS_ERR(mixer->bus_clk)) {
> >>  + dev_err(dev, "Couldn't get the mixer bus clock\n");
> >>  + ret = PTR_ERR(mixer->bus_clk);
> >>  + goto err_assert_reset;
> >>  + }
> >>  + clk_prepare_enable(mixer->bus_clk);
> >>  +
> >>  + mixer->mod_clk = devm_clk_get(dev, "mod");
> >>  + if (IS_ERR(mixer->mod_clk)) {
> >>  + dev_err(dev, "Couldn't get the mixer module clock\n");
> >>  + ret = PTR_ERR(mixer->mod_clk);
> >>  + goto err_disable_bus_clk;
> >>  + }
> >>  + clk_prepare_enable(mixer->mod_clk);
> >
> > Supporting runtime_pm would be better.
> 
> But I think it's at least not support yet for DE1 backend...

This is true, but unrelated.

> >>  + /* Reset the registers */
> >>  + for (i = 0x0; i < 0x20000; i += 4)
> >>  + regmap_write(mixer->regs, i, 0);
> >
> > Do you still need to reset it? Isn't the reset line enough?
> 
> Nope, some strange data lies in the DE2 space.
> 
> Here's a reg dump of a running DE2 's channel 2 on V3s:
> => md 01104000
> 01104000: ff000403 010f01df 00000000 00000780    ................
> 01104010: 03f80000 00000000 00000000 00000000    ................
> 01104020: 00000000 00000000 00000000 00000000    ................
> 01104030: 00000000 00000000 00000000 00000000    ................
> 01104040: 00000000 00000000 00000000 00000000    ................
> 01104050: 00000000 00000000 00000000 00000000    ................
> 01104060: 00000000 00000000 00000000 00000000    ................
> 01104070: 00000000 00000000 00000000 00000000    ................
> 01104080: 00000000 00000000 010f01df bbe4d3b0    ................
> 01104090: 54daaf98 13835927 a1479b58 8396b8ad    ...T'Y..X.G.....
> 011040a0: 07d02ede a39a18da 87d88aba a2d23cf6    .............<..
> 011040b0: e8bfa8f7 2c8d2b7c f8bbeb3e 98013b75    ....|+.,>...u;..
> 011040c0: 7c186f48 4ddcdbde b658caf8 76b770d6    Ho.|...M..X..p.v
> 011040d0: b9a620ef fe215cc1 edd6c4b3 c5f7a66c    . ...\!.....l...
> 011040e0: 0d1ff6d3 956ca9e8 7f51f80a ad9a184a    ......l...Q.J...
> 011040f0: ff23e428 772d8d14 f4c03077 8bf495ca    (.#...-ww0......
> 
> (P.S. only first 0x88 bytes are used in a UI channel, so the following is
> not reseted by U-Boot DE2 driver and is kept the original after reset line
> deasserting)

is it causing any issues? This is not unusual to have !0 default
values out of reset, and this shouldn't cause any troubles.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:54       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 22:54 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

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

Hi Icenowy,

(Please fix your mailer, its quotation is broken and mangles all the
indentation)

On Thu, Feb 23, 2017 at 04:28:42AM +0800, Icenowy Zheng wrote:
> >>  @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
> >>
> >>           return layers;
> >>   }
> >>  +
> >>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> >
> > And store this (and sun4i_layers_init) in an structure holding the
> > function pointers for those.
> 
> How should I do it?
> If I create a ops struct, where should I reference it?

I'm not sure I get your question. You can create that structure, fill
a field in the sun4i_drv structure at bind time, and call those
function pointers directly where you need to.

> >>  +{
> >>  + struct sun4i_layer **layers;
> >>  + int i;
> >>  +
> >>  + layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> >>  + sizeof(**layers), GFP_KERNEL);
> >>  + if (!layers)
> >>  + return ERR_PTR(-ENOMEM);
> >>  +
> >>  + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> >>  + const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> >>  + struct sun4i_layer *layer = layers[i];
> >>  +
> >>  + layer = sun4i_layer_init_one(drm, plane);
> >>  + if (IS_ERR(layer)) {
> >>  + dev_err(drm->dev, "Couldn't initialize %s plane\n",
> >>  + i ? "overlay" : "primary");
> >>  + return ERR_CAST(layer);
> >>  + };
> >>  +
> >>  + layer->id = i;
> >>  + };
> >>  +
> >>  + return layers;
> >>  +}
> >>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  index a2f65d7a3f4e..f7b9e5daea50 100644
> >>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
> >>   }
> >>
> >>   struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> >>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> >>
> >>   #endif /* _SUN4I_LAYER_H_ */
> >>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >>  new file mode 100644
> >>  index 000000000000..9427b57240d3
> >>  --- /dev/null
> >>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >>  @@ -0,0 +1,417 @@
> >>  +/*
> >>  + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
> >>  + *
> >>  + * Based on sun4i_backend.c, which is:
> >>  + * Copyright (C) 2015 Free Electrons
> >>  + * Copyright (C) 2015 NextThing Co
> >>  + *
> >>  + * This program is free software; you can redistribute it and/or
> >>  + * modify it under the terms of the GNU General Public License as
> >>  + * published by the Free Software Foundation; either version 2 of
> >>  + * the License, or (at your option) any later version.
> >>  + */
> >>  +
> >>  +#include <drm/drmP.h>
> >>  +#include <drm/drm_atomic_helper.h>
> >>  +#include <drm/drm_crtc.h>
> >>  +#include <drm/drm_crtc_helper.h>
> >>  +#include <drm/drm_fb_cma_helper.h>
> >>  +#include <drm/drm_gem_cma_helper.h>
> >>  +#include <drm/drm_plane_helper.h>
> >>  +
> >>  +#include <linux/component.h>
> >>  +#include <linux/reset.h>
> >>  +#include <linux/of_device.h>
> >>  +
> >>  +#include "sun8i_mixer.h"
> >>  +#include "sun4i_drv.h"
> >>  +
> >>  +#define SUN8I_DRAM_OFFSET 0x40000000
> >
> > PHYS_OFFSET?
> 
> Any name is OK.

What I meant is that there is already a variable holding that value
that is PHYS_OFFSET.

> (P.S. this seems also needed for some DE1s)

Did you encounter any issue on it?

> >>  +#if defined CONFIG_DRM_SUN4I_DE2
> >
> > That ifdef should be in the header
> 
> So the file only compile if this option is enabled?

Yes.

> And if this option is disabled, inlined null stubs should be
> made in header?

Yes.

> >>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> >>  + int layer, bool enable)
> >>  +{
> >>  + u32 val;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> >>  +
> >>  + if (enable)
> >>  + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> >>  + else
> >>  + val = 0;
> >
> > So you only support the UI channel?
> >
> > Why do you expose several planes then?
> 
> Currently I didn't find any way to enable more than one channel
> at the same time, so only the first UI channel is used.
> 
> After more knowledges are gained for DE2 mixers we can implement
> more functions (for example, Jernejsk have already discovered how
> to do color space correlation in DE2 for TVE).

Then please expose only the primary plane. You also expose an overlay
here that is not functional from what you tell me.

> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> >>  +
> >>  + /* Set the alpha configuration */
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> >
> > This should be in a property
> 
> What property?

Alpha?

> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> >>  +
> >>  +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> >>  + u32 format, u32 *mode)
> >>  +{
> >>  + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> >>  + (format == DRM_FORMAT_ARGB8888))
> >>  + format = DRM_FORMAT_XRGB8888;
> >
> > Do you actually have that issue.
> 
> Yes, it really do, at least screen go black when I set this to ARGB8888 in
> U-Boot. (U-Boot is a good experiement area ;-) )

Ok. And you have some color when you set the background to some colour ?

> >>  + switch (format) {
> >>  + case DRM_FORMAT_ARGB8888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> >>  + break;
> >>  +
> >>  + case DRM_FORMAT_XRGB8888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> >>  + break;
> >>  +
> >>  + case DRM_FORMAT_RGB888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> >>  + break;
> >>  +
> >>  + default:
> >>  + return -EINVAL;
> >>  + }
> >>  +
> >>  + return 0;
> >>  +}
> >>  +
> >>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int i;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> >>  +
> >>  + if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> >>  + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> >>  + state->crtc_w, state->crtc_h);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + DRM_DEBUG_DRIVER("Updating blender size\n");
> >>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> >>  + regmap_write(mixer->regs,
> >>  + SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + DRM_DEBUG_DRIVER("Updating channel size\n");
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + }
> >>  +
> >>  + /* Set the line width */
> >>  + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> >>  + fb->pitches[0]);
> >>  +
> >>  + /* Set height and width */
> >>  + DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> >>  + state->crtc_w, state->crtc_h);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> >>  +
> >>  + /* Set base coordinates */
> >>  + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> >>  + state->crtc_x, state->crtc_y);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> >>  + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> >>  +
> >>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + bool interlaced = false;
> >>  + u32 val;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int ret;
> >>  +
> >>  + if (plane->state->crtc)
> >>  + interlaced = plane->state->crtc->state->adjusted_mode.flags
> >>  + & DRM_MODE_FLAG_INTERLACE;
> >>  +
> >>  + regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> >>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> >>  + interlaced ?
> >>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> >>  +
> >>  + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> >>  + interlaced ? "on" : "off");
> >>  +
> >>  + ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> >>  + &val);
> >>  + if (ret) {
> >>  + DRM_DEBUG_DRIVER("Invalid format\n");
> >>  + return ret;
> >>  + }
> >>  +
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> >>  +
> >>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + struct drm_gem_cma_object *gem;
> >>  + dma_addr_t paddr;
> >>  + uint32_t paddr_u32;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int bpp;
> >>  +
> >>  + /* Get the physical address of the buffer in memory */
> >>  + gem = drm_fb_cma_get_gem_obj(fb, 0);
> >>  +
> >>  + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> >>  +
> >>  + /* Compute the start of the displayed memory */
> >>  + bpp = fb->format->cpp[0];
> >>  + paddr = gem->paddr + fb->offsets[0];
> >>  + paddr += (state->src_x >> 16) * bpp;
> >>  + paddr += (state->src_y >> 16) * fb->pitches[0];
> >>  + paddr -= SUN8I_DRAM_OFFSET;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> >>  +
> >>  + paddr_u32 = (uint32_t) paddr;
> >>  +
> >>  + regmap_write(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> >>  + paddr_u32);
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> >>  +
> >>  +static struct regmap_config sun8i_mixer_regmap_config = {
> >>  + .reg_bits = 32,
> >>  + .val_bits = 32,
> >>  + .reg_stride = 4,
> >>  + .max_register = 0xbffc, /* guessed */
> >>  +};
> >>  +
> >>  +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> >>  + void *data)
> >>  +{
> >>  + struct platform_device *pdev = to_platform_device(dev);
> >>  + struct drm_device *drm = data;
> >>  + struct sun4i_drv *drv = drm->dev_private;
> >>  + struct sun8i_mixer *mixer;
> >>  + struct resource *res;
> >>  + void __iomem *regs;
> >>  + int i, ret;
> >>  +
> >>  + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> >>  + if (!mixer)
> >>  + return -ENOMEM;
> >>  + dev_set_drvdata(dev, mixer);
> >>  + drv->mixer = mixer;
> >>  +
> >>  + mixer->cfg = of_device_get_match_data(dev);
> >>  + if (!mixer->cfg)
> >>  + return -EINVAL;
> >>  +
> >>  + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>  + regs = devm_ioremap_resource(dev, res);
> >>  + if (IS_ERR(regs))
> >>  + return PTR_ERR(regs);
> >>  +
> >>  + mixer->regs = devm_regmap_init_mmio(dev, regs,
> >>  + &sun8i_mixer_regmap_config);
> >>  + if (IS_ERR(mixer->regs)) {
> >>  + dev_err(dev, "Couldn't create the mixer regmap\n");
> >>  + return PTR_ERR(mixer->regs);
> >>  + }
> >>  +
> >>  + mixer->reset = devm_reset_control_get(dev, NULL);
> >>  + if (IS_ERR(mixer->reset)) {
> >>  + dev_err(dev, "Couldn't get our reset line\n");
> >>  + return PTR_ERR(mixer->reset);
> >>  + }
> >>  +
> >>  + ret = reset_control_deassert(mixer->reset);
> >>  + if (ret) {
> >>  + dev_err(dev, "Couldn't deassert our reset line\n");
> >>  + return ret;
> >>  + }
> >>  +
> >>  + mixer->bus_clk = devm_clk_get(dev, "bus");
> >>  + if (IS_ERR(mixer->bus_clk)) {
> >>  + dev_err(dev, "Couldn't get the mixer bus clock\n");
> >>  + ret = PTR_ERR(mixer->bus_clk);
> >>  + goto err_assert_reset;
> >>  + }
> >>  + clk_prepare_enable(mixer->bus_clk);
> >>  +
> >>  + mixer->mod_clk = devm_clk_get(dev, "mod");
> >>  + if (IS_ERR(mixer->mod_clk)) {
> >>  + dev_err(dev, "Couldn't get the mixer module clock\n");
> >>  + ret = PTR_ERR(mixer->mod_clk);
> >>  + goto err_disable_bus_clk;
> >>  + }
> >>  + clk_prepare_enable(mixer->mod_clk);
> >
> > Supporting runtime_pm would be better.
> 
> But I think it's at least not support yet for DE1 backend...

This is true, but unrelated.

> >>  + /* Reset the registers */
> >>  + for (i = 0x0; i < 0x20000; i += 4)
> >>  + regmap_write(mixer->regs, i, 0);
> >
> > Do you still need to reset it? Isn't the reset line enough?
> 
> Nope, some strange data lies in the DE2 space.
> 
> Here's a reg dump of a running DE2 's channel 2 on V3s:
> => md 01104000
> 01104000: ff000403 010f01df 00000000 00000780    ................
> 01104010: 03f80000 00000000 00000000 00000000    ................
> 01104020: 00000000 00000000 00000000 00000000    ................
> 01104030: 00000000 00000000 00000000 00000000    ................
> 01104040: 00000000 00000000 00000000 00000000    ................
> 01104050: 00000000 00000000 00000000 00000000    ................
> 01104060: 00000000 00000000 00000000 00000000    ................
> 01104070: 00000000 00000000 00000000 00000000    ................
> 01104080: 00000000 00000000 010f01df bbe4d3b0    ................
> 01104090: 54daaf98 13835927 a1479b58 8396b8ad    ...T'Y..X.G.....
> 011040a0: 07d02ede a39a18da 87d88aba a2d23cf6    .............<..
> 011040b0: e8bfa8f7 2c8d2b7c f8bbeb3e 98013b75    ....|+.,>...u;..
> 011040c0: 7c186f48 4ddcdbde b658caf8 76b770d6    Ho.|...M..X..p.v
> 011040d0: b9a620ef fe215cc1 edd6c4b3 c5f7a66c    . ...\!.....l...
> 011040e0: 0d1ff6d3 956ca9e8 7f51f80a ad9a184a    ......l...Q.J...
> 011040f0: ff23e428 772d8d14 f4c03077 8bf495ca    (.#...-ww0......
> 
> (P.S. only first 0x88 bytes are used in a UI channel, so the following is
> not reseted by U-Boot DE2 driver and is kept the original after reset line
> deasserting)

is it causing any issues? This is not unusual to have !0 default
values out of reset, and this shouldn't cause any troubles.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:54       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 22:54 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, David Airlie,
	linux-sunxi, linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
	linux-clk, linux-arm-kernel


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

Hi Icenowy,

(Please fix your mailer, its quotation is broken and mangles all the
indentation)

On Thu, Feb 23, 2017 at 04:28:42AM +0800, Icenowy Zheng wrote:
> >>  @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
> >>
> >>           return layers;
> >>   }
> >>  +
> >>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> >
> > And store this (and sun4i_layers_init) in an structure holding the
> > function pointers for those.
> 
> How should I do it?
> If I create a ops struct, where should I reference it?

I'm not sure I get your question. You can create that structure, fill
a field in the sun4i_drv structure at bind time, and call those
function pointers directly where you need to.

> >>  +{
> >>  + struct sun4i_layer **layers;
> >>  + int i;
> >>  +
> >>  + layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> >>  + sizeof(**layers), GFP_KERNEL);
> >>  + if (!layers)
> >>  + return ERR_PTR(-ENOMEM);
> >>  +
> >>  + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> >>  + const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> >>  + struct sun4i_layer *layer = layers[i];
> >>  +
> >>  + layer = sun4i_layer_init_one(drm, plane);
> >>  + if (IS_ERR(layer)) {
> >>  + dev_err(drm->dev, "Couldn't initialize %s plane\n",
> >>  + i ? "overlay" : "primary");
> >>  + return ERR_CAST(layer);
> >>  + };
> >>  +
> >>  + layer->id = i;
> >>  + };
> >>  +
> >>  + return layers;
> >>  +}
> >>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  index a2f65d7a3f4e..f7b9e5daea50 100644
> >>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >>  @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
> >>   }
> >>
> >>   struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> >>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> >>
> >>   #endif /* _SUN4I_LAYER_H_ */
> >>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >>  new file mode 100644
> >>  index 000000000000..9427b57240d3
> >>  --- /dev/null
> >>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >>  @@ -0,0 +1,417 @@
> >>  +/*
> >>  + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> >>  + *
> >>  + * Based on sun4i_backend.c, which is:
> >>  + * Copyright (C) 2015 Free Electrons
> >>  + * Copyright (C) 2015 NextThing Co
> >>  + *
> >>  + * This program is free software; you can redistribute it and/or
> >>  + * modify it under the terms of the GNU General Public License as
> >>  + * published by the Free Software Foundation; either version 2 of
> >>  + * the License, or (at your option) any later version.
> >>  + */
> >>  +
> >>  +#include <drm/drmP.h>
> >>  +#include <drm/drm_atomic_helper.h>
> >>  +#include <drm/drm_crtc.h>
> >>  +#include <drm/drm_crtc_helper.h>
> >>  +#include <drm/drm_fb_cma_helper.h>
> >>  +#include <drm/drm_gem_cma_helper.h>
> >>  +#include <drm/drm_plane_helper.h>
> >>  +
> >>  +#include <linux/component.h>
> >>  +#include <linux/reset.h>
> >>  +#include <linux/of_device.h>
> >>  +
> >>  +#include "sun8i_mixer.h"
> >>  +#include "sun4i_drv.h"
> >>  +
> >>  +#define SUN8I_DRAM_OFFSET 0x40000000
> >
> > PHYS_OFFSET?
> 
> Any name is OK.

What I meant is that there is already a variable holding that value
that is PHYS_OFFSET.

> (P.S. this seems also needed for some DE1s)

Did you encounter any issue on it?

> >>  +#if defined CONFIG_DRM_SUN4I_DE2
> >
> > That ifdef should be in the header
> 
> So the file only compile if this option is enabled?

Yes.

> And if this option is disabled, inlined null stubs should be
> made in header?

Yes.

> >>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> >>  + int layer, bool enable)
> >>  +{
> >>  + u32 val;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> >>  +
> >>  + if (enable)
> >>  + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> >>  + else
> >>  + val = 0;
> >
> > So you only support the UI channel?
> >
> > Why do you expose several planes then?
> 
> Currently I didn't find any way to enable more than one channel
> at the same time, so only the first UI channel is used.
> 
> After more knowledges are gained for DE2 mixers we can implement
> more functions (for example, Jernejsk have already discovered how
> to do color space correlation in DE2 for TVE).

Then please expose only the primary plane. You also expose an overlay
here that is not functional from what you tell me.

> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> >>  +
> >>  + /* Set the alpha configuration */
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> >
> > This should be in a property
> 
> What property?

Alpha?

> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> >>  +
> >>  +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> >>  + u32 format, u32 *mode)
> >>  +{
> >>  + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> >>  + (format == DRM_FORMAT_ARGB8888))
> >>  + format = DRM_FORMAT_XRGB8888;
> >
> > Do you actually have that issue.
> 
> Yes, it really do, at least screen go black when I set this to ARGB8888 in
> U-Boot. (U-Boot is a good experiement area ;-) )

Ok. And you have some color when you set the background to some colour ?

> >>  + switch (format) {
> >>  + case DRM_FORMAT_ARGB8888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> >>  + break;
> >>  +
> >>  + case DRM_FORMAT_XRGB8888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> >>  + break;
> >>  +
> >>  + case DRM_FORMAT_RGB888:
> >>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> >>  + break;
> >>  +
> >>  + default:
> >>  + return -EINVAL;
> >>  + }
> >>  +
> >>  + return 0;
> >>  +}
> >>  +
> >>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int i;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> >>  +
> >>  + if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> >>  + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> >>  + state->crtc_w, state->crtc_h);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + DRM_DEBUG_DRIVER("Updating blender size\n");
> >>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> >>  + regmap_write(mixer->regs,
> >>  + SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + DRM_DEBUG_DRIVER("Updating channel size\n");
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w,
> >>  + state->crtc_h));
> >>  + }
> >>  +
> >>  + /* Set the line width */
> >>  + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> >>  + fb->pitches[0]);
> >>  +
> >>  + /* Set height and width */
> >>  + DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> >>  + state->crtc_w, state->crtc_h);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> >>  + SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> >>  +
> >>  + /* Set base coordinates */
> >>  + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> >>  + state->crtc_x, state->crtc_y);
> >>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> >>  + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> >>  +
> >>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + bool interlaced = false;
> >>  + u32 val;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int ret;
> >>  +
> >>  + if (plane->state->crtc)
> >>  + interlaced = plane->state->crtc->state->adjusted_mode.flags
> >>  + & DRM_MODE_FLAG_INTERLACE;
> >>  +
> >>  + regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> >>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> >>  + interlaced ?
> >>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> >>  +
> >>  + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> >>  + interlaced ? "on" : "off");
> >>  +
> >>  + ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> >>  + &val);
> >>  + if (ret) {
> >>  + DRM_DEBUG_DRIVER("Invalid format\n");
> >>  + return ret;
> >>  + }
> >>  +
> >>  + regmap_update_bits(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> >>  +
> >>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> >>  + int layer, struct drm_plane *plane)
> >>  +{
> >>  + struct drm_plane_state *state = plane->state;
> >>  + struct drm_framebuffer *fb = state->fb;
> >>  + struct drm_gem_cma_object *gem;
> >>  + dma_addr_t paddr;
> >>  + uint32_t paddr_u32;
> >>  + /* Currently the first UI channel is used */
> >>  + int chan = mixer->cfg->vi_num;
> >>  + int bpp;
> >>  +
> >>  + /* Get the physical address of the buffer in memory */
> >>  + gem = drm_fb_cma_get_gem_obj(fb, 0);
> >>  +
> >>  + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> >>  +
> >>  + /* Compute the start of the displayed memory */
> >>  + bpp = fb->format->cpp[0];
> >>  + paddr = gem->paddr + fb->offsets[0];
> >>  + paddr += (state->src_x >> 16) * bpp;
> >>  + paddr += (state->src_y >> 16) * fb->pitches[0];
> >>  + paddr -= SUN8I_DRAM_OFFSET;
> >>  +
> >>  + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> >>  +
> >>  + paddr_u32 = (uint32_t) paddr;
> >>  +
> >>  + regmap_write(mixer->regs,
> >>  + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> >>  + paddr_u32);
> >>  +
> >>  + return 0;
> >>  +}
> >>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> >>  +
> >>  +static struct regmap_config sun8i_mixer_regmap_config = {
> >>  + .reg_bits = 32,
> >>  + .val_bits = 32,
> >>  + .reg_stride = 4,
> >>  + .max_register = 0xbffc, /* guessed */
> >>  +};
> >>  +
> >>  +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> >>  + void *data)
> >>  +{
> >>  + struct platform_device *pdev = to_platform_device(dev);
> >>  + struct drm_device *drm = data;
> >>  + struct sun4i_drv *drv = drm->dev_private;
> >>  + struct sun8i_mixer *mixer;
> >>  + struct resource *res;
> >>  + void __iomem *regs;
> >>  + int i, ret;
> >>  +
> >>  + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> >>  + if (!mixer)
> >>  + return -ENOMEM;
> >>  + dev_set_drvdata(dev, mixer);
> >>  + drv->mixer = mixer;
> >>  +
> >>  + mixer->cfg = of_device_get_match_data(dev);
> >>  + if (!mixer->cfg)
> >>  + return -EINVAL;
> >>  +
> >>  + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>  + regs = devm_ioremap_resource(dev, res);
> >>  + if (IS_ERR(regs))
> >>  + return PTR_ERR(regs);
> >>  +
> >>  + mixer->regs = devm_regmap_init_mmio(dev, regs,
> >>  + &sun8i_mixer_regmap_config);
> >>  + if (IS_ERR(mixer->regs)) {
> >>  + dev_err(dev, "Couldn't create the mixer regmap\n");
> >>  + return PTR_ERR(mixer->regs);
> >>  + }
> >>  +
> >>  + mixer->reset = devm_reset_control_get(dev, NULL);
> >>  + if (IS_ERR(mixer->reset)) {
> >>  + dev_err(dev, "Couldn't get our reset line\n");
> >>  + return PTR_ERR(mixer->reset);
> >>  + }
> >>  +
> >>  + ret = reset_control_deassert(mixer->reset);
> >>  + if (ret) {
> >>  + dev_err(dev, "Couldn't deassert our reset line\n");
> >>  + return ret;
> >>  + }
> >>  +
> >>  + mixer->bus_clk = devm_clk_get(dev, "bus");
> >>  + if (IS_ERR(mixer->bus_clk)) {
> >>  + dev_err(dev, "Couldn't get the mixer bus clock\n");
> >>  + ret = PTR_ERR(mixer->bus_clk);
> >>  + goto err_assert_reset;
> >>  + }
> >>  + clk_prepare_enable(mixer->bus_clk);
> >>  +
> >>  + mixer->mod_clk = devm_clk_get(dev, "mod");
> >>  + if (IS_ERR(mixer->mod_clk)) {
> >>  + dev_err(dev, "Couldn't get the mixer module clock\n");
> >>  + ret = PTR_ERR(mixer->mod_clk);
> >>  + goto err_disable_bus_clk;
> >>  + }
> >>  + clk_prepare_enable(mixer->mod_clk);
> >
> > Supporting runtime_pm would be better.
> 
> But I think it's at least not support yet for DE1 backend...

This is true, but unrelated.

> >>  + /* Reset the registers */
> >>  + for (i = 0x0; i < 0x20000; i += 4)
> >>  + regmap_write(mixer->regs, i, 0);
> >
> > Do you still need to reset it? Isn't the reset line enough?
> 
> Nope, some strange data lies in the DE2 space.
> 
> Here's a reg dump of a running DE2 's channel 2 on V3s:
> => md 01104000
> 01104000: ff000403 010f01df 00000000 00000780    ................
> 01104010: 03f80000 00000000 00000000 00000000    ................
> 01104020: 00000000 00000000 00000000 00000000    ................
> 01104030: 00000000 00000000 00000000 00000000    ................
> 01104040: 00000000 00000000 00000000 00000000    ................
> 01104050: 00000000 00000000 00000000 00000000    ................
> 01104060: 00000000 00000000 00000000 00000000    ................
> 01104070: 00000000 00000000 00000000 00000000    ................
> 01104080: 00000000 00000000 010f01df bbe4d3b0    ................
> 01104090: 54daaf98 13835927 a1479b58 8396b8ad    ...T'Y..X.G.....
> 011040a0: 07d02ede a39a18da 87d88aba a2d23cf6    .............<..
> 011040b0: e8bfa8f7 2c8d2b7c f8bbeb3e 98013b75    ....|+.,>...u;..
> 011040c0: 7c186f48 4ddcdbde b658caf8 76b770d6    Ho.|...M..X..p.v
> 011040d0: b9a620ef fe215cc1 edd6c4b3 c5f7a66c    . ...\!.....l...
> 011040e0: 0d1ff6d3 956ca9e8 7f51f80a ad9a184a    ......l...Q.J...
> 011040f0: ff23e428 772d8d14 f4c03077 8bf495ca    (.#...-ww0......
> 
> (P.S. only first 0x88 bytes are used in a UI channel, so the following is
> not reseted by U-Boot DE2 driver and is kept the original after reset line
> deasserting)

is it causing any issues? This is not unusual to have !0 default
values out of reset, and this shouldn't cause any troubles.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 22:54       ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 22:54 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Icenowy,

(Please fix your mailer, its quotation is broken and mangles all the
indentation)

On Thu, Feb 23, 2017 at 04:28:42AM +0800, Icenowy Zheng wrote:
> >> ?@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
> >>
> >> ??????????return layers;
> >> ??}
> >> ?+
> >> ?+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
> >
> > And store this (and sun4i_layers_init) in an structure holding the
> > function pointers for those.
> 
> How should I do it?
> If I create a ops struct, where should I reference it?

I'm not sure I get your question. You can create that structure, fill
a field in the sun4i_drv structure at bind time, and call those
function pointers directly where you need to.

> >> ?+{
> >> ?+ struct sun4i_layer **layers;
> >> ?+ int i;
> >> ?+
> >> ?+ layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> >> ?+ sizeof(**layers), GFP_KERNEL);
> >> ?+ if (!layers)
> >> ?+ return ERR_PTR(-ENOMEM);
> >> ?+
> >> ?+ for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> >> ?+ const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> >> ?+ struct sun4i_layer *layer = layers[i];
> >> ?+
> >> ?+ layer = sun4i_layer_init_one(drm, plane);
> >> ?+ if (IS_ERR(layer)) {
> >> ?+ dev_err(drm->dev, "Couldn't initialize %s plane\n",
> >> ?+ i ? "overlay" : "primary");
> >> ?+ return ERR_CAST(layer);
> >> ?+ };
> >> ?+
> >> ?+ layer->id = i;
> >> ?+ };
> >> ?+
> >> ?+ return layers;
> >> ?+}
> >> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >> ?index a2f65d7a3f4e..f7b9e5daea50 100644
> >> ?--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> >> ?+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> >> ?@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
> >> ??}
> >>
> >> ??struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> >> ?+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
> >>
> >> ??#endif /* _SUN4I_LAYER_H_ */
> >> ?diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> ?new file mode 100644
> >> ?index 000000000000..9427b57240d3
> >> ?--- /dev/null
> >> ?+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> >> ?@@ -0,0 +1,417 @@
> >> ?+/*
> >> ?+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> >> ?+ *
> >> ?+ * Based on sun4i_backend.c, which is:
> >> ?+ * Copyright (C) 2015 Free Electrons
> >> ?+ * Copyright (C) 2015 NextThing Co
> >> ?+ *
> >> ?+ * This program is free software; you can redistribute it and/or
> >> ?+ * modify it under the terms of the GNU General Public License as
> >> ?+ * published by the Free Software Foundation; either version 2 of
> >> ?+ * the License, or (at your option) any later version.
> >> ?+ */
> >> ?+
> >> ?+#include <drm/drmP.h>
> >> ?+#include <drm/drm_atomic_helper.h>
> >> ?+#include <drm/drm_crtc.h>
> >> ?+#include <drm/drm_crtc_helper.h>
> >> ?+#include <drm/drm_fb_cma_helper.h>
> >> ?+#include <drm/drm_gem_cma_helper.h>
> >> ?+#include <drm/drm_plane_helper.h>
> >> ?+
> >> ?+#include <linux/component.h>
> >> ?+#include <linux/reset.h>
> >> ?+#include <linux/of_device.h>
> >> ?+
> >> ?+#include "sun8i_mixer.h"
> >> ?+#include "sun4i_drv.h"
> >> ?+
> >> ?+#define SUN8I_DRAM_OFFSET 0x40000000
> >
> > PHYS_OFFSET?
> 
> Any name is OK.

What I meant is that there is already a variable holding that value
that is PHYS_OFFSET.

> (P.S. this seems also needed for some DE1s)

Did you encounter any issue on it?

> >> ?+#if defined CONFIG_DRM_SUN4I_DE2
> >
> > That ifdef should be in the header
> 
> So the file only compile if this option is enabled?

Yes.

> And if this option is disabled, inlined null stubs should be
> made in header?

Yes.

> >> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> >> ?+ int layer, bool enable)
> >> ?+{
> >> ?+ u32 val;
> >> ?+ /* Currently the first UI channel is used */
> >> ?+ int chan = mixer->cfg->vi_num;
> >> ?+
> >> ?+ DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> >> ?+
> >> ?+ if (enable)
> >> ?+ val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> >> ?+ else
> >> ?+ val = 0;
> >
> > So you only support the UI channel?
> >
> > Why do you expose several planes then?
> 
> Currently I didn't find any way to enable more than one channel
> at the same time, so only the first UI channel is used.
> 
> After more knowledges are gained for DE2 mixers we can implement
> more functions (for example, Jernejsk have already discovered how
> to do color space correlation in DE2 for TVE).

Then please expose only the primary plane. You also expose an overlay
here that is not functional from what you tell me.

> >> ?+ regmap_update_bits(mixer->regs,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> >> ?+
> >> ?+ /* Set the alpha configuration */
> >> ?+ regmap_update_bits(mixer->regs,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> >> ?+ regmap_update_bits(mixer->regs,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
> >
> > This should be in a property
> 
> What property?

Alpha?

> >> ?+}
> >> ?+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> >> ?+
> >> ?+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> >> ?+ u32 format, u32 *mode)
> >> ?+{
> >> ?+ if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> >> ?+ (format == DRM_FORMAT_ARGB8888))
> >> ?+ format = DRM_FORMAT_XRGB8888;
> >
> > Do you actually have that issue.
> 
> Yes, it really do, at least screen go black when I set this to ARGB8888 in
> U-Boot. (U-Boot is a good experiement area ;-) )

Ok. And you have some color when you set the background to some colour ?

> >> ?+ switch (format) {
> >> ?+ case DRM_FORMAT_ARGB8888:
> >> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> >> ?+ break;
> >> ?+
> >> ?+ case DRM_FORMAT_XRGB8888:
> >> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> >> ?+ break;
> >> ?+
> >> ?+ case DRM_FORMAT_RGB888:
> >> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> >> ?+ break;
> >> ?+
> >> ?+ default:
> >> ?+ return -EINVAL;
> >> ?+ }
> >> ?+
> >> ?+ return 0;
> >> ?+}
> >> ?+
> >> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> >> ?+ int layer, struct drm_plane *plane)
> >> ?+{
> >> ?+ struct drm_plane_state *state = plane->state;
> >> ?+ struct drm_framebuffer *fb = state->fb;
> >> ?+ /* Currently the first UI channel is used */
> >> ?+ int chan = mixer->cfg->vi_num;
> >> ?+ int i;
> >> ?+
> >> ?+ DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> >> ?+
> >> ?+ if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> >> ?+ DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> >> ?+ state->crtc_w, state->crtc_h);
> >> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> >> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
> >> ?+ state->crtc_h));
> >> ?+ DRM_DEBUG_DRIVER("Updating blender size\n");
> >> ?+ for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> >> ?+ regmap_write(mixer->regs,
> >> ?+ SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> >> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
> >> ?+ state->crtc_h));
> >> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> >> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
> >> ?+ state->crtc_h));
> >> ?+ DRM_DEBUG_DRIVER("Updating channel size\n");
> >> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> >> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
> >> ?+ state->crtc_h));
> >> ?+ }
> >> ?+
> >> ?+ /* Set the line width */
> >> ?+ DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> >> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> >> ?+ fb->pitches[0]);
> >> ?+
> >> ?+ /* Set height and width */
> >> ?+ DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> >> ?+ state->crtc_w, state->crtc_h);
> >> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> >> ?+ SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> >> ?+
> >> ?+ /* Set base coordinates */
> >> ?+ DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> >> ?+ state->crtc_x, state->crtc_y);
> >> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> >> ?+ SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> >> ?+
> >> ?+ return 0;
> >> ?+}
> >> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> >> ?+
> >> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> >> ?+ int layer, struct drm_plane *plane)
> >> ?+{
> >> ?+ struct drm_plane_state *state = plane->state;
> >> ?+ struct drm_framebuffer *fb = state->fb;
> >> ?+ bool interlaced = false;
> >> ?+ u32 val;
> >> ?+ /* Currently the first UI channel is used */
> >> ?+ int chan = mixer->cfg->vi_num;
> >> ?+ int ret;
> >> ?+
> >> ?+ if (plane->state->crtc)
> >> ?+ interlaced = plane->state->crtc->state->adjusted_mode.flags
> >> ?+ & DRM_MODE_FLAG_INTERLACE;
> >> ?+
> >> ?+ regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> >> ?+ SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> >> ?+ interlaced ?
> >> ?+ SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> >> ?+
> >> ?+ DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> >> ?+ interlaced ? "on" : "off");
> >> ?+
> >> ?+ ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> >> ?+ &val);
> >> ?+ if (ret) {
> >> ?+ DRM_DEBUG_DRIVER("Invalid format\n");
> >> ?+ return ret;
> >> ?+ }
> >> ?+
> >> ?+ regmap_update_bits(mixer->regs,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> >> ?+
> >> ?+ return 0;
> >> ?+}
> >> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> >> ?+
> >> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> >> ?+ int layer, struct drm_plane *plane)
> >> ?+{
> >> ?+ struct drm_plane_state *state = plane->state;
> >> ?+ struct drm_framebuffer *fb = state->fb;
> >> ?+ struct drm_gem_cma_object *gem;
> >> ?+ dma_addr_t paddr;
> >> ?+ uint32_t paddr_u32;
> >> ?+ /* Currently the first UI channel is used */
> >> ?+ int chan = mixer->cfg->vi_num;
> >> ?+ int bpp;
> >> ?+
> >> ?+ /* Get the physical address of the buffer in memory */
> >> ?+ gem = drm_fb_cma_get_gem_obj(fb, 0);
> >> ?+
> >> ?+ DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> >> ?+
> >> ?+ /* Compute the start of the displayed memory */
> >> ?+ bpp = fb->format->cpp[0];
> >> ?+ paddr = gem->paddr + fb->offsets[0];
> >> ?+ paddr += (state->src_x >> 16) * bpp;
> >> ?+ paddr += (state->src_y >> 16) * fb->pitches[0];
> >> ?+ paddr -= SUN8I_DRAM_OFFSET;
> >> ?+
> >> ?+ DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> >> ?+
> >> ?+ paddr_u32 = (uint32_t) paddr;
> >> ?+
> >> ?+ regmap_write(mixer->regs,
> >> ?+ SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> >> ?+ paddr_u32);
> >> ?+
> >> ?+ return 0;
> >> ?+}
> >> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> >> ?+
> >> ?+static struct regmap_config sun8i_mixer_regmap_config = {
> >> ?+ .reg_bits = 32,
> >> ?+ .val_bits = 32,
> >> ?+ .reg_stride = 4,
> >> ?+ .max_register = 0xbffc, /* guessed */
> >> ?+};
> >> ?+
> >> ?+static int sun8i_mixer_bind(struct device *dev, struct device *master,
> >> ?+ void *data)
> >> ?+{
> >> ?+ struct platform_device *pdev = to_platform_device(dev);
> >> ?+ struct drm_device *drm = data;
> >> ?+ struct sun4i_drv *drv = drm->dev_private;
> >> ?+ struct sun8i_mixer *mixer;
> >> ?+ struct resource *res;
> >> ?+ void __iomem *regs;
> >> ?+ int i, ret;
> >> ?+
> >> ?+ mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> >> ?+ if (!mixer)
> >> ?+ return -ENOMEM;
> >> ?+ dev_set_drvdata(dev, mixer);
> >> ?+ drv->mixer = mixer;
> >> ?+
> >> ?+ mixer->cfg = of_device_get_match_data(dev);
> >> ?+ if (!mixer->cfg)
> >> ?+ return -EINVAL;
> >> ?+
> >> ?+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> ?+ regs = devm_ioremap_resource(dev, res);
> >> ?+ if (IS_ERR(regs))
> >> ?+ return PTR_ERR(regs);
> >> ?+
> >> ?+ mixer->regs = devm_regmap_init_mmio(dev, regs,
> >> ?+ &sun8i_mixer_regmap_config);
> >> ?+ if (IS_ERR(mixer->regs)) {
> >> ?+ dev_err(dev, "Couldn't create the mixer regmap\n");
> >> ?+ return PTR_ERR(mixer->regs);
> >> ?+ }
> >> ?+
> >> ?+ mixer->reset = devm_reset_control_get(dev, NULL);
> >> ?+ if (IS_ERR(mixer->reset)) {
> >> ?+ dev_err(dev, "Couldn't get our reset line\n");
> >> ?+ return PTR_ERR(mixer->reset);
> >> ?+ }
> >> ?+
> >> ?+ ret = reset_control_deassert(mixer->reset);
> >> ?+ if (ret) {
> >> ?+ dev_err(dev, "Couldn't deassert our reset line\n");
> >> ?+ return ret;
> >> ?+ }
> >> ?+
> >> ?+ mixer->bus_clk = devm_clk_get(dev, "bus");
> >> ?+ if (IS_ERR(mixer->bus_clk)) {
> >> ?+ dev_err(dev, "Couldn't get the mixer bus clock\n");
> >> ?+ ret = PTR_ERR(mixer->bus_clk);
> >> ?+ goto err_assert_reset;
> >> ?+ }
> >> ?+ clk_prepare_enable(mixer->bus_clk);
> >> ?+
> >> ?+ mixer->mod_clk = devm_clk_get(dev, "mod");
> >> ?+ if (IS_ERR(mixer->mod_clk)) {
> >> ?+ dev_err(dev, "Couldn't get the mixer module clock\n");
> >> ?+ ret = PTR_ERR(mixer->mod_clk);
> >> ?+ goto err_disable_bus_clk;
> >> ?+ }
> >> ?+ clk_prepare_enable(mixer->mod_clk);
> >
> > Supporting runtime_pm would be better.
> 
> But I think it's at least not support yet for DE1 backend...

This is true, but unrelated.

> >> ?+ /* Reset the registers */
> >> ?+ for (i = 0x0; i < 0x20000; i += 4)
> >> ?+ regmap_write(mixer->regs, i, 0);
> >
> > Do you still need to reset it? Isn't the reset line enough?
> 
> Nope, some strange data lies in the DE2 space.
> 
> Here's a reg dump of a running DE2 's channel 2 on V3s:
> => md 01104000
> 01104000: ff000403 010f01df 00000000 00000780    ................
> 01104010: 03f80000 00000000 00000000 00000000    ................
> 01104020: 00000000 00000000 00000000 00000000    ................
> 01104030: 00000000 00000000 00000000 00000000    ................
> 01104040: 00000000 00000000 00000000 00000000    ................
> 01104050: 00000000 00000000 00000000 00000000    ................
> 01104060: 00000000 00000000 00000000 00000000    ................
> 01104070: 00000000 00000000 00000000 00000000    ................
> 01104080: 00000000 00000000 010f01df bbe4d3b0    ................
> 01104090: 54daaf98 13835927 a1479b58 8396b8ad    ...T'Y..X.G.....
> 011040a0: 07d02ede a39a18da 87d88aba a2d23cf6    .............<..
> 011040b0: e8bfa8f7 2c8d2b7c f8bbeb3e 98013b75    ....|+.,>...u;..
> 011040c0: 7c186f48 4ddcdbde b658caf8 76b770d6    Ho.|...M..X..p.v
> 011040d0: b9a620ef fe215cc1 edd6c4b3 c5f7a66c    . ...\!.....l...
> 011040e0: 0d1ff6d3 956ca9e8 7f51f80a ad9a184a    ......l...Q.J...
> 011040f0: ff23e428 772d8d14 f4c03077 8bf495ca    (.#...-ww0......
> 
> (P.S. only first 0x88 bytes are used in a UI channel, so the following is
> not reseted by U-Boot DE2 driver and is kept the original after reset line
> deasserting)

is it causing any issues? This is not unusual to have !0 default
values out of reset, and this shouldn't cause any troubles.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170222/fdc5a10b/attachment-0001.sig>

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 20:09   ` Maxime Ripard
  (?)
@ 2017-02-22 20:28     ` Icenowy Zheng
  -1 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 20:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Rob Herring, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw



23.02.2017, 04:09, "Maxime Ripard" <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>:
> Hi,
>
> On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
>>  Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>>  in a new "Display Engine" (mixers instead of old backends and
>>  frontends).
>>
>>  Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>>  Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  ---
>>   drivers/gpu/drm/sun4i/Kconfig | 8 +
>>   drivers/gpu/drm/sun4i/Makefile | 1 +
>>   drivers/gpu/drm/sun4i/sun4i_crtc.c | 6 +-
>>   drivers/gpu/drm/sun4i/sun4i_drv.c | 38 +++-
>>   drivers/gpu/drm/sun4i/sun4i_drv.h | 1 +
>>   drivers/gpu/drm/sun4i/sun4i_layer.c | 92 ++++++--
>>   drivers/gpu/drm/sun4i/sun4i_layer.h | 1 +
>>   drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>>   9 files changed, 674 insertions(+), 23 deletions(-)
>>   create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>>   create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
>>
>>  diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>>  index a4b357db8856..8df401fff145 100644
>>  --- a/drivers/gpu/drm/sun4i/Kconfig
>>  +++ b/drivers/gpu/drm/sun4i/Kconfig
>>  @@ -12,3 +12,11 @@ config DRM_SUN4I
>>             Choose this option if you have an Allwinner SoC with a
>>             Display Engine. If M is selected the module will be called
>>             sun4i-drm.
>>  +
>>  +config DRM_SUN4I_DE2
>>  + bool "Support Display Engine 2.0"
>>  + depends on DRM_SUN4I
>>  + default MACH_SUN8I
>>  + help
>>  + Choose this option if you have an Allwinner SoC with a
>>  + "Display Engine 2.0".
>>  diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>>  index d625a82a6e5f..890e6e50dfee 100644
>>  --- a/drivers/gpu/drm/sun4i/Makefile
>>  +++ b/drivers/gpu/drm/sun4i/Makefile
>>  @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>>
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
>>  +obj-$(CONFIG_DRM_SUN4I) += sun8i_mixer.o
>>   obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>  index 4a192210574f..4d2228454726 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>  @@ -25,6 +25,7 @@
>>   #include <video/videomode.h>
>>
>>   #include "sun4i_backend.h"
>>  +#include "sun8i_mixer.h"
>>   #include "sun4i_crtc.h"
>>   #include "sun4i_drv.h"
>>   #include "sun4i_tcon.h"
>>  @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
>>
>>           DRM_DEBUG_DRIVER("Committing plane changes\n");
>>
>>  - sun4i_backend_commit(drv->backend);
>>  + if (drv->backend)
>>  + sun4i_backend_commit(drv->backend);
>>  + else if (drv->mixer)
>>  + sun8i_mixer_commit(drv->mixer);
>>
>>           if (event) {
>>                   crtc->state->event = NULL;
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
>>  index 4ce665349f6b..58af38f5e833 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
>>  @@ -20,12 +20,17 @@
>>   #include <drm/drm_fb_helper.h>
>>   #include <drm/drm_of.h>
>>
>>  +#include <linux/of_device.h>
>>  +
>>   #include "sun4i_crtc.h"
>>   #include "sun4i_drv.h"
>>   #include "sun4i_framebuffer.h"
>>   #include "sun4i_layer.h"
>>   #include "sun4i_tcon.h"
>>
>>  +#define DE_VER_DE 0
>>  +#define DE_VER_DE2 1
>>  +
>>   static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
>>   {
>>           struct sun4i_drv *drv = drm->dev_private;
>>  @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>>   {
>>           struct drm_device *drm;
>>           struct sun4i_drv *drv;
>>  - int ret;
>>  + int ret, de_ver;
>>  +
>>  + de_ver = (int)of_device_get_match_data(dev);
>>
>>           drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>>           if (IS_ERR(drm))
>>  @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>>           }
>>
>>           /* Create our layers */
>>  - drv->layers = sun4i_layers_init(drm);
>>  + if (de_ver == DE_VER_DE2)
>>  + drv->layers = sun8i_layers_init(drm);
>>  + else
>>  + drv->layers = sun4i_layers_init(drm);
>>           if (IS_ERR(drv->layers)) {
>>                   dev_err(drm->dev, "Couldn't create the planes\n");
>>                   ret = PTR_ERR(drv->layers);
>>  @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>>   }
>>
>>   static const struct of_device_id sun4i_drv_of_table[] = {
>>  - { .compatible = "allwinner,sun5i-a13-display-engine" },
>>  - { .compatible = "allwinner,sun6i-a31-display-engine" },
>>  - { .compatible = "allwinner,sun6i-a31s-display-engine" },
>>  - { .compatible = "allwinner,sun8i-a33-display-engine" },
>>  + {
>>  + .compatible = "allwinner,sun5i-a13-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun6i-a31-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun6i-a31s-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun8i-a33-display-engine",
>>  + .data = (void *)DE_VER_DE,
>>  + },
>>  + {
>>  + .compatible = "allwinner,sun8i-v3s-display-engine",
>>  + .data = (void *)DE_VER_DE2,
>>  + },
>>           { }
>>   };
>>   MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
>>  index 597353eab728..cf1da95b85bd 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
>>  @@ -18,6 +18,7 @@
>>
>>   struct sun4i_drv {
>>           struct sun4i_backend *backend;
>>  + struct sun8i_mixer *mixer;
>>           struct sun4i_crtc *crtc;
>>           struct sun4i_tcon *tcon;
>>
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>  index 5d53c977bca5..6dcdac38ab69 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>  @@ -16,52 +16,66 @@
>>   #include <drm/drmP.h>
>>
>>   #include "sun4i_backend.h"
>>  +#include "sun8i_mixer.h"
>>   #include "sun4i_drv.h"
>>   #include "sun4i_layer.h"
>>
>>   struct sun4i_plane_desc {
>>                  enum drm_plane_type type;
>>  + /* Pipe is not used in sun8i-mixer */
>>                  u8 pipe;
>>                  const uint32_t *formats;
>>                  uint32_t nformats;
>>   };
>>
>>  -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
>>  +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>>                                               struct drm_plane_state *state)
>>   {
>>           return 0;
>>   }
>>
>>  -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
>>  +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>>                                                  struct drm_plane_state *old_state)
>>   {
>>           struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>>           struct sun4i_drv *drv = layer->drv;
>>           struct sun4i_backend *backend = drv->backend;
>>  + struct sun8i_mixer *mixer = drv->mixer;
>>
>>  - sun4i_backend_layer_enable(backend, layer->id, false);
>>  + if (backend)
>>  + sun4i_backend_layer_enable(backend, layer->id, false);
>>  + else if (mixer)
>>  + sun8i_mixer_layer_enable(mixer, layer->id, false);
>>   }
>
> I'm not sure it makes sense to reuse that part. You can just create a
> new plane driver entirely.
>
>>  -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
>>  +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>>                                                 struct drm_plane_state *old_state)
>>   {
>>           struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>>           struct sun4i_drv *drv = layer->drv;
>>           struct sun4i_backend *backend = drv->backend;
>>  -
>>  - sun4i_backend_update_layer_coord(backend, layer->id, plane);
>>  - sun4i_backend_update_layer_formats(backend, layer->id, plane);
>>  - sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>>  - sun4i_backend_layer_enable(backend, layer->id, true);
>>  + struct sun8i_mixer *mixer = drv->mixer;
>>  +
>>  + if (backend) {
>>  + sun4i_backend_update_layer_coord(backend, layer->id, plane);
>>  + sun4i_backend_update_layer_formats(backend, layer->id, plane);
>>  + sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>>  + sun4i_backend_layer_enable(backend, layer->id, true);
>>  + } else if (mixer) {
>>  + sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
>>  + sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
>>  + sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
>>  + sun8i_mixer_layer_enable(mixer, layer->id, true);
>>  + }
>>   }
>>
>>  -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
>>  - .atomic_check = sun4i_backend_layer_atomic_check,
>>  - .atomic_disable = sun4i_backend_layer_atomic_disable,
>>  - .atomic_update = sun4i_backend_layer_atomic_update,
>>  +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
>>  + .atomic_check = sun4i_layer_atomic_check,
>>  + .atomic_disable = sun4i_layer_atomic_disable,
>>  + .atomic_update = sun4i_layer_atomic_update,
>>   };
>>
>>  -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
>>  +static const struct drm_plane_funcs sun4i_layer_funcs = {
>>           .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>>           .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>>           .destroy = drm_plane_cleanup,
>>  @@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
>>           DRM_FORMAT_XRGB8888,
>>   };
>>
>>  +static const uint32_t sun8i_mixer_layer_formats[] = {
>>  + DRM_FORMAT_ARGB8888,
>>  + DRM_FORMAT_RGB888,
>>  + DRM_FORMAT_XRGB8888,
>>  +};
>>  +
>>   static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>>           {
>>                   .type = DRM_PLANE_TYPE_PRIMARY,
>>  @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>>           },
>>   };
>>
>>  +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
>>  + {
>>  + .type = DRM_PLANE_TYPE_PRIMARY,
>>  + .formats = sun8i_mixer_layer_formats,
>>  + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>>  + },
>>  + {
>>  + .type = DRM_PLANE_TYPE_OVERLAY,
>>  + .formats = sun8i_mixer_layer_formats,
>>  + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>>  + },
>>  +};
>>  +
>>   static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>>                                                   const struct sun4i_plane_desc *plane)
>>   {
>>  @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>>                   return ERR_PTR(-ENOMEM);
>>
>>           ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
>>  - &sun4i_backend_layer_funcs,
>>  + &sun4i_layer_funcs,
>>                                          plane->formats, plane->nformats,
>>                                          plane->type, NULL);
>>           if (ret) {
>>  @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>>           }
>>
>>           drm_plane_helper_add(&layer->plane,
>>  - &sun4i_backend_layer_helper_funcs);
>>  + &sun4i_layer_helper_funcs);
>>           layer->drv = drv;
>>
>>           if (plane->type == DRM_PLANE_TYPE_PRIMARY)
>>  @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
>>
>>           return layers;
>>   }
>>  +
>>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
>
> And store this (and sun4i_layers_init) in an structure holding the
> function pointers for those.

How should I do it?
If I create a ops struct, where should I reference it?

>
>>  +{
>>  + struct sun4i_layer **layers;
>>  + int i;
>>  +
>>  + layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
>>  + sizeof(**layers), GFP_KERNEL);
>>  + if (!layers)
>>  + return ERR_PTR(-ENOMEM);
>>  +
>>  + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>>  + const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
>>  + struct sun4i_layer *layer = layers[i];
>>  +
>>  + layer = sun4i_layer_init_one(drm, plane);
>>  + if (IS_ERR(layer)) {
>>  + dev_err(drm->dev, "Couldn't initialize %s plane\n",
>>  + i ? "overlay" : "primary");
>>  + return ERR_CAST(layer);
>>  + };
>>  +
>>  + layer->id = i;
>>  + };
>>  +
>>  + return layers;
>>  +}
>>  diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>  index a2f65d7a3f4e..f7b9e5daea50 100644
>>  --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>  +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>  @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>>   }
>>
>>   struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
>>  +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>>
>>   #endif /* _SUN4I_LAYER_H_ */
>>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>  new file mode 100644
>>  index 000000000000..9427b57240d3
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>  @@ -0,0 +1,417 @@
>>  +/*
>>  + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  + *
>>  + * Based on sun4i_backend.c, which is:
>>  + * Copyright (C) 2015 Free Electrons
>>  + * Copyright (C) 2015 NextThing Co
>>  + *
>>  + * This program is free software; you can redistribute it and/or
>>  + * modify it under the terms of the GNU General Public License as
>>  + * published by the Free Software Foundation; either version 2 of
>>  + * the License, or (at your option) any later version.
>>  + */
>>  +
>>  +#include <drm/drmP.h>
>>  +#include <drm/drm_atomic_helper.h>
>>  +#include <drm/drm_crtc.h>
>>  +#include <drm/drm_crtc_helper.h>
>>  +#include <drm/drm_fb_cma_helper.h>
>>  +#include <drm/drm_gem_cma_helper.h>
>>  +#include <drm/drm_plane_helper.h>
>>  +
>>  +#include <linux/component.h>
>>  +#include <linux/reset.h>
>>  +#include <linux/of_device.h>
>>  +
>>  +#include "sun8i_mixer.h"
>>  +#include "sun4i_drv.h"
>>  +
>>  +#define SUN8I_DRAM_OFFSET 0x40000000
>
> PHYS_OFFSET?

Any name is OK.

(P.S. this seems also needed for some DE1s)

>
>>  +
>>  +#if defined CONFIG_DRM_SUN4I_DE2
>
> That ifdef should be in the header

So the file only compile if this option is enabled?

And if this option is disabled, inlined null stubs should be
made in header?

>
>>  +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>>  +{
>>  + DRM_DEBUG_DRIVER("Committing changes\n");
>>  +
>>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>>  + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_commit);
>
> Commit could be one of these ops too.
>
>>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>>  + int layer, bool enable)
>>  +{
>>  + u32 val;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  +
>>  + DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
>>  +
>>  + if (enable)
>>  + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
>>  + else
>>  + val = 0;
>
> So you only support the UI channel?
>
> Why do you expose several planes then?

Currently I didn't find any way to enable more than one channel
at the same time, so only the first UI channel is used.

After more knowledges are gained for DE2 mixers we can implement
more functions (for example, Jernejsk have already discovered how
to do color space correlation in DE2 for TVE).

>
>>  +
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
>>  +
>>  + /* Set the alpha configuration */
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
>
> This should be in a property

What property?

>
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
>>  +
>>  +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>>  + u32 format, u32 *mode)
>>  +{
>>  + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
>>  + (format == DRM_FORMAT_ARGB8888))
>>  + format = DRM_FORMAT_XRGB8888;
>
> Do you actually have that issue.

Yes, it really do, at least screen go black when I set this to ARGB8888 in
U-Boot. (U-Boot is a good experiement area ;-) )

>
>>  + switch (format) {
>>  + case DRM_FORMAT_ARGB8888:
>>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
>>  + break;
>>  +
>>  + case DRM_FORMAT_XRGB8888:
>>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
>>  + break;
>>  +
>>  + case DRM_FORMAT_RGB888:
>>  + *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
>>  + break;
>>  +
>>  + default:
>>  + return -EINVAL;
>>  + }
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + struct drm_plane_state *state = plane->state;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  + int i;
>>  +
>>  + DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
>>  +
>>  + if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
>>  + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
>>  + state->crtc_w, state->crtc_h);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + DRM_DEBUG_DRIVER("Updating blender size\n");
>>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>>  + regmap_write(mixer->regs,
>>  + SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + DRM_DEBUG_DRIVER("Updating channel size\n");
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
>>  + SUN8I_MIXER_SIZE(state->crtc_w,
>>  + state->crtc_h));
>>  + }
>>  +
>>  + /* Set the line width */
>>  + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
>>  + fb->pitches[0]);
>>  +
>>  + /* Set height and width */
>>  + DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
>>  + state->crtc_w, state->crtc_h);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
>>  + SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
>>  +
>>  + /* Set base coordinates */
>>  + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
>>  + state->crtc_x, state->crtc_y);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>>  + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>>  +
>>  + return 0;
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
>>  +
>>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + struct drm_plane_state *state = plane->state;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + bool interlaced = false;
>>  + u32 val;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  + int ret;
>>  +
>>  + if (plane->state->crtc)
>>  + interlaced = plane->state->crtc->state->adjusted_mode.flags
>>  + & DRM_MODE_FLAG_INTERLACE;
>>  +
>>  + regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
>>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
>>  + interlaced ?
>>  + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
>>  +
>>  + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
>>  + interlaced ? "on" : "off");
>>  +
>>  + ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
>>  + &val);
>>  + if (ret) {
>>  + DRM_DEBUG_DRIVER("Invalid format\n");
>>  + return ret;
>>  + }
>>  +
>>  + regmap_update_bits(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>  + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>>  +
>>  + return 0;
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
>>  +
>>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + struct drm_plane_state *state = plane->state;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + struct drm_gem_cma_object *gem;
>>  + dma_addr_t paddr;
>>  + uint32_t paddr_u32;
>>  + /* Currently the first UI channel is used */
>>  + int chan = mixer->cfg->vi_num;
>>  + int bpp;
>>  +
>>  + /* Get the physical address of the buffer in memory */
>>  + gem = drm_fb_cma_get_gem_obj(fb, 0);
>>  +
>>  + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
>>  +
>>  + /* Compute the start of the displayed memory */
>>  + bpp = fb->format->cpp[0];
>>  + paddr = gem->paddr + fb->offsets[0];
>>  + paddr += (state->src_x >> 16) * bpp;
>>  + paddr += (state->src_y >> 16) * fb->pitches[0];
>>  + paddr -= SUN8I_DRAM_OFFSET;
>>  +
>>  + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>>  +
>>  + paddr_u32 = (uint32_t) paddr;
>>  +
>>  + regmap_write(mixer->regs,
>>  + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>>  + paddr_u32);
>>  +
>>  + return 0;
>>  +}
>>  +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
>>  +
>>  +static struct regmap_config sun8i_mixer_regmap_config = {
>>  + .reg_bits = 32,
>>  + .val_bits = 32,
>>  + .reg_stride = 4,
>>  + .max_register = 0xbffc, /* guessed */
>>  +};
>>  +
>>  +static int sun8i_mixer_bind(struct device *dev, struct device *master,
>>  + void *data)
>>  +{
>>  + struct platform_device *pdev = to_platform_device(dev);
>>  + struct drm_device *drm = data;
>>  + struct sun4i_drv *drv = drm->dev_private;
>>  + struct sun8i_mixer *mixer;
>>  + struct resource *res;
>>  + void __iomem *regs;
>>  + int i, ret;
>>  +
>>  + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>>  + if (!mixer)
>>  + return -ENOMEM;
>>  + dev_set_drvdata(dev, mixer);
>>  + drv->mixer = mixer;
>>  +
>>  + mixer->cfg = of_device_get_match_data(dev);
>>  + if (!mixer->cfg)
>>  + return -EINVAL;
>>  +
>>  + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>  + regs = devm_ioremap_resource(dev, res);
>>  + if (IS_ERR(regs))
>>  + return PTR_ERR(regs);
>>  +
>>  + mixer->regs = devm_regmap_init_mmio(dev, regs,
>>  + &sun8i_mixer_regmap_config);
>>  + if (IS_ERR(mixer->regs)) {
>>  + dev_err(dev, "Couldn't create the mixer regmap\n");
>>  + return PTR_ERR(mixer->regs);
>>  + }
>>  +
>>  + mixer->reset = devm_reset_control_get(dev, NULL);
>>  + if (IS_ERR(mixer->reset)) {
>>  + dev_err(dev, "Couldn't get our reset line\n");
>>  + return PTR_ERR(mixer->reset);
>>  + }
>>  +
>>  + ret = reset_control_deassert(mixer->reset);
>>  + if (ret) {
>>  + dev_err(dev, "Couldn't deassert our reset line\n");
>>  + return ret;
>>  + }
>>  +
>>  + mixer->bus_clk = devm_clk_get(dev, "bus");
>>  + if (IS_ERR(mixer->bus_clk)) {
>>  + dev_err(dev, "Couldn't get the mixer bus clock\n");
>>  + ret = PTR_ERR(mixer->bus_clk);
>>  + goto err_assert_reset;
>>  + }
>>  + clk_prepare_enable(mixer->bus_clk);
>>  +
>>  + mixer->mod_clk = devm_clk_get(dev, "mod");
>>  + if (IS_ERR(mixer->mod_clk)) {
>>  + dev_err(dev, "Couldn't get the mixer module clock\n");
>>  + ret = PTR_ERR(mixer->mod_clk);
>>  + goto err_disable_bus_clk;
>>  + }
>>  + clk_prepare_enable(mixer->mod_clk);
>
> Supporting runtime_pm would be better.

But I think it's at least not support yet for DE1 backend...

>
>>  + /* Reset the registers */
>>  + for (i = 0x0; i < 0x20000; i += 4)
>>  + regmap_write(mixer->regs, i, 0);
>
> Do you still need to reset it? Isn't the reset line enough?

Nope, some strange data lies in the DE2 space.

Here's a reg dump of a running DE2 's channel 2 on V3s:
=> md 01104000
01104000: ff000403 010f01df 00000000 00000780    ................
01104010: 03f80000 00000000 00000000 00000000    ................
01104020: 00000000 00000000 00000000 00000000    ................
01104030: 00000000 00000000 00000000 00000000    ................
01104040: 00000000 00000000 00000000 00000000    ................
01104050: 00000000 00000000 00000000 00000000    ................
01104060: 00000000 00000000 00000000 00000000    ................
01104070: 00000000 00000000 00000000 00000000    ................
01104080: 00000000 00000000 010f01df bbe4d3b0    ................
01104090: 54daaf98 13835927 a1479b58 8396b8ad    ...T'Y..X.G.....
011040a0: 07d02ede a39a18da 87d88aba a2d23cf6    .............<..
011040b0: e8bfa8f7 2c8d2b7c f8bbeb3e 98013b75    ....|+.,>...u;..
011040c0: 7c186f48 4ddcdbde b658caf8 76b770d6    Ho.|...M..X..p.v
011040d0: b9a620ef fe215cc1 edd6c4b3 c5f7a66c    . ...\!.....l...
011040e0: 0d1ff6d3 956ca9e8 7f51f80a ad9a184a    ......l...Q.J...
011040f0: ff23e428 772d8d14 f4c03077 8bf495ca    (.#...-ww0......

(P.S. only first 0x88 bytes are used in a UI channel, so the following is
not reseted by U-Boot DE2 driver and is kept the original after reset line
deasserting)

>
>>  + /* Enable the mixer */
>>  + regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
>>  + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>>  +
>>  + /* Initialize blender */
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>>  + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>>  + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
>>  + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
>>  + SUN8I_MIXER_BLEND_MODE_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
>>  + SUN8I_MIXER_BLEND_MODE_DEF);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
>>  + SUN8I_MIXER_BLEND_CK_CTL_DEF);
>>  +
>>  + for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>>  + regmap_write(mixer->regs,
>>  + SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
>>  + SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
>>  +
>>  + /* Select the first UI channel */
>>  + DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
>>  + mixer->cfg->vi_num);
>>  + regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
>>  + mixer->cfg->vi_num);
>>  +
>>  + return 0;
>>  +
>>  + clk_disable_unprepare(mixer->mod_clk);
>>  +err_disable_bus_clk:
>>  + clk_disable_unprepare(mixer->bus_clk);
>>  +err_assert_reset:
>>  + reset_control_assert(mixer->reset);
>>  + return ret;
>>  +}
>>  +
>>  +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
>>  + void *data)
>>  +{
>>  + struct sun8i_mixer *mixer = dev_get_drvdata(dev);
>>  +
>>  + clk_disable_unprepare(mixer->mod_clk);
>>  + clk_disable_unprepare(mixer->bus_clk);
>>  + reset_control_assert(mixer->reset);
>>  +}
>>  +
>>  +static const struct component_ops sun8i_mixer_ops = {
>>  + .bind = sun8i_mixer_bind,
>>  + .unbind = sun8i_mixer_unbind,
>>  +};
>>  +
>>  +static int sun8i_mixer_probe(struct platform_device *pdev)
>>  +{
>>  + return component_add(&pdev->dev, &sun8i_mixer_ops);
>>  +}
>>  +
>>  +static int sun8i_mixer_remove(struct platform_device *pdev)
>>  +{
>>  + component_del(&pdev->dev, &sun8i_mixer_ops);
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
>>  + .vi_num = 2,
>>  + .ui_num = 1,
>>  +};
>>  +
>>  +static const struct of_device_id sun8i_mixer_of_table[] = {
>>  + {
>>  + .compatible = "allwinner,sun8i-v3s-de2-mixer",
>>  + .data = &sun8i_v3s_mixer_cfg
>>  + },
>>  + { }
>>  +};
>>  +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
>>  +
>>  +static struct platform_driver sun8i_mixer_platform_driver = {
>>  + .probe = sun8i_mixer_probe,
>>  + .remove = sun8i_mixer_remove,
>>  + .driver = {
>>  + .name = "sun8i-mixer",
>>  + .of_match_table = sun8i_mixer_of_table,
>>  + },
>>  +};
>>  +module_platform_driver(sun8i_mixer_platform_driver);
>>  +
>>  +MODULE_AUTHOR("Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>");
>>  +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
>>  +MODULE_LICENSE("GPL");
>>  +#else /* DRM_SUN4I_DE2 */
>>  +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>>  +{
>>  +}
>>  +
>>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>>  + int layer, bool enable)
>>  +{
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + return -ENOENT;
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + return -ENOENT;
>>  +}
>>  +
>>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane)
>>  +{
>>  + return -ENOENT;
>>  +}
>>  +#endif /* CONFIG_DRM_SUN4I_DE2 */
>>  diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
>>  new file mode 100644
>>  index 000000000000..0bc134b3bc98
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
>>  @@ -0,0 +1,133 @@
>>  +/*
>>  + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  + *
>>  + * This program is free software; you can redistribute it and/or
>>  + * modify it under the terms of the GNU General Public License as
>>  + * published by the Free Software Foundation; either version 2 of
>>  + * the License, or (at your option) any later version.
>>  + */
>>  +
>>  +#ifndef _SUN8I_MIXER_H_
>>  +#define _SUN8I_MIXER_H_
>>  +
>>  +#include <linux/clk.h>
>>  +#include <linux/regmap.h>
>>  +#include <linux/reset.h>
>>  +
>>  +#include "sun4i_layer.h"
>>  +
>>  +#define SUN8I_MIXER_MAX_CHAN_COUNT 4
>>  +
>>  +#define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
>>  +#define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x))
>>  +
>>  +#define SUN8I_MIXER_GLOBAL_CTL 0x0
>>  +#define SUN8I_MIXER_GLOBAL_STATUS 0x4
>>  +#define SUN8I_MIXER_GLOBAL_DBUFF 0x8
>>  +#define SUN8I_MIXER_GLOBAL_SIZE 0xc
>>  +
>>  +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1
>>  +
>>  +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1
>>  +
>>  +#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000
>>  +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0)
>>  +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4)
>>  +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8)
>>  +#define SUN8I_MIXER_BLEND_ROUTE 0x1080
>>  +#define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084
>>  +#define SUN8I_MIXER_BLEND_BKCOLOR 0x1088
>>  +#define SUN8I_MIXER_BLEND_OUTSIZE 0x108c
>>  +#define SUN8I_MIXER_BLEND_MODE(x) (0x1090 + 0x04 * (x))
>>  +#define SUN8I_MIXER_BLEND_CK_CTL 0x10b0
>>  +#define SUN8I_MIXER_BLEND_CK_CFG 0x10b4
>>  +#define SUN8I_MIXER_BLEND_CK_MAX(x) (0x10c0 + 0x04 * (x))
>>  +#define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x))
>>  +#define SUN8I_MIXER_BLEND_OUTCTL 0x10fc
>>  +
>>  +/* The following numbers are some still unknown magic numbers */
>>  +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000
>>  +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101
>>  +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0
>>  +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000
>>  +#define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301
>>  +#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0
>>  +
>>  +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1)
>>  +
>>  +/*
>>  + * VI channels are not used now, but the support of them may be introduced in
>>  + * the future.
>>  + */
>>  +
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
>>  + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
>>  +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80)
>>  +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84)
>>  +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88)
>>  +
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8)
>>  +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24)
>>  +
>>  +/*
>>  + * These sun-engines are still unknown now, the EN registers are here only to
>>  + * be used to disable these sub-engines.
>>  + */
>>  +#define SUN8I_MIXER_VSU_EN 0x20000
>>  +#define SUN8I_MIXER_GSU1_EN 0x30000
>>  +#define SUN8I_MIXER_GSU2_EN 0x40000
>>  +#define SUN8I_MIXER_GSU3_EN 0x50000
>>  +#define SUN8I_MIXER_FCE_EN 0xa0000
>>  +#define SUN8I_MIXER_BWS_EN 0xa2000
>>  +#define SUN8I_MIXER_LTI_EN 0xa4000
>>  +#define SUN8I_MIXER_PEAK_EN 0xa6000
>>  +#define SUN8I_MIXER_ASE_EN 0xa8000
>>  +#define SUN8I_MIXER_FCC_EN 0xaa000
>>  +#define SUN8I_MIXER_DCSC_EN 0xb0000
>>  +
>>  +struct sun8i_mixer_cfg {
>>  + int vi_num;
>>  + int ui_num;
>>  +};
>>  +
>>  +struct sun8i_mixer {
>>  + struct regmap *regs;
>>  +
>>  + const struct sun8i_mixer_cfg *cfg;
>>  +
>>  + struct reset_control *reset;
>>  +
>>  + struct clk *bus_clk;
>>  + struct clk *mod_clk;
>>  +};
>>  +
>>  +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
>>  +
>>  +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>>  + int layer, bool enable);
>>  +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane);
>>  +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane);
>>  +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>>  + int layer, struct drm_plane *plane);
>>  +#endif /* _SUN8I_MIXER_H_ */
>>  --
>>  2.11.1
>
> Thanks,
> Maxime
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 20:28     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 20:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, David Airlie,
	linux-sunxi, linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
	linux-clk, linux-arm-kernel

CgoyMy4wMi4yMDE3LCAwNDowOSwgIk1heGltZSBSaXBhcmQiIDxtYXhpbWUucmlwYXJkQGZyZWUt
ZWxlY3Ryb25zLmNvbT46Cj4gSGksCj4KPiBPbiBXZWQsIEZlYiAyMiwgMjAxNyBhdCAxMToyMzow
NlBNICswODAwLCBJY2Vub3d5IFpoZW5nIHdyb3RlOgo+PiDCoEFsbHdpbm5lciBoYXZlIGEgbmV3
ICJEaXNwbGF5IEVuZ2luZSAyLjAiIGluIHRoZXJlIG5ldyBTb0NzLCB3aGljaCBjb21lcwo+PiDC
oGluIGEgbmV3ICJEaXNwbGF5IEVuZ2luZSIgKG1peGVycyBpbnN0ZWFkIG9mIG9sZCBiYWNrZW5k
cyBhbmQKPj4gwqBmcm9udGVuZHMpLgo+Pgo+PiDCoEFkZCBzdXBwb3J0IGZvciB0aGUgbWl4ZXIg
b24gQWxsd2lubmVyIFYzcyBTb0M7IGl0J3MgdGhlIHNpbXBsZXN0IG9uZS4KPj4KPj4gwqBTaWdu
ZWQtb2ZmLWJ5OiBJY2Vub3d5IFpoZW5nIDxpY2Vub3d5QGFvc2MueHl6Pgo+PiDCoC0tLQo+PiDC
oMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL0tjb25maWcgfCA4ICsKPj4gwqDCoGRyaXZlcnMvZ3B1
L2RybS9zdW40aS9NYWtlZmlsZSB8IDEgKwo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1
bjRpX2NydGMuYyB8IDYgKy0KPj4gwqDCoGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9kcnYu
YyB8IDM4ICsrKy0KPj4gwqDCoGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9kcnYuaCB8IDEg
Kwo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmMgfCA5MiArKysrKyst
LQo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmggfCAxICsKPj4gwqDC
oGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW44aV9taXhlci5jIHwgNDE3ICsrKysrKysrKysrKysr
KysrKysrKysrKysrKysrKysrKysrKwo+PiDCoMKgZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjhp
X21peGVyLmggfCAxMzMgKysrKysrKysrKysrCj4+IMKgwqA5IGZpbGVzIGNoYW5nZWQsIDY3NCBp
bnNlcnRpb25zKCspLCAyMyBkZWxldGlvbnMoLSkKPj4gwqDCoGNyZWF0ZSBtb2RlIDEwMDY0NCBk
cml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuOGlfbWl4ZXIuYwo+PiDCoMKgY3JlYXRlIG1vZGUgMTAw
NjQ0IGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW44aV9taXhlci5oCj4+Cj4+IMKgZGlmZiAtLWdp
dCBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9LY29uZmlnIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRp
L0tjb25maWcKPj4gwqBpbmRleCBhNGIzNTdkYjg4NTYuLjhkZjQwMWZmZjE0NSAxMDA2NDQKPj4g
wqAtLS0gYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvS2NvbmZpZwo+PiDCoCsrKyBiL2RyaXZlcnMv
Z3B1L2RybS9zdW40aS9LY29uZmlnCj4+IMKgQEAgLTEyLDMgKzEyLDExIEBAIGNvbmZpZyBEUk1f
U1VONEkKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgQ2hvb3NlIHRoaXMgb3B0aW9uIGlmIHlv
dSBoYXZlIGFuIEFsbHdpbm5lciBTb0Mgd2l0aCBhCj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oERpc3BsYXkgRW5naW5lLiBJZiBNIGlzIHNlbGVjdGVkIHRoZSBtb2R1bGUgd2lsbCBiZSBjYWxs
ZWQKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgc3VuNGktZHJtLgo+PiDCoCsKPj4gwqArY29u
ZmlnIERSTV9TVU40SV9ERTIKPj4gwqArIGJvb2wgIlN1cHBvcnQgRGlzcGxheSBFbmdpbmUgMi4w
Igo+PiDCoCsgZGVwZW5kcyBvbiBEUk1fU1VONEkKPj4gwqArIGRlZmF1bHQgTUFDSF9TVU44SQo+
PiDCoCsgaGVscAo+PiDCoCsgQ2hvb3NlIHRoaXMgb3B0aW9uIGlmIHlvdSBoYXZlIGFuIEFsbHdp
bm5lciBTb0Mgd2l0aCBhCj4+IMKgKyAiRGlzcGxheSBFbmdpbmUgMi4wIi4KPj4gwqBkaWZmIC0t
Z2l0IGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL01ha2VmaWxlIGIvZHJpdmVycy9ncHUvZHJtL3N1
bjRpL01ha2VmaWxlCj4+IMKgaW5kZXggZDYyNWE4MmE2ZTVmLi44OTBlNmU1MGRmZWUgMTAwNjQ0
Cj4+IMKgLS0tIGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL01ha2VmaWxlCj4+IMKgKysrIGIvZHJp
dmVycy9ncHUvZHJtL3N1bjRpL01ha2VmaWxlCj4+IMKgQEAgLTksNSArOSw2IEBAIHN1bjRpLXRj
b24teSArPSBzdW40aV9kb3RjbG9jay5vCj4+Cj4+IMKgwqBvYmotJChDT05GSUdfRFJNX1NVTjRJ
KSArPSBzdW40aS1kcm0ubyBzdW40aS10Y29uLm8KPj4gwqDCoG9iai0kKENPTkZJR19EUk1fU1VO
NEkpICs9IHN1bjRpX2JhY2tlbmQubwo+PiDCoCtvYmotJChDT05GSUdfRFJNX1NVTjRJKSArPSBz
dW44aV9taXhlci5vCj4+IMKgwqBvYmotJChDT05GSUdfRFJNX1NVTjRJKSArPSBzdW42aV9kcmMu
bwo+PiDCoMKgb2JqLSQoQ09ORklHX0RSTV9TVU40SSkgKz0gc3VuNGlfdHYubwo+PiDCoGRpZmYg
LS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfY3J0Yy5jIGIvZHJpdmVycy9ncHUv
ZHJtL3N1bjRpL3N1bjRpX2NydGMuYwo+PiDCoGluZGV4IDRhMTkyMjEwNTc0Zi4uNGQyMjI4NDU0
NzI2IDEwMDY0NAo+PiDCoC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9jcnRjLmMK
Pj4gwqArKysgYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfY3J0Yy5jCj4+IMKgQEAgLTI1
LDYgKzI1LDcgQEAKPj4gwqDCoCNpbmNsdWRlIDx2aWRlby92aWRlb21vZGUuaD4KPj4KPj4gwqDC
oCNpbmNsdWRlICJzdW40aV9iYWNrZW5kLmgiCj4+IMKgKyNpbmNsdWRlICJzdW44aV9taXhlci5o
Igo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX2NydGMuaCIKPj4gwqDCoCNpbmNsdWRlICJzdW40aV9k
cnYuaCIKPj4gwqDCoCNpbmNsdWRlICJzdW40aV90Y29uLmgiCj4+IMKgQEAgLTU1LDcgKzU2LDEw
IEBAIHN0YXRpYyB2b2lkIHN1bjRpX2NydGNfYXRvbWljX2ZsdXNoKHN0cnVjdCBkcm1fY3J0YyAq
Y3J0YywKPj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBEUk1fREVCVUdfRFJJVkVSKCJDb21taXR0
aW5nIHBsYW5lIGNoYW5nZXNcbiIpOwo+Pgo+PiDCoC0gc3VuNGlfYmFja2VuZF9jb21taXQoZHJ2
LT5iYWNrZW5kKTsKPj4gwqArIGlmIChkcnYtPmJhY2tlbmQpCj4+IMKgKyBzdW40aV9iYWNrZW5k
X2NvbW1pdChkcnYtPmJhY2tlbmQpOwo+PiDCoCsgZWxzZSBpZiAoZHJ2LT5taXhlcikKPj4gwqAr
IHN1bjhpX21peGVyX2NvbW1pdChkcnYtPm1peGVyKTsKPj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKg
wqBpZiAoZXZlbnQpIHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgY3J0
Yy0+c3RhdGUtPmV2ZW50ID0gTlVMTDsKPj4gwqBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUvZHJt
L3N1bjRpL3N1bjRpX2Rydi5jIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2Rydi5jCj4+
IMKgaW5kZXggNGNlNjY1MzQ5ZjZiLi41OGFmMzhmNWU4MzMgMTAwNjQ0Cj4+IMKgLS0tIGEvZHJp
dmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2Rydi5jCj4+IMKgKysrIGIvZHJpdmVycy9ncHUvZHJt
L3N1bjRpL3N1bjRpX2Rydi5jCj4+IMKgQEAgLTIwLDEyICsyMCwxNyBAQAo+PiDCoMKgI2luY2x1
ZGUgPGRybS9kcm1fZmJfaGVscGVyLmg+Cj4+IMKgwqAjaW5jbHVkZSA8ZHJtL2RybV9vZi5oPgo+
Pgo+PiDCoCsjaW5jbHVkZSA8bGludXgvb2ZfZGV2aWNlLmg+Cj4+IMKgKwo+PiDCoMKgI2luY2x1
ZGUgInN1bjRpX2NydGMuaCIKPj4gwqDCoCNpbmNsdWRlICJzdW40aV9kcnYuaCIKPj4gwqDCoCNp
bmNsdWRlICJzdW40aV9mcmFtZWJ1ZmZlci5oIgo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX2xheWVy
LmgiCj4+IMKgwqAjaW5jbHVkZSAic3VuNGlfdGNvbi5oIgo+Pgo+PiDCoCsjZGVmaW5lIERFX1ZF
Ul9ERSAwCj4+IMKgKyNkZWZpbmUgREVfVkVSX0RFMiAxCj4+IMKgKwo+PiDCoMKgc3RhdGljIGlu
dCBzdW40aV9kcnZfZW5hYmxlX3ZibGFuayhzdHJ1Y3QgZHJtX2RldmljZSAqZHJtLCB1bnNpZ25l
ZCBpbnQgcGlwZSkKPj4gwqDCoHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1Y3Qgc3VuNGlf
ZHJ2ICpkcnYgPSBkcm0tPmRldl9wcml2YXRlOwo+PiDCoEBAIC0xMTcsNyArMTIyLDkgQEAgc3Rh
dGljIGludCBzdW40aV9kcnZfYmluZChzdHJ1Y3QgZGV2aWNlICpkZXYpCj4+IMKgwqB7Cj4+IMKg
wqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IGRybV9kZXZpY2UgKmRybTsKPj4gwqDCoMKgwqDCoMKg
wqDCoMKgwqBzdHJ1Y3Qgc3VuNGlfZHJ2ICpkcnY7Cj4+IMKgLSBpbnQgcmV0Owo+PiDCoCsgaW50
IHJldCwgZGVfdmVyOwo+PiDCoCsKPj4gwqArIGRlX3ZlciA9IChpbnQpb2ZfZGV2aWNlX2dldF9t
YXRjaF9kYXRhKGRldik7Cj4+Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgZHJtID0gZHJtX2Rldl9h
bGxvYygmc3VuNGlfZHJ2X2RyaXZlciwgZGV2KTsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBpZiAo
SVNfRVJSKGRybSkpCj4+IMKgQEAgLTE0MCw3ICsxNDcsMTAgQEAgc3RhdGljIGludCBzdW40aV9k
cnZfYmluZChzdHJ1Y3QgZGV2aWNlICpkZXYpCj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgfQo+Pgo+
PiDCoMKgwqDCoMKgwqDCoMKgwqDCoC8qIENyZWF0ZSBvdXIgbGF5ZXJzICovCj4+IMKgLSBkcnYt
PmxheWVycyA9IHN1bjRpX2xheWVyc19pbml0KGRybSk7Cj4+IMKgKyBpZiAoZGVfdmVyID09IERF
X1ZFUl9ERTIpCj4+IMKgKyBkcnYtPmxheWVycyA9IHN1bjhpX2xheWVyc19pbml0KGRybSk7Cj4+
IMKgKyBlbHNlCj4+IMKgKyBkcnYtPmxheWVycyA9IHN1bjRpX2xheWVyc19pbml0KGRybSk7Cj4+
IMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKElTX0VSUihkcnYtPmxheWVycykpIHsKPj4gwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgZGV2X2Vycihkcm0tPmRldiwgIkNvdWxkbid0
IGNyZWF0ZSB0aGUgcGxhbmVzXG4iKTsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgcmV0ID0gUFRSX0VSUihkcnYtPmxheWVycyk7Cj4+IMKgQEAgLTMyMywxMCArMzMzLDI2
IEBAIHN0YXRpYyBpbnQgc3VuNGlfZHJ2X3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpw
ZGV2KQo+PiDCoMKgfQo+Pgo+PiDCoMKgc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2VfaWQg
c3VuNGlfZHJ2X29mX3RhYmxlW10gPSB7Cj4+IMKgLSB7IC5jb21wYXRpYmxlID0gImFsbHdpbm5l
cixzdW41aS1hMTMtZGlzcGxheS1lbmdpbmUiIH0sCj4+IMKgLSB7IC5jb21wYXRpYmxlID0gImFs
bHdpbm5lcixzdW42aS1hMzEtZGlzcGxheS1lbmdpbmUiIH0sCj4+IMKgLSB7IC5jb21wYXRpYmxl
ID0gImFsbHdpbm5lcixzdW42aS1hMzFzLWRpc3BsYXktZW5naW5lIiB9LAo+PiDCoC0geyAuY29t
cGF0aWJsZSA9ICJhbGx3aW5uZXIsc3VuOGktYTMzLWRpc3BsYXktZW5naW5lIiB9LAo+PiDCoCsg
ewo+PiDCoCsgLmNvbXBhdGlibGUgPSAiYWxsd2lubmVyLHN1bjVpLWExMy1kaXNwbGF5LWVuZ2lu
ZSIsCj4+IMKgKyAuZGF0YSA9ICh2b2lkICopREVfVkVSX0RFLAo+PiDCoCsgfSwKPj4gwqArIHsK
Pj4gwqArIC5jb21wYXRpYmxlID0gImFsbHdpbm5lcixzdW42aS1hMzEtZGlzcGxheS1lbmdpbmUi
LAo+PiDCoCsgLmRhdGEgPSAodm9pZCAqKURFX1ZFUl9ERSwKPj4gwqArIH0sCj4+IMKgKyB7Cj4+
IMKgKyAuY29tcGF0aWJsZSA9ICJhbGx3aW5uZXIsc3VuNmktYTMxcy1kaXNwbGF5LWVuZ2luZSIs
Cj4+IMKgKyAuZGF0YSA9ICh2b2lkICopREVfVkVSX0RFLAo+PiDCoCsgfSwKPj4gwqArIHsKPj4g
wqArIC5jb21wYXRpYmxlID0gImFsbHdpbm5lcixzdW44aS1hMzMtZGlzcGxheS1lbmdpbmUiLAo+
PiDCoCsgLmRhdGEgPSAodm9pZCAqKURFX1ZFUl9ERSwKPj4gwqArIH0sCj4+IMKgKyB7Cj4+IMKg
KyAuY29tcGF0aWJsZSA9ICJhbGx3aW5uZXIsc3VuOGktdjNzLWRpc3BsYXktZW5naW5lIiwKPj4g
wqArIC5kYXRhID0gKHZvaWQgKilERV9WRVJfREUyLAo+PiDCoCsgfSwKPj4gwqDCoMKgwqDCoMKg
wqDCoMKgwqB7IH0KPj4gwqDCoH07Cj4+IMKgwqBNT0RVTEVfREVWSUNFX1RBQkxFKG9mLCBzdW40
aV9kcnZfb2ZfdGFibGUpOwo+PiDCoGRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkv
c3VuNGlfZHJ2LmggYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfZHJ2LmgKPj4gwqBpbmRl
eCA1OTczNTNlYWI3MjguLmNmMWRhOTViODViZCAxMDA2NDQKPj4gwqAtLS0gYS9kcml2ZXJzL2dw
dS9kcm0vc3VuNGkvc3VuNGlfZHJ2LmgKPj4gwqArKysgYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkv
c3VuNGlfZHJ2LmgKPj4gwqBAQCAtMTgsNiArMTgsNyBAQAo+Pgo+PiDCoMKgc3RydWN0IHN1bjRp
X2RydiB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IHN1bjRpX2JhY2tlbmQgKmJhY2tl
bmQ7Cj4+IMKgKyBzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyOwo+PiDCoMKgwqDCoMKgwqDCoMKg
wqDCoHN0cnVjdCBzdW40aV9jcnRjICpjcnRjOwo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVj
dCBzdW40aV90Y29uICp0Y29uOwo+Pgo+PiDCoGRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0v
c3VuNGkvc3VuNGlfbGF5ZXIuYyBiL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9sYXllci5j
Cj4+IMKgaW5kZXggNWQ1M2M5NzdiY2E1Li42ZGNkYWMzOGFiNjkgMTAwNjQ0Cj4+IMKgLS0tIGEv
ZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmMKPj4gwqArKysgYi9kcml2ZXJzL2dw
dS9kcm0vc3VuNGkvc3VuNGlfbGF5ZXIuYwo+PiDCoEBAIC0xNiw1MiArMTYsNjYgQEAKPj4gwqDC
oCNpbmNsdWRlIDxkcm0vZHJtUC5oPgo+Pgo+PiDCoMKgI2luY2x1ZGUgInN1bjRpX2JhY2tlbmQu
aCIKPj4gwqArI2luY2x1ZGUgInN1bjhpX21peGVyLmgiCj4+IMKgwqAjaW5jbHVkZSAic3VuNGlf
ZHJ2LmgiCj4+IMKgwqAjaW5jbHVkZSAic3VuNGlfbGF5ZXIuaCIKPj4KPj4gwqDCoHN0cnVjdCBz
dW40aV9wbGFuZV9kZXNjIHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGVu
dW0gZHJtX3BsYW5lX3R5cGUgdHlwZTsKPj4gwqArIC8qIFBpcGUgaXMgbm90IHVzZWQgaW4gc3Vu
OGktbWl4ZXIgKi8KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHU4IHBpcGU7
Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBjb25zdCB1aW50MzJfdCAqZm9y
bWF0czsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHVpbnQzMl90IG5mb3Jt
YXRzOwo+PiDCoMKgfTsKPj4KPj4gwqAtc3RhdGljIGludCBzdW40aV9iYWNrZW5kX2xheWVyX2F0
b21pY19jaGVjayhzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSwKPj4gwqArc3RhdGljIGludCBzdW40
aV9sYXllcl9hdG9taWNfY2hlY2soc3RydWN0IGRybV9wbGFuZSAqcGxhbmUsCj4+IMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgc3RydWN0IGRybV9wbGFuZV9zdGF0ZSAqc3RhdGUp
Cj4+IMKgwqB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIDA7Cj4+IMKgwqB9Cj4+Cj4+
IMKgLXN0YXRpYyB2b2lkIHN1bjRpX2JhY2tlbmRfbGF5ZXJfYXRvbWljX2Rpc2FibGUoc3RydWN0
IGRybV9wbGFuZSAqcGxhbmUsCj4+IMKgK3N0YXRpYyB2b2lkIHN1bjRpX2xheWVyX2F0b21pY19k
aXNhYmxlKHN0cnVjdCBkcm1fcGxhbmUgKnBsYW5lLAo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVjdCBkcm1fcGxhbmVfc3RhdGUgKm9sZF9zdGF0ZSkKPj4g
wqDCoHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1Y3Qgc3VuNGlfbGF5ZXIgKmxheWVyID0g
cGxhbmVfdG9fc3VuNGlfbGF5ZXIocGxhbmUpOwo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVj
dCBzdW40aV9kcnYgKmRydiA9IGxheWVyLT5kcnY7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgc3Ry
dWN0IHN1bjRpX2JhY2tlbmQgKmJhY2tlbmQgPSBkcnYtPmJhY2tlbmQ7Cj4+IMKgKyBzdHJ1Y3Qg
c3VuOGlfbWl4ZXIgKm1peGVyID0gZHJ2LT5taXhlcjsKPj4KPj4gwqAtIHN1bjRpX2JhY2tlbmRf
bGF5ZXJfZW5hYmxlKGJhY2tlbmQsIGxheWVyLT5pZCwgZmFsc2UpOwo+PiDCoCsgaWYgKGJhY2tl
bmQpCj4+IMKgKyBzdW40aV9iYWNrZW5kX2xheWVyX2VuYWJsZShiYWNrZW5kLCBsYXllci0+aWQs
IGZhbHNlKTsKPj4gwqArIGVsc2UgaWYgKG1peGVyKQo+PiDCoCsgc3VuOGlfbWl4ZXJfbGF5ZXJf
ZW5hYmxlKG1peGVyLCBsYXllci0+aWQsIGZhbHNlKTsKPj4gwqDCoH0KPgo+IEknbSBub3Qgc3Vy
ZSBpdCBtYWtlcyBzZW5zZSB0byByZXVzZSB0aGF0IHBhcnQuIFlvdSBjYW4ganVzdCBjcmVhdGUg
YQo+IG5ldyBwbGFuZSBkcml2ZXIgZW50aXJlbHkuCj4KPj4gwqAtc3RhdGljIHZvaWQgc3VuNGlf
YmFja2VuZF9sYXllcl9hdG9taWNfdXBkYXRlKHN0cnVjdCBkcm1fcGxhbmUgKnBsYW5lLAo+PiDC
oCtzdGF0aWMgdm9pZCBzdW40aV9sYXllcl9hdG9taWNfdXBkYXRlKHN0cnVjdCBkcm1fcGxhbmUg
KnBsYW5lLAo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1Y3Qg
ZHJtX3BsYW5lX3N0YXRlICpvbGRfc3RhdGUpCj4+IMKgwqB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDC
oMKgc3RydWN0IHN1bjRpX2xheWVyICpsYXllciA9IHBsYW5lX3RvX3N1bjRpX2xheWVyKHBsYW5l
KTsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqBzdHJ1Y3Qgc3VuNGlfZHJ2ICpkcnYgPSBsYXllci0+
ZHJ2Owo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHN0cnVjdCBzdW40aV9iYWNrZW5kICpiYWNrZW5k
ID0gZHJ2LT5iYWNrZW5kOwo+PiDCoC0KPj4gwqAtIHN1bjRpX2JhY2tlbmRfdXBkYXRlX2xheWVy
X2Nvb3JkKGJhY2tlbmQsIGxheWVyLT5pZCwgcGxhbmUpOwo+PiDCoC0gc3VuNGlfYmFja2VuZF91
cGRhdGVfbGF5ZXJfZm9ybWF0cyhiYWNrZW5kLCBsYXllci0+aWQsIHBsYW5lKTsKPj4gwqAtIHN1
bjRpX2JhY2tlbmRfdXBkYXRlX2xheWVyX2J1ZmZlcihiYWNrZW5kLCBsYXllci0+aWQsIHBsYW5l
KTsKPj4gwqAtIHN1bjRpX2JhY2tlbmRfbGF5ZXJfZW5hYmxlKGJhY2tlbmQsIGxheWVyLT5pZCwg
dHJ1ZSk7Cj4+IMKgKyBzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyID0gZHJ2LT5taXhlcjsKPj4g
wqArCj4+IMKgKyBpZiAoYmFja2VuZCkgewo+PiDCoCsgc3VuNGlfYmFja2VuZF91cGRhdGVfbGF5
ZXJfY29vcmQoYmFja2VuZCwgbGF5ZXItPmlkLCBwbGFuZSk7Cj4+IMKgKyBzdW40aV9iYWNrZW5k
X3VwZGF0ZV9sYXllcl9mb3JtYXRzKGJhY2tlbmQsIGxheWVyLT5pZCwgcGxhbmUpOwo+PiDCoCsg
c3VuNGlfYmFja2VuZF91cGRhdGVfbGF5ZXJfYnVmZmVyKGJhY2tlbmQsIGxheWVyLT5pZCwgcGxh
bmUpOwo+PiDCoCsgc3VuNGlfYmFja2VuZF9sYXllcl9lbmFibGUoYmFja2VuZCwgbGF5ZXItPmlk
LCB0cnVlKTsKPj4gwqArIH0gZWxzZSBpZiAobWl4ZXIpIHsKPj4gwqArIHN1bjhpX21peGVyX3Vw
ZGF0ZV9sYXllcl9jb29yZChtaXhlciwgbGF5ZXItPmlkLCBwbGFuZSk7Cj4+IMKgKyBzdW44aV9t
aXhlcl91cGRhdGVfbGF5ZXJfZm9ybWF0cyhtaXhlciwgbGF5ZXItPmlkLCBwbGFuZSk7Cj4+IMKg
KyBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfYnVmZmVyKG1peGVyLCBsYXllci0+aWQsIHBsYW5l
KTsKPj4gwqArIHN1bjhpX21peGVyX2xheWVyX2VuYWJsZShtaXhlciwgbGF5ZXItPmlkLCB0cnVl
KTsKPj4gwqArIH0KPj4gwqDCoH0KPj4KPj4gwqAtc3RhdGljIHN0cnVjdCBkcm1fcGxhbmVfaGVs
cGVyX2Z1bmNzIHN1bjRpX2JhY2tlbmRfbGF5ZXJfaGVscGVyX2Z1bmNzID0gewo+PiDCoC0gLmF0
b21pY19jaGVjayA9IHN1bjRpX2JhY2tlbmRfbGF5ZXJfYXRvbWljX2NoZWNrLAo+PiDCoC0gLmF0
b21pY19kaXNhYmxlID0gc3VuNGlfYmFja2VuZF9sYXllcl9hdG9taWNfZGlzYWJsZSwKPj4gwqAt
IC5hdG9taWNfdXBkYXRlID0gc3VuNGlfYmFja2VuZF9sYXllcl9hdG9taWNfdXBkYXRlLAo+PiDC
oCtzdGF0aWMgc3RydWN0IGRybV9wbGFuZV9oZWxwZXJfZnVuY3Mgc3VuNGlfbGF5ZXJfaGVscGVy
X2Z1bmNzID0gewo+PiDCoCsgLmF0b21pY19jaGVjayA9IHN1bjRpX2xheWVyX2F0b21pY19jaGVj
aywKPj4gwqArIC5hdG9taWNfZGlzYWJsZSA9IHN1bjRpX2xheWVyX2F0b21pY19kaXNhYmxlLAo+
PiDCoCsgLmF0b21pY191cGRhdGUgPSBzdW40aV9sYXllcl9hdG9taWNfdXBkYXRlLAo+PiDCoMKg
fTsKPj4KPj4gwqAtc3RhdGljIGNvbnN0IHN0cnVjdCBkcm1fcGxhbmVfZnVuY3Mgc3VuNGlfYmFj
a2VuZF9sYXllcl9mdW5jcyA9IHsKPj4gwqArc3RhdGljIGNvbnN0IHN0cnVjdCBkcm1fcGxhbmVf
ZnVuY3Mgc3VuNGlfbGF5ZXJfZnVuY3MgPSB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgLmF0b21p
Y19kZXN0cm95X3N0YXRlID0gZHJtX2F0b21pY19oZWxwZXJfcGxhbmVfZGVzdHJveV9zdGF0ZSwK
Pj4gwqDCoMKgwqDCoMKgwqDCoMKgwqAuYXRvbWljX2R1cGxpY2F0ZV9zdGF0ZSA9IGRybV9hdG9t
aWNfaGVscGVyX3BsYW5lX2R1cGxpY2F0ZV9zdGF0ZSwKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqAu
ZGVzdHJveSA9IGRybV9wbGFuZV9jbGVhbnVwLAo+PiDCoEBAIC04OCw2ICsxMDIsMTIgQEAgc3Rh
dGljIGNvbnN0IHVpbnQzMl90IHN1bjRpX2JhY2tlbmRfbGF5ZXJfZm9ybWF0c19vdmVybGF5W10g
PSB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgRFJNX0ZPUk1BVF9YUkdCODg4OCwKPj4gwqDCoH07
Cj4+Cj4+IMKgK3N0YXRpYyBjb25zdCB1aW50MzJfdCBzdW44aV9taXhlcl9sYXllcl9mb3JtYXRz
W10gPSB7Cj4+IMKgKyBEUk1fRk9STUFUX0FSR0I4ODg4LAo+PiDCoCsgRFJNX0ZPUk1BVF9SR0I4
ODgsCj4+IMKgKyBEUk1fRk9STUFUX1hSR0I4ODg4LAo+PiDCoCt9Owo+PiDCoCsKPj4gwqDCoHN0
YXRpYyBjb25zdCBzdHJ1Y3Qgc3VuNGlfcGxhbmVfZGVzYyBzdW40aV9iYWNrZW5kX3BsYW5lc1td
ID0gewo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoHsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgLnR5cGUgPSBEUk1fUExBTkVfVFlQRV9QUklNQVJZLAo+PiDCoEBAIC0xMDMs
NiArMTIzLDE5IEBAIHN0YXRpYyBjb25zdCBzdHJ1Y3Qgc3VuNGlfcGxhbmVfZGVzYyBzdW40aV9i
YWNrZW5kX3BsYW5lc1tdID0gewo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoH0sCj4+IMKgwqB9Owo+
Pgo+PiDCoCtzdGF0aWMgY29uc3Qgc3RydWN0IHN1bjRpX3BsYW5lX2Rlc2Mgc3VuOGlfbWl4ZXJf
cGxhbmVzW10gPSB7Cj4+IMKgKyB7Cj4+IMKgKyAudHlwZSA9IERSTV9QTEFORV9UWVBFX1BSSU1B
UlksCj4+IMKgKyAuZm9ybWF0cyA9IHN1bjhpX21peGVyX2xheWVyX2Zvcm1hdHMsCj4+IMKgKyAu
bmZvcm1hdHMgPSBBUlJBWV9TSVpFKHN1bjhpX21peGVyX2xheWVyX2Zvcm1hdHMpLAo+PiDCoCsg
fSwKPj4gwqArIHsKPj4gwqArIC50eXBlID0gRFJNX1BMQU5FX1RZUEVfT1ZFUkxBWSwKPj4gwqAr
IC5mb3JtYXRzID0gc3VuOGlfbWl4ZXJfbGF5ZXJfZm9ybWF0cywKPj4gwqArIC5uZm9ybWF0cyA9
IEFSUkFZX1NJWkUoc3VuOGlfbWl4ZXJfbGF5ZXJfZm9ybWF0cyksCj4+IMKgKyB9LAo+PiDCoCt9
Owo+PiDCoCsKPj4gwqDCoHN0YXRpYyBzdHJ1Y3Qgc3VuNGlfbGF5ZXIgKnN1bjRpX2xheWVyX2lu
aXRfb25lKHN0cnVjdCBkcm1fZGV2aWNlICpkcm0sCj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqBjb25zdCBzdHJ1Y3Qgc3VuNGlfcGxhbmVfZGVzYyAqcGxhbmUp
Cj4+IMKgwqB7Cj4+IMKgQEAgLTExNSw3ICsxNDgsNyBAQCBzdGF0aWMgc3RydWN0IHN1bjRpX2xh
eWVyICpzdW40aV9sYXllcl9pbml0X29uZShzdHJ1Y3QgZHJtX2RldmljZSAqZHJtLAo+PiDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqByZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsK
Pj4KPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqByZXQgPSBkcm1fdW5pdmVyc2FsX3BsYW5lX2luaXQo
ZHJtLCAmbGF5ZXItPnBsYW5lLCBCSVQoMCksCj4+IMKgLSAmc3VuNGlfYmFja2VuZF9sYXllcl9m
dW5jcywKPj4gwqArICZzdW40aV9sYXllcl9mdW5jcywKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoHBsYW5lLT5mb3JtYXRzLCBwbGFuZS0+bmZvcm1hdHMsCj4+IMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqBwbGFuZS0+dHlwZSwgTlVMTCk7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKHJl
dCkgewo+PiDCoEBAIC0xMjQsNyArMTU3LDcgQEAgc3RhdGljIHN0cnVjdCBzdW40aV9sYXllciAq
c3VuNGlfbGF5ZXJfaW5pdF9vbmUoc3RydWN0IGRybV9kZXZpY2UgKmRybSwKPj4gwqDCoMKgwqDC
oMKgwqDCoMKgwqB9Cj4+Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgZHJtX3BsYW5lX2hlbHBlcl9h
ZGQoJmxheWVyLT5wbGFuZSwKPj4gwqAtICZzdW40aV9iYWNrZW5kX2xheWVyX2hlbHBlcl9mdW5j
cyk7Cj4+IMKgKyAmc3VuNGlfbGF5ZXJfaGVscGVyX2Z1bmNzKTsKPj4gwqDCoMKgwqDCoMKgwqDC
oMKgwqBsYXllci0+ZHJ2ID0gZHJ2Owo+Pgo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoGlmIChwbGFu
ZS0+dHlwZSA9PSBEUk1fUExBTkVfVFlQRV9QUklNQVJZKQo+PiDCoEBAIC0xODcsMyArMjIwLDMw
IEBAIHN0cnVjdCBzdW40aV9sYXllciAqKnN1bjRpX2xheWVyc19pbml0KHN0cnVjdCBkcm1fZGV2
aWNlICpkcm0pCj4+Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIGxheWVyczsKPj4gwqDC
oH0KPj4gwqArCj4+IMKgK3N0cnVjdCBzdW40aV9sYXllciAqKnN1bjhpX2xheWVyc19pbml0KHN0
cnVjdCBkcm1fZGV2aWNlICpkcm0pCj4KPiBBbmQgc3RvcmUgdGhpcyAoYW5kIHN1bjRpX2xheWVy
c19pbml0KSBpbiBhbiBzdHJ1Y3R1cmUgaG9sZGluZyB0aGUKPiBmdW5jdGlvbiBwb2ludGVycyBm
b3IgdGhvc2UuCgpIb3cgc2hvdWxkIEkgZG8gaXQ/CklmIEkgY3JlYXRlIGEgb3BzIHN0cnVjdCwg
d2hlcmUgc2hvdWxkIEkgcmVmZXJlbmNlIGl0PwoKPgo+PiDCoCt7Cj4+IMKgKyBzdHJ1Y3Qgc3Vu
NGlfbGF5ZXIgKipsYXllcnM7Cj4+IMKgKyBpbnQgaTsKPj4gwqArCj4+IMKgKyBsYXllcnMgPSBk
ZXZtX2tjYWxsb2MoZHJtLT5kZXYsIEFSUkFZX1NJWkUoc3VuOGlfbWl4ZXJfcGxhbmVzKSwKPj4g
wqArIHNpemVvZigqKmxheWVycyksIEdGUF9LRVJORUwpOwo+PiDCoCsgaWYgKCFsYXllcnMpCj4+
IMKgKyByZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKPj4gwqArCj4+IMKgKyBmb3IgKGkgPSAwOyBp
IDwgQVJSQVlfU0laRShzdW44aV9taXhlcl9wbGFuZXMpOyBpKyspIHsKPj4gwqArIGNvbnN0IHN0
cnVjdCBzdW40aV9wbGFuZV9kZXNjICpwbGFuZSA9ICZzdW44aV9taXhlcl9wbGFuZXNbaV07Cj4+
IMKgKyBzdHJ1Y3Qgc3VuNGlfbGF5ZXIgKmxheWVyID0gbGF5ZXJzW2ldOwo+PiDCoCsKPj4gwqAr
IGxheWVyID0gc3VuNGlfbGF5ZXJfaW5pdF9vbmUoZHJtLCBwbGFuZSk7Cj4+IMKgKyBpZiAoSVNf
RVJSKGxheWVyKSkgewo+PiDCoCsgZGV2X2Vycihkcm0tPmRldiwgIkNvdWxkbid0IGluaXRpYWxp
emUgJXMgcGxhbmVcbiIsCj4+IMKgKyBpID8gIm92ZXJsYXkiIDogInByaW1hcnkiKTsKPj4gwqAr
IHJldHVybiBFUlJfQ0FTVChsYXllcik7Cj4+IMKgKyB9Owo+PiDCoCsKPj4gwqArIGxheWVyLT5p
ZCA9IGk7Cj4+IMKgKyB9Owo+PiDCoCsKPj4gwqArIHJldHVybiBsYXllcnM7Cj4+IMKgK30KPj4g
wqBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmggYi9kcml2
ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfbGF5ZXIuaAo+PiDCoGluZGV4IGEyZjY1ZDdhM2Y0ZS4u
ZjdiOWU1ZGFlYTUwIDEwMDY0NAo+PiDCoC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40
aV9sYXllci5oCj4+IMKgKysrIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2xheWVyLmgK
Pj4gwqBAQCAtMjYsNSArMjYsNiBAQCBwbGFuZV90b19zdW40aV9sYXllcihzdHJ1Y3QgZHJtX3Bs
YW5lICpwbGFuZSkKPj4gwqDCoH0KPj4KPj4gwqDCoHN0cnVjdCBzdW40aV9sYXllciAqKnN1bjRp
X2xheWVyc19pbml0KHN0cnVjdCBkcm1fZGV2aWNlICpkcm0pOwo+PiDCoCtzdHJ1Y3Qgc3VuNGlf
bGF5ZXIgKipzdW44aV9sYXllcnNfaW5pdChzdHJ1Y3QgZHJtX2RldmljZSAqZHJtKTsKPj4KPj4g
wqDCoCNlbmRpZiAvKiBfU1VONElfTEFZRVJfSF8gKi8KPj4gwqBkaWZmIC0tZ2l0IGEvZHJpdmVy
cy9ncHUvZHJtL3N1bjRpL3N1bjhpX21peGVyLmMgYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3Vu
OGlfbWl4ZXIuYwo+PiDCoG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4+IMKgaW5kZXggMDAwMDAwMDAw
MDAwLi45NDI3YjU3MjQwZDMKPj4gwqAtLS0gL2Rldi9udWxsCj4+IMKgKysrIGIvZHJpdmVycy9n
cHUvZHJtL3N1bjRpL3N1bjhpX21peGVyLmMKPj4gwqBAQCAtMCwwICsxLDQxNyBAQAo+PiDCoCsv
Kgo+PiDCoCsgKiBDb3B5cmlnaHQgKEMpIDIwMTcgSWNlbm93eSBaaGVuZyA8aWNlbm93eUBhb3Nj
Lnh5ej4KPj4gwqArICoKPj4gwqArICogQmFzZWQgb24gc3VuNGlfYmFja2VuZC5jLCB3aGljaCBp
czoKPj4gwqArICogQ29weXJpZ2h0IChDKSAyMDE1IEZyZWUgRWxlY3Ryb25zCj4+IMKgKyAqIENv
cHlyaWdodCAoQykgMjAxNSBOZXh0VGhpbmcgQ28KPj4gwqArICoKPj4gwqArICogVGhpcyBwcm9n
cmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgo+PiDC
oCsgKiBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMg
TGljZW5zZSBhcwo+PiDCoCsgKiBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRh
dGlvbjsgZWl0aGVyIHZlcnNpb24gMiBvZgo+PiDCoCsgKiB0aGUgTGljZW5zZSwgb3IgKGF0IHlv
dXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4KPj4gwqArICovCj4+IMKgKwo+PiDCoCsjaW5j
bHVkZSA8ZHJtL2RybVAuaD4KPj4gwqArI2luY2x1ZGUgPGRybS9kcm1fYXRvbWljX2hlbHBlci5o
Pgo+PiDCoCsjaW5jbHVkZSA8ZHJtL2RybV9jcnRjLmg+Cj4+IMKgKyNpbmNsdWRlIDxkcm0vZHJt
X2NydGNfaGVscGVyLmg+Cj4+IMKgKyNpbmNsdWRlIDxkcm0vZHJtX2ZiX2NtYV9oZWxwZXIuaD4K
Pj4gwqArI2luY2x1ZGUgPGRybS9kcm1fZ2VtX2NtYV9oZWxwZXIuaD4KPj4gwqArI2luY2x1ZGUg
PGRybS9kcm1fcGxhbmVfaGVscGVyLmg+Cj4+IMKgKwo+PiDCoCsjaW5jbHVkZSA8bGludXgvY29t
cG9uZW50Lmg+Cj4+IMKgKyNpbmNsdWRlIDxsaW51eC9yZXNldC5oPgo+PiDCoCsjaW5jbHVkZSA8
bGludXgvb2ZfZGV2aWNlLmg+Cj4+IMKgKwo+PiDCoCsjaW5jbHVkZSAic3VuOGlfbWl4ZXIuaCIK
Pj4gwqArI2luY2x1ZGUgInN1bjRpX2Rydi5oIgo+PiDCoCsKPj4gwqArI2RlZmluZSBTVU44SV9E
UkFNX09GRlNFVCAweDQwMDAwMDAwCj4KPiBQSFlTX09GRlNFVD8KCkFueSBuYW1lIGlzIE9LLgoK
KFAuUy4gdGhpcyBzZWVtcyBhbHNvIG5lZWRlZCBmb3Igc29tZSBERTFzKQoKPgo+PiDCoCsKPj4g
wqArI2lmIGRlZmluZWQgQ09ORklHX0RSTV9TVU40SV9ERTIKPgo+IFRoYXQgaWZkZWYgc2hvdWxk
IGJlIGluIHRoZSBoZWFkZXIKClNvIHRoZSBmaWxlIG9ubHkgY29tcGlsZSBpZiB0aGlzIG9wdGlv
biBpcyBlbmFibGVkPwoKQW5kIGlmIHRoaXMgb3B0aW9uIGlzIGRpc2FibGVkLCBpbmxpbmVkIG51
bGwgc3R1YnMgc2hvdWxkIGJlCm1hZGUgaW4gaGVhZGVyPwoKPgo+PiDCoCt2b2lkIHN1bjhpX21p
eGVyX2NvbW1pdChzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyKQo+PiDCoCt7Cj4+IMKgKyBEUk1f
REVCVUdfRFJJVkVSKCJDb21taXR0aW5nIGNoYW5nZXNcbiIpOwo+PiDCoCsKPj4gwqArIHJlZ21h
cF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfR0xPQkFMX0RCVUZGLAo+PiDCoCsgU1VO
OElfTUlYRVJfR0xPQkFMX0RCVUZGX0VOQUJMRSk7Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJP
TChzdW44aV9taXhlcl9jb21taXQpOwo+Cj4gQ29tbWl0IGNvdWxkIGJlIG9uZSBvZiB0aGVzZSBv
cHMgdG9vLgo+Cj4+IMKgK3ZvaWQgc3VuOGlfbWl4ZXJfbGF5ZXJfZW5hYmxlKHN0cnVjdCBzdW44
aV9taXhlciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIsIGJvb2wgZW5hYmxlKQo+PiDCoCt7Cj4+
IMKgKyB1MzIgdmFsOwo+PiDCoCsgLyogQ3VycmVudGx5IHRoZSBmaXJzdCBVSSBjaGFubmVsIGlz
IHVzZWQgKi8KPj4gwqArIGludCBjaGFuID0gbWl4ZXItPmNmZy0+dmlfbnVtOwo+PiDCoCsKPj4g
wqArIERSTV9ERUJVR19EUklWRVIoIkVuYWJsaW5nIGxheWVyICVkIGluIGNoYW5uZWwgJWRcbiIs
IGxheWVyLCBjaGFuKTsKPj4gwqArCj4+IMKgKyBpZiAoZW5hYmxlKQo+PiDCoCsgdmFsID0gU1VO
OElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0VOOwo+PiDCoCsgZWxzZQo+PiDCoCsgdmFsID0g
MDsKPgo+IFNvIHlvdSBvbmx5IHN1cHBvcnQgdGhlIFVJIGNoYW5uZWw/Cj4KPiBXaHkgZG8geW91
IGV4cG9zZSBzZXZlcmFsIHBsYW5lcyB0aGVuPwoKQ3VycmVudGx5IEkgZGlkbid0IGZpbmQgYW55
IHdheSB0byBlbmFibGUgbW9yZSB0aGFuIG9uZSBjaGFubmVsCmF0IHRoZSBzYW1lIHRpbWUsIHNv
IG9ubHkgdGhlIGZpcnN0IFVJIGNoYW5uZWwgaXMgdXNlZC4KCkFmdGVyIG1vcmUga25vd2xlZGdl
cyBhcmUgZ2FpbmVkIGZvciBERTIgbWl4ZXJzIHdlIGNhbiBpbXBsZW1lbnQKbW9yZSBmdW5jdGlv
bnMgKGZvciBleGFtcGxlLCBKZXJuZWpzayBoYXZlIGFscmVhZHkgZGlzY292ZXJlZCBob3cKdG8g
ZG8gY29sb3Igc3BhY2UgY29ycmVsYXRpb24gaW4gREUyIGZvciBUVkUpLgoKPgo+PiDCoCsKPj4g
wqArIHJlZ21hcF91cGRhdGVfYml0cyhtaXhlci0+cmVncywKPj4gwqArIFNVTjhJX01JWEVSX0NI
QU5fVUlfTEFZRVJfQVRUUihjaGFuLCBsYXllciksCj4+IMKgKyBTVU44SV9NSVhFUl9DSEFOX1VJ
X0xBWUVSX0FUVFJfRU4sIHZhbCk7Cj4+IMKgKwo+PiDCoCsgLyogU2V0IHRoZSBhbHBoYSBjb25m
aWd1cmF0aW9uICovCj4+IMKgKyByZWdtYXBfdXBkYXRlX2JpdHMobWl4ZXItPnJlZ3MsCj4+IMKg
KyBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFIoY2hhbiwgbGF5ZXIpLAo+PiDCoCsgU1VO
OElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0FMUEhBX01PREVfTUFTSywKPj4gwqArIFNVTjhJ
X01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9BTFBIQV9NT0RFX0RFRik7Cj4+IMKgKyByZWdtYXBf
dXBkYXRlX2JpdHMobWl4ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVS
X0FUVFIoY2hhbiwgbGF5ZXIpLAo+PiDCoCsgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRS
X0FMUEhBX01BU0ssCj4+IMKgKyBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFJfQUxQSEFf
REVGKTsKPgo+IFRoaXMgc2hvdWxkIGJlIGluIGEgcHJvcGVydHkKCldoYXQgcHJvcGVydHk/Cgo+
Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJPTChzdW44aV9taXhlcl9sYXllcl9lbmFibGUpOwo+
PiDCoCsKPj4gwqArc3RhdGljIGludCBzdW44aV9taXhlcl9kcm1fZm9ybWF0X3RvX2xheWVyKHN0
cnVjdCBkcm1fcGxhbmUgKnBsYW5lLAo+PiDCoCsgdTMyIGZvcm1hdCwgdTMyICptb2RlKQo+PiDC
oCt7Cj4+IMKgKyBpZiAoKHBsYW5lLT50eXBlID09IERSTV9QTEFORV9UWVBFX1BSSU1BUlkpICYm
Cj4+IMKgKyAoZm9ybWF0ID09IERSTV9GT1JNQVRfQVJHQjg4ODgpKQo+PiDCoCsgZm9ybWF0ID0g
RFJNX0ZPUk1BVF9YUkdCODg4ODsKPgo+IERvIHlvdSBhY3R1YWxseSBoYXZlIHRoYXQgaXNzdWUu
CgpZZXMsIGl0IHJlYWxseSBkbywgYXQgbGVhc3Qgc2NyZWVuIGdvIGJsYWNrIHdoZW4gSSBzZXQg
dGhpcyB0byBBUkdCODg4OCBpbgpVLUJvb3QuIChVLUJvb3QgaXMgYSBnb29kIGV4cGVyaWVtZW50
IGFyZWEgOy0pICkKCj4KPj4gwqArIHN3aXRjaCAoZm9ybWF0KSB7Cj4+IMKgKyBjYXNlIERSTV9G
T1JNQVRfQVJHQjg4ODg6Cj4+IMKgKyAqbW9kZSA9IFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJf
QVRUUl9GQkZNVF9BUkdCODg4ODsKPj4gwqArIGJyZWFrOwo+PiDCoCsKPj4gwqArIGNhc2UgRFJN
X0ZPUk1BVF9YUkdCODg4ODoKPj4gwqArICptb2RlID0gU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlF
Ul9BVFRSX0ZCRk1UX1hSR0I4ODg4Owo+PiDCoCsgYnJlYWs7Cj4+IMKgKwo+PiDCoCsgY2FzZSBE
Uk1fRk9STUFUX1JHQjg4ODoKPj4gwqArICptb2RlID0gU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlF
Ul9BVFRSX0ZCRk1UX1JHQjg4ODsKPj4gwqArIGJyZWFrOwo+PiDCoCsKPj4gwqArIGRlZmF1bHQ6
Cj4+IMKgKyByZXR1cm4gLUVJTlZBTDsKPj4gwqArIH0KPj4gwqArCj4+IMKgKyByZXR1cm4gMDsK
Pj4gwqArfQo+PiDCoCsKPj4gwqAraW50IHN1bjhpX21peGVyX3VwZGF0ZV9sYXllcl9jb29yZChz
dHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3Bs
YW5lICpwbGFuZSkKPj4gwqArewo+PiDCoCsgc3RydWN0IGRybV9wbGFuZV9zdGF0ZSAqc3RhdGUg
PSBwbGFuZS0+c3RhdGU7Cj4+IMKgKyBzdHJ1Y3QgZHJtX2ZyYW1lYnVmZmVyICpmYiA9IHN0YXRl
LT5mYjsKPj4gwqArIC8qIEN1cnJlbnRseSB0aGUgZmlyc3QgVUkgY2hhbm5lbCBpcyB1c2VkICov
Cj4+IMKgKyBpbnQgY2hhbiA9IG1peGVyLT5jZmctPnZpX251bTsKPj4gwqArIGludCBpOwo+PiDC
oCsKPj4gwqArIERSTV9ERUJVR19EUklWRVIoIlVwZGF0aW5nIGxheWVyICVkXG4iLCBsYXllcik7
Cj4+IMKgKwo+PiDCoCsgaWYgKHBsYW5lLT50eXBlID09IERSTV9QTEFORV9UWVBFX1BSSU1BUlkp
IHsKPj4gwqArIERSTV9ERUJVR19EUklWRVIoIlByaW1hcnkgbGF5ZXIsIHVwZGF0aW5nIGdsb2Jh
bCBzaXplIFc6ICV1IEg6ICV1XG4iLAo+PiDCoCsgc3RhdGUtPmNydGNfdywgc3RhdGUtPmNydGNf
aCk7Cj4+IMKgKyByZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0dMT0JBTF9T
SVpFLAo+PiDCoCsgU1VOOElfTUlYRVJfU0laRShzdGF0ZS0+Y3J0Y193LAo+PiDCoCsgc3RhdGUt
PmNydGNfaCkpOwo+PiDCoCsgRFJNX0RFQlVHX0RSSVZFUigiVXBkYXRpbmcgYmxlbmRlciBzaXpl
XG4iKTsKPj4gwqArIGZvciAoaSA9IDA7IGkgPCBTVU44SV9NSVhFUl9NQVhfQ0hBTl9DT1VOVDsg
aSsrKQo+PiDCoCsgcmVnbWFwX3dyaXRlKG1peGVyLT5yZWdzLAo+PiDCoCsgU1VOOElfTUlYRVJf
QkxFTkRfQVRUUl9JTlNJWkUoaSksCj4+IMKgKyBTVU44SV9NSVhFUl9TSVpFKHN0YXRlLT5jcnRj
X3csCj4+IMKgKyBzdGF0ZS0+Y3J0Y19oKSk7Cj4+IMKgKyByZWdtYXBfd3JpdGUobWl4ZXItPnJl
Z3MsIFNVTjhJX01JWEVSX0JMRU5EX09VVFNJWkUsCj4+IMKgKyBTVU44SV9NSVhFUl9TSVpFKHN0
YXRlLT5jcnRjX3csCj4+IMKgKyBzdGF0ZS0+Y3J0Y19oKSk7Cj4+IMKgKyBEUk1fREVCVUdfRFJJ
VkVSKCJVcGRhdGluZyBjaGFubmVsIHNpemVcbiIpOwo+PiDCoCsgcmVnbWFwX3dyaXRlKG1peGVy
LT5yZWdzLCBTVU44SV9NSVhFUl9DSEFOX1VJX09WTF9TSVpFKGNoYW4pLAo+PiDCoCsgU1VOOElf
TUlYRVJfU0laRShzdGF0ZS0+Y3J0Y193LAo+PiDCoCsgc3RhdGUtPmNydGNfaCkpOwo+PiDCoCsg
fQo+PiDCoCsKPj4gwqArIC8qIFNldCB0aGUgbGluZSB3aWR0aCAqLwo+PiDCoCsgRFJNX0RFQlVH
X0RSSVZFUigiTGF5ZXIgbGluZSB3aWR0aDogJWQgYnl0ZXNcbiIsIGZiLT5waXRjaGVzWzBdKTsK
Pj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlF
Ul9QSVRDSChjaGFuLCBsYXllciksCj4+IMKgKyBmYi0+cGl0Y2hlc1swXSk7Cj4+IMKgKwo+PiDC
oCsgLyogU2V0IGhlaWdodCBhbmQgd2lkdGggKi8KPj4gwqArIERSTV9ERUJVR19EUklWRVIoIkxh
eWVyIHNpemUgVzogJXUgSDogJXVcbiIsCj4+IMKgKyBzdGF0ZS0+Y3J0Y193LCBzdGF0ZS0+Y3J0
Y19oKTsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQ0hBTl9V
SV9MQVlFUl9TSVpFKGNoYW4sIGxheWVyKSwKPj4gwqArIFNVTjhJX01JWEVSX1NJWkUoc3RhdGUt
PmNydGNfdywgc3RhdGUtPmNydGNfaCkpOwo+PiDCoCsKPj4gwqArIC8qIFNldCBiYXNlIGNvb3Jk
aW5hdGVzICovCj4+IMKgKyBEUk1fREVCVUdfRFJJVkVSKCJMYXllciBjb29yZGluYXRlcyBYOiAl
ZCBZOiAlZFxuIiwKPj4gwqArIHN0YXRlLT5jcnRjX3gsIHN0YXRlLT5jcnRjX3kpOwo+PiDCoCsg
cmVnbWFwX3dyaXRlKG1peGVyLT5yZWdzLCBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0NPT1JE
KGNoYW4sIGxheWVyKSwKPj4gwqArIFNVTjhJX01JWEVSX0NPT1JEKHN0YXRlLT5jcnRjX3gsIHN0
YXRlLT5jcnRjX3kpKTsKPj4gwqArCj4+IMKgKyByZXR1cm4gMDsKPj4gwqArfQo+PiDCoCtFWFBP
UlRfU1lNQk9MKHN1bjhpX21peGVyX3VwZGF0ZV9sYXllcl9jb29yZCk7Cj4+IMKgKwo+PiDCoCtp
bnQgc3VuOGlfbWl4ZXJfdXBkYXRlX2xheWVyX2Zvcm1hdHMoc3RydWN0IHN1bjhpX21peGVyICpt
aXhlciwKPj4gwqArIGludCBsYXllciwgc3RydWN0IGRybV9wbGFuZSAqcGxhbmUpCj4+IMKgK3sK
Pj4gwqArIHN0cnVjdCBkcm1fcGxhbmVfc3RhdGUgKnN0YXRlID0gcGxhbmUtPnN0YXRlOwo+PiDC
oCsgc3RydWN0IGRybV9mcmFtZWJ1ZmZlciAqZmIgPSBzdGF0ZS0+ZmI7Cj4+IMKgKyBib29sIGlu
dGVybGFjZWQgPSBmYWxzZTsKPj4gwqArIHUzMiB2YWw7Cj4+IMKgKyAvKiBDdXJyZW50bHkgdGhl
IGZpcnN0IFVJIGNoYW5uZWwgaXMgdXNlZCAqLwo+PiDCoCsgaW50IGNoYW4gPSBtaXhlci0+Y2Zn
LT52aV9udW07Cj4+IMKgKyBpbnQgcmV0Owo+PiDCoCsKPj4gwqArIGlmIChwbGFuZS0+c3RhdGUt
PmNydGMpCj4+IMKgKyBpbnRlcmxhY2VkID0gcGxhbmUtPnN0YXRlLT5jcnRjLT5zdGF0ZS0+YWRq
dXN0ZWRfbW9kZS5mbGFncwo+PiDCoCsgJiBEUk1fTU9ERV9GTEFHX0lOVEVSTEFDRTsKPj4gwqAr
Cj4+IMKgKyByZWdtYXBfdXBkYXRlX2JpdHMobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0JMRU5E
X09VVENUTCwKPj4gwqArIFNVTjhJX01JWEVSX0JMRU5EX09VVENUTF9JTlRFUkxBQ0VELAo+PiDC
oCsgaW50ZXJsYWNlZCA/Cj4+IMKgKyBTVU44SV9NSVhFUl9CTEVORF9PVVRDVExfSU5URVJMQUNF
RCA6IDApOwo+PiDCoCsKPj4gwqArIERSTV9ERUJVR19EUklWRVIoIlN3aXRjaGluZyBkaXNwbGF5
IG1peGVyIGludGVybGFjZWQgbW9kZSAlc1xuIiwKPj4gwqArIGludGVybGFjZWQgPyAib24iIDog
Im9mZiIpOwo+PiDCoCsKPj4gwqArIHJldCA9IHN1bjhpX21peGVyX2RybV9mb3JtYXRfdG9fbGF5
ZXIocGxhbmUsIGZiLT5mb3JtYXQtPmZvcm1hdCwKPj4gwqArICZ2YWwpOwo+PiDCoCsgaWYgKHJl
dCkgewo+PiDCoCsgRFJNX0RFQlVHX0RSSVZFUigiSW52YWxpZCBmb3JtYXRcbiIpOwo+PiDCoCsg
cmV0dXJuIHJldDsKPj4gwqArIH0KPj4gwqArCj4+IMKgKyByZWdtYXBfdXBkYXRlX2JpdHMobWl4
ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9NSVhFUl9DSEFOX1VJX0xBWUVSX0FUVFIoY2hhbiwgbGF5
ZXIpLAo+PiDCoCsgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX01BU0ssIHZh
bCk7Cj4+IMKgKwo+PiDCoCsgcmV0dXJuIDA7Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJPTChz
dW44aV9taXhlcl91cGRhdGVfbGF5ZXJfZm9ybWF0cyk7Cj4+IMKgKwo+PiDCoCtpbnQgc3VuOGlf
bWl4ZXJfdXBkYXRlX2xheWVyX2J1ZmZlcihzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDC
oCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDCoCsgc3Ry
dWN0IGRybV9wbGFuZV9zdGF0ZSAqc3RhdGUgPSBwbGFuZS0+c3RhdGU7Cj4+IMKgKyBzdHJ1Y3Qg
ZHJtX2ZyYW1lYnVmZmVyICpmYiA9IHN0YXRlLT5mYjsKPj4gwqArIHN0cnVjdCBkcm1fZ2VtX2Nt
YV9vYmplY3QgKmdlbTsKPj4gwqArIGRtYV9hZGRyX3QgcGFkZHI7Cj4+IMKgKyB1aW50MzJfdCBw
YWRkcl91MzI7Cj4+IMKgKyAvKiBDdXJyZW50bHkgdGhlIGZpcnN0IFVJIGNoYW5uZWwgaXMgdXNl
ZCAqLwo+PiDCoCsgaW50IGNoYW4gPSBtaXhlci0+Y2ZnLT52aV9udW07Cj4+IMKgKyBpbnQgYnBw
Owo+PiDCoCsKPj4gwqArIC8qIEdldCB0aGUgcGh5c2ljYWwgYWRkcmVzcyBvZiB0aGUgYnVmZmVy
IGluIG1lbW9yeSAqLwo+PiDCoCsgZ2VtID0gZHJtX2ZiX2NtYV9nZXRfZ2VtX29iaihmYiwgMCk7
Cj4+IMKgKwo+PiDCoCsgRFJNX0RFQlVHX0RSSVZFUigiVXNpbmcgR0VNIEAgJXBhZFxuIiwgJmdl
bS0+cGFkZHIpOwo+PiDCoCsKPj4gwqArIC8qIENvbXB1dGUgdGhlIHN0YXJ0IG9mIHRoZSBkaXNw
bGF5ZWQgbWVtb3J5ICovCj4+IMKgKyBicHAgPSBmYi0+Zm9ybWF0LT5jcHBbMF07Cj4+IMKgKyBw
YWRkciA9IGdlbS0+cGFkZHIgKyBmYi0+b2Zmc2V0c1swXTsKPj4gwqArIHBhZGRyICs9IChzdGF0
ZS0+c3JjX3ggPj4gMTYpICogYnBwOwo+PiDCoCsgcGFkZHIgKz0gKHN0YXRlLT5zcmNfeSA+PiAx
NikgKiBmYi0+cGl0Y2hlc1swXTsKPj4gwqArIHBhZGRyIC09IFNVTjhJX0RSQU1fT0ZGU0VUOwo+
PiDCoCsKPj4gwqArIERSTV9ERUJVR19EUklWRVIoIlNldHRpbmcgYnVmZmVyIGFkZHJlc3MgdG8g
JXBhZFxuIiwgJnBhZGRyKTsKPj4gwqArCj4+IMKgKyBwYWRkcl91MzIgPSAodWludDMyX3QpIHBh
ZGRyOwo+PiDCoCsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywKPj4gwqArIFNVTjhJ
X01JWEVSX0NIQU5fVUlfTEFZRVJfVE9QX0xBRERSKGNoYW4sIGxheWVyKSwKPj4gwqArIHBhZGRy
X3UzMik7Cj4+IMKgKwo+PiDCoCsgcmV0dXJuIDA7Cj4+IMKgK30KPj4gwqArRVhQT1JUX1NZTUJP
TChzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfYnVmZmVyKTsKPj4gwqArCj4+IMKgK3N0YXRpYyBz
dHJ1Y3QgcmVnbWFwX2NvbmZpZyBzdW44aV9taXhlcl9yZWdtYXBfY29uZmlnID0gewo+PiDCoCsg
LnJlZ19iaXRzID0gMzIsCj4+IMKgKyAudmFsX2JpdHMgPSAzMiwKPj4gwqArIC5yZWdfc3RyaWRl
ID0gNCwKPj4gwqArIC5tYXhfcmVnaXN0ZXIgPSAweGJmZmMsIC8qIGd1ZXNzZWQgKi8KPj4gwqAr
fTsKPj4gwqArCj4+IMKgK3N0YXRpYyBpbnQgc3VuOGlfbWl4ZXJfYmluZChzdHJ1Y3QgZGV2aWNl
ICpkZXYsIHN0cnVjdCBkZXZpY2UgKm1hc3RlciwKPj4gwqArIHZvaWQgKmRhdGEpCj4+IMKgK3sK
Pj4gwqArIHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYgPSB0b19wbGF0Zm9ybV9kZXZpY2Uo
ZGV2KTsKPj4gwqArIHN0cnVjdCBkcm1fZGV2aWNlICpkcm0gPSBkYXRhOwo+PiDCoCsgc3RydWN0
IHN1bjRpX2RydiAqZHJ2ID0gZHJtLT5kZXZfcHJpdmF0ZTsKPj4gwqArIHN0cnVjdCBzdW44aV9t
aXhlciAqbWl4ZXI7Cj4+IMKgKyBzdHJ1Y3QgcmVzb3VyY2UgKnJlczsKPj4gwqArIHZvaWQgX19p
b21lbSAqcmVnczsKPj4gwqArIGludCBpLCByZXQ7Cj4+IMKgKwo+PiDCoCsgbWl4ZXIgPSBkZXZt
X2t6YWxsb2MoZGV2LCBzaXplb2YoKm1peGVyKSwgR0ZQX0tFUk5FTCk7Cj4+IMKgKyBpZiAoIW1p
eGVyKQo+PiDCoCsgcmV0dXJuIC1FTk9NRU07Cj4+IMKgKyBkZXZfc2V0X2RydmRhdGEoZGV2LCBt
aXhlcik7Cj4+IMKgKyBkcnYtPm1peGVyID0gbWl4ZXI7Cj4+IMKgKwo+PiDCoCsgbWl4ZXItPmNm
ZyA9IG9mX2RldmljZV9nZXRfbWF0Y2hfZGF0YShkZXYpOwo+PiDCoCsgaWYgKCFtaXhlci0+Y2Zn
KQo+PiDCoCsgcmV0dXJuIC1FSU5WQUw7Cj4+IMKgKwo+PiDCoCsgcmVzID0gcGxhdGZvcm1fZ2V0
X3Jlc291cmNlKHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAwKTsKPj4gwqArIHJlZ3MgPSBkZXZtX2lv
cmVtYXBfcmVzb3VyY2UoZGV2LCByZXMpOwo+PiDCoCsgaWYgKElTX0VSUihyZWdzKSkKPj4gwqAr
IHJldHVybiBQVFJfRVJSKHJlZ3MpOwo+PiDCoCsKPj4gwqArIG1peGVyLT5yZWdzID0gZGV2bV9y
ZWdtYXBfaW5pdF9tbWlvKGRldiwgcmVncywKPj4gwqArICZzdW44aV9taXhlcl9yZWdtYXBfY29u
ZmlnKTsKPj4gwqArIGlmIChJU19FUlIobWl4ZXItPnJlZ3MpKSB7Cj4+IMKgKyBkZXZfZXJyKGRl
diwgIkNvdWxkbid0IGNyZWF0ZSB0aGUgbWl4ZXIgcmVnbWFwXG4iKTsKPj4gwqArIHJldHVybiBQ
VFJfRVJSKG1peGVyLT5yZWdzKTsKPj4gwqArIH0KPj4gwqArCj4+IMKgKyBtaXhlci0+cmVzZXQg
PSBkZXZtX3Jlc2V0X2NvbnRyb2xfZ2V0KGRldiwgTlVMTCk7Cj4+IMKgKyBpZiAoSVNfRVJSKG1p
eGVyLT5yZXNldCkpIHsKPj4gwqArIGRldl9lcnIoZGV2LCAiQ291bGRuJ3QgZ2V0IG91ciByZXNl
dCBsaW5lXG4iKTsKPj4gwqArIHJldHVybiBQVFJfRVJSKG1peGVyLT5yZXNldCk7Cj4+IMKgKyB9
Cj4+IMKgKwo+PiDCoCsgcmV0ID0gcmVzZXRfY29udHJvbF9kZWFzc2VydChtaXhlci0+cmVzZXQp
Owo+PiDCoCsgaWYgKHJldCkgewo+PiDCoCsgZGV2X2VycihkZXYsICJDb3VsZG4ndCBkZWFzc2Vy
dCBvdXIgcmVzZXQgbGluZVxuIik7Cj4+IMKgKyByZXR1cm4gcmV0Owo+PiDCoCsgfQo+PiDCoCsK
Pj4gwqArIG1peGVyLT5idXNfY2xrID0gZGV2bV9jbGtfZ2V0KGRldiwgImJ1cyIpOwo+PiDCoCsg
aWYgKElTX0VSUihtaXhlci0+YnVzX2NsaykpIHsKPj4gwqArIGRldl9lcnIoZGV2LCAiQ291bGRu
J3QgZ2V0IHRoZSBtaXhlciBidXMgY2xvY2tcbiIpOwo+PiDCoCsgcmV0ID0gUFRSX0VSUihtaXhl
ci0+YnVzX2Nsayk7Cj4+IMKgKyBnb3RvIGVycl9hc3NlcnRfcmVzZXQ7Cj4+IMKgKyB9Cj4+IMKg
KyBjbGtfcHJlcGFyZV9lbmFibGUobWl4ZXItPmJ1c19jbGspOwo+PiDCoCsKPj4gwqArIG1peGVy
LT5tb2RfY2xrID0gZGV2bV9jbGtfZ2V0KGRldiwgIm1vZCIpOwo+PiDCoCsgaWYgKElTX0VSUiht
aXhlci0+bW9kX2NsaykpIHsKPj4gwqArIGRldl9lcnIoZGV2LCAiQ291bGRuJ3QgZ2V0IHRoZSBt
aXhlciBtb2R1bGUgY2xvY2tcbiIpOwo+PiDCoCsgcmV0ID0gUFRSX0VSUihtaXhlci0+bW9kX2Ns
ayk7Cj4+IMKgKyBnb3RvIGVycl9kaXNhYmxlX2J1c19jbGs7Cj4+IMKgKyB9Cj4+IMKgKyBjbGtf
cHJlcGFyZV9lbmFibGUobWl4ZXItPm1vZF9jbGspOwo+Cj4gU3VwcG9ydGluZyBydW50aW1lX3Bt
IHdvdWxkIGJlIGJldHRlci4KCkJ1dCBJIHRoaW5rIGl0J3MgYXQgbGVhc3Qgbm90IHN1cHBvcnQg
eWV0IGZvciBERTEgYmFja2VuZC4uLgoKPgo+PiDCoCsgLyogUmVzZXQgdGhlIHJlZ2lzdGVycyAq
Lwo+PiDCoCsgZm9yIChpID0gMHgwOyBpIDwgMHgyMDAwMDsgaSArPSA0KQo+PiDCoCsgcmVnbWFw
X3dyaXRlKG1peGVyLT5yZWdzLCBpLCAwKTsKPgo+IERvIHlvdSBzdGlsbCBuZWVkIHRvIHJlc2V0
IGl0PyBJc24ndCB0aGUgcmVzZXQgbGluZSBlbm91Z2g/CgpOb3BlLCBzb21lIHN0cmFuZ2UgZGF0
YSBsaWVzIGluIHRoZSBERTIgc3BhY2UuCgpIZXJlJ3MgYSByZWcgZHVtcCBvZiBhIHJ1bm5pbmcg
REUyICdzIGNoYW5uZWwgMiBvbiBWM3M6Cj0+IG1kIDAxMTA0MDAwCjAxMTA0MDAwOiBmZjAwMDQw
MyAwMTBmMDFkZiAwMDAwMDAwMCAwMDAwMDc4MCAgICAuLi4uLi4uLi4uLi4uLi4uCjAxMTA0MDEw
OiAwM2Y4MDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAgICAuLi4uLi4uLi4uLi4uLi4u
CjAxMTA0MDIwOiAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAgICAuLi4uLi4u
Li4uLi4uLi4uCjAxMTA0MDMwOiAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAg
ICAuLi4uLi4uLi4uLi4uLi4uCjAxMTA0MDQwOiAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAw
MDAwMDAwMCAgICAuLi4uLi4uLi4uLi4uLi4uCjAxMTA0MDUwOiAwMDAwMDAwMCAwMDAwMDAwMCAw
MDAwMDAwMCAwMDAwMDAwMCAgICAuLi4uLi4uLi4uLi4uLi4uCjAxMTA0MDYwOiAwMDAwMDAwMCAw
MDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAgICAuLi4uLi4uLi4uLi4uLi4uCjAxMTA0MDcwOiAw
MDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMCAgICAuLi4uLi4uLi4uLi4uLi4uCjAx
MTA0MDgwOiAwMDAwMDAwMCAwMDAwMDAwMCAwMTBmMDFkZiBiYmU0ZDNiMCAgICAuLi4uLi4uLi4u
Li4uLi4uCjAxMTA0MDkwOiA1NGRhYWY5OCAxMzgzNTkyNyBhMTQ3OWI1OCA4Mzk2YjhhZCAgICAu
Li5UJ1kuLlguRy4uLi4uCjAxMTA0MGEwOiAwN2QwMmVkZSBhMzlhMThkYSA4N2Q4OGFiYSBhMmQy
M2NmNiAgICAuLi4uLi4uLi4uLi4uPC4uCjAxMTA0MGIwOiBlOGJmYThmNyAyYzhkMmI3YyBmOGJi
ZWIzZSA5ODAxM2I3NSAgICAuLi4ufCsuLD4uLi51Oy4uCjAxMTA0MGMwOiA3YzE4NmY0OCA0ZGRj
ZGJkZSBiNjU4Y2FmOCA3NmI3NzBkNiAgICBIby58Li4uTS4uWC4ucC52CjAxMTA0MGQwOiBiOWE2
MjBlZiBmZTIxNWNjMSBlZGQ2YzRiMyBjNWY3YTY2YyAgICAuIC4uLlwhLi4uLi5sLi4uCjAxMTA0
MGUwOiAwZDFmZjZkMyA5NTZjYTllOCA3ZjUxZjgwYSBhZDlhMTg0YSAgICAuLi4uLi5sLi4uUS5K
Li4uCjAxMTA0MGYwOiBmZjIzZTQyOCA3NzJkOGQxNCBmNGMwMzA3NyA4YmY0OTVjYSAgICAoLiMu
Li4td3cwLi4uLi4uCgooUC5TLiBvbmx5IGZpcnN0IDB4ODggYnl0ZXMgYXJlIHVzZWQgaW4gYSBV
SSBjaGFubmVsLCBzbyB0aGUgZm9sbG93aW5nIGlzCm5vdCByZXNldGVkIGJ5IFUtQm9vdCBERTIg
ZHJpdmVyIGFuZCBpcyBrZXB0IHRoZSBvcmlnaW5hbCBhZnRlciByZXNldCBsaW5lCmRlYXNzZXJ0
aW5nKQoKPgo+PiDCoCsgLyogRW5hYmxlIHRoZSBtaXhlciAqLwo+PiDCoCsgcmVnbWFwX3dyaXRl
KG1peGVyLT5yZWdzLCBTVU44SV9NSVhFUl9HTE9CQUxfQ1RMLAo+PiDCoCsgU1VOOElfTUlYRVJf
R0xPQkFMX0NUTF9SVF9FTik7Cj4+IMKgKwo+PiDCoCsgLyogSW5pdGlhbGl6ZSBibGVuZGVyICov
Cj4+IMKgKyByZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0JMRU5EX0ZDT0xP
Ul9DVEwsCj4+IMKgKyBTVU44SV9NSVhFUl9CTEVORF9GQ09MT1JfQ1RMX0RFRik7Cj4+IMKgKyBy
ZWdtYXBfd3JpdGUobWl4ZXItPnJlZ3MsIFNVTjhJX01JWEVSX0JMRU5EX1BSRU1VTFRJUExZLAo+
PiDCoCsgU1VOOElfTUlYRVJfQkxFTkRfUFJFTVVMVElQTFlfREVGKTsKPj4gwqArIHJlZ21hcF93
cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQkxFTkRfQktDT0xPUiwKPj4gwqArIFNVTjhJ
X01JWEVSX0JMRU5EX0JLQ09MT1JfREVGKTsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVn
cywgU1VOOElfTUlYRVJfQkxFTkRfTU9ERSgwKSwKPj4gwqArIFNVTjhJX01JWEVSX0JMRU5EX01P
REVfREVGKTsKPj4gwqArIHJlZ21hcF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQkxF
TkRfTU9ERSgxKSwKPj4gwqArIFNVTjhJX01JWEVSX0JMRU5EX01PREVfREVGKTsKPj4gwqArIHJl
Z21hcF93cml0ZShtaXhlci0+cmVncywgU1VOOElfTUlYRVJfQkxFTkRfQ0tfQ1RMLAo+PiDCoCsg
U1VOOElfTUlYRVJfQkxFTkRfQ0tfQ1RMX0RFRik7Cj4+IMKgKwo+PiDCoCsgZm9yIChpID0gMDsg
aSA8IFNVTjhJX01JWEVSX01BWF9DSEFOX0NPVU5UOyBpKyspCj4+IMKgKyByZWdtYXBfd3JpdGUo
bWl4ZXItPnJlZ3MsCj4+IMKgKyBTVU44SV9NSVhFUl9CTEVORF9BVFRSX0ZDT0xPUihpKSwKPj4g
wqArIFNVTjhJX01JWEVSX0JMRU5EX0FUVFJfRkNPTE9SX0RFRik7Cj4+IMKgKwo+PiDCoCsgLyog
U2VsZWN0IHRoZSBmaXJzdCBVSSBjaGFubmVsICovCj4+IMKgKyBEUk1fREVCVUdfRFJJVkVSKCJT
ZWxlY3RpbmcgY2hhbm5lbCAlZCAoZmlyc3QgVUkgY2hhbm5lbClcbiIsCj4+IMKgKyBtaXhlci0+
Y2ZnLT52aV9udW0pOwo+PiDCoCsgcmVnbWFwX3dyaXRlKG1peGVyLT5yZWdzLCBTVU44SV9NSVhF
Ul9CTEVORF9ST1VURSwKPj4gwqArIG1peGVyLT5jZmctPnZpX251bSk7Cj4+IMKgKwo+PiDCoCsg
cmV0dXJuIDA7Cj4+IMKgKwo+PiDCoCsgY2xrX2Rpc2FibGVfdW5wcmVwYXJlKG1peGVyLT5tb2Rf
Y2xrKTsKPj4gwqArZXJyX2Rpc2FibGVfYnVzX2NsazoKPj4gwqArIGNsa19kaXNhYmxlX3VucHJl
cGFyZShtaXhlci0+YnVzX2Nsayk7Cj4+IMKgK2Vycl9hc3NlcnRfcmVzZXQ6Cj4+IMKgKyByZXNl
dF9jb250cm9sX2Fzc2VydChtaXhlci0+cmVzZXQpOwo+PiDCoCsgcmV0dXJuIHJldDsKPj4gwqAr
fQo+PiDCoCsKPj4gwqArc3RhdGljIHZvaWQgc3VuOGlfbWl4ZXJfdW5iaW5kKHN0cnVjdCBkZXZp
Y2UgKmRldiwgc3RydWN0IGRldmljZSAqbWFzdGVyLAo+PiDCoCsgdm9pZCAqZGF0YSkKPj4gwqAr
ewo+PiDCoCsgc3RydWN0IHN1bjhpX21peGVyICptaXhlciA9IGRldl9nZXRfZHJ2ZGF0YShkZXYp
Owo+PiDCoCsKPj4gwqArIGNsa19kaXNhYmxlX3VucHJlcGFyZShtaXhlci0+bW9kX2Nsayk7Cj4+
IMKgKyBjbGtfZGlzYWJsZV91bnByZXBhcmUobWl4ZXItPmJ1c19jbGspOwo+PiDCoCsgcmVzZXRf
Y29udHJvbF9hc3NlcnQobWl4ZXItPnJlc2V0KTsKPj4gwqArfQo+PiDCoCsKPj4gwqArc3RhdGlj
IGNvbnN0IHN0cnVjdCBjb21wb25lbnRfb3BzIHN1bjhpX21peGVyX29wcyA9IHsKPj4gwqArIC5i
aW5kID0gc3VuOGlfbWl4ZXJfYmluZCwKPj4gwqArIC51bmJpbmQgPSBzdW44aV9taXhlcl91bmJp
bmQsCj4+IMKgK307Cj4+IMKgKwo+PiDCoCtzdGF0aWMgaW50IHN1bjhpX21peGVyX3Byb2JlKHN0
cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpCj4+IMKgK3sKPj4gwqArIHJldHVybiBjb21wb25l
bnRfYWRkKCZwZGV2LT5kZXYsICZzdW44aV9taXhlcl9vcHMpOwo+PiDCoCt9Cj4+IMKgKwo+PiDC
oCtzdGF0aWMgaW50IHN1bjhpX21peGVyX3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpw
ZGV2KQo+PiDCoCt7Cj4+IMKgKyBjb21wb25lbnRfZGVsKCZwZGV2LT5kZXYsICZzdW44aV9taXhl
cl9vcHMpOwo+PiDCoCsKPj4gwqArIHJldHVybiAwOwo+PiDCoCt9Cj4+IMKgKwo+PiDCoCtzdGF0
aWMgY29uc3Qgc3RydWN0IHN1bjhpX21peGVyX2NmZyBzdW44aV92M3NfbWl4ZXJfY2ZnID0gewo+
PiDCoCsgLnZpX251bSA9IDIsCj4+IMKgKyAudWlfbnVtID0gMSwKPj4gwqArfTsKPj4gwqArCj4+
IMKgK3N0YXRpYyBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkIHN1bjhpX21peGVyX29mX3RhYmxl
W10gPSB7Cj4+IMKgKyB7Cj4+IMKgKyAuY29tcGF0aWJsZSA9ICJhbGx3aW5uZXIsc3VuOGktdjNz
LWRlMi1taXhlciIsCj4+IMKgKyAuZGF0YSA9ICZzdW44aV92M3NfbWl4ZXJfY2ZnCj4+IMKgKyB9
LAo+PiDCoCsgeyB9Cj4+IMKgK307Cj4+IMKgK01PRFVMRV9ERVZJQ0VfVEFCTEUob2YsIHN1bjhp
X21peGVyX29mX3RhYmxlKTsKPj4gwqArCj4+IMKgK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJp
dmVyIHN1bjhpX21peGVyX3BsYXRmb3JtX2RyaXZlciA9IHsKPj4gwqArIC5wcm9iZSA9IHN1bjhp
X21peGVyX3Byb2JlLAo+PiDCoCsgLnJlbW92ZSA9IHN1bjhpX21peGVyX3JlbW92ZSwKPj4gwqAr
IC5kcml2ZXIgPSB7Cj4+IMKgKyAubmFtZSA9ICJzdW44aS1taXhlciIsCj4+IMKgKyAub2ZfbWF0
Y2hfdGFibGUgPSBzdW44aV9taXhlcl9vZl90YWJsZSwKPj4gwqArIH0sCj4+IMKgK307Cj4+IMKg
K21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIoc3VuOGlfbWl4ZXJfcGxhdGZvcm1fZHJpdmVyKTsKPj4g
wqArCj4+IMKgK01PRFVMRV9BVVRIT1IoIkljZW5vd3kgWmhlbmcgPGljZW5vd3lAYW9zYy54eXo+
Iik7Cj4+IMKgK01PRFVMRV9ERVNDUklQVElPTigiQWxsd2lubmVyIERFMiBNaXhlciBkcml2ZXIi
KTsKPj4gwqArTU9EVUxFX0xJQ0VOU0UoIkdQTCIpOwo+PiDCoCsjZWxzZSAvKiBEUk1fU1VONElf
REUyICovCj4+IMKgK3ZvaWQgc3VuOGlfbWl4ZXJfY29tbWl0KHN0cnVjdCBzdW44aV9taXhlciAq
bWl4ZXIpCj4+IMKgK3sKPj4gwqArfQo+PiDCoCsKPj4gwqArdm9pZCBzdW44aV9taXhlcl9sYXll
cl9lbmFibGUoc3RydWN0IHN1bjhpX21peGVyICptaXhlciwKPj4gwqArIGludCBsYXllciwgYm9v
bCBlbmFibGUpCj4+IMKgK3sKPj4gwqArfQo+PiDCoCsKPj4gwqAraW50IHN1bjhpX21peGVyX3Vw
ZGF0ZV9sYXllcl9jb29yZChzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxh
eWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDCoCsgcmV0dXJuIC1FTk9F
TlQ7Cj4+IMKgK30KPj4gwqArCj4+IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfZm9y
bWF0cyhzdHJ1Y3Qgc3VuOGlfbWl4ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3Qg
ZHJtX3BsYW5lICpwbGFuZSkKPj4gwqArewo+PiDCoCsgcmV0dXJuIC1FTk9FTlQ7Cj4+IMKgK30K
Pj4gwqArCj4+IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfYnVmZmVyKHN0cnVjdCBz
dW44aV9taXhlciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIsIHN0cnVjdCBkcm1fcGxhbmUgKnBs
YW5lKQo+PiDCoCt7Cj4+IMKgKyByZXR1cm4gLUVOT0VOVDsKPj4gwqArfQo+PiDCoCsjZW5kaWYg
LyogQ09ORklHX0RSTV9TVU40SV9ERTIgKi8KPj4gwqBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUv
ZHJtL3N1bjRpL3N1bjhpX21peGVyLmggYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuOGlfbWl4
ZXIuaAo+PiDCoG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4+IMKgaW5kZXggMDAwMDAwMDAwMDAwLi4w
YmMxMzRiM2JjOTgKPj4gwqAtLS0gL2Rldi9udWxsCj4+IMKgKysrIGIvZHJpdmVycy9ncHUvZHJt
L3N1bjRpL3N1bjhpX21peGVyLmgKPj4gwqBAQCAtMCwwICsxLDEzMyBAQAo+PiDCoCsvKgo+PiDC
oCsgKiBDb3B5cmlnaHQgKEMpIDIwMTcgSWNlbm93eSBaaGVuZyA8aWNlbm93eUBhb3NjLnh5ej4K
Pj4gwqArICoKPj4gwqArICogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4g
cmVkaXN0cmlidXRlIGl0IGFuZC9vcgo+PiDCoCsgKiBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1z
IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcwo+PiDCoCsgKiBwdWJsaXNoZWQg
YnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgZWl0aGVyIHZlcnNpb24gMiBvZgo+PiDC
oCsgKiB0aGUgTGljZW5zZSwgb3IgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4K
Pj4gwqArICovCj4+IMKgKwo+PiDCoCsjaWZuZGVmIF9TVU44SV9NSVhFUl9IXwo+PiDCoCsjZGVm
aW5lIF9TVU44SV9NSVhFUl9IXwo+PiDCoCsKPj4gwqArI2luY2x1ZGUgPGxpbnV4L2Nsay5oPgo+
PiDCoCsjaW5jbHVkZSA8bGludXgvcmVnbWFwLmg+Cj4+IMKgKyNpbmNsdWRlIDxsaW51eC9yZXNl
dC5oPgo+PiDCoCsKPj4gwqArI2luY2x1ZGUgInN1bjRpX2xheWVyLmgiCj4+IMKgKwo+PiDCoCsj
ZGVmaW5lIFNVTjhJX01JWEVSX01BWF9DSEFOX0NPVU5UIDQKPj4gwqArCj4+IMKgKyNkZWZpbmUg
U1VOOElfTUlYRVJfU0laRSh3LCBoKSAoKChoKSAtIDEpIDw8IDE2IHwgKCh3KSAtIDEpKQo+PiDC
oCsjZGVmaW5lIFNVTjhJX01JWEVSX0NPT1JEKHgsIHkpICgoeSkgPDwgMTYgfCAoeCkpCj4+IMKg
Kwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0dMT0JBTF9DVEwgMHgwCj4+IMKgKyNkZWZpbmUg
U1VOOElfTUlYRVJfR0xPQkFMX1NUQVRVUyAweDQKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9H
TE9CQUxfREJVRkYgMHg4Cj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfR0xPQkFMX1NJWkUgMHhj
Cj4+IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0dMT0JBTF9DVExfUlRfRU4gMHgxCj4+
IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0dMT0JBTF9EQlVGRl9FTkFCTEUgMHgxCj4+
IMKgKwo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX0ZDT0xPUl9DVEwgMHgxMDAwCj4+
IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQVRUUl9GQ09MT1IoeCkgKDB4MTAwNCArIDB4
MTAgKiAoeCkgKyAweDApCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQVRUUl9JTlNJ
WkUoeCkgKDB4MTAwNCArIDB4MTAgKiAoeCkgKyAweDQpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlY
RVJfQkxFTkRfQVRUUl9PRkZTRVQoeCkgKDB4MTAwNCArIDB4MTAgKiAoeCkgKyAweDgpCj4+IMKg
KyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfUk9VVEUgMHgxMDgwCj4+IMKgKyNkZWZpbmUgU1VO
OElfTUlYRVJfQkxFTkRfUFJFTVVMVElQTFkgMHgxMDg0Cj4+IMKgKyNkZWZpbmUgU1VOOElfTUlY
RVJfQkxFTkRfQktDT0xPUiAweDEwODgKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9P
VVRTSVpFIDB4MTA4Ywo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX01PREUoeCkgKDB4
MTA5MCArIDB4MDQgKiAoeCkpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQ0tfQ1RM
IDB4MTBiMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX0NLX0NGRyAweDEwYjQKPj4g
wqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9DS19NQVgoeCkgKDB4MTBjMCArIDB4MDQgKiAo
eCkpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQ0tfTUlOKHgpICgweDEwZTAgKyAw
eDA0ICogKHgpKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX09VVENUTCAweDEwZmMK
Pj4gwqArCj4+IMKgKy8qIFRoZSBmb2xsb3dpbmcgbnVtYmVycyBhcmUgc29tZSBzdGlsbCB1bmtu
b3duIG1hZ2ljIG51bWJlcnMgKi8KPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9BVFRS
X0ZDT0xPUl9ERUYgMHhmZjAwMDAwMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX0ZD
T0xPUl9DVExfREVGIDB4MDAwMDAxMDEKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9Q
UkVNVUxUSVBMWV9ERUYgMHgwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQkxFTkRfQktDT0xP
Ul9ERUYgMHhmZjAwMDAwMAo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0JMRU5EX01PREVfREVG
IDB4MDMwMTAzMDEKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9DS19DVExfREVGIDB4
MAo+PiDCoCsKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CTEVORF9PVVRDVExfSU5URVJMQUNF
RCBCSVQoMSkKPj4gwqArCj4+IMKgKy8qCj4+IMKgKyAqIFZJIGNoYW5uZWxzIGFyZSBub3QgdXNl
ZCBub3csIGJ1dCB0aGUgc3VwcG9ydCBvZiB0aGVtIG1heSBiZSBpbnRyb2R1Y2VkIGluCj4+IMKg
KyAqIHRoZSBmdXR1cmUuCj4+IMKgKyAqLwo+PiDCoCsKPj4gwqArI2RlZmluZSBTVU44SV9NSVhF
Ul9DSEFOX1VJX0xBWUVSX0FUVFIoY2gsIGxheWVyKSBcCj4+IMKgKyAoMHgyMDAwICsgMHgxMDAw
ICogKGNoKSArIDB4MjAgKiAobGF5ZXIpICsgMHgwKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVS
X0NIQU5fVUlfTEFZRVJfU0laRShjaCwgbGF5ZXIpIFwKPj4gwqArICgweDIwMDAgKyAweDEwMDAg
KiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweDQpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
Q0hBTl9VSV9MQVlFUl9DT09SRChjaCwgbGF5ZXIpIFwKPj4gwqArICgweDIwMDAgKyAweDEwMDAg
KiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweDgpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
Q0hBTl9VSV9MQVlFUl9QSVRDSChjaCwgbGF5ZXIpIFwKPj4gwqArICgweDIwMDAgKyAweDEwMDAg
KiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweGMpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
Q0hBTl9VSV9MQVlFUl9UT1BfTEFERFIoY2gsIGxheWVyKSBcCj4+IMKgKyAoMHgyMDAwICsgMHgx
MDAwICogKGNoKSArIDB4MjAgKiAobGF5ZXIpICsgMHgxMCkKPj4gwqArI2RlZmluZSBTVU44SV9N
SVhFUl9DSEFOX1VJX0xBWUVSX0JPVF9MQUREUihjaCwgbGF5ZXIpIFwKPj4gwqArICgweDIwMDAg
KyAweDEwMDAgKiAoY2gpICsgMHgyMCAqIChsYXllcikgKyAweDE0KQo+PiDCoCsjZGVmaW5lIFNV
TjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfRkNPTE9SKGNoLCBsYXllcikgXAo+PiDCoCsgKDB4MjAw
MCArIDB4MTAwMCAqIChjaCkgKyAweDIwICogKGxheWVyKSArIDB4MTgpCj4+IMKgKyNkZWZpbmUg
U1VOOElfTUlYRVJfQ0hBTl9VSV9UT1BfSEFERFIoY2gpICgweDIwMDAgKyAweDEwMDAgKiAoY2gp
ICsgMHg4MCkKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9DSEFOX1VJX0JPVF9IQUREUihjaCkg
KDB4MjAwMCArIDB4MTAwMCAqIChjaCkgKyAweDg0KQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVS
X0NIQU5fVUlfT1ZMX1NJWkUoY2gpICgweDIwMDAgKyAweDEwMDAgKiAoY2gpICsgMHg4OCkKPj4g
wqArCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0VOIEJJVCgw
KQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9BTFBIQV9NT0RF
X01BU0sgR0VOTUFTSygyLCAxKQo+PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0NIQU5fVUlfTEFZ
RVJfQVRUUl9GQkZNVF9NQVNLIEdFTk1BU0soMTEsIDgpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlY
RVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0FMUEhBX01BU0sgR0VOTUFTSygzMSwgMjQpCj4+IMKgKyNk
ZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0FMUEhBX01PREVfREVGICgxIDw8
IDEpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9BVFRSX0ZCRk1UX0FS
R0I4ODg4ICgwIDw8IDgpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hBTl9VSV9MQVlFUl9B
VFRSX0ZCRk1UX1hSR0I4ODg4ICg0IDw8IDgpCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQ0hB
Tl9VSV9MQVlFUl9BVFRSX0ZCRk1UX1JHQjg4OCAoOCA8PCA4KQo+PiDCoCsjZGVmaW5lIFNVTjhJ
X01JWEVSX0NIQU5fVUlfTEFZRVJfQVRUUl9BTFBIQV9ERUYgKDB4ZmYgPDwgMjQpCj4+IMKgKwo+
PiDCoCsvKgo+PiDCoCsgKiBUaGVzZSBzdW4tZW5naW5lcyBhcmUgc3RpbGwgdW5rbm93biBub3cs
IHRoZSBFTiByZWdpc3RlcnMgYXJlIGhlcmUgb25seSB0bwo+PiDCoCsgKiBiZSB1c2VkIHRvIGRp
c2FibGUgdGhlc2Ugc3ViLWVuZ2luZXMuCj4+IMKgKyAqLwo+PiDCoCsjZGVmaW5lIFNVTjhJX01J
WEVSX1ZTVV9FTiAweDIwMDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfR1NVMV9FTiAweDMw
MDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfR1NVMl9FTiAweDQwMDAwCj4+IMKgKyNkZWZp
bmUgU1VOOElfTUlYRVJfR1NVM19FTiAweDUwMDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJf
RkNFX0VOIDB4YTAwMDAKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9CV1NfRU4gMHhhMjAwMAo+
PiDCoCsjZGVmaW5lIFNVTjhJX01JWEVSX0xUSV9FTiAweGE0MDAwCj4+IMKgKyNkZWZpbmUgU1VO
OElfTUlYRVJfUEVBS19FTiAweGE2MDAwCj4+IMKgKyNkZWZpbmUgU1VOOElfTUlYRVJfQVNFX0VO
IDB4YTgwMDAKPj4gwqArI2RlZmluZSBTVU44SV9NSVhFUl9GQ0NfRU4gMHhhYTAwMAo+PiDCoCsj
ZGVmaW5lIFNVTjhJX01JWEVSX0RDU0NfRU4gMHhiMDAwMAo+PiDCoCsKPj4gwqArc3RydWN0IHN1
bjhpX21peGVyX2NmZyB7Cj4+IMKgKyBpbnQgdmlfbnVtOwo+PiDCoCsgaW50IHVpX251bTsKPj4g
wqArfTsKPj4gwqArCj4+IMKgK3N0cnVjdCBzdW44aV9taXhlciB7Cj4+IMKgKyBzdHJ1Y3QgcmVn
bWFwICpyZWdzOwo+PiDCoCsKPj4gwqArIGNvbnN0IHN0cnVjdCBzdW44aV9taXhlcl9jZmcgKmNm
ZzsKPj4gwqArCj4+IMKgKyBzdHJ1Y3QgcmVzZXRfY29udHJvbCAqcmVzZXQ7Cj4+IMKgKwo+PiDC
oCsgc3RydWN0IGNsayAqYnVzX2NsazsKPj4gwqArIHN0cnVjdCBjbGsgKm1vZF9jbGs7Cj4+IMKg
K307Cj4+IMKgKwo+PiDCoCt2b2lkIHN1bjhpX21peGVyX2NvbW1pdChzdHJ1Y3Qgc3VuOGlfbWl4
ZXIgKm1peGVyKTsKPj4gwqArCj4+IMKgK3ZvaWQgc3VuOGlfbWl4ZXJfbGF5ZXJfZW5hYmxlKHN0
cnVjdCBzdW44aV9taXhlciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIsIGJvb2wgZW5hYmxlKTsK
Pj4gwqAraW50IHN1bjhpX21peGVyX3VwZGF0ZV9sYXllcl9jb29yZChzdHJ1Y3Qgc3VuOGlfbWl4
ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSk7Cj4+
IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfZm9ybWF0cyhzdHJ1Y3Qgc3VuOGlfbWl4
ZXIgKm1peGVyLAo+PiDCoCsgaW50IGxheWVyLCBzdHJ1Y3QgZHJtX3BsYW5lICpwbGFuZSk7Cj4+
IMKgK2ludCBzdW44aV9taXhlcl91cGRhdGVfbGF5ZXJfYnVmZmVyKHN0cnVjdCBzdW44aV9taXhl
ciAqbWl4ZXIsCj4+IMKgKyBpbnQgbGF5ZXIsIHN0cnVjdCBkcm1fcGxhbmUgKnBsYW5lKTsKPj4g
wqArI2VuZGlmIC8qIF9TVU44SV9NSVhFUl9IXyAqLwo+PiDCoC0tCj4+IMKgMi4xMS4xCj4KPiBU
aGFua3MsCj4gTWF4aW1lCj4KPiAtLQo+IE1heGltZSBSaXBhcmQsIEZyZWUgRWxlY3Ryb25zCj4g
RW1iZWRkZWQgTGludXggYW5kIEtlcm5lbCBlbmdpbmVlcmluZwo+IGh0dHA6Ly9mcmVlLWVsZWN0
cm9ucy5jb20KCl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
CmxpbnV4LWFybS1rZXJuZWwgbWFpbGluZyBsaXN0CmxpbnV4LWFybS1rZXJuZWxAbGlzdHMuaW5m
cmFkZWFkLm9yZwpodHRwOi8vbGlzdHMuaW5mcmFkZWFkLm9yZy9tYWlsbWFuL2xpc3RpbmZvL2xp
bnV4LWFybS1rZXJuZWwK

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 20:28     ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 20:28 UTC (permalink / raw)
  To: linux-arm-kernel



23.02.2017, 04:09, "Maxime Ripard" <maxime.ripard@free-electrons.com>:
> Hi,
>
> On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
>> ?Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
>> ?in a new "Display Engine" (mixers instead of old backends and
>> ?frontends).
>>
>> ?Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> ?Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ?---
>> ??drivers/gpu/drm/sun4i/Kconfig | 8 +
>> ??drivers/gpu/drm/sun4i/Makefile | 1 +
>> ??drivers/gpu/drm/sun4i/sun4i_crtc.c | 6 +-
>> ??drivers/gpu/drm/sun4i/sun4i_drv.c | 38 +++-
>> ??drivers/gpu/drm/sun4i/sun4i_drv.h | 1 +
>> ??drivers/gpu/drm/sun4i/sun4i_layer.c | 92 ++++++--
>> ??drivers/gpu/drm/sun4i/sun4i_layer.h | 1 +
>> ??drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>> ??drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>> ??9 files changed, 674 insertions(+), 23 deletions(-)
>> ??create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>> ??create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
>>
>> ?diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
>> ?index a4b357db8856..8df401fff145 100644
>> ?--- a/drivers/gpu/drm/sun4i/Kconfig
>> ?+++ b/drivers/gpu/drm/sun4i/Kconfig
>> ?@@ -12,3 +12,11 @@ config DRM_SUN4I
>> ????????????Choose this option if you have an Allwinner SoC with a
>> ????????????Display Engine. If M is selected the module will be called
>> ????????????sun4i-drm.
>> ?+
>> ?+config DRM_SUN4I_DE2
>> ?+ bool "Support Display Engine 2.0"
>> ?+ depends on DRM_SUN4I
>> ?+ default MACH_SUN8I
>> ?+ help
>> ?+ Choose this option if you have an Allwinner SoC with a
>> ?+ "Display Engine 2.0".
>> ?diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
>> ?index d625a82a6e5f..890e6e50dfee 100644
>> ?--- a/drivers/gpu/drm/sun4i/Makefile
>> ?+++ b/drivers/gpu/drm/sun4i/Makefile
>> ?@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>>
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
>> ?+obj-$(CONFIG_DRM_SUN4I) += sun8i_mixer.o
>> ??obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>> ?index 4a192210574f..4d2228454726 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>> ?@@ -25,6 +25,7 @@
>> ??#include <video/videomode.h>
>>
>> ??#include "sun4i_backend.h"
>> ?+#include "sun8i_mixer.h"
>> ??#include "sun4i_crtc.h"
>> ??#include "sun4i_drv.h"
>> ??#include "sun4i_tcon.h"
>> ?@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
>>
>> ??????????DRM_DEBUG_DRIVER("Committing plane changes\n");
>>
>> ?- sun4i_backend_commit(drv->backend);
>> ?+ if (drv->backend)
>> ?+ sun4i_backend_commit(drv->backend);
>> ?+ else if (drv->mixer)
>> ?+ sun8i_mixer_commit(drv->mixer);
>>
>> ??????????if (event) {
>> ??????????????????crtc->state->event = NULL;
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
>> ?index 4ce665349f6b..58af38f5e833 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
>> ?@@ -20,12 +20,17 @@
>> ??#include <drm/drm_fb_helper.h>
>> ??#include <drm/drm_of.h>
>>
>> ?+#include <linux/of_device.h>
>> ?+
>> ??#include "sun4i_crtc.h"
>> ??#include "sun4i_drv.h"
>> ??#include "sun4i_framebuffer.h"
>> ??#include "sun4i_layer.h"
>> ??#include "sun4i_tcon.h"
>>
>> ?+#define DE_VER_DE 0
>> ?+#define DE_VER_DE2 1
>> ?+
>> ??static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
>> ??{
>> ??????????struct sun4i_drv *drv = drm->dev_private;
>> ?@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>> ??{
>> ??????????struct drm_device *drm;
>> ??????????struct sun4i_drv *drv;
>> ?- int ret;
>> ?+ int ret, de_ver;
>> ?+
>> ?+ de_ver = (int)of_device_get_match_data(dev);
>>
>> ??????????drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>> ??????????if (IS_ERR(drm))
>> ?@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>> ??????????}
>>
>> ??????????/* Create our layers */
>> ?- drv->layers = sun4i_layers_init(drm);
>> ?+ if (de_ver == DE_VER_DE2)
>> ?+ drv->layers = sun8i_layers_init(drm);
>> ?+ else
>> ?+ drv->layers = sun4i_layers_init(drm);
>> ??????????if (IS_ERR(drv->layers)) {
>> ??????????????????dev_err(drm->dev, "Couldn't create the planes\n");
>> ??????????????????ret = PTR_ERR(drv->layers);
>> ?@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>> ??}
>>
>> ??static const struct of_device_id sun4i_drv_of_table[] = {
>> ?- { .compatible = "allwinner,sun5i-a13-display-engine" },
>> ?- { .compatible = "allwinner,sun6i-a31-display-engine" },
>> ?- { .compatible = "allwinner,sun6i-a31s-display-engine" },
>> ?- { .compatible = "allwinner,sun8i-a33-display-engine" },
>> ?+ {
>> ?+ .compatible = "allwinner,sun5i-a13-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun6i-a31-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun6i-a31s-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun8i-a33-display-engine",
>> ?+ .data = (void *)DE_VER_DE,
>> ?+ },
>> ?+ {
>> ?+ .compatible = "allwinner,sun8i-v3s-display-engine",
>> ?+ .data = (void *)DE_VER_DE2,
>> ?+ },
>> ??????????{ }
>> ??};
>> ??MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
>> ?index 597353eab728..cf1da95b85bd 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
>> ?@@ -18,6 +18,7 @@
>>
>> ??struct sun4i_drv {
>> ??????????struct sun4i_backend *backend;
>> ?+ struct sun8i_mixer *mixer;
>> ??????????struct sun4i_crtc *crtc;
>> ??????????struct sun4i_tcon *tcon;
>>
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
>> ?index 5d53c977bca5..6dcdac38ab69 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
>> ?@@ -16,52 +16,66 @@
>> ??#include <drm/drmP.h>
>>
>> ??#include "sun4i_backend.h"
>> ?+#include "sun8i_mixer.h"
>> ??#include "sun4i_drv.h"
>> ??#include "sun4i_layer.h"
>>
>> ??struct sun4i_plane_desc {
>> ?????????????????enum drm_plane_type type;
>> ?+ /* Pipe is not used in sun8i-mixer */
>> ?????????????????u8 pipe;
>> ?????????????????const uint32_t *formats;
>> ?????????????????uint32_t nformats;
>> ??};
>>
>> ?-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
>> ?+static int sun4i_layer_atomic_check(struct drm_plane *plane,
>> ??????????????????????????????????????????????struct drm_plane_state *state)
>> ??{
>> ??????????return 0;
>> ??}
>>
>> ?-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
>> ?+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>> ?????????????????????????????????????????????????struct drm_plane_state *old_state)
>> ??{
>> ??????????struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>> ??????????struct sun4i_drv *drv = layer->drv;
>> ??????????struct sun4i_backend *backend = drv->backend;
>> ?+ struct sun8i_mixer *mixer = drv->mixer;
>>
>> ?- sun4i_backend_layer_enable(backend, layer->id, false);
>> ?+ if (backend)
>> ?+ sun4i_backend_layer_enable(backend, layer->id, false);
>> ?+ else if (mixer)
>> ?+ sun8i_mixer_layer_enable(mixer, layer->id, false);
>> ??}
>
> I'm not sure it makes sense to reuse that part. You can just create a
> new plane driver entirely.
>
>> ?-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
>> ?+static void sun4i_layer_atomic_update(struct drm_plane *plane,
>> ????????????????????????????????????????????????struct drm_plane_state *old_state)
>> ??{
>> ??????????struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>> ??????????struct sun4i_drv *drv = layer->drv;
>> ??????????struct sun4i_backend *backend = drv->backend;
>> ?-
>> ?- sun4i_backend_update_layer_coord(backend, layer->id, plane);
>> ?- sun4i_backend_update_layer_formats(backend, layer->id, plane);
>> ?- sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>> ?- sun4i_backend_layer_enable(backend, layer->id, true);
>> ?+ struct sun8i_mixer *mixer = drv->mixer;
>> ?+
>> ?+ if (backend) {
>> ?+ sun4i_backend_update_layer_coord(backend, layer->id, plane);
>> ?+ sun4i_backend_update_layer_formats(backend, layer->id, plane);
>> ?+ sun4i_backend_update_layer_buffer(backend, layer->id, plane);
>> ?+ sun4i_backend_layer_enable(backend, layer->id, true);
>> ?+ } else if (mixer) {
>> ?+ sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
>> ?+ sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
>> ?+ sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
>> ?+ sun8i_mixer_layer_enable(mixer, layer->id, true);
>> ?+ }
>> ??}
>>
>> ?-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
>> ?- .atomic_check = sun4i_backend_layer_atomic_check,
>> ?- .atomic_disable = sun4i_backend_layer_atomic_disable,
>> ?- .atomic_update = sun4i_backend_layer_atomic_update,
>> ?+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
>> ?+ .atomic_check = sun4i_layer_atomic_check,
>> ?+ .atomic_disable = sun4i_layer_atomic_disable,
>> ?+ .atomic_update = sun4i_layer_atomic_update,
>> ??};
>>
>> ?-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
>> ?+static const struct drm_plane_funcs sun4i_layer_funcs = {
>> ??????????.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>> ??????????.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>> ??????????.destroy = drm_plane_cleanup,
>> ?@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
>> ??????????DRM_FORMAT_XRGB8888,
>> ??};
>>
>> ?+static const uint32_t sun8i_mixer_layer_formats[] = {
>> ?+ DRM_FORMAT_ARGB8888,
>> ?+ DRM_FORMAT_RGB888,
>> ?+ DRM_FORMAT_XRGB8888,
>> ?+};
>> ?+
>> ??static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>> ??????????{
>> ??????????????????.type = DRM_PLANE_TYPE_PRIMARY,
>> ?@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>> ??????????},
>> ??};
>>
>> ?+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
>> ?+ {
>> ?+ .type = DRM_PLANE_TYPE_PRIMARY,
>> ?+ .formats = sun8i_mixer_layer_formats,
>> ?+ .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>> ?+ },
>> ?+ {
>> ?+ .type = DRM_PLANE_TYPE_OVERLAY,
>> ?+ .formats = sun8i_mixer_layer_formats,
>> ?+ .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>> ?+ },
>> ?+};
>> ?+
>> ??static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>> ??????????????????????????????????????????????????const struct sun4i_plane_desc *plane)
>> ??{
>> ?@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>> ??????????????????return ERR_PTR(-ENOMEM);
>>
>> ??????????ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
>> ?- &sun4i_backend_layer_funcs,
>> ?+ &sun4i_layer_funcs,
>> ?????????????????????????????????????????plane->formats, plane->nformats,
>> ?????????????????????????????????????????plane->type, NULL);
>> ??????????if (ret) {
>> ?@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>> ??????????}
>>
>> ??????????drm_plane_helper_add(&layer->plane,
>> ?- &sun4i_backend_layer_helper_funcs);
>> ?+ &sun4i_layer_helper_funcs);
>> ??????????layer->drv = drv;
>>
>> ??????????if (plane->type == DRM_PLANE_TYPE_PRIMARY)
>> ?@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
>>
>> ??????????return layers;
>> ??}
>> ?+
>> ?+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
>
> And store this (and sun4i_layers_init) in an structure holding the
> function pointers for those.

How should I do it?
If I create a ops struct, where should I reference it?

>
>> ?+{
>> ?+ struct sun4i_layer **layers;
>> ?+ int i;
>> ?+
>> ?+ layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
>> ?+ sizeof(**layers), GFP_KERNEL);
>> ?+ if (!layers)
>> ?+ return ERR_PTR(-ENOMEM);
>> ?+
>> ?+ for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>> ?+ const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
>> ?+ struct sun4i_layer *layer = layers[i];
>> ?+
>> ?+ layer = sun4i_layer_init_one(drm, plane);
>> ?+ if (IS_ERR(layer)) {
>> ?+ dev_err(drm->dev, "Couldn't initialize %s plane\n",
>> ?+ i ? "overlay" : "primary");
>> ?+ return ERR_CAST(layer);
>> ?+ };
>> ?+
>> ?+ layer->id = i;
>> ?+ };
>> ?+
>> ?+ return layers;
>> ?+}
>> ?diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
>> ?index a2f65d7a3f4e..f7b9e5daea50 100644
>> ?--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
>> ?+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
>> ?@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>> ??}
>>
>> ??struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
>> ?+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>>
>> ??#endif /* _SUN4I_LAYER_H_ */
>> ?diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> ?new file mode 100644
>> ?index 000000000000..9427b57240d3
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> ?@@ -0,0 +1,417 @@
>> ?+/*
>> ?+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> ?+ *
>> ?+ * Based on sun4i_backend.c, which is:
>> ?+ * Copyright (C) 2015 Free Electrons
>> ?+ * Copyright (C) 2015 NextThing Co
>> ?+ *
>> ?+ * This program is free software; you can redistribute it and/or
>> ?+ * modify it under the terms of the GNU General Public License as
>> ?+ * published by the Free Software Foundation; either version 2 of
>> ?+ * the License, or (at your option) any later version.
>> ?+ */
>> ?+
>> ?+#include <drm/drmP.h>
>> ?+#include <drm/drm_atomic_helper.h>
>> ?+#include <drm/drm_crtc.h>
>> ?+#include <drm/drm_crtc_helper.h>
>> ?+#include <drm/drm_fb_cma_helper.h>
>> ?+#include <drm/drm_gem_cma_helper.h>
>> ?+#include <drm/drm_plane_helper.h>
>> ?+
>> ?+#include <linux/component.h>
>> ?+#include <linux/reset.h>
>> ?+#include <linux/of_device.h>
>> ?+
>> ?+#include "sun8i_mixer.h"
>> ?+#include "sun4i_drv.h"
>> ?+
>> ?+#define SUN8I_DRAM_OFFSET 0x40000000
>
> PHYS_OFFSET?

Any name is OK.

(P.S. this seems also needed for some DE1s)

>
>> ?+
>> ?+#if defined CONFIG_DRM_SUN4I_DE2
>
> That ifdef should be in the header

So the file only compile if this option is enabled?

And if this option is disabled, inlined null stubs should be
made in header?

>
>> ?+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>> ?+{
>> ?+ DRM_DEBUG_DRIVER("Committing changes\n");
>> ?+
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>> ?+ SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_commit);
>
> Commit could be one of these ops too.
>
>> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>> ?+ int layer, bool enable)
>> ?+{
>> ?+ u32 val;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
>> ?+
>> ?+ if (enable)
>> ?+ val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
>> ?+ else
>> ?+ val = 0;
>
> So you only support the UI channel?
>
> Why do you expose several planes then?

Currently I didn't find any way to enable more than one channel
at the same time, so only the first UI channel is used.

After more knowledges are gained for DE2 mixers we can implement
more functions (for example, Jernejsk have already discovered how
to do color space correlation in DE2 for TVE).

>
>> ?+
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
>> ?+
>> ?+ /* Set the alpha configuration */
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
>
> This should be in a property

What property?

>
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
>> ?+
>> ?+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>> ?+ u32 format, u32 *mode)
>> ?+{
>> ?+ if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
>> ?+ (format == DRM_FORMAT_ARGB8888))
>> ?+ format = DRM_FORMAT_XRGB8888;
>
> Do you actually have that issue.

Yes, it really do, at least screen go black when I set this to ARGB8888 in
U-Boot. (U-Boot is a good experiement area ;-) )

>
>> ?+ switch (format) {
>> ?+ case DRM_FORMAT_ARGB8888:
>> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
>> ?+ break;
>> ?+
>> ?+ case DRM_FORMAT_XRGB8888:
>> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
>> ?+ break;
>> ?+
>> ?+ case DRM_FORMAT_RGB888:
>> ?+ *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
>> ?+ break;
>> ?+
>> ?+ default:
>> ?+ return -EINVAL;
>> ?+ }
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ struct drm_plane_state *state = plane->state;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+ int i;
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
>> ?+
>> ?+ if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
>> ?+ DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
>> ?+ state->crtc_w, state->crtc_h);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ DRM_DEBUG_DRIVER("Updating blender size\n");
>> ?+ for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>> ?+ regmap_write(mixer->regs,
>> ?+ SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ DRM_DEBUG_DRIVER("Updating channel size\n");
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w,
>> ?+ state->crtc_h));
>> ?+ }
>> ?+
>> ?+ /* Set the line width */
>> ?+ DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
>> ?+ fb->pitches[0]);
>> ?+
>> ?+ /* Set height and width */
>> ?+ DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
>> ?+ state->crtc_w, state->crtc_h);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
>> ?+ SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
>> ?+
>> ?+ /* Set base coordinates */
>> ?+ DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
>> ?+ state->crtc_x, state->crtc_y);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>> ?+ SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
>> ?+
>> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ struct drm_plane_state *state = plane->state;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ bool interlaced = false;
>> ?+ u32 val;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+ int ret;
>> ?+
>> ?+ if (plane->state->crtc)
>> ?+ interlaced = plane->state->crtc->state->adjusted_mode.flags
>> ?+ & DRM_MODE_FLAG_INTERLACE;
>> ?+
>> ?+ regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
>> ?+ SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
>> ?+ interlaced ?
>> ?+ SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
>> ?+ interlaced ? "on" : "off");
>> ?+
>> ?+ ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
>> ?+ &val);
>> ?+ if (ret) {
>> ?+ DRM_DEBUG_DRIVER("Invalid format\n");
>> ?+ return ret;
>> ?+ }
>> ?+
>> ?+ regmap_update_bits(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
>> ?+
>> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ struct drm_plane_state *state = plane->state;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ struct drm_gem_cma_object *gem;
>> ?+ dma_addr_t paddr;
>> ?+ uint32_t paddr_u32;
>> ?+ /* Currently the first UI channel is used */
>> ?+ int chan = mixer->cfg->vi_num;
>> ?+ int bpp;
>> ?+
>> ?+ /* Get the physical address of the buffer in memory */
>> ?+ gem = drm_fb_cma_get_gem_obj(fb, 0);
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
>> ?+
>> ?+ /* Compute the start of the displayed memory */
>> ?+ bpp = fb->format->cpp[0];
>> ?+ paddr = gem->paddr + fb->offsets[0];
>> ?+ paddr += (state->src_x >> 16) * bpp;
>> ?+ paddr += (state->src_y >> 16) * fb->pitches[0];
>> ?+ paddr -= SUN8I_DRAM_OFFSET;
>> ?+
>> ?+ DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>> ?+
>> ?+ paddr_u32 = (uint32_t) paddr;
>> ?+
>> ?+ regmap_write(mixer->regs,
>> ?+ SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>> ?+ paddr_u32);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
>> ?+
>> ?+static struct regmap_config sun8i_mixer_regmap_config = {
>> ?+ .reg_bits = 32,
>> ?+ .val_bits = 32,
>> ?+ .reg_stride = 4,
>> ?+ .max_register = 0xbffc, /* guessed */
>> ?+};
>> ?+
>> ?+static int sun8i_mixer_bind(struct device *dev, struct device *master,
>> ?+ void *data)
>> ?+{
>> ?+ struct platform_device *pdev = to_platform_device(dev);
>> ?+ struct drm_device *drm = data;
>> ?+ struct sun4i_drv *drv = drm->dev_private;
>> ?+ struct sun8i_mixer *mixer;
>> ?+ struct resource *res;
>> ?+ void __iomem *regs;
>> ?+ int i, ret;
>> ?+
>> ?+ mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>> ?+ if (!mixer)
>> ?+ return -ENOMEM;
>> ?+ dev_set_drvdata(dev, mixer);
>> ?+ drv->mixer = mixer;
>> ?+
>> ?+ mixer->cfg = of_device_get_match_data(dev);
>> ?+ if (!mixer->cfg)
>> ?+ return -EINVAL;
>> ?+
>> ?+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> ?+ regs = devm_ioremap_resource(dev, res);
>> ?+ if (IS_ERR(regs))
>> ?+ return PTR_ERR(regs);
>> ?+
>> ?+ mixer->regs = devm_regmap_init_mmio(dev, regs,
>> ?+ &sun8i_mixer_regmap_config);
>> ?+ if (IS_ERR(mixer->regs)) {
>> ?+ dev_err(dev, "Couldn't create the mixer regmap\n");
>> ?+ return PTR_ERR(mixer->regs);
>> ?+ }
>> ?+
>> ?+ mixer->reset = devm_reset_control_get(dev, NULL);
>> ?+ if (IS_ERR(mixer->reset)) {
>> ?+ dev_err(dev, "Couldn't get our reset line\n");
>> ?+ return PTR_ERR(mixer->reset);
>> ?+ }
>> ?+
>> ?+ ret = reset_control_deassert(mixer->reset);
>> ?+ if (ret) {
>> ?+ dev_err(dev, "Couldn't deassert our reset line\n");
>> ?+ return ret;
>> ?+ }
>> ?+
>> ?+ mixer->bus_clk = devm_clk_get(dev, "bus");
>> ?+ if (IS_ERR(mixer->bus_clk)) {
>> ?+ dev_err(dev, "Couldn't get the mixer bus clock\n");
>> ?+ ret = PTR_ERR(mixer->bus_clk);
>> ?+ goto err_assert_reset;
>> ?+ }
>> ?+ clk_prepare_enable(mixer->bus_clk);
>> ?+
>> ?+ mixer->mod_clk = devm_clk_get(dev, "mod");
>> ?+ if (IS_ERR(mixer->mod_clk)) {
>> ?+ dev_err(dev, "Couldn't get the mixer module clock\n");
>> ?+ ret = PTR_ERR(mixer->mod_clk);
>> ?+ goto err_disable_bus_clk;
>> ?+ }
>> ?+ clk_prepare_enable(mixer->mod_clk);
>
> Supporting runtime_pm would be better.

But I think it's at least not support yet for DE1 backend...

>
>> ?+ /* Reset the registers */
>> ?+ for (i = 0x0; i < 0x20000; i += 4)
>> ?+ regmap_write(mixer->regs, i, 0);
>
> Do you still need to reset it? Isn't the reset line enough?

Nope, some strange data lies in the DE2 space.

Here's a reg dump of a running DE2 's channel 2 on V3s:
=> md 01104000
01104000: ff000403 010f01df 00000000 00000780    ................
01104010: 03f80000 00000000 00000000 00000000    ................
01104020: 00000000 00000000 00000000 00000000    ................
01104030: 00000000 00000000 00000000 00000000    ................
01104040: 00000000 00000000 00000000 00000000    ................
01104050: 00000000 00000000 00000000 00000000    ................
01104060: 00000000 00000000 00000000 00000000    ................
01104070: 00000000 00000000 00000000 00000000    ................
01104080: 00000000 00000000 010f01df bbe4d3b0    ................
01104090: 54daaf98 13835927 a1479b58 8396b8ad    ...T'Y..X.G.....
011040a0: 07d02ede a39a18da 87d88aba a2d23cf6    .............<..
011040b0: e8bfa8f7 2c8d2b7c f8bbeb3e 98013b75    ....|+.,>...u;..
011040c0: 7c186f48 4ddcdbde b658caf8 76b770d6    Ho.|...M..X..p.v
011040d0: b9a620ef fe215cc1 edd6c4b3 c5f7a66c    . ...\!.....l...
011040e0: 0d1ff6d3 956ca9e8 7f51f80a ad9a184a    ......l...Q.J...
011040f0: ff23e428 772d8d14 f4c03077 8bf495ca    (.#...-ww0......

(P.S. only first 0x88 bytes are used in a UI channel, so the following is
not reseted by U-Boot DE2 driver and is kept the original after reset line
deasserting)

>
>> ?+ /* Enable the mixer */
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
>> ?+ SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>> ?+
>> ?+ /* Initialize blender */
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>> ?+ SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>> ?+ SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
>> ?+ SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
>> ?+ SUN8I_MIXER_BLEND_MODE_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
>> ?+ SUN8I_MIXER_BLEND_MODE_DEF);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
>> ?+ SUN8I_MIXER_BLEND_CK_CTL_DEF);
>> ?+
>> ?+ for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
>> ?+ regmap_write(mixer->regs,
>> ?+ SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
>> ?+ SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
>> ?+
>> ?+ /* Select the first UI channel */
>> ?+ DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
>> ?+ mixer->cfg->vi_num);
>> ?+ regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
>> ?+ mixer->cfg->vi_num);
>> ?+
>> ?+ return 0;
>> ?+
>> ?+ clk_disable_unprepare(mixer->mod_clk);
>> ?+err_disable_bus_clk:
>> ?+ clk_disable_unprepare(mixer->bus_clk);
>> ?+err_assert_reset:
>> ?+ reset_control_assert(mixer->reset);
>> ?+ return ret;
>> ?+}
>> ?+
>> ?+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
>> ?+ void *data)
>> ?+{
>> ?+ struct sun8i_mixer *mixer = dev_get_drvdata(dev);
>> ?+
>> ?+ clk_disable_unprepare(mixer->mod_clk);
>> ?+ clk_disable_unprepare(mixer->bus_clk);
>> ?+ reset_control_assert(mixer->reset);
>> ?+}
>> ?+
>> ?+static const struct component_ops sun8i_mixer_ops = {
>> ?+ .bind = sun8i_mixer_bind,
>> ?+ .unbind = sun8i_mixer_unbind,
>> ?+};
>> ?+
>> ?+static int sun8i_mixer_probe(struct platform_device *pdev)
>> ?+{
>> ?+ return component_add(&pdev->dev, &sun8i_mixer_ops);
>> ?+}
>> ?+
>> ?+static int sun8i_mixer_remove(struct platform_device *pdev)
>> ?+{
>> ?+ component_del(&pdev->dev, &sun8i_mixer_ops);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
>> ?+ .vi_num = 2,
>> ?+ .ui_num = 1,
>> ?+};
>> ?+
>> ?+static const struct of_device_id sun8i_mixer_of_table[] = {
>> ?+ {
>> ?+ .compatible = "allwinner,sun8i-v3s-de2-mixer",
>> ?+ .data = &sun8i_v3s_mixer_cfg
>> ?+ },
>> ?+ { }
>> ?+};
>> ?+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
>> ?+
>> ?+static struct platform_driver sun8i_mixer_platform_driver = {
>> ?+ .probe = sun8i_mixer_probe,
>> ?+ .remove = sun8i_mixer_remove,
>> ?+ .driver = {
>> ?+ .name = "sun8i-mixer",
>> ?+ .of_match_table = sun8i_mixer_of_table,
>> ?+ },
>> ?+};
>> ?+module_platform_driver(sun8i_mixer_platform_driver);
>> ?+
>> ?+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
>> ?+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
>> ?+MODULE_LICENSE("GPL");
>> ?+#else /* DRM_SUN4I_DE2 */
>> ?+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
>> ?+{
>> ?+}
>> ?+
>> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>> ?+ int layer, bool enable)
>> ?+{
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ return -ENOENT;
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ return -ENOENT;
>> ?+}
>> ?+
>> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane)
>> ?+{
>> ?+ return -ENOENT;
>> ?+}
>> ?+#endif /* CONFIG_DRM_SUN4I_DE2 */
>> ?diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
>> ?new file mode 100644
>> ?index 000000000000..0bc134b3bc98
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
>> ?@@ -0,0 +1,133 @@
>> ?+/*
>> ?+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
>> ?+ *
>> ?+ * This program is free software; you can redistribute it and/or
>> ?+ * modify it under the terms of the GNU General Public License as
>> ?+ * published by the Free Software Foundation; either version 2 of
>> ?+ * the License, or (at your option) any later version.
>> ?+ */
>> ?+
>> ?+#ifndef _SUN8I_MIXER_H_
>> ?+#define _SUN8I_MIXER_H_
>> ?+
>> ?+#include <linux/clk.h>
>> ?+#include <linux/regmap.h>
>> ?+#include <linux/reset.h>
>> ?+
>> ?+#include "sun4i_layer.h"
>> ?+
>> ?+#define SUN8I_MIXER_MAX_CHAN_COUNT 4
>> ?+
>> ?+#define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
>> ?+#define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x))
>> ?+
>> ?+#define SUN8I_MIXER_GLOBAL_CTL 0x0
>> ?+#define SUN8I_MIXER_GLOBAL_STATUS 0x4
>> ?+#define SUN8I_MIXER_GLOBAL_DBUFF 0x8
>> ?+#define SUN8I_MIXER_GLOBAL_SIZE 0xc
>> ?+
>> ?+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1
>> ?+
>> ?+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1
>> ?+
>> ?+#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000
>> ?+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0)
>> ?+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4)
>> ?+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8)
>> ?+#define SUN8I_MIXER_BLEND_ROUTE 0x1080
>> ?+#define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084
>> ?+#define SUN8I_MIXER_BLEND_BKCOLOR 0x1088
>> ?+#define SUN8I_MIXER_BLEND_OUTSIZE 0x108c
>> ?+#define SUN8I_MIXER_BLEND_MODE(x) (0x1090 + 0x04 * (x))
>> ?+#define SUN8I_MIXER_BLEND_CK_CTL 0x10b0
>> ?+#define SUN8I_MIXER_BLEND_CK_CFG 0x10b4
>> ?+#define SUN8I_MIXER_BLEND_CK_MAX(x) (0x10c0 + 0x04 * (x))
>> ?+#define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x))
>> ?+#define SUN8I_MIXER_BLEND_OUTCTL 0x10fc
>> ?+
>> ?+/* The following numbers are some still unknown magic numbers */
>> ?+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000
>> ?+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101
>> ?+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0
>> ?+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000
>> ?+#define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301
>> ?+#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0
>> ?+
>> ?+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1)
>> ?+
>> ?+/*
>> ?+ * VI channels are not used now, but the support of them may be introduced in
>> ?+ * the future.
>> ?+ */
>> ?+
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
>> ?+ (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
>> ?+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80)
>> ?+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84)
>> ?+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88)
>> ?+
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8)
>> ?+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24)
>> ?+
>> ?+/*
>> ?+ * These sun-engines are still unknown now, the EN registers are here only to
>> ?+ * be used to disable these sub-engines.
>> ?+ */
>> ?+#define SUN8I_MIXER_VSU_EN 0x20000
>> ?+#define SUN8I_MIXER_GSU1_EN 0x30000
>> ?+#define SUN8I_MIXER_GSU2_EN 0x40000
>> ?+#define SUN8I_MIXER_GSU3_EN 0x50000
>> ?+#define SUN8I_MIXER_FCE_EN 0xa0000
>> ?+#define SUN8I_MIXER_BWS_EN 0xa2000
>> ?+#define SUN8I_MIXER_LTI_EN 0xa4000
>> ?+#define SUN8I_MIXER_PEAK_EN 0xa6000
>> ?+#define SUN8I_MIXER_ASE_EN 0xa8000
>> ?+#define SUN8I_MIXER_FCC_EN 0xaa000
>> ?+#define SUN8I_MIXER_DCSC_EN 0xb0000
>> ?+
>> ?+struct sun8i_mixer_cfg {
>> ?+ int vi_num;
>> ?+ int ui_num;
>> ?+};
>> ?+
>> ?+struct sun8i_mixer {
>> ?+ struct regmap *regs;
>> ?+
>> ?+ const struct sun8i_mixer_cfg *cfg;
>> ?+
>> ?+ struct reset_control *reset;
>> ?+
>> ?+ struct clk *bus_clk;
>> ?+ struct clk *mod_clk;
>> ?+};
>> ?+
>> ?+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
>> ?+
>> ?+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
>> ?+ int layer, bool enable);
>> ?+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane);
>> ?+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane);
>> ?+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
>> ?+ int layer, struct drm_plane *plane);
>> ?+#endif /* _SUN8I_MIXER_H_ */
>> ?--
>> ?2.11.1
>
> Thanks,
> Maxime
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
  2017-02-22 15:23 ` Icenowy Zheng
  (?)
@ 2017-02-22 20:09   ` Maxime Ripard
  -1 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 20:09 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Rob Herring, Chen-Yu Tsai, Jernej Skrabec, David Airlie,
	Jean-Francois Moine, linux-clk, devicetree, linux-arm-kernel,
	linux-kernel, dri-devel, linux-sunxi

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

Hi,

On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>  
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> index 4a192210574f..4d2228454726 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
>  
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
>  
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
>  
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);
>  
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
>  
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
>  
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
>  {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
>  
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
>  
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>  }
>  
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
> index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
>  
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
>  
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
> index 5d53c977bca5..6dcdac38ab69 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
>  
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
>  
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
>  
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
>  
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
>  
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }

I'm not sure it makes sense to reuse that part. You can just create a
new plane driver entirely.

>  
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
>  
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
>  
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
>  	DRM_FORMAT_XRGB8888,
>  };
>  
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	},
>  };
>  
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  		return ERR_PTR(-ENOMEM);
>  
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  	}
>  
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
>  
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
>  
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)

And store this (and sun4i_layers_init) in an structure holding the
function pointers for those.

> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> index a2f65d7a3f4e..f7b9e5daea50 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
>  
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>  
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000

PHYS_OFFSET?

> +
> +#if defined CONFIG_DRM_SUN4I_DE2

That ifdef should be in the header

> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);

Commit could be one of these ops too.

> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;

So you only support the UI channel?

Why do you expose several planes then?

> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);

This should be in a property

> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;

Do you actually have that issue.

> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);

Supporting runtime_pm would be better.

> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);

Do you still need to reset it? Isn't the reset line enough?

> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced in
> + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only to
> + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> -- 
> 2.11.1
> 

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 20:09   ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 20:09 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: Jean-Francois Moine, Jernej Skrabec, devicetree, linux-sunxi,
	linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring, linux-clk,
	linux-arm-kernel


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

Hi,

On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>  
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> index 4a192210574f..4d2228454726 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
>  
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
>  
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
>  
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);
>  
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
>  
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
>  
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
>  {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
>  
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
>  
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>  }
>  
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
> index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
>  
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
>  
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
> index 5d53c977bca5..6dcdac38ab69 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
>  
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
>  
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
>  
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
>  
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
>  
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }

I'm not sure it makes sense to reuse that part. You can just create a
new plane driver entirely.

>  
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
>  
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
>  
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
>  	DRM_FORMAT_XRGB8888,
>  };
>  
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	},
>  };
>  
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  		return ERR_PTR(-ENOMEM);
>  
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  	}
>  
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
>  
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
>  
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)

And store this (and sun4i_layers_init) in an structure holding the
function pointers for those.

> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> index a2f65d7a3f4e..f7b9e5daea50 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
>  
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>  
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000

PHYS_OFFSET?

> +
> +#if defined CONFIG_DRM_SUN4I_DE2

That ifdef should be in the header

> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);

Commit could be one of these ops too.

> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;

So you only support the UI channel?

Why do you expose several planes then?

> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);

This should be in a property

> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;

Do you actually have that issue.

> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);

Supporting runtime_pm would be better.

> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);

Do you still need to reset it? Isn't the reset line enough?

> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced in
> + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only to
> + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> -- 
> 2.11.1
> 

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

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

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 20:09   ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2017-02-22 20:09 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Wed, Feb 22, 2017 at 11:23:06PM +0800, Icenowy Zheng wrote:
> Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
> in a new "Display Engine" (mixers instead of old backends and
> frontends).
> 
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> ---
>  drivers/gpu/drm/sun4i/Kconfig       |   8 +
>  drivers/gpu/drm/sun4i/Makefile      |   1 +
>  drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
>  drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
>  drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
>  drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
>  drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
>  drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
>  9 files changed, 674 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index a4b357db8856..8df401fff145 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -12,3 +12,11 @@ config DRM_SUN4I
>  	  Choose this option if you have an Allwinner SoC with a
>  	  Display Engine. If M is selected the module will be called
>  	  sun4i-drm.
> +
> +config DRM_SUN4I_DE2
> +	bool "Support Display Engine 2.0"
> +	depends on DRM_SUN4I
> +	default MACH_SUN8I
> +	help
> +	  Choose this option if you have an Allwinner SoC with a
> +	  "Display Engine 2.0".
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index d625a82a6e5f..890e6e50dfee 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
>  
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
> +obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> index 4a192210574f..4d2228454726 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
> @@ -25,6 +25,7 @@
>  #include <video/videomode.h>
>  
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_tcon.h"
> @@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
>  
>  	DRM_DEBUG_DRIVER("Committing plane changes\n");
>  
> -	sun4i_backend_commit(drv->backend);
> +	if (drv->backend)
> +		sun4i_backend_commit(drv->backend);
> +	else if (drv->mixer)
> +		sun8i_mixer_commit(drv->mixer);
>  
>  	if (event) {
>  		crtc->state->event = NULL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 4ce665349f6b..58af38f5e833 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -20,12 +20,17 @@
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_of.h>
>  
> +#include <linux/of_device.h>
> +
>  #include "sun4i_crtc.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_framebuffer.h"
>  #include "sun4i_layer.h"
>  #include "sun4i_tcon.h"
>  
> +#define DE_VER_DE	0
> +#define DE_VER_DE2	1
> +
>  static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
>  {
>  	struct sun4i_drv *drv = drm->dev_private;
> @@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
>  {
>  	struct drm_device *drm;
>  	struct sun4i_drv *drv;
> -	int ret;
> +	int ret, de_ver;
> +
> +	de_ver = (int)of_device_get_match_data(dev);
>  
>  	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
>  	if (IS_ERR(drm))
> @@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
>  	}
>  
>  	/* Create our layers */
> -	drv->layers = sun4i_layers_init(drm);
> +	if (de_ver == DE_VER_DE2)
> +		drv->layers = sun8i_layers_init(drm);
> +	else
> +		drv->layers = sun4i_layers_init(drm);
>  	if (IS_ERR(drv->layers)) {
>  		dev_err(drm->dev, "Couldn't create the planes\n");
>  		ret = PTR_ERR(drv->layers);
> @@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>  }
>  
>  static const struct of_device_id sun4i_drv_of_table[] = {
> -	{ .compatible = "allwinner,sun5i-a13-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31-display-engine" },
> -	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
> -	{ .compatible = "allwinner,sun8i-a33-display-engine" },
> +	{
> +		.compatible = "allwinner,sun5i-a13-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31s-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-a33-display-engine",
> +		.data = (void *)DE_VER_DE,
> +	},
> +	{
> +		.compatible = "allwinner,sun8i-v3s-display-engine",
> +		.data = (void *)DE_VER_DE2,
> +	},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
> index 597353eab728..cf1da95b85bd 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
> @@ -18,6 +18,7 @@
>  
>  struct sun4i_drv {
>  	struct sun4i_backend	*backend;
> +	struct sun8i_mixer	*mixer;
>  	struct sun4i_crtc	*crtc;
>  	struct sun4i_tcon	*tcon;
>  
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
> index 5d53c977bca5..6dcdac38ab69 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> @@ -16,52 +16,66 @@
>  #include <drm/drmP.h>
>  
>  #include "sun4i_backend.h"
> +#include "sun8i_mixer.h"
>  #include "sun4i_drv.h"
>  #include "sun4i_layer.h"
>  
>  struct sun4i_plane_desc {
>  	       enum drm_plane_type     type;
> +	       /* Pipe is not used in sun8i-mixer */
>  	       u8                      pipe;
>  	       const uint32_t          *formats;
>  	       uint32_t                nformats;
>  };
>  
> -static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> +static int sun4i_layer_atomic_check(struct drm_plane *plane,
>  					    struct drm_plane_state *state)
>  {
>  	return 0;
>  }
>  
> -static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
> +static void sun4i_layer_atomic_disable(struct drm_plane *plane,
>  					       struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> +	struct sun8i_mixer *mixer = drv->mixer;
>  
> -	sun4i_backend_layer_enable(backend, layer->id, false);
> +	if (backend)
> +		sun4i_backend_layer_enable(backend, layer->id, false);
> +	else if (mixer)
> +		sun8i_mixer_layer_enable(mixer, layer->id, false);
>  }

I'm not sure it makes sense to reuse that part. You can just create a
new plane driver entirely.

>  
> -static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
> +static void sun4i_layer_atomic_update(struct drm_plane *plane,
>  					      struct drm_plane_state *old_state)
>  {
>  	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
>  	struct sun4i_drv *drv = layer->drv;
>  	struct sun4i_backend *backend = drv->backend;
> -
> -	sun4i_backend_update_layer_coord(backend, layer->id, plane);
> -	sun4i_backend_update_layer_formats(backend, layer->id, plane);
> -	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> -	sun4i_backend_layer_enable(backend, layer->id, true);
> +	struct sun8i_mixer *mixer = drv->mixer;
> +
> +	if (backend) {
> +		sun4i_backend_update_layer_coord(backend, layer->id, plane);
> +		sun4i_backend_update_layer_formats(backend, layer->id, plane);
> +		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
> +		sun4i_backend_layer_enable(backend, layer->id, true);
> +	} else if (mixer) {
> +		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
> +		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
> +		sun8i_mixer_layer_enable(mixer, layer->id, true);
> +	}
>  }
>  
> -static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
> -	.atomic_check	= sun4i_backend_layer_atomic_check,
> -	.atomic_disable	= sun4i_backend_layer_atomic_disable,
> -	.atomic_update	= sun4i_backend_layer_atomic_update,
> +static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
> +	.atomic_check	= sun4i_layer_atomic_check,
> +	.atomic_disable	= sun4i_layer_atomic_disable,
> +	.atomic_update	= sun4i_layer_atomic_update,
>  };
>  
> -static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> +static const struct drm_plane_funcs sun4i_layer_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
>  	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
>  	.destroy		= drm_plane_cleanup,
> @@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
>  	DRM_FORMAT_XRGB8888,
>  };
>  
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_XRGB8888,
> +};
> +
>  static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	{
>  		.type = DRM_PLANE_TYPE_PRIMARY,
> @@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
>  	},
>  };
>  
> +static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
> +	{
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +	{
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.formats = sun8i_mixer_layer_formats,
> +		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> +	},
> +};
> +
>  static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  						const struct sun4i_plane_desc *plane)
>  {
> @@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  		return ERR_PTR(-ENOMEM);
>  
>  	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
> -				       &sun4i_backend_layer_funcs,
> +				       &sun4i_layer_funcs,
>  				       plane->formats, plane->nformats,
>  				       plane->type, NULL);
>  	if (ret) {
> @@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>  	}
>  
>  	drm_plane_helper_add(&layer->plane,
> -			     &sun4i_backend_layer_helper_funcs);
> +			     &sun4i_layer_helper_funcs);
>  	layer->drv = drv;
>  
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
> @@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
>  
>  	return layers;
>  }
> +
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)

And store this (and sun4i_layers_init) in an structure holding the
function pointers for those.

> +{
> +	struct sun4i_layer **layers;
> +	int i;
> +
> +	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
> +			      sizeof(**layers), GFP_KERNEL);
> +	if (!layers)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> +		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
> +		struct sun4i_layer *layer = layers[i];
> +
> +		layer = sun4i_layer_init_one(drm, plane);
> +		if (IS_ERR(layer)) {
> +			dev_err(drm->dev, "Couldn't initialize %s plane\n",
> +				i ? "overlay" : "primary");
> +			return ERR_CAST(layer);
> +		};
> +
> +		layer->id = i;
> +	};
> +
> +	return layers;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
> index a2f65d7a3f4e..f7b9e5daea50 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
> @@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>  }
>  
>  struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
> +struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
>  
>  #endif /* _SUN4I_LAYER_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> new file mode 100644
> index 000000000000..9427b57240d3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * Based on sun4i_backend.c, which is:
> + *   Copyright (C) 2015 Free Electrons
> + *   Copyright (C) 2015 NextThing Co
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include <linux/component.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun8i_mixer.h"
> +#include "sun4i_drv.h"
> +
> +#define SUN8I_DRAM_OFFSET 0x40000000

PHYS_OFFSET?

> +
> +#if defined CONFIG_DRM_SUN4I_DE2

That ifdef should be in the header

> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +	DRM_DEBUG_DRIVER("Committing changes\n");
> +
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> +		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
> +EXPORT_SYMBOL(sun8i_mixer_commit);

Commit could be one of these ops too.

> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +
> +	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
> +
> +	if (enable)
> +		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
> +	else
> +		val = 0;

So you only support the UI channel?

Why do you expose several planes then?

> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
> +
> +	/* Set the alpha configuration */
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);

This should be in a property

> +}
> +EXPORT_SYMBOL(sun8i_mixer_layer_enable);
> +
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> +					     u32 format, u32 *mode)
> +{
> +	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +	    (format == DRM_FORMAT_ARGB8888))
> +		format = DRM_FORMAT_XRGB8888;

Do you actually have that issue.

> +	switch (format) {
> +	case DRM_FORMAT_ARGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
> +		break;
> +
> +	case DRM_FORMAT_XRGB8888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
> +		break;
> +
> +	case DRM_FORMAT_RGB888:
> +		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
> +
> +	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
> +				 state->crtc_w, state->crtc_h);
> +		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating blender size\n");
> +		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +			regmap_write(mixer->regs,
> +				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
> +				     SUN8I_MIXER_SIZE(state->crtc_w,
> +						      state->crtc_h));
> +		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +		DRM_DEBUG_DRIVER("Updating channel size\n");
> +		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
> +			     SUN8I_MIXER_SIZE(state->crtc_w,
> +					      state->crtc_h));
> +	}
> +
> +	/* Set the line width */
> +	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
> +		     fb->pitches[0]);
> +
> +	/* Set height and width */
> +	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
> +			 state->crtc_w, state->crtc_h);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
> +		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
> +
> +	/* Set base coordinates */
> +	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> +			 state->crtc_x, state->crtc_y);
> +	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> +		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	bool interlaced = false;
> +	u32 val;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int ret;
> +
> +	if (plane->state->crtc)
> +		interlaced = plane->state->crtc->state->adjusted_mode.flags
> +			& DRM_MODE_FLAG_INTERLACE;
> +
> +	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
> +			   interlaced ?
> +			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
> +
> +	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
> +			 interlaced ? "on" : "off");
> +
> +	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
> +						&val);
> +	if (ret) {
> +		DRM_DEBUG_DRIVER("Invalid format\n");
> +		return ret;
> +	}
> +
> +	regmap_update_bits(mixer->regs,
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> +			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	dma_addr_t paddr;
> +	uint32_t paddr_u32;
> +	/* Currently the first UI channel is used */
> +	int chan = mixer->cfg->vi_num;
> +	int bpp;
> +
> +	/* Get the physical address of the buffer in memory */
> +	gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> +	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
> +
> +	/* Compute the start of the displayed memory */
> +	bpp = fb->format->cpp[0];
> +	paddr = gem->paddr + fb->offsets[0];
> +	paddr += (state->src_x >> 16) * bpp;
> +	paddr += (state->src_y >> 16) * fb->pitches[0];
> +	paddr -= SUN8I_DRAM_OFFSET;
> +
> +	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> +	paddr_u32 = (uint32_t) paddr;
> +
> +	regmap_write(mixer->regs,
> +		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> +		     paddr_u32);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0xbffc, /* guessed */
> +};
> +
> +static int sun8i_mixer_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct sun4i_drv *drv = drm->dev_private;
> +	struct sun8i_mixer *mixer;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int i, ret;
> +
> +	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +	if (!mixer)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, mixer);
> +	drv->mixer = mixer;
> +
> +	mixer->cfg = of_device_get_match_data(dev);
> +	if (!mixer->cfg)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	mixer->regs = devm_regmap_init_mmio(dev, regs,
> +					      &sun8i_mixer_regmap_config);
> +	if (IS_ERR(mixer->regs)) {
> +		dev_err(dev, "Couldn't create the mixer regmap\n");
> +		return PTR_ERR(mixer->regs);
> +	}
> +
> +	mixer->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(mixer->reset)) {
> +		dev_err(dev, "Couldn't get our reset line\n");
> +		return PTR_ERR(mixer->reset);
> +	}
> +
> +	ret = reset_control_deassert(mixer->reset);
> +	if (ret) {
> +		dev_err(dev, "Couldn't deassert our reset line\n");
> +		return ret;
> +	}
> +
> +	mixer->bus_clk = devm_clk_get(dev, "bus");
> +	if (IS_ERR(mixer->bus_clk)) {
> +		dev_err(dev, "Couldn't get the mixer bus clock\n");
> +		ret = PTR_ERR(mixer->bus_clk);
> +		goto err_assert_reset;
> +	}
> +	clk_prepare_enable(mixer->bus_clk);
> +
> +	mixer->mod_clk = devm_clk_get(dev, "mod");
> +	if (IS_ERR(mixer->mod_clk)) {
> +		dev_err(dev, "Couldn't get the mixer module clock\n");
> +		ret = PTR_ERR(mixer->mod_clk);
> +		goto err_disable_bus_clk;
> +	}
> +	clk_prepare_enable(mixer->mod_clk);

Supporting runtime_pm would be better.

> +	/* Reset the registers */
> +	for (i = 0x0; i < 0x20000; i += 4)
> +		regmap_write(mixer->regs, i, 0);

Do you still need to reset it? Isn't the reset line enough?

> +	/* Enable the mixer */
> +	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
> +		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> +	/* Initialize blender */
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> +		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> +		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
> +		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
> +		     SUN8I_MIXER_BLEND_MODE_DEF);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
> +		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> +	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
> +		regmap_write(mixer->regs,
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
> +			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
> +
> +	/* Select the first UI channel */
> +	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
> +			 mixer->cfg->vi_num);
> +	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
> +		     mixer->cfg->vi_num);
> +
> +	return 0;
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +err_disable_bus_clk:
> +	clk_disable_unprepare(mixer->bus_clk);
> +err_assert_reset:
> +	reset_control_assert(mixer->reset);
> +	return ret;
> +}
> +
> +static void sun8i_mixer_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mixer->mod_clk);
> +	clk_disable_unprepare(mixer->bus_clk);
> +	reset_control_assert(mixer->reset);
> +}
> +
> +static const struct component_ops sun8i_mixer_ops = {
> +	.bind	= sun8i_mixer_bind,
> +	.unbind	= sun8i_mixer_unbind,
> +};
> +
> +static int sun8i_mixer_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &sun8i_mixer_ops);
> +}
> +
> +static int sun8i_mixer_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &sun8i_mixer_ops);
> +
> +	return 0;
> +}
> +
> +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
> +	.vi_num = 2,
> +	.ui_num = 1,
> +};
> +
> +static const struct of_device_id sun8i_mixer_of_table[] = {
> +	{
> +		.compatible = "allwinner,sun8i-v3s-de2-mixer",
> +		.data = &sun8i_v3s_mixer_cfg
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
> +
> +static struct platform_driver sun8i_mixer_platform_driver = {
> +	.probe		= sun8i_mixer_probe,
> +	.remove		= sun8i_mixer_remove,
> +	.driver		= {
> +		.name		= "sun8i-mixer",
> +		.of_match_table	= sun8i_mixer_of_table,
> +	},
> +};
> +module_platform_driver(sun8i_mixer_platform_driver);
> +
> +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
> +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
> +MODULE_LICENSE("GPL");
> +#else /* DRM_SUN4I_DE2 */
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer)
> +{
> +}
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable)
> +{
> +}
> +
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane)
> +{
> +	return -ENOENT;
> +}
> +#endif /* CONFIG_DRM_SUN4I_DE2 */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> new file mode 100644
> index 000000000000..0bc134b3bc98
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#ifndef _SUN8I_MIXER_H_
> +#define _SUN8I_MIXER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include "sun4i_layer.h"
> +
> +#define SUN8I_MIXER_MAX_CHAN_COUNT		4
> +
> +#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
> +#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
> +
> +#define SUN8I_MIXER_GLOBAL_CTL			0x0
> +#define SUN8I_MIXER_GLOBAL_STATUS		0x4
> +#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
> +#define SUN8I_MIXER_GLOBAL_SIZE			0xc
> +
> +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
> +
> +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
> +
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
> +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
> +#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
> +#define SUN8I_MIXER_BLEND_ROUTE			0x1080
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
> +#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
> +#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
> +#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
> +#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
> +#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
> +#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
> +
> +/* The following numbers are some still unknown magic numbers */
> +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
> +#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
> +#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
> +#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
> +#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
> +#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
> +
> +#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
> +
> +/*
> + * VI channels are not used now, but the support of them may be introduced in
> + * the future.
> + */
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
> +			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
> +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
> +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
> +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
> +
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
> +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
> +
> +/*
> + * These sun-engines are still unknown now, the EN registers are here only to
> + * be used to disable these sub-engines.
> + */
> +#define SUN8I_MIXER_VSU_EN			0x20000
> +#define SUN8I_MIXER_GSU1_EN			0x30000
> +#define SUN8I_MIXER_GSU2_EN			0x40000
> +#define SUN8I_MIXER_GSU3_EN			0x50000
> +#define SUN8I_MIXER_FCE_EN			0xa0000
> +#define SUN8I_MIXER_BWS_EN			0xa2000
> +#define SUN8I_MIXER_LTI_EN			0xa4000
> +#define SUN8I_MIXER_PEAK_EN			0xa6000
> +#define SUN8I_MIXER_ASE_EN			0xa8000
> +#define SUN8I_MIXER_FCC_EN			0xaa000
> +#define SUN8I_MIXER_DCSC_EN			0xb0000
> +
> +struct sun8i_mixer_cfg {
> +	int		vi_num;
> +	int		ui_num;
> +};
> +
> +struct sun8i_mixer {
> +	struct regmap			*regs;
> +
> +	const struct sun8i_mixer_cfg	*cfg;
> +
> +	struct reset_control		*reset;
> +
> +	struct clk			*bus_clk;
> +	struct clk			*mod_clk;
> +};
> +
> +void sun8i_mixer_commit(struct sun8i_mixer *mixer);
> +
> +void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
> +				int layer, bool enable);
> +int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
> +				     int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
> +				       int layer, struct drm_plane *plane);
> +int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
> +				      int layer, struct drm_plane *plane);
> +#endif /* _SUN8I_MIXER_H_ */
> -- 
> 2.11.1
> 

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170222/1f4aa19f/attachment-0001.sig>

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 15:23 ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:23 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng

Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
in a new "Display Engine" (mixers instead of old backends and
frontends).

Add support for the mixer on Allwinner V3s SoC; it's the simplest one.

Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
---
 drivers/gpu/drm/sun4i/Kconfig       |   8 +
 drivers/gpu/drm/sun4i/Makefile      |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
 drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
 drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
 9 files changed, 674 insertions(+), 23 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..8df401fff145 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,11 @@ config DRM_SUN4I
 	  Choose this option if you have an Allwinner SoC with a
 	  Display Engine. If M is selected the module will be called
 	  sun4i-drm.
+
+config DRM_SUN4I_DE2
+	bool "Support Display Engine 2.0"
+	depends on DRM_SUN4I
+	default MACH_SUN8I
+	help
+	  Choose this option if you have an Allwinner SoC with a
+	  "Display Engine 2.0".
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index d625a82a6e5f..890e6e50dfee 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a192210574f..4d2228454726 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,6 +25,7 @@
 #include <video/videomode.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	DRM_DEBUG_DRIVER("Committing plane changes\n");
 
-	sun4i_backend_commit(drv->backend);
+	if (drv->backend)
+		sun4i_backend_commit(drv->backend);
+	else if (drv->mixer)
+		sun8i_mixer_commit(drv->mixer);
 
 	if (event) {
 		crtc->state->event = NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 4ce665349f6b..58af38f5e833 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -20,12 +20,17 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/of_device.h>
+
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_framebuffer.h"
 #include "sun4i_layer.h"
 #include "sun4i_tcon.h"
 
+#define DE_VER_DE	0
+#define DE_VER_DE2	1
+
 static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
 	struct sun4i_drv *drv = drm->dev_private;
@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
 {
 	struct drm_device *drm;
 	struct sun4i_drv *drv;
-	int ret;
+	int ret, de_ver;
+
+	de_ver = (int)of_device_get_match_data(dev);
 
 	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 	if (IS_ERR(drm))
@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
 	}
 
 	/* Create our layers */
-	drv->layers = sun4i_layers_init(drm);
+	if (de_ver == DE_VER_DE2)
+		drv->layers = sun8i_layers_init(drm);
+	else
+		drv->layers = sun4i_layers_init(drm);
 	if (IS_ERR(drv->layers)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
 		ret = PTR_ERR(drv->layers);
@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_drv_of_table[] = {
-	{ .compatible = "allwinner,sun5i-a13-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
-	{ .compatible = "allwinner,sun8i-a33-display-engine" },
+	{
+		.compatible = "allwinner,sun5i-a13-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-a33-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-display-engine",
+		.data = (void *)DE_VER_DE2,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 597353eab728..cf1da95b85bd 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -18,6 +18,7 @@
 
 struct sun4i_drv {
 	struct sun4i_backend	*backend;
+	struct sun8i_mixer	*mixer;
 	struct sun4i_crtc	*crtc;
 	struct sun4i_tcon	*tcon;
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index 5d53c977bca5..6dcdac38ab69 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,52 +16,66 @@
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
 struct sun4i_plane_desc {
 	       enum drm_plane_type     type;
+	       /* Pipe is not used in sun8i-mixer */
 	       u8                      pipe;
 	       const uint32_t          *formats;
 	       uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+static int sun4i_layer_atomic_check(struct drm_plane *plane,
 					    struct drm_plane_state *state)
 {
 	return 0;
 }
 
-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
+	struct sun8i_mixer *mixer = drv->mixer;
 
-	sun4i_backend_layer_enable(backend, layer->id, false);
+	if (backend)
+		sun4i_backend_layer_enable(backend, layer->id, false);
+	else if (mixer)
+		sun8i_mixer_layer_enable(mixer, layer->id, false);
 }
 
-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+static void sun4i_layer_atomic_update(struct drm_plane *plane,
 					      struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
-
-	sun4i_backend_update_layer_coord(backend, layer->id, plane);
-	sun4i_backend_update_layer_formats(backend, layer->id, plane);
-	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
-	sun4i_backend_layer_enable(backend, layer->id, true);
+	struct sun8i_mixer *mixer = drv->mixer;
+
+	if (backend) {
+		sun4i_backend_update_layer_coord(backend, layer->id, plane);
+		sun4i_backend_update_layer_formats(backend, layer->id, plane);
+		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+		sun4i_backend_layer_enable(backend, layer->id, true);
+	} else if (mixer) {
+		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
+		sun8i_mixer_layer_enable(mixer, layer->id, true);
+	}
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-	.atomic_check	= sun4i_backend_layer_atomic_check,
-	.atomic_disable	= sun4i_backend_layer_atomic_disable,
-	.atomic_update	= sun4i_backend_layer_atomic_update,
+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
+	.atomic_check	= sun4i_layer_atomic_check,
+	.atomic_disable	= sun4i_layer_atomic_disable,
+	.atomic_update	= sun4i_layer_atomic_update,
 };
 
-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+static const struct drm_plane_funcs sun4i_layer_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
 	.destroy		= drm_plane_cleanup,
@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
+static const uint32_t sun8i_mixer_layer_formats[] = {
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XRGB8888,
+};
+
 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	{
 		.type = DRM_PLANE_TYPE_PRIMARY,
@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	},
 };
 
+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
+	{
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+	{
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 						const struct sun4i_plane_desc *plane)
 {
@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 
 	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
-				       &sun4i_backend_layer_funcs,
+				       &sun4i_layer_funcs,
 				       plane->formats, plane->nformats,
 				       plane->type, NULL);
 	if (ret) {
@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 	}
 
 	drm_plane_helper_add(&layer->plane,
-			     &sun4i_backend_layer_helper_funcs);
+			     &sun4i_layer_helper_funcs);
 	layer->drv = drv;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
 
 	return layers;
 }
+
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
+{
+	struct sun4i_layer **layers;
+	int i;
+
+	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
+			      sizeof(**layers), GFP_KERNEL);
+	if (!layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
+		struct sun4i_layer *layer = layers[i];
+
+		layer = sun4i_layer_init_one(drm, plane);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
+
+		layer->id = i;
+	};
+
+	return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index a2f65d7a3f4e..f7b9e5daea50 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
 }
 
 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
 
 #endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..9427b57240d3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * Based on sun4i_backend.c, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun8i_mixer.h"
+#include "sun4i_drv.h"
+
+#define SUN8I_DRAM_OFFSET 0x40000000
+
+#if defined CONFIG_DRM_SUN4I_DE2
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+	DRM_DEBUG_DRIVER("Committing changes\n");
+
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+EXPORT_SYMBOL(sun8i_mixer_commit);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+
+	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	/* Set the alpha configuration */
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
+}
+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+					     u32 format, u32 *mode)
+{
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+	    (format == DRM_FORMAT_ARGB8888))
+		format = DRM_FORMAT_XRGB8888;
+
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
+		break;
+
+	case DRM_FORMAT_RGB888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int i;
+
+	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 state->crtc_w, state->crtc_h);
+		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating blender size\n");
+		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+			regmap_write(mixer->regs,
+				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
+				     SUN8I_MIXER_SIZE(state->crtc_w,
+						      state->crtc_h));
+		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating channel size\n");
+		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+	}
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     fb->pitches[0]);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+			 state->crtc_w, state->crtc_h);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->crtc_x, state->crtc_y);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	bool interlaced = false;
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int ret;
+
+	if (plane->state->crtc)
+		interlaced = plane->state->crtc->state->adjusted_mode.flags
+			& DRM_MODE_FLAG_INTERLACE;
+
+	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+			   interlaced ?
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
+
+	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+			 interlaced ? "on" : "off");
+
+	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
+						&val);
+	if (ret) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return ret;
+	}
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	uint32_t paddr_u32;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+	paddr += (state->src_x >> 16) * bpp;
+	paddr += (state->src_y >> 16) * fb->pitches[0];
+	paddr -= SUN8I_DRAM_OFFSET;
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	paddr_u32 = (uint32_t) paddr;
+
+	regmap_write(mixer->regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     paddr_u32);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0xbffc, /* guessed */
+};
+
+static int sun8i_mixer_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct sun4i_drv *drv = drm->dev_private;
+	struct sun8i_mixer *mixer;
+	struct resource *res;
+	void __iomem *regs;
+	int i, ret;
+
+	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	dev_set_drvdata(dev, mixer);
+	drv->mixer = mixer;
+
+	mixer->cfg = of_device_get_match_data(dev);
+	if (!mixer->cfg)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mixer->regs = devm_regmap_init_mmio(dev, regs,
+					      &sun8i_mixer_regmap_config);
+	if (IS_ERR(mixer->regs)) {
+		dev_err(dev, "Couldn't create the mixer regmap\n");
+		return PTR_ERR(mixer->regs);
+	}
+
+	mixer->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(mixer->reset)) {
+		dev_err(dev, "Couldn't get our reset line\n");
+		return PTR_ERR(mixer->reset);
+	}
+
+	ret = reset_control_deassert(mixer->reset);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert our reset line\n");
+		return ret;
+	}
+
+	mixer->bus_clk = devm_clk_get(dev, "bus");
+	if (IS_ERR(mixer->bus_clk)) {
+		dev_err(dev, "Couldn't get the mixer bus clock\n");
+		ret = PTR_ERR(mixer->bus_clk);
+		goto err_assert_reset;
+	}
+	clk_prepare_enable(mixer->bus_clk);
+
+	mixer->mod_clk = devm_clk_get(dev, "mod");
+	if (IS_ERR(mixer->mod_clk)) {
+		dev_err(dev, "Couldn't get the mixer module clock\n");
+		ret = PTR_ERR(mixer->mod_clk);
+		goto err_disable_bus_clk;
+	}
+	clk_prepare_enable(mixer->mod_clk);
+
+	/* Reset the registers */
+	for (i = 0x0; i < 0x20000; i += 4)
+		regmap_write(mixer->regs, i, 0);
+
+	/* Enable the mixer */
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
+		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+	/* Initialize blender */
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
+		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
+		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+		regmap_write(mixer->regs,
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
+
+	/* Select the first UI channel */
+	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
+			 mixer->cfg->vi_num);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
+		     mixer->cfg->vi_num);
+
+	return 0;
+
+	clk_disable_unprepare(mixer->mod_clk);
+err_disable_bus_clk:
+	clk_disable_unprepare(mixer->bus_clk);
+err_assert_reset:
+	reset_control_assert(mixer->reset);
+	return ret;
+}
+
+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(mixer->mod_clk);
+	clk_disable_unprepare(mixer->bus_clk);
+	reset_control_assert(mixer->reset);
+}
+
+static const struct component_ops sun8i_mixer_ops = {
+	.bind	= sun8i_mixer_bind,
+	.unbind	= sun8i_mixer_unbind,
+};
+
+static int sun8i_mixer_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &sun8i_mixer_ops);
+}
+
+static int sun8i_mixer_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sun8i_mixer_ops);
+
+	return 0;
+}
+
+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
+	.vi_num = 2,
+	.ui_num = 1,
+};
+
+static const struct of_device_id sun8i_mixer_of_table[] = {
+	{
+		.compatible = "allwinner,sun8i-v3s-de2-mixer",
+		.data = &sun8i_v3s_mixer_cfg
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
+
+static struct platform_driver sun8i_mixer_platform_driver = {
+	.probe		= sun8i_mixer_probe,
+	.remove		= sun8i_mixer_remove,
+	.driver		= {
+		.name		= "sun8i-mixer",
+		.of_match_table	= sun8i_mixer_of_table,
+	},
+};
+module_platform_driver(sun8i_mixer_platform_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
+#else /* DRM_SUN4I_DE2 */
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+}
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_DRM_SUN4I_DE2 */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..0bc134b3bc98
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_layer.h"
+
+#define SUN8I_MIXER_MAX_CHAN_COUNT		4
+
+#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
+#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
+
+#define SUN8I_MIXER_GLOBAL_CTL			0x0
+#define SUN8I_MIXER_GLOBAL_STATUS		0x4
+#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
+#define SUN8I_MIXER_GLOBAL_SIZE			0xc
+
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ROUTE			0x1080
+#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
+#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
+#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
+#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
+#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
+#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
+
+/* The following numbers are some still unknown magic numbers */
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
+#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
+#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
+
+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
+
+/*
+ * VI channels are not used now, but the support of them may be introduced in
+ * the future.
+ */
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
+
+/*
+ * These sun-engines are still unknown now, the EN registers are here only to
+ * be used to disable these sub-engines.
+ */
+#define SUN8I_MIXER_VSU_EN			0x20000
+#define SUN8I_MIXER_GSU1_EN			0x30000
+#define SUN8I_MIXER_GSU2_EN			0x40000
+#define SUN8I_MIXER_GSU3_EN			0x50000
+#define SUN8I_MIXER_FCE_EN			0xa0000
+#define SUN8I_MIXER_BWS_EN			0xa2000
+#define SUN8I_MIXER_LTI_EN			0xa4000
+#define SUN8I_MIXER_PEAK_EN			0xa6000
+#define SUN8I_MIXER_ASE_EN			0xa8000
+#define SUN8I_MIXER_FCC_EN			0xaa000
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+
+struct sun8i_mixer_cfg {
+	int		vi_num;
+	int		ui_num;
+};
+
+struct sun8i_mixer {
+	struct regmap			*regs;
+
+	const struct sun8i_mixer_cfg	*cfg;
+
+	struct reset_control		*reset;
+
+	struct clk			*bus_clk;
+	struct clk			*mod_clk;
+};
+
+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane);
+#endif /* _SUN8I_MIXER_H_ */
-- 
2.11.1

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 15:23 ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:23 UTC (permalink / raw)
  To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec, David Airlie
  Cc: Jean-Francois Moine, devicetree, linux-kernel, dri-devel,
	linux-sunxi, Icenowy Zheng, linux-clk, linux-arm-kernel

Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
in a new "Display Engine" (mixers instead of old backends and
frontends).

Add support for the mixer on Allwinner V3s SoC; it's the simplest one.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/gpu/drm/sun4i/Kconfig       |   8 +
 drivers/gpu/drm/sun4i/Makefile      |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
 drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
 drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
 9 files changed, 674 insertions(+), 23 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..8df401fff145 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,11 @@ config DRM_SUN4I
 	  Choose this option if you have an Allwinner SoC with a
 	  Display Engine. If M is selected the module will be called
 	  sun4i-drm.
+
+config DRM_SUN4I_DE2
+	bool "Support Display Engine 2.0"
+	depends on DRM_SUN4I
+	default MACH_SUN8I
+	help
+	  Choose this option if you have an Allwinner SoC with a
+	  "Display Engine 2.0".
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index d625a82a6e5f..890e6e50dfee 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a192210574f..4d2228454726 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,6 +25,7 @@
 #include <video/videomode.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	DRM_DEBUG_DRIVER("Committing plane changes\n");
 
-	sun4i_backend_commit(drv->backend);
+	if (drv->backend)
+		sun4i_backend_commit(drv->backend);
+	else if (drv->mixer)
+		sun8i_mixer_commit(drv->mixer);
 
 	if (event) {
 		crtc->state->event = NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 4ce665349f6b..58af38f5e833 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -20,12 +20,17 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/of_device.h>
+
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_framebuffer.h"
 #include "sun4i_layer.h"
 #include "sun4i_tcon.h"
 
+#define DE_VER_DE	0
+#define DE_VER_DE2	1
+
 static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
 	struct sun4i_drv *drv = drm->dev_private;
@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
 {
 	struct drm_device *drm;
 	struct sun4i_drv *drv;
-	int ret;
+	int ret, de_ver;
+
+	de_ver = (int)of_device_get_match_data(dev);
 
 	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 	if (IS_ERR(drm))
@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
 	}
 
 	/* Create our layers */
-	drv->layers = sun4i_layers_init(drm);
+	if (de_ver == DE_VER_DE2)
+		drv->layers = sun8i_layers_init(drm);
+	else
+		drv->layers = sun4i_layers_init(drm);
 	if (IS_ERR(drv->layers)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
 		ret = PTR_ERR(drv->layers);
@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_drv_of_table[] = {
-	{ .compatible = "allwinner,sun5i-a13-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
-	{ .compatible = "allwinner,sun8i-a33-display-engine" },
+	{
+		.compatible = "allwinner,sun5i-a13-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-a33-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-display-engine",
+		.data = (void *)DE_VER_DE2,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 597353eab728..cf1da95b85bd 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -18,6 +18,7 @@
 
 struct sun4i_drv {
 	struct sun4i_backend	*backend;
+	struct sun8i_mixer	*mixer;
 	struct sun4i_crtc	*crtc;
 	struct sun4i_tcon	*tcon;
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index 5d53c977bca5..6dcdac38ab69 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,52 +16,66 @@
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
 struct sun4i_plane_desc {
 	       enum drm_plane_type     type;
+	       /* Pipe is not used in sun8i-mixer */
 	       u8                      pipe;
 	       const uint32_t          *formats;
 	       uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+static int sun4i_layer_atomic_check(struct drm_plane *plane,
 					    struct drm_plane_state *state)
 {
 	return 0;
 }
 
-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
+	struct sun8i_mixer *mixer = drv->mixer;
 
-	sun4i_backend_layer_enable(backend, layer->id, false);
+	if (backend)
+		sun4i_backend_layer_enable(backend, layer->id, false);
+	else if (mixer)
+		sun8i_mixer_layer_enable(mixer, layer->id, false);
 }
 
-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+static void sun4i_layer_atomic_update(struct drm_plane *plane,
 					      struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
-
-	sun4i_backend_update_layer_coord(backend, layer->id, plane);
-	sun4i_backend_update_layer_formats(backend, layer->id, plane);
-	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
-	sun4i_backend_layer_enable(backend, layer->id, true);
+	struct sun8i_mixer *mixer = drv->mixer;
+
+	if (backend) {
+		sun4i_backend_update_layer_coord(backend, layer->id, plane);
+		sun4i_backend_update_layer_formats(backend, layer->id, plane);
+		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+		sun4i_backend_layer_enable(backend, layer->id, true);
+	} else if (mixer) {
+		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
+		sun8i_mixer_layer_enable(mixer, layer->id, true);
+	}
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-	.atomic_check	= sun4i_backend_layer_atomic_check,
-	.atomic_disable	= sun4i_backend_layer_atomic_disable,
-	.atomic_update	= sun4i_backend_layer_atomic_update,
+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
+	.atomic_check	= sun4i_layer_atomic_check,
+	.atomic_disable	= sun4i_layer_atomic_disable,
+	.atomic_update	= sun4i_layer_atomic_update,
 };
 
-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+static const struct drm_plane_funcs sun4i_layer_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
 	.destroy		= drm_plane_cleanup,
@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
+static const uint32_t sun8i_mixer_layer_formats[] = {
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XRGB8888,
+};
+
 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	{
 		.type = DRM_PLANE_TYPE_PRIMARY,
@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	},
 };
 
+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
+	{
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+	{
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 						const struct sun4i_plane_desc *plane)
 {
@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 
 	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
-				       &sun4i_backend_layer_funcs,
+				       &sun4i_layer_funcs,
 				       plane->formats, plane->nformats,
 				       plane->type, NULL);
 	if (ret) {
@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 	}
 
 	drm_plane_helper_add(&layer->plane,
-			     &sun4i_backend_layer_helper_funcs);
+			     &sun4i_layer_helper_funcs);
 	layer->drv = drv;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
 
 	return layers;
 }
+
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
+{
+	struct sun4i_layer **layers;
+	int i;
+
+	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
+			      sizeof(**layers), GFP_KERNEL);
+	if (!layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
+		struct sun4i_layer *layer = layers[i];
+
+		layer = sun4i_layer_init_one(drm, plane);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
+
+		layer->id = i;
+	};
+
+	return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index a2f65d7a3f4e..f7b9e5daea50 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
 }
 
 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
 
 #endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..9427b57240d3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * Based on sun4i_backend.c, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun8i_mixer.h"
+#include "sun4i_drv.h"
+
+#define SUN8I_DRAM_OFFSET 0x40000000
+
+#if defined CONFIG_DRM_SUN4I_DE2
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+	DRM_DEBUG_DRIVER("Committing changes\n");
+
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+EXPORT_SYMBOL(sun8i_mixer_commit);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+
+	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	/* Set the alpha configuration */
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
+}
+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+					     u32 format, u32 *mode)
+{
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+	    (format == DRM_FORMAT_ARGB8888))
+		format = DRM_FORMAT_XRGB8888;
+
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
+		break;
+
+	case DRM_FORMAT_RGB888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int i;
+
+	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 state->crtc_w, state->crtc_h);
+		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating blender size\n");
+		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+			regmap_write(mixer->regs,
+				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
+				     SUN8I_MIXER_SIZE(state->crtc_w,
+						      state->crtc_h));
+		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating channel size\n");
+		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+	}
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     fb->pitches[0]);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+			 state->crtc_w, state->crtc_h);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->crtc_x, state->crtc_y);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	bool interlaced = false;
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int ret;
+
+	if (plane->state->crtc)
+		interlaced = plane->state->crtc->state->adjusted_mode.flags
+			& DRM_MODE_FLAG_INTERLACE;
+
+	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+			   interlaced ?
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
+
+	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+			 interlaced ? "on" : "off");
+
+	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
+						&val);
+	if (ret) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return ret;
+	}
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	uint32_t paddr_u32;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+	paddr += (state->src_x >> 16) * bpp;
+	paddr += (state->src_y >> 16) * fb->pitches[0];
+	paddr -= SUN8I_DRAM_OFFSET;
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	paddr_u32 = (uint32_t) paddr;
+
+	regmap_write(mixer->regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     paddr_u32);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0xbffc, /* guessed */
+};
+
+static int sun8i_mixer_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct sun4i_drv *drv = drm->dev_private;
+	struct sun8i_mixer *mixer;
+	struct resource *res;
+	void __iomem *regs;
+	int i, ret;
+
+	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	dev_set_drvdata(dev, mixer);
+	drv->mixer = mixer;
+
+	mixer->cfg = of_device_get_match_data(dev);
+	if (!mixer->cfg)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mixer->regs = devm_regmap_init_mmio(dev, regs,
+					      &sun8i_mixer_regmap_config);
+	if (IS_ERR(mixer->regs)) {
+		dev_err(dev, "Couldn't create the mixer regmap\n");
+		return PTR_ERR(mixer->regs);
+	}
+
+	mixer->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(mixer->reset)) {
+		dev_err(dev, "Couldn't get our reset line\n");
+		return PTR_ERR(mixer->reset);
+	}
+
+	ret = reset_control_deassert(mixer->reset);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert our reset line\n");
+		return ret;
+	}
+
+	mixer->bus_clk = devm_clk_get(dev, "bus");
+	if (IS_ERR(mixer->bus_clk)) {
+		dev_err(dev, "Couldn't get the mixer bus clock\n");
+		ret = PTR_ERR(mixer->bus_clk);
+		goto err_assert_reset;
+	}
+	clk_prepare_enable(mixer->bus_clk);
+
+	mixer->mod_clk = devm_clk_get(dev, "mod");
+	if (IS_ERR(mixer->mod_clk)) {
+		dev_err(dev, "Couldn't get the mixer module clock\n");
+		ret = PTR_ERR(mixer->mod_clk);
+		goto err_disable_bus_clk;
+	}
+	clk_prepare_enable(mixer->mod_clk);
+
+	/* Reset the registers */
+	for (i = 0x0; i < 0x20000; i += 4)
+		regmap_write(mixer->regs, i, 0);
+
+	/* Enable the mixer */
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
+		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+	/* Initialize blender */
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
+		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
+		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+		regmap_write(mixer->regs,
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
+
+	/* Select the first UI channel */
+	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
+			 mixer->cfg->vi_num);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
+		     mixer->cfg->vi_num);
+
+	return 0;
+
+	clk_disable_unprepare(mixer->mod_clk);
+err_disable_bus_clk:
+	clk_disable_unprepare(mixer->bus_clk);
+err_assert_reset:
+	reset_control_assert(mixer->reset);
+	return ret;
+}
+
+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(mixer->mod_clk);
+	clk_disable_unprepare(mixer->bus_clk);
+	reset_control_assert(mixer->reset);
+}
+
+static const struct component_ops sun8i_mixer_ops = {
+	.bind	= sun8i_mixer_bind,
+	.unbind	= sun8i_mixer_unbind,
+};
+
+static int sun8i_mixer_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &sun8i_mixer_ops);
+}
+
+static int sun8i_mixer_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sun8i_mixer_ops);
+
+	return 0;
+}
+
+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
+	.vi_num = 2,
+	.ui_num = 1,
+};
+
+static const struct of_device_id sun8i_mixer_of_table[] = {
+	{
+		.compatible = "allwinner,sun8i-v3s-de2-mixer",
+		.data = &sun8i_v3s_mixer_cfg
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
+
+static struct platform_driver sun8i_mixer_platform_driver = {
+	.probe		= sun8i_mixer_probe,
+	.remove		= sun8i_mixer_remove,
+	.driver		= {
+		.name		= "sun8i-mixer",
+		.of_match_table	= sun8i_mixer_of_table,
+	},
+};
+module_platform_driver(sun8i_mixer_platform_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
+#else /* DRM_SUN4I_DE2 */
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+}
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_DRM_SUN4I_DE2 */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..0bc134b3bc98
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_layer.h"
+
+#define SUN8I_MIXER_MAX_CHAN_COUNT		4
+
+#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
+#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
+
+#define SUN8I_MIXER_GLOBAL_CTL			0x0
+#define SUN8I_MIXER_GLOBAL_STATUS		0x4
+#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
+#define SUN8I_MIXER_GLOBAL_SIZE			0xc
+
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ROUTE			0x1080
+#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
+#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
+#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
+#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
+#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
+#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
+
+/* The following numbers are some still unknown magic numbers */
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
+#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
+#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
+
+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
+
+/*
+ * VI channels are not used now, but the support of them may be introduced in
+ * the future.
+ */
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
+
+/*
+ * These sun-engines are still unknown now, the EN registers are here only to
+ * be used to disable these sub-engines.
+ */
+#define SUN8I_MIXER_VSU_EN			0x20000
+#define SUN8I_MIXER_GSU1_EN			0x30000
+#define SUN8I_MIXER_GSU2_EN			0x40000
+#define SUN8I_MIXER_GSU3_EN			0x50000
+#define SUN8I_MIXER_FCE_EN			0xa0000
+#define SUN8I_MIXER_BWS_EN			0xa2000
+#define SUN8I_MIXER_LTI_EN			0xa4000
+#define SUN8I_MIXER_PEAK_EN			0xa6000
+#define SUN8I_MIXER_ASE_EN			0xa8000
+#define SUN8I_MIXER_FCC_EN			0xaa000
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+
+struct sun8i_mixer_cfg {
+	int		vi_num;
+	int		ui_num;
+};
+
+struct sun8i_mixer {
+	struct regmap			*regs;
+
+	const struct sun8i_mixer_cfg	*cfg;
+
+	struct reset_control		*reset;
+
+	struct clk			*bus_clk;
+	struct clk			*mod_clk;
+};
+
+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane);
+#endif /* _SUN8I_MIXER_H_ */
-- 
2.11.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines
@ 2017-02-22 15:23 ` Icenowy Zheng
  0 siblings, 0 replies; 57+ messages in thread
From: Icenowy Zheng @ 2017-02-22 15:23 UTC (permalink / raw)
  To: linux-arm-kernel

Allwinner have a new "Display Engine 2.0" in there new SoCs, which comes
in a new "Display Engine" (mixers instead of old backends and
frontends).

Add support for the mixer on Allwinner V3s SoC; it's the simplest one.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/gpu/drm/sun4i/Kconfig       |   8 +
 drivers/gpu/drm/sun4i/Makefile      |   1 +
 drivers/gpu/drm/sun4i/sun4i_crtc.c  |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c   |  38 +++-
 drivers/gpu/drm/sun4i/sun4i_drv.h   |   1 +
 drivers/gpu/drm/sun4i/sun4i_layer.c |  92 ++++++--
 drivers/gpu/drm/sun4i/sun4i_layer.h |   1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 417 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 133 ++++++++++++
 9 files changed, 674 insertions(+), 23 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..8df401fff145 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,11 @@ config DRM_SUN4I
 	  Choose this option if you have an Allwinner SoC with a
 	  Display Engine. If M is selected the module will be called
 	  sun4i-drm.
+
+config DRM_SUN4I_DE2
+	bool "Support Display Engine 2.0"
+	depends on DRM_SUN4I
+	default MACH_SUN8I
+	help
+	  Choose this option if you have an Allwinner SoC with a
+	  "Display Engine 2.0".
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index d625a82a6e5f..890e6e50dfee 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,5 +9,6 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I)		+= sun8i_mixer.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a192210574f..4d2228454726 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,6 +25,7 @@
 #include <video/videomode.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
@@ -55,7 +56,10 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	DRM_DEBUG_DRIVER("Committing plane changes\n");
 
-	sun4i_backend_commit(drv->backend);
+	if (drv->backend)
+		sun4i_backend_commit(drv->backend);
+	else if (drv->mixer)
+		sun8i_mixer_commit(drv->mixer);
 
 	if (event) {
 		crtc->state->event = NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 4ce665349f6b..58af38f5e833 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -20,12 +20,17 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_of.h>
 
+#include <linux/of_device.h>
+
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
 #include "sun4i_framebuffer.h"
 #include "sun4i_layer.h"
 #include "sun4i_tcon.h"
 
+#define DE_VER_DE	0
+#define DE_VER_DE2	1
+
 static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
 	struct sun4i_drv *drv = drm->dev_private;
@@ -117,7 +122,9 @@ static int sun4i_drv_bind(struct device *dev)
 {
 	struct drm_device *drm;
 	struct sun4i_drv *drv;
-	int ret;
+	int ret, de_ver;
+
+	de_ver = (int)of_device_get_match_data(dev);
 
 	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
 	if (IS_ERR(drm))
@@ -140,7 +147,10 @@ static int sun4i_drv_bind(struct device *dev)
 	}
 
 	/* Create our layers */
-	drv->layers = sun4i_layers_init(drm);
+	if (de_ver == DE_VER_DE2)
+		drv->layers = sun8i_layers_init(drm);
+	else
+		drv->layers = sun4i_layers_init(drm);
 	if (IS_ERR(drv->layers)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
 		ret = PTR_ERR(drv->layers);
@@ -323,10 +333,26 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_drv_of_table[] = {
-	{ .compatible = "allwinner,sun5i-a13-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31-display-engine" },
-	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
-	{ .compatible = "allwinner,sun8i-a33-display-engine" },
+	{
+		.compatible = "allwinner,sun5i-a13-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-a33-display-engine",
+		.data = (void *)DE_VER_DE,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-display-engine",
+		.data = (void *)DE_VER_DE2,
+	},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 597353eab728..cf1da95b85bd 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -18,6 +18,7 @@
 
 struct sun4i_drv {
 	struct sun4i_backend	*backend;
+	struct sun8i_mixer	*mixer;
 	struct sun4i_crtc	*crtc;
 	struct sun4i_tcon	*tcon;
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index 5d53c977bca5..6dcdac38ab69 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,52 +16,66 @@
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun8i_mixer.h"
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
 struct sun4i_plane_desc {
 	       enum drm_plane_type     type;
+	       /* Pipe is not used in sun8i-mixer */
 	       u8                      pipe;
 	       const uint32_t          *formats;
 	       uint32_t                nformats;
 };
 
-static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+static int sun4i_layer_atomic_check(struct drm_plane *plane,
 					    struct drm_plane_state *state)
 {
 	return 0;
 }
 
-static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+static void sun4i_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
+	struct sun8i_mixer *mixer = drv->mixer;
 
-	sun4i_backend_layer_enable(backend, layer->id, false);
+	if (backend)
+		sun4i_backend_layer_enable(backend, layer->id, false);
+	else if (mixer)
+		sun8i_mixer_layer_enable(mixer, layer->id, false);
 }
 
-static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+static void sun4i_layer_atomic_update(struct drm_plane *plane,
 					      struct drm_plane_state *old_state)
 {
 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
 	struct sun4i_drv *drv = layer->drv;
 	struct sun4i_backend *backend = drv->backend;
-
-	sun4i_backend_update_layer_coord(backend, layer->id, plane);
-	sun4i_backend_update_layer_formats(backend, layer->id, plane);
-	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
-	sun4i_backend_layer_enable(backend, layer->id, true);
+	struct sun8i_mixer *mixer = drv->mixer;
+
+	if (backend) {
+		sun4i_backend_update_layer_coord(backend, layer->id, plane);
+		sun4i_backend_update_layer_formats(backend, layer->id, plane);
+		sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+		sun4i_backend_layer_enable(backend, layer->id, true);
+	} else if (mixer) {
+		sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
+		sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
+		sun8i_mixer_layer_enable(mixer, layer->id, true);
+	}
 }
 
-static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
-	.atomic_check	= sun4i_backend_layer_atomic_check,
-	.atomic_disable	= sun4i_backend_layer_atomic_disable,
-	.atomic_update	= sun4i_backend_layer_atomic_update,
+static struct drm_plane_helper_funcs sun4i_layer_helper_funcs = {
+	.atomic_check	= sun4i_layer_atomic_check,
+	.atomic_disable	= sun4i_layer_atomic_disable,
+	.atomic_update	= sun4i_layer_atomic_update,
 };
 
-static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+static const struct drm_plane_funcs sun4i_layer_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
 	.destroy		= drm_plane_cleanup,
@@ -88,6 +102,12 @@ static const uint32_t sun4i_backend_layer_formats_overlay[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
+static const uint32_t sun8i_mixer_layer_formats[] = {
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_XRGB8888,
+};
+
 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	{
 		.type = DRM_PLANE_TYPE_PRIMARY,
@@ -103,6 +123,19 @@ static const struct sun4i_plane_desc sun4i_backend_planes[] = {
 	},
 };
 
+static const struct sun4i_plane_desc sun8i_mixer_planes[] = {
+	{
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+	{
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.formats = sun8i_mixer_layer_formats,
+		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+	},
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 						const struct sun4i_plane_desc *plane)
 {
@@ -115,7 +148,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 
 	ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
-				       &sun4i_backend_layer_funcs,
+				       &sun4i_layer_funcs,
 				       plane->formats, plane->nformats,
 				       plane->type, NULL);
 	if (ret) {
@@ -124,7 +157,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 	}
 
 	drm_plane_helper_add(&layer->plane,
-			     &sun4i_backend_layer_helper_funcs);
+			     &sun4i_layer_helper_funcs);
 	layer->drv = drv;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
@@ -187,3 +220,30 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
 
 	return layers;
 }
+
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm)
+{
+	struct sun4i_layer **layers;
+	int i;
+
+	layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes),
+			      sizeof(**layers), GFP_KERNEL);
+	if (!layers)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+		const struct sun4i_plane_desc *plane = &sun8i_mixer_planes[i];
+		struct sun4i_layer *layer = layers[i];
+
+		layer = sun4i_layer_init_one(drm, plane);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
+
+		layer->id = i;
+	};
+
+	return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index a2f65d7a3f4e..f7b9e5daea50 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,5 +26,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
 }
 
 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+struct sun4i_layer **sun8i_layers_init(struct drm_device *drm);
 
 #endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..9427b57240d3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * Based on sun4i_backend.c, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun8i_mixer.h"
+#include "sun4i_drv.h"
+
+#define SUN8I_DRAM_OFFSET 0x40000000
+
+#if defined CONFIG_DRM_SUN4I_DE2
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+	DRM_DEBUG_DRIVER("Committing changes\n");
+
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+EXPORT_SYMBOL(sun8i_mixer_commit);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+
+	DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	/* Set the alpha configuration */
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
+}
+EXPORT_SYMBOL(sun8i_mixer_layer_enable);
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+					     u32 format, u32 *mode)
+{
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+	    (format == DRM_FORMAT_ARGB8888))
+		format = DRM_FORMAT_XRGB8888;
+
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
+		break;
+
+	case DRM_FORMAT_RGB888:
+		*mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int i;
+
+	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 state->crtc_w, state->crtc_h);
+		regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_SIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating blender size\n");
+		for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+			regmap_write(mixer->regs,
+				     SUN8I_MIXER_BLEND_ATTR_INSIZE(i),
+				     SUN8I_MIXER_SIZE(state->crtc_w,
+						      state->crtc_h));
+		regmap_write(mixer->regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+		DRM_DEBUG_DRIVER("Updating channel size\n");
+		regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_SIZE(state->crtc_w,
+					      state->crtc_h));
+	}
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     fb->pitches[0]);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+			 state->crtc_w, state->crtc_h);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->crtc_x, state->crtc_y);
+	regmap_write(mixer->regs, SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_coord);
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	bool interlaced = false;
+	u32 val;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int ret;
+
+	if (plane->state->crtc)
+		interlaced = plane->state->crtc->state->adjusted_mode.flags
+			& DRM_MODE_FLAG_INTERLACE;
+
+	regmap_update_bits(mixer->regs, SUN8I_MIXER_BLEND_OUTCTL,
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+			   interlaced ?
+			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
+
+	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+			 interlaced ? "on" : "off");
+
+	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
+						&val);
+	if (ret) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return ret;
+	}
+
+	regmap_update_bits(mixer->regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_formats);
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	uint32_t paddr_u32;
+	/* Currently the first UI channel is used */
+	int chan = mixer->cfg->vi_num;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+	paddr += (state->src_x >> 16) * bpp;
+	paddr += (state->src_y >> 16) * fb->pitches[0];
+	paddr -= SUN8I_DRAM_OFFSET;
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	paddr_u32 = (uint32_t) paddr;
+
+	regmap_write(mixer->regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     paddr_u32);
+
+	return 0;
+}
+EXPORT_SYMBOL(sun8i_mixer_update_layer_buffer);
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0xbffc, /* guessed */
+};
+
+static int sun8i_mixer_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct sun4i_drv *drv = drm->dev_private;
+	struct sun8i_mixer *mixer;
+	struct resource *res;
+	void __iomem *regs;
+	int i, ret;
+
+	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	dev_set_drvdata(dev, mixer);
+	drv->mixer = mixer;
+
+	mixer->cfg = of_device_get_match_data(dev);
+	if (!mixer->cfg)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mixer->regs = devm_regmap_init_mmio(dev, regs,
+					      &sun8i_mixer_regmap_config);
+	if (IS_ERR(mixer->regs)) {
+		dev_err(dev, "Couldn't create the mixer regmap\n");
+		return PTR_ERR(mixer->regs);
+	}
+
+	mixer->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(mixer->reset)) {
+		dev_err(dev, "Couldn't get our reset line\n");
+		return PTR_ERR(mixer->reset);
+	}
+
+	ret = reset_control_deassert(mixer->reset);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert our reset line\n");
+		return ret;
+	}
+
+	mixer->bus_clk = devm_clk_get(dev, "bus");
+	if (IS_ERR(mixer->bus_clk)) {
+		dev_err(dev, "Couldn't get the mixer bus clock\n");
+		ret = PTR_ERR(mixer->bus_clk);
+		goto err_assert_reset;
+	}
+	clk_prepare_enable(mixer->bus_clk);
+
+	mixer->mod_clk = devm_clk_get(dev, "mod");
+	if (IS_ERR(mixer->mod_clk)) {
+		dev_err(dev, "Couldn't get the mixer module clock\n");
+		ret = PTR_ERR(mixer->mod_clk);
+		goto err_disable_bus_clk;
+	}
+	clk_prepare_enable(mixer->mod_clk);
+
+	/* Reset the registers */
+	for (i = 0x0; i < 0x20000; i += 4)
+		regmap_write(mixer->regs, i, 0);
+
+	/* Enable the mixer */
+	regmap_write(mixer->regs, SUN8I_MIXER_GLOBAL_CTL,
+		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+	/* Initialize blender */
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+		     SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_BKCOLOR,
+		     SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(0),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_MODE(1),
+		     SUN8I_MIXER_BLEND_MODE_DEF);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_CK_CTL,
+		     SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+	for (i = 0; i < SUN8I_MIXER_MAX_CHAN_COUNT; i++)
+		regmap_write(mixer->regs,
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR(i),
+			     SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
+
+	/* Select the first UI channel */
+	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
+			 mixer->cfg->vi_num);
+	regmap_write(mixer->regs, SUN8I_MIXER_BLEND_ROUTE,
+		     mixer->cfg->vi_num);
+
+	return 0;
+
+	clk_disable_unprepare(mixer->mod_clk);
+err_disable_bus_clk:
+	clk_disable_unprepare(mixer->bus_clk);
+err_assert_reset:
+	reset_control_assert(mixer->reset);
+	return ret;
+}
+
+static void sun8i_mixer_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(mixer->mod_clk);
+	clk_disable_unprepare(mixer->bus_clk);
+	reset_control_assert(mixer->reset);
+}
+
+static const struct component_ops sun8i_mixer_ops = {
+	.bind	= sun8i_mixer_bind,
+	.unbind	= sun8i_mixer_unbind,
+};
+
+static int sun8i_mixer_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &sun8i_mixer_ops);
+}
+
+static int sun8i_mixer_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sun8i_mixer_ops);
+
+	return 0;
+}
+
+static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
+	.vi_num = 2,
+	.ui_num = 1,
+};
+
+static const struct of_device_id sun8i_mixer_of_table[] = {
+	{
+		.compatible = "allwinner,sun8i-v3s-de2-mixer",
+		.data = &sun8i_v3s_mixer_cfg
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
+
+static struct platform_driver sun8i_mixer_platform_driver = {
+	.probe		= sun8i_mixer_probe,
+	.remove		= sun8i_mixer_remove,
+	.driver		= {
+		.name		= "sun8i-mixer",
+		.of_match_table	= sun8i_mixer_of_table,
+	},
+};
+module_platform_driver(sun8i_mixer_platform_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
+#else /* DRM_SUN4I_DE2 */
+void sun8i_mixer_commit(struct sun8i_mixer *mixer)
+{
+}
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable)
+{
+}
+
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane)
+{
+	return -ENOENT;
+}
+#endif /* CONFIG_DRM_SUN4I_DE2 */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..0bc134b3bc98
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_layer.h"
+
+#define SUN8I_MIXER_MAX_CHAN_COUNT		4
+
+#define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
+#define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
+
+#define SUN8I_MIXER_GLOBAL_CTL			0x0
+#define SUN8I_MIXER_GLOBAL_STATUS		0x4
+#define SUN8I_MIXER_GLOBAL_DBUFF		0x8
+#define SUN8I_MIXER_GLOBAL_SIZE			0xc
+
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
+#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
+#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ROUTE			0x1080
+#define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
+#define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
+#define SUN8I_MIXER_BLEND_OUTSIZE		0x108c
+#define SUN8I_MIXER_BLEND_MODE(x)		(0x1090 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_CTL		0x10b0
+#define SUN8I_MIXER_BLEND_CK_CFG		0x10b4
+#define SUN8I_MIXER_BLEND_CK_MAX(x)		(0x10c0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
+#define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
+
+/* The following numbers are some still unknown magic numbers */
+#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF	0xff000000
+#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
+#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF	0x0
+#define SUN8I_MIXER_BLEND_BKCOLOR_DEF		0xff000000
+#define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
+#define SUN8I_MIXER_BLEND_CK_CTL_DEF		0x0
+
+#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
+
+/*
+ * VI channels are not used now, but the support of them may be introduced in
+ * the future.
+ */
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(11, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF	(1 << 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888	(0 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888	(4 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888	(8 << 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF	(0xff << 24)
+
+/*
+ * These sun-engines are still unknown now, the EN registers are here only to
+ * be used to disable these sub-engines.
+ */
+#define SUN8I_MIXER_VSU_EN			0x20000
+#define SUN8I_MIXER_GSU1_EN			0x30000
+#define SUN8I_MIXER_GSU2_EN			0x40000
+#define SUN8I_MIXER_GSU3_EN			0x50000
+#define SUN8I_MIXER_FCE_EN			0xa0000
+#define SUN8I_MIXER_BWS_EN			0xa2000
+#define SUN8I_MIXER_LTI_EN			0xa4000
+#define SUN8I_MIXER_PEAK_EN			0xa6000
+#define SUN8I_MIXER_ASE_EN			0xa8000
+#define SUN8I_MIXER_FCC_EN			0xaa000
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+
+struct sun8i_mixer_cfg {
+	int		vi_num;
+	int		ui_num;
+};
+
+struct sun8i_mixer {
+	struct regmap			*regs;
+
+	const struct sun8i_mixer_cfg	*cfg;
+
+	struct reset_control		*reset;
+
+	struct clk			*bus_clk;
+	struct clk			*mod_clk;
+};
+
+void sun8i_mixer_commit(struct sun8i_mixer *mixer);
+
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
+				int layer, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
+				     int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
+				       int layer, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
+				      int layer, struct drm_plane *plane);
+#endif /* _SUN8I_MIXER_H_ */
-- 
2.11.1

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

end of thread, other threads:[~2017-02-24 15:42 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-22 15:18 [PATCH 0/8] Initial Allwinner Display Engine 2.0 Support Icenowy Zheng
2017-02-22 15:18 ` Icenowy Zheng
2017-02-22 15:18 ` Icenowy Zheng
     [not found] ` <20170222151854.3280-1-icenowy-ymACFijhrKM@public.gmane.org>
2017-02-22 15:18   ` [PATCH 1/8] dt-bindings: add binding for the Allwinner DE2 CCU Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 15:18   ` [PATCH 2/8] clk: sunxi-ng: add support for " Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 19:09     ` Maxime Ripard
2017-02-22 19:09       ` Maxime Ripard
2017-02-22 19:09       ` Maxime Ripard
2017-02-22 19:09       ` Maxime Ripard
2017-02-22 15:18   ` [PATCH 3/8] dt-bindings: add bindings for DE2 on V3s SoC Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 15:18   ` [PATCH 4/8] drm/sun4i: add support for sun8i DE2 mixers and display engines Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 15:18     ` Icenowy Zheng
2017-02-22 22:01     ` Jernej Škrabec
2017-02-22 22:01       ` Jernej Škrabec
2017-02-22 22:01       ` Jernej Škrabec
2017-02-22 22:01       ` Jernej Škrabec
2017-02-22 22:04       ` Icenowy Zheng
2017-02-22 22:04         ` Icenowy Zheng
2017-02-22 22:04         ` Icenowy Zheng
2017-02-23 14:27     ` Stefan Monnier
2017-02-23 14:27       ` Stefan Monnier
2017-02-23 17:44     ` Emil Velikov
2017-02-23 17:44       ` Emil Velikov
2017-02-23 17:44       ` Emil Velikov
2017-02-23 17:44       ` Emil Velikov
2017-02-24  0:46       ` Maxime Ripard
2017-02-24  0:46         ` Maxime Ripard
2017-02-24  0:46         ` Maxime Ripard
2017-02-24  0:46         ` Maxime Ripard
2017-02-22 15:23 Icenowy Zheng
2017-02-22 15:23 ` Icenowy Zheng
2017-02-22 15:23 ` Icenowy Zheng
2017-02-22 20:09 ` Maxime Ripard
2017-02-22 20:09   ` Maxime Ripard
2017-02-22 20:09   ` Maxime Ripard
2017-02-22 20:28   ` Icenowy Zheng
2017-02-22 20:28     ` Icenowy Zheng
2017-02-22 20:28     ` Icenowy Zheng
2017-02-22 22:54     ` Maxime Ripard
2017-02-22 22:54       ` Maxime Ripard
2017-02-22 22:54       ` Maxime Ripard
2017-02-22 22:54       ` Maxime Ripard
2017-02-24 13:30   ` Rob Herring
2017-02-24 13:30     ` Rob Herring
2017-02-24 13:30     ` Rob Herring
2017-02-24 13:30     ` Rob Herring
2017-02-24 15:30     ` Jernej Škrabec
2017-02-24 15:30       ` Jernej Škrabec
2017-02-24 15:30       ` Jernej Škrabec
2017-02-24 15:30       ` Jernej Škrabec

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