Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1635650 > unrolled thread
| Started by | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| First post | 2017-05-04 13:50 +0200 |
| Last post | 2017-05-05 05:30 +0200 |
| Articles | 20 on this page of 37 — 4 participants |
Back to article view | Back to linux.kernel
[PATCH v6 00/13] Initial Allwinner Display Engine 2.0 Support Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 13:50 +0200
[PATCH v6 01/13] dt-bindings: add binding for the Allwinner DE2 CCU Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 13:50 +0200
[PATCH v6 10/13] drm/sun4i: tcon: add support for V3s TCON Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 10/13] drm/sun4i: tcon: add support for V3s TCON Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:40 +0200
[PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:40 +0200
Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC icenowy@aosc.io - 2017-05-05 11:00 +0200
Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC Icenowy Zheng <icenowy@aosc.io> - 2017-05-05 14:40 +0200
Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC Maxime Ripard <maxime.ripard@free-electrons.com> - 2017-05-09 21:30 +0200
Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC Maxime Ripard <maxime.ripard@free-electrons.com> - 2017-05-05 14:40 +0200
[PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers Maxime Ripard <maxime.ripard@free-electrons.com> - 2017-05-04 15:10 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers icenowy@aosc.io - 2017-05-04 19:00 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers icenowy@aosc.io - 2017-05-04 19:20 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers icenowy@aosc.io - 2017-05-04 19:00 +0200
Re: [linux-sunxi] Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:50 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers icenowy@aosc.io - 2017-05-04 19:00 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers Maxime Ripard <maxime.ripard@free-electrons.com> - 2017-05-05 14:40 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers Icenowy Zheng <icenowy@aosc.io> - 2017-05-05 14:40 +0200
Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers Maxime Ripard <maxime.ripard@free-electrons.com> - 2017-05-09 22:30 +0200
[PATCH v6 13/13] [DO NOT MERGE] ARM: dts: sun8i: enable LCD panel of Lichee Pi Zero Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
[PATCH v6 09/13] drm/sun4i: Add compatible string for V3s display engine Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
[PATCH v6 05/13] drm/sun4i: abstract a engine type Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 05/13] drm/sun4i: abstract a engine type Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:00 +0200
Re: [linux-sunxi] [PATCH v6 05/13] drm/sun4i: abstract a engine type Chen-Yu Tsai <wens@csie.org> - 2017-05-05 10:40 +0200
Re: [linux-sunxi] [PATCH v6 05/13] drm/sun4i: abstract a engine type icenowy@aosc.io - 2017-05-05 10:40 +0200
[PATCH v6 04/13] drm/sun4i: return only planes for layers created Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
[PATCH v6 03/13] dt-bindings: add bindings for DE2 on V3s SoC Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 03/13] dt-bindings: add bindings for DE2 on V3s SoC Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:30 +0200
[PATCH v6 07/13] drm/sun4i: add a Kconfig option for sun4i-backend Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 07/13] drm/sun4i: add a Kconfig option for sun4i-backend Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:20 +0200
[PATCH v6 02/13] clk: sunxi-ng: add support for DE2 CCU Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [PATCH v6 02/13] clk: sunxi-ng: add support for DE2 CCU Maxime Ripard <maxime.ripard@free-electrons.com> - 2017-05-04 15:00 +0200
[PATCH v6 06/13] drm/sun4i: add a dedicated module for sun4i-backend and sun4i-layer Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 06/13] drm/sun4i: add a dedicated module for sun4i-backend and sun4i-layer Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:20 +0200
[PATCH v6 12/13] ARM: dts: sun8i: add pinmux for LCD pins of V3s SoC Icenowy Zheng <icenowy@aosc.io> - 2017-05-04 14:00 +0200
Re: [linux-sunxi] [PATCH v6 12/13] ARM: dts: sun8i: add pinmux for LCD pins of V3s SoC Chen-Yu Tsai <wens@csie.org> - 2017-05-05 05:30 +0200
Page 1 of 2 [1] 2 Next page →
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-04 13:50 +0200 |
| Subject | [PATCH v6 00/13] Initial Allwinner Display Engine 2.0 Support |
| Message-ID | <tDnqF-2R5-1@gated-at.bofh.it> |
I'm sorry that some patch from this version is wrongly sent without decorations. 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. (Some WIP code have already been written based on v4 of this patchset by Jernej Skrabec and be, including TVE support (basical but usable) and HDMI support (can display something, but still not so usable because of some problem). ) 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 (13): 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: return only planes for layers created drm/sun4i: abstract a engine type drm/sun4i: add a dedicated module for sun4i-backend and sun4i-layer drm/sun4i: add a Kconfig option for sun4i-backend drm/sun4i: add support for Allwinner DE2 mixers drm/sun4i: Add compatible string for V3s display engine 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/sun8i-de2.txt | 30 ++ .../bindings/display/sunxi/sun4i-drm.txt | 29 +- arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts | 36 ++ arch/arm/boot/dts/sun8i-v3s.dtsi | 96 +++++ drivers/clk/sunxi-ng/Kconfig | 5 + drivers/clk/sunxi-ng/Makefile | 1 + drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 218 ++++++++++++ drivers/clk/sunxi-ng/ccu-sun8i-de2.h | 28 ++ drivers/gpu/drm/sun4i/Kconfig | 20 ++ drivers/gpu/drm/sun4i/Makefile | 8 +- drivers/gpu/drm/sun4i/sun4i_backend.c | 72 ++-- drivers/gpu/drm/sun4i/sun4i_backend.h | 17 +- drivers/gpu/drm/sun4i/sun4i_crtc.c | 32 +- drivers/gpu/drm/sun4i/sun4i_crtc.h | 5 +- drivers/gpu/drm/sun4i/sun4i_drv.c | 6 +- drivers/gpu/drm/sun4i/sun4i_drv.h | 2 +- drivers/gpu/drm/sun4i/sun4i_layer.c | 20 +- drivers/gpu/drm/sun4i/sun4i_layer.h | 7 +- drivers/gpu/drm/sun4i/sun4i_tcon.c | 41 ++- drivers/gpu/drm/sun4i/sun4i_tv.c | 9 +- drivers/gpu/drm/sun4i/sun8i_layer.c | 140 ++++++++ drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++ drivers/gpu/drm/sun4i/sun8i_mixer.c | 394 +++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++ drivers/gpu/drm/sun4i/sunxi_engine.h | 112 ++++++ include/dt-bindings/clock/sun8i-de2.h | 18 + include/dt-bindings/reset/sun8i-de2.h | 14 + 27 files changed, 1431 insertions(+), 102 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/sun8i-de2.txt create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-de2.c create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-de2.h create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.c create mode 100644 drivers/gpu/drm/sun4i/sun8i_mixer.h create mode 100644 drivers/gpu/drm/sun4i/sunxi_engine.h create mode 100644 include/dt-bindings/clock/sun8i-de2.h create mode 100644 include/dt-bindings/reset/sun8i-de2.h -- 2.12.2
[toc] | [next] | [standalone]
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-04 13:50 +0200 |
| Subject | [PATCH v6 01/13] dt-bindings: add binding for the Allwinner DE2 CCU |
| Message-ID | <tDnqF-2R5-13@gated-at.bofh.it> |
| In reply to | #1635650 |
Allwinner "Display Engine 2.0" contains some clock controls in it.
In order to add them as clock drivers, we need a device tree binding.
Add the binding here.
Also add the device tree binding headers.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v6:
- Added Rob's ACK.
- Drpoped A64's compatible as it's not appropriate now.
Changes in v5:
- Moved dt-binding headers here.
- Changed dt-binding headers' license header to SPDX license.
Changes in v4:
- Dropped the leading 0 in clock at 1000000 .
Changes in v3:
- Fill the address space length of DE2 CCU to 0x100000, just reach the start of mixer0.
.../devicetree/bindings/clock/sun8i-de2.txt | 30 ++++++++++++++++++++++
include/dt-bindings/clock/sun8i-de2.h | 18 +++++++++++++
include/dt-bindings/reset/sun8i-de2.h | 14 ++++++++++
3 files changed, 62 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/sun8i-de2.txt
create mode 100644 include/dt-bindings/clock/sun8i-de2.h
create mode 100644 include/dt-bindings/reset/sun8i-de2.h
diff --git a/Documentation/devicetree/bindings/clock/sun8i-de2.txt b/Documentation/devicetree/bindings/clock/sun8i-de2.txt
new file mode 100644
index 000000000000..d710c0111cd3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sun8i-de2.txt
@@ -0,0 +1,30 @@
+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-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@1000000 {
+ compatible = "allwinner,sun8i-a83t-de2-clk";
+ reg = <0x01000000 0x100000>;
+ clocks = <&ccu CLK_DE>,
+ <&ccu CLK_BUS_DE>;
+ clock-names = "mod",
+ "bus";
+ resets = <&ccu RST_BUS_DE>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+};
diff --git a/include/dt-bindings/clock/sun8i-de2.h b/include/dt-bindings/clock/sun8i-de2.h
new file mode 100644
index 000000000000..3bed63b524aa
--- /dev/null
+++ b/include/dt-bindings/clock/sun8i-de2.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_SUN8I_DE2_H_
+#define _DT_BINDINGS_CLOCK_SUN8I_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_SUN8I_DE2_H_ */
diff --git a/include/dt-bindings/reset/sun8i-de2.h b/include/dt-bindings/reset/sun8i-de2.h
new file mode 100644
index 000000000000..9526017432f0
--- /dev/null
+++ b/include/dt-bindings/reset/sun8i-de2.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+ */
+
+#ifndef _DT_BINDINGS_RESET_SUN8I_DE2_H_
+#define _DT_BINDINGS_RESET_SUN8I_DE2_H_
+
+#define RST_MIXER0 0
+#define RST_MIXER1 1
+#define RST_WB 2
+
+#endif /* _DT_BINDINGS_RESET_SUN8I_DE2_H_ */
--
2.12.2
[toc] | [prev] | [next] | [standalone]
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-04 14:00 +0200 |
| Subject | [PATCH v6 10/13] drm/sun4i: tcon: add support for V3s TCON |
| Message-ID | <tDnAl-2VI-1@gated-at.bofh.it> |
| In reply to | #1635650 |
Allwinner V3s SoC features a TCON without channel 1.
Add support for it.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
drivers/gpu/drm/sun4i/sun4i_drv.c | 3 ++-
drivers/gpu/drm/sun4i/sun4i_tcon.c | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index dcfb241f817d..367e4e8e9656 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -188,7 +188,8 @@ static bool sun4i_drv_node_is_tcon(struct device_node *node)
return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") ||
of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") ||
- of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
+ of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") ||
+ of_device_is_compatible(node, "allwinner,sun8i-v3s-tcon");
}
static int compare_of(struct device *dev, void *data)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index c48135a10fda..e76acf06e91b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -681,11 +681,16 @@ static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
/* nothing is supported */
};
+static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {
+ /* nothing is supported */
+};
+
static const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
{ .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
{ .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
{ .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
+ { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },
{ }
};
MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
--
2.12.2
[toc] | [prev] | [next] | [standalone]
| From | Chen-Yu Tsai <wens@csie.org> |
|---|---|
| Date | 2017-05-05 05:40 +0200 |
| Subject | Re: [linux-sunxi] [PATCH v6 10/13] drm/sun4i: tcon: add support for V3s TCON |
| Message-ID | <tDCg1-4wN-7@gated-at.bofh.it> |
| In reply to | #1635659 |
On Thu, May 4, 2017 at 7:48 PM, Icenowy Zheng <icenowy@aosc.io> wrote: > Allwinner V3s SoC features a TCON without channel 1. > > Add support for it. > > Signed-off-by: Icenowy Zheng <icenowy@aosc.io> Reviewed-by: Chen-Yu Tsai <wens@csie.org>
[toc] | [prev] | [next] | [standalone]
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-04 14:00 +0200 |
| Subject | [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC |
| Message-ID | <tDnAm-2VI-5@gated-at.bofh.it> |
| In reply to | #1635650 |
Allwinner V3s SoC features a "Display Engine 2.0" with only one TCON
which have RGB LCD output.
Add device nodes for it as well as the TCON.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
arch/arm/boot/dts/sun8i-v3s.dtsi | 87 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 71075969e5e6..0a895179d8ae 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -41,6 +41,10 @@
*/
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/sun8i-v3s-ccu.h>
+#include <dt-bindings/clock/sun8i-de2.h>
+#include <dt-bindings/reset/sun8i-v3s-ccu.h>
+#include <dt-bindings/reset/sun8i-de2.h>
/ {
#address-cells = <1>;
@@ -59,6 +63,12 @@
};
};
+ de: display-engine {
+ compatible = "allwinner,sun8i-v3s-display-engine";
+ allwinner,pipelines = <&de2_mixer0>;
+ status = "disabled";
+ };
+
timer {
compatible = "arm,armv7-timer";
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
@@ -93,6 +103,83 @@
#size-cells = <1>;
ranges;
+ de2_clocks: clock@1000000 {
+ compatible = "allwinner,sun50i-h5-de2-clk";
+ reg = <0x01000000 0x100000>;
+ clocks = <&ccu CLK_DE>,
+ <&ccu CLK_BUS_DE>;
+ clock-names = "mod",
+ "bus";
+ resets = <&ccu RST_BUS_DE>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ de2_mixer0: mixer@1100000 {
+ compatible = "allwinner,sun8i-v3s-de2-mixer";
+ reg = <0x01100000 0x100000>;
+ clocks = <&de2_clocks CLK_MIXER0>,
+ <&de2_clocks CLK_BUS_MIXER0>;
+ clock-names = "mod",
+ "bus";
+ resets = <&de2_clocks RST_MIXER0>;
+ assigned-clocks = <&de2_clocks CLK_MIXER0>;
+ assigned-clock-rates = <150000000>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mixer0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ mixer0_out_tcon0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_in_mixer0>;
+ };
+ };
+ };
+ };
+
+ tcon0: lcd-controller@1c0c000 {
+ compatible = "allwinner,sun8i-v3s-tcon";
+ reg = <0x01c0c000 0x1000>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_TCON0>,
+ <&ccu CLK_TCON0>;
+ clock-names = "ahb",
+ "tcon-ch0";
+ clock-output-names = "tcon-pixel-clock";
+ resets = <&ccu RST_BUS_TCON0>;
+ reset-names = "lcd";
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tcon0_in: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ tcon0_in_mixer0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&mixer0_out_tcon0>;
+ };
+ };
+
+ tcon0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ };
+ };
+
+
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun7i-a20-mmc";
reg = <0x01c0f000 0x1000>;
--
2.12.2
[toc] | [prev] | [next] | [standalone]
| From | Chen-Yu Tsai <wens@csie.org> |
|---|---|
| Date | 2017-05-05 05:40 +0200 |
| Subject | Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC |
| Message-ID | <tDCg1-4wN-5@gated-at.bofh.it> |
| In reply to | #1635660 |
On Thu, May 4, 2017 at 7:48 PM, Icenowy Zheng <icenowy@aosc.io> wrote:
> Allwinner V3s SoC features a "Display Engine 2.0" with only one TCON
> which have RGB LCD output.
Please also mention that it only has one mixer.
For the subject, you could just say "Add device nodes for the display pipeline".
>
> Add device nodes for it as well as the TCON.
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
> ---
> arch/arm/boot/dts/sun8i-v3s.dtsi | 87 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 87 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
> index 71075969e5e6..0a895179d8ae 100644
> --- a/arch/arm/boot/dts/sun8i-v3s.dtsi
> +++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
> @@ -41,6 +41,10 @@
> */
>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include <dt-bindings/clock/sun8i-v3s-ccu.h>
> +#include <dt-bindings/clock/sun8i-de2.h>
> +#include <dt-bindings/reset/sun8i-v3s-ccu.h>
> +#include <dt-bindings/reset/sun8i-de2.h>
>
> / {
> #address-cells = <1>;
> @@ -59,6 +63,12 @@
> };
> };
>
> + de: display-engine {
> + compatible = "allwinner,sun8i-v3s-display-engine";
> + allwinner,pipelines = <&de2_mixer0>;
> + status = "disabled";
> + };
> +
> timer {
> compatible = "arm,armv7-timer";
> interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
> @@ -93,6 +103,83 @@
> #size-cells = <1>;
> ranges;
>
> + de2_clocks: clock@1000000 {
> + compatible = "allwinner,sun50i-h5-de2-clk";
I am a bit skeptical about this. Since the V3S only has one mixer, do the clocks
for the second one even exist?
> + reg = <0x01000000 0x100000>;
> + clocks = <&ccu CLK_DE>,
> + <&ccu CLK_BUS_DE>;
> + clock-names = "mod",
> + "bus";
> + resets = <&ccu RST_BUS_DE>;
> + #clock-cells = <1>;
> + #reset-cells = <1>;
> + };
> +
> + de2_mixer0: mixer@1100000 {
> + compatible = "allwinner,sun8i-v3s-de2-mixer";
> + reg = <0x01100000 0x100000>;
> + clocks = <&de2_clocks CLK_MIXER0>,
> + <&de2_clocks CLK_BUS_MIXER0>;
> + clock-names = "mod",
> + "bus";
Nit: could you list the bus clock first?
Regards
ChenYu
> + resets = <&de2_clocks RST_MIXER0>;
> + assigned-clocks = <&de2_clocks CLK_MIXER0>;
> + assigned-clock-rates = <150000000>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + mixer0_out: port@1 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <1>;
> +
> + mixer0_out_tcon0: endpoint@0 {
> + reg = <0>;
> + remote-endpoint = <&tcon0_in_mixer0>;
> + };
> + };
> + };
> + };
> +
> + tcon0: lcd-controller@1c0c000 {
> + compatible = "allwinner,sun8i-v3s-tcon";
> + reg = <0x01c0c000 0x1000>;
> + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&ccu CLK_BUS_TCON0>,
> + <&ccu CLK_TCON0>;
> + clock-names = "ahb",
> + "tcon-ch0";
> + clock-output-names = "tcon-pixel-clock";
> + resets = <&ccu RST_BUS_TCON0>;
> + reset-names = "lcd";
> + status = "disabled";
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + tcon0_in: port@0 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <0>;
> +
> + tcon0_in_mixer0: endpoint@0 {
> + reg = <0>;
> + remote-endpoint = <&mixer0_out_tcon0>;
> + };
> + };
> +
> + tcon0_out: port@1 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <1>;
> + };
> + };
> + };
> +
> +
> mmc0: mmc@01c0f000 {
> compatible = "allwinner,sun7i-a20-mmc";
> reg = <0x01c0f000 0x1000>;
> --
> 2.12.2
>
> --
> 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@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
[toc] | [prev] | [next] | [standalone]
| From | icenowy@aosc.io |
|---|---|
| Date | 2017-05-05 11:00 +0200 |
| Subject | Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC |
| Message-ID | <tDHfH-7Oz-17@gated-at.bofh.it> |
| In reply to | #1636144 |
在 2017-05-05 11:31,Chen-Yu Tsai 写道:
> On Thu, May 4, 2017 at 7:48 PM, Icenowy Zheng <icenowy@aosc.io> wrote:
>> Allwinner V3s SoC features a "Display Engine 2.0" with only one TCON
>> which have RGB LCD output.
>
> Please also mention that it only has one mixer.
>
> For the subject, you could just say "Add device nodes for the display
> pipeline".
>
>>
>> Add device nodes for it as well as the TCON.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
>> ---
>> arch/arm/boot/dts/sun8i-v3s.dtsi | 87
>> ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 87 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi
>> b/arch/arm/boot/dts/sun8i-v3s.dtsi
>> index 71075969e5e6..0a895179d8ae 100644
>> --- a/arch/arm/boot/dts/sun8i-v3s.dtsi
>> +++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
>> @@ -41,6 +41,10 @@
>> */
>>
>> #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +#include <dt-bindings/clock/sun8i-v3s-ccu.h>
>> +#include <dt-bindings/clock/sun8i-de2.h>
>> +#include <dt-bindings/reset/sun8i-v3s-ccu.h>
>> +#include <dt-bindings/reset/sun8i-de2.h>
>>
>> / {
>> #address-cells = <1>;
>> @@ -59,6 +63,12 @@
>> };
>> };
>>
>> + de: display-engine {
>> + compatible = "allwinner,sun8i-v3s-display-engine";
>> + allwinner,pipelines = <&de2_mixer0>;
>> + status = "disabled";
>> + };
>> +
>> timer {
>> compatible = "arm,armv7-timer";
>> interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
>> IRQ_TYPE_LEVEL_LOW)>,
>> @@ -93,6 +103,83 @@
>> #size-cells = <1>;
>> ranges;
>>
>> + de2_clocks: clock@1000000 {
>> + compatible = "allwinner,sun50i-h5-de2-clk";
>
> I am a bit skeptical about this. Since the V3S only has one mixer, do
> the clocks
> for the second one even exist?
It's described in the de_clock.c in the BSP source code, and in hardware
these bits can be really set (although without clock output).
So I use this compatible which has still the extra clocks.
>
>> + reg = <0x01000000 0x100000>;
>> + clocks = <&ccu CLK_DE>,
>> + <&ccu CLK_BUS_DE>;
>> + clock-names = "mod",
>> + "bus";
>> + resets = <&ccu RST_BUS_DE>;
>> + #clock-cells = <1>;
>> + #reset-cells = <1>;
>> + };
>> +
>> + de2_mixer0: mixer@1100000 {
>> + compatible = "allwinner,sun8i-v3s-de2-mixer";
>> + reg = <0x01100000 0x100000>;
>> + clocks = <&de2_clocks CLK_MIXER0>,
>> + <&de2_clocks CLK_BUS_MIXER0>;
>> + clock-names = "mod",
>> + "bus";
>
> Nit: could you list the bus clock first?
>
> Regards
> ChenYu
>
>> + resets = <&de2_clocks RST_MIXER0>;
>> + assigned-clocks = <&de2_clocks CLK_MIXER0>;
>> + assigned-clock-rates = <150000000>;
>> +
>> + ports {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + mixer0_out: port@1 {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + reg = <1>;
>> +
>> + mixer0_out_tcon0: endpoint@0 {
>> + reg = <0>;
>> + remote-endpoint =
>> <&tcon0_in_mixer0>;
>> + };
>> + };
>> + };
>> + };
>> +
>> + tcon0: lcd-controller@1c0c000 {
>> + compatible = "allwinner,sun8i-v3s-tcon";
>> + reg = <0x01c0c000 0x1000>;
>> + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&ccu CLK_BUS_TCON0>,
>> + <&ccu CLK_TCON0>;
>> + clock-names = "ahb",
>> + "tcon-ch0";
>> + clock-output-names = "tcon-pixel-clock";
>> + resets = <&ccu RST_BUS_TCON0>;
>> + reset-names = "lcd";
>> + status = "disabled";
>> +
>> + ports {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + tcon0_in: port@0 {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + reg = <0>;
>> +
>> + tcon0_in_mixer0: endpoint@0 {
>> + reg = <0>;
>> + remote-endpoint =
>> <&mixer0_out_tcon0>;
>> + };
>> + };
>> +
>> + tcon0_out: port@1 {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + reg = <1>;
>> + };
>> + };
>> + };
>> +
>> +
>> mmc0: mmc@01c0f000 {
>> compatible = "allwinner,sun7i-a20-mmc";
>> reg = <0x01c0f000 0x1000>;
>> --
>> 2.12.2
>>
>> --
>> 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@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
[toc] | [prev] | [next] | [standalone]
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-05 14:40 +0200 |
| Subject | Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC |
| Message-ID | <tDKGC-1J7-11@gated-at.bofh.it> |
| In reply to | #1636258 |
于 2017年5月5日 GMT+08:00 下午8:30:35, Maxime Ripard <maxime.ripard@free-electrons.com> 写到:
>On Fri, May 05, 2017 at 04:53:43PM +0800, icenowy@aosc.io wrote:
>> > > + de2_clocks: clock@1000000 {
>> > > + compatible =
>"allwinner,sun50i-h5-de2-clk";
>> >
>> > I am a bit skeptical about this. Since the V3S only has one mixer,
>do
>> > the clocks
>> > for the second one even exist?
>>
>> It's described in the de_clock.c in the BSP source code, and in
>hardware
>> these bits can be really set (although without clock output).
>>
>> So I use this compatible which has still the extra clocks.
>
>If it's not usable, then it shouldn't be in the code, it's basically
>dead code.
Thus should we have one more DE2 CCU compatible without mixer1
clocks for V3s?
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2017-05-09 21:30 +0200 |
| Subject | Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC |
| Message-ID | <tFiZA-66p-11@gated-at.bofh.it> |
| In reply to | #1636352 |
[Multipart message — attachments visible in raw view] — view raw
1;4601;0c
On Fri, May 05, 2017 at 08:34:16PM +0800, Icenowy Zheng wrote:
>
>
> 于 2017年5月5日 GMT+08:00 下午8:30:35, Maxime Ripard <maxime.ripard@free-electrons.com> 写到:
> >On Fri, May 05, 2017 at 04:53:43PM +0800, icenowy@aosc.io wrote:
> >> > > + de2_clocks: clock@1000000 {
> >> > > + compatible =
> >"allwinner,sun50i-h5-de2-clk";
> >> >
> >> > I am a bit skeptical about this. Since the V3S only has one mixer,
> >do
> >> > the clocks
> >> > for the second one even exist?
> >>
> >> It's described in the de_clock.c in the BSP source code, and in
> >hardware
> >> these bits can be really set (although without clock output).
> >>
> >> So I use this compatible which has still the extra clocks.
> >
> >If it's not usable, then it shouldn't be in the code, it's basically
> >dead code.
>
> Thus should we have one more DE2 CCU compatible without mixer1
> clocks for V3s?
If those clocks don't exist on v3s, then yes.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2017-05-05 14:40 +0200 |
| Subject | Re: [linux-sunxi] [PATCH v6 11/13] ARM: dts: sun8i: add DE2 nodes for V3s SoC |
| Message-ID | <tDKGC-1J7-13@gated-at.bofh.it> |
| In reply to | #1636258 |
[Multipart message — attachments visible in raw view] — view raw
On Fri, May 05, 2017 at 04:53:43PM +0800, icenowy@aosc.io wrote:
> > > + de2_clocks: clock@1000000 {
> > > + compatible = "allwinner,sun50i-h5-de2-clk";
> >
> > I am a bit skeptical about this. Since the V3S only has one mixer, do
> > the clocks
> > for the second one even exist?
>
> It's described in the de_clock.c in the BSP source code, and in hardware
> these bits can be really set (although without clock output).
>
> So I use this compatible which has still the extra clocks.
If it's not usable, then it shouldn't be in the code, it's basically
dead code.
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [next] | [standalone]
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-04 14:00 +0200 |
| Subject | [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDnAm-2VI-7@gated-at.bofh.it> |
| In reply to | #1635650 |
Allwinner have a new "Display Engine 2.0" in their new SoCs, which comes
with mixers to do graphic processing and feed data to TCON, like the old
backends and frontends.
Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
Currently a lot of functions are still missing -- more investigations
are needed to gain enough information for them.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
Changes in v6:
- Rebased on wens's multi-pipeline patchset.
Changes in v5:
- Changed some code alignment.
- Request real 32-bit DMA (prepare for 64-bit SoCs).
Changes in v4:
- Killed some dead code according to Jernej.
drivers/gpu/drm/sun4i/Kconfig | 10 +
drivers/gpu/drm/sun4i/Makefile | 3 +
drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
drivers/gpu/drm/sun4i/sun8i_mixer.c | 394 ++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
6 files changed, 720 insertions(+)
create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
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 5a8227f37cc4..15557484520d 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -22,3 +22,13 @@ config DRM_SUN4I_BACKEND
original Allwinner Display Engine, which has a backend to
do some alpha blending and feed graphics to TCON. If M is
selected the module will be called sun4i-backend.
+
+config DRM_SUN4I_SUN8I_MIXER
+ tristate "Support for Allwinner Display Engine 2.0 Mixer"
+ depends on DRM_SUN4I
+ default MACH_SUN8I
+ help
+ Choose this option if you have an Allwinner SoC with the
+ Allwinner Display Engine 2.0, which has a mixer to do some
+ graphics mixture and feed graphics to TCON, If M is
+ selected the module will be called sun8i-mixer.
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index a08df56759e3..a876c6b3027c 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -8,7 +8,10 @@ sun4i-tcon-y += sun4i_crtc.o
sun4i-backend-y += sun4i_backend.o sun4i_layer.o
+sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
+
obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
+obj-$(CONFIG_DRM_SUN4I_SUN8I_MIXER) += sun8i-mixer.o
obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
new file mode 100644
index 000000000000..48f33d8e013b
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
+ *
+ * Based on sun4i_layer.h, which is:
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * 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/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drmP.h>
+
+#include "sun8i_layer.h"
+#include "sun8i_mixer.h"
+
+struct sun8i_plane_desc {
+ enum drm_plane_type type;
+ const uint32_t *formats;
+ uint32_t nformats;
+};
+
+static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ return 0;
+}
+
+static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
+ struct sun8i_mixer *mixer = layer->mixer;
+
+ sun8i_mixer_layer_enable(mixer, layer->id, false);
+}
+
+static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
+ struct sun8i_mixer *mixer = layer->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 sun8i_mixer_layer_helper_funcs = {
+ .atomic_check = sun8i_mixer_layer_atomic_check,
+ .atomic_disable = sun8i_mixer_layer_atomic_disable,
+ .atomic_update = sun8i_mixer_layer_atomic_update,
+};
+
+static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .destroy = drm_plane_cleanup,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .update_plane = drm_atomic_helper_update_plane,
+};
+
+static const uint32_t sun8i_mixer_layer_formats[] = {
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_XRGB8888,
+};
+
+static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
+ {
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .formats = sun8i_mixer_layer_formats,
+ .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
+ },
+};
+
+static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm,
+ struct sun8i_mixer *mixer,
+ const struct sun8i_plane_desc *plane)
+{
+ struct sun8i_layer *layer;
+ int ret;
+
+ layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
+ if (!layer)
+ return ERR_PTR(-ENOMEM);
+
+ /* possible crtcs are set later */
+ ret = drm_universal_plane_init(drm, &layer->plane, 0,
+ &sun8i_mixer_layer_funcs,
+ plane->formats, plane->nformats,
+ plane->type, NULL);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't initialize layer\n");
+ return ERR_PTR(ret);
+ }
+
+ drm_plane_helper_add(&layer->plane,
+ &sun8i_mixer_layer_helper_funcs);
+ layer->mixer = mixer;
+
+ return layer;
+}
+
+struct drm_plane **sun8i_layers_init(struct drm_device *drm,
+ struct sunxi_engine *engine)
+{
+ struct drm_plane **planes;
+ struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+ int i;
+
+ planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
+ sizeof(*planes), GFP_KERNEL);
+ if (!planes)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
+ const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
+ struct sun8i_layer *layer;
+
+ layer = sun8i_layer_init_one(drm, mixer, 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;
+ planes[i] = &layer->plane;
+ };
+
+ return planes;
+}
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h
new file mode 100644
index 000000000000..e5eccd27cff0
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
+ *
+ * Based on sun4i_layer.h, which is:
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * 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_LAYER_H_
+#define _SUN8I_LAYER_H_
+
+struct sunxi_engine;
+
+struct sun8i_layer {
+ struct drm_plane plane;
+ struct sun4i_drv *drv;
+ struct sun8i_mixer *mixer;
+ int id;
+};
+
+static inline struct sun8i_layer *
+plane_to_sun8i_layer(struct drm_plane *plane)
+{
+ return container_of(plane, struct sun8i_layer, plane);
+}
+
+struct drm_plane **sun8i_layers_init(struct drm_device *drm,
+ struct sunxi_engine *engine);
+#endif /* _SUN8I_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..e216b84d5bb2
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * 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/dma-mapping.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "sun4i_drv.h"
+#include "sun8i_mixer.h"
+#include "sun8i_layer.h"
+#include "sunxi_engine.h"
+
+void sun8i_mixer_commit(struct sunxi_engine *engine)
+{
+ DRM_DEBUG_DRIVER("Committing changes\n");
+
+ regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
+ SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+}
+
+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->engine.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->engine.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->engine.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);
+}
+
+static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
+ u32 format, u32 *mode)
+{
+ switch (format) {
+ 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;
+
+ 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->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
+ SUN8I_MIXER_SIZE(state->crtc_w,
+ state->crtc_h));
+ DRM_DEBUG_DRIVER("Updating blender size\n");
+ regmap_write(mixer->engine.regs,
+ SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
+ SUN8I_MIXER_SIZE(state->crtc_w,
+ state->crtc_h));
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
+ SUN8I_MIXER_SIZE(state->crtc_w,
+ state->crtc_h));
+ DRM_DEBUG_DRIVER("Updating channel size\n");
+ regmap_write(mixer->engine.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->engine.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->engine.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->engine.regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+ SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+
+ return 0;
+}
+
+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->engine.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->engine.regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+ return 0;
+}
+
+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;
+ /* 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];
+
+ DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+ regmap_write(mixer->engine.regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+ lower_32_bits(paddr));
+
+ return 0;
+}
+
+static const struct sunxi_engine_ops sun8i_engine_ops = {
+ .commit = sun8i_mixer_commit,
+ .layers_init = sun8i_layers_init,
+};
+
+static struct regmap_config sun8i_mixer_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0xbfffc, /* 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;
+
+ /*
+ * The mixer uses single 32-bit register to store memory
+ * addresses, so that it cannot deal with 64-bit memory
+ * addresses.
+ * Restrict the DMA mask so that the mixer won't be
+ * allocated some memory that is too high.
+ */
+ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "Cannot do 32-bit DMA.\n");
+ return ret;
+ }
+
+ mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+ if (!mixer)
+ return -ENOMEM;
+ dev_set_drvdata(dev, mixer);
+ mixer->engine.ops = &sun8i_engine_ops;
+ mixer->engine.node = dev->of_node;
+
+ 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->engine.regs = devm_regmap_init_mmio(dev, regs,
+ &sun8i_mixer_regmap_config);
+ if (IS_ERR(mixer->engine.regs)) {
+ dev_err(dev, "Couldn't create the mixer regmap\n");
+ return PTR_ERR(mixer->engine.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);
+
+ list_add_tail(&mixer->engine.list, &drv->engine_list);
+
+ /* Reset the registers */
+ for (i = 0x0; i < 0x20000; i += 4)
+ regmap_write(mixer->engine.regs, i, 0);
+
+ /* Enable the mixer */
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
+ SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+
+ /* Initialize blender */
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
+ SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
+ SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
+ SUN8I_MIXER_BLEND_BKCOLOR_DEF);
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
+ SUN8I_MIXER_BLEND_MODE_DEF);
+ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
+ SUN8I_MIXER_BLEND_CK_CTL_DEF);
+
+ regmap_write(mixer->engine.regs,
+ SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
+ 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->engine.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);
+
+ list_del(&mixer->engine.list);
+
+ 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.io>");
+MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..4785ac090b8c
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * 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 "sunxi_engine.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 sub-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 sunxi_engine engine;
+
+ const struct sun8i_mixer_cfg *cfg;
+
+ struct reset_control *reset;
+
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+};
+
+static inline struct sun8i_mixer *
+engine_to_sun8i_mixer(struct sunxi_engine *engine)
+{
+ return container_of(engine, struct sun8i_mixer, engine);
+}
+
+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.12.2
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2017-05-04 15:10 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDoG5-3Su-5@gated-at.bofh.it> |
| In reply to | #1635661 |
[Multipart message — attachments visible in raw view] — view raw
On Thu, May 04, 2017 at 07:48:53PM +0800, Icenowy Zheng wrote:
> Allwinner have a new "Display Engine 2.0" in their new SoCs, which comes
> with mixers to do graphic processing and feed data to TCON, like the old
> backends and frontends.
>
> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>
> Currently a lot of functions are still missing -- more investigations
> are needed to gain enough information for them.
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
> ---
> Changes in v6:
> - Rebased on wens's multi-pipeline patchset.
> Changes in v5:
> - Changed some code alignment.
> - Request real 32-bit DMA (prepare for 64-bit SoCs).
> Changes in v4:
> - Killed some dead code according to Jernej.
>
> drivers/gpu/drm/sun4i/Kconfig | 10 +
> drivers/gpu/drm/sun4i/Makefile | 3 +
> drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
> drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
> drivers/gpu/drm/sun4i/sun8i_mixer.c | 394 ++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
> 6 files changed, 720 insertions(+)
> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
> 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 5a8227f37cc4..15557484520d 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -22,3 +22,13 @@ config DRM_SUN4I_BACKEND
> original Allwinner Display Engine, which has a backend to
> do some alpha blending and feed graphics to TCON. If M is
> selected the module will be called sun4i-backend.
> +
> +config DRM_SUN4I_SUN8I_MIXER
DRM_SUN8I_MIXER?
> + tristate "Support for Allwinner Display Engine 2.0 Mixer"
> + depends on DRM_SUN4I
> + default MACH_SUN8I
> + help
> + Choose this option if you have an Allwinner SoC with the
> + Allwinner Display Engine 2.0, which has a mixer to do some
> + graphics mixture and feed graphics to TCON, If M is
> + selected the module will be called sun8i-mixer.
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index a08df56759e3..a876c6b3027c 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -8,7 +8,10 @@ sun4i-tcon-y += sun4i_crtc.o
>
> sun4i-backend-y += sun4i_backend.o sun4i_layer.o
>
> +sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
> +
> obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
> obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
> +obj-$(CONFIG_DRM_SUN4I_SUN8I_MIXER) += sun8i-mixer.o
> obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
> obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
> new file mode 100644
> index 000000000000..48f33d8e013b
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
> @@ -0,0 +1,140 @@
> +/*
> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
> + *
> + * Based on sun4i_layer.h, which is:
> + * Copyright (C) 2015 Free Electrons
> + * Copyright (C) 2015 NextThing Co
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * 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/drm_atomic_helper.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drmP.h>
> +
> +#include "sun8i_layer.h"
> +#include "sun8i_mixer.h"
> +
> +struct sun8i_plane_desc {
> + enum drm_plane_type type;
> + const uint32_t *formats;
> + uint32_t nformats;
> +};
> +
> +static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
> + struct drm_plane_state *state)
> +{
> + return 0;
> +}
This isn't needed.
> +static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
> + struct sun8i_mixer *mixer = layer->mixer;
> +
> + sun8i_mixer_layer_enable(mixer, layer->id, false);
> +}
> +
> +static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
> + struct sun8i_mixer *mixer = layer->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 sun8i_mixer_layer_helper_funcs = {
> + .atomic_check = sun8i_mixer_layer_atomic_check,
> + .atomic_disable = sun8i_mixer_layer_atomic_disable,
> + .atomic_update = sun8i_mixer_layer_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
> + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> + .destroy = drm_plane_cleanup,
> + .disable_plane = drm_atomic_helper_disable_plane,
> + .reset = drm_atomic_helper_plane_reset,
> + .update_plane = drm_atomic_helper_update_plane,
> +};
> +
> +static const uint32_t sun8i_mixer_layer_formats[] = {
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_XRGB8888,
> +};
> +
> +static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
> + {
> + .type = DRM_PLANE_TYPE_PRIMARY,
> + .formats = sun8i_mixer_layer_formats,
> + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
> + },
> +};
> +
> +static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm,
> + struct sun8i_mixer *mixer,
> + const struct sun8i_plane_desc *plane)
> +{
> + struct sun8i_layer *layer;
> + int ret;
> +
> + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
> + if (!layer)
> + return ERR_PTR(-ENOMEM);
> +
> + /* possible crtcs are set later */
> + ret = drm_universal_plane_init(drm, &layer->plane, 0,
> + &sun8i_mixer_layer_funcs,
> + plane->formats, plane->nformats,
> + plane->type, NULL);
> + if (ret) {
> + dev_err(drm->dev, "Couldn't initialize layer\n");
> + return ERR_PTR(ret);
> + }
> +
> + drm_plane_helper_add(&layer->plane,
> + &sun8i_mixer_layer_helper_funcs);
> + layer->mixer = mixer;
> +
> + return layer;
> +}
> +
> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
> + struct sunxi_engine *engine)
> +{
> + struct drm_plane **planes;
> + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
> + int i;
> +
> + planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
> + sizeof(*planes), GFP_KERNEL);
> + if (!planes)
> + return ERR_PTR(-ENOMEM);
> +
> + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
> + const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
> + struct sun8i_layer *layer;
> +
> + layer = sun8i_layer_init_one(drm, mixer, 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;
> + planes[i] = &layer->plane;
> + };
> +
> + return planes;
> +}
> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h
> new file mode 100644
> index 000000000000..e5eccd27cff0
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
> + *
> + * Based on sun4i_layer.h, which is:
> + * Copyright (C) 2015 Free Electrons
> + * Copyright (C) 2015 NextThing Co
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * 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_LAYER_H_
> +#define _SUN8I_LAYER_H_
> +
> +struct sunxi_engine;
> +
> +struct sun8i_layer {
> + struct drm_plane plane;
> + struct sun4i_drv *drv;
> + struct sun8i_mixer *mixer;
> + int id;
> +};
> +
> +static inline struct sun8i_layer *
> +plane_to_sun8i_layer(struct drm_plane *plane)
> +{
> + return container_of(plane, struct sun8i_layer, plane);
> +}
> +
> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
> + struct sunxi_engine *engine);
> +#endif /* _SUN8I_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..e216b84d5bb2
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
> @@ -0,0 +1,394 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
> + *
> + * 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/dma-mapping.h>
> +#include <linux/reset.h>
> +#include <linux/of_device.h>
> +
> +#include "sun4i_drv.h"
> +#include "sun8i_mixer.h"
> +#include "sun8i_layer.h"
> +#include "sunxi_engine.h"
> +
> +void sun8i_mixer_commit(struct sunxi_engine *engine)
> +{
> + DRM_DEBUG_DRIVER("Committing changes\n");
> +
> + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
> + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
> +}
This function can be static
> +
> +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->engine.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->engine.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->engine.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 one too.
> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
> + u32 format, u32 *mode)
> +{
> + switch (format) {
> + 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;
> +
> + 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->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
> + SUN8I_MIXER_SIZE(state->crtc_w,
> + state->crtc_h));
> + DRM_DEBUG_DRIVER("Updating blender size\n");
> + regmap_write(mixer->engine.regs,
> + SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
> + SUN8I_MIXER_SIZE(state->crtc_w,
> + state->crtc_h));
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
> + SUN8I_MIXER_SIZE(state->crtc_w,
> + state->crtc_h));
> + DRM_DEBUG_DRIVER("Updating channel size\n");
> + regmap_write(mixer->engine.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->engine.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->engine.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->engine.regs,
> + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
X and Y are fixed point numbers. You want to keep only the higher 16
bits there.
> +
> + return 0;
> +}
> +
> +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->engine.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->engine.regs,
> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
> +
> + return 0;
> +}
> +
> +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;
> + /* 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];
> +
> + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
> +
> + regmap_write(mixer->engine.regs,
> + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
> + lower_32_bits(paddr));
> +
> + return 0;
> +}
> +
> +static const struct sunxi_engine_ops sun8i_engine_ops = {
> + .commit = sun8i_mixer_commit,
> + .layers_init = sun8i_layers_init,
> +};
> +
> +static struct regmap_config sun8i_mixer_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = 0xbfffc, /* 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;
> +
> + /*
> + * The mixer uses single 32-bit register to store memory
> + * addresses, so that it cannot deal with 64-bit memory
> + * addresses.
> + * Restrict the DMA mask so that the mixer won't be
> + * allocated some memory that is too high.
> + */
> + ret = dma_set_mask(dev, DMA_BIT_MASK(32));
> + if (ret) {
> + dev_err(dev, "Cannot do 32-bit DMA.\n");
> + return ret;
> + }
> +
> + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> + if (!mixer)
> + return -ENOMEM;
> + dev_set_drvdata(dev, mixer);
> + mixer->engine.ops = &sun8i_engine_ops;
> + mixer->engine.node = dev->of_node;
> +
> + 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->engine.regs = devm_regmap_init_mmio(dev, regs,
> + &sun8i_mixer_regmap_config);
> + if (IS_ERR(mixer->engine.regs)) {
> + dev_err(dev, "Couldn't create the mixer regmap\n");
> + return PTR_ERR(mixer->engine.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);
> +
> + list_add_tail(&mixer->engine.list, &drv->engine_list);
You didn't call INIT_LIST_HEAD on that list.
> +
> + /* Reset the registers */
> + for (i = 0x0; i < 0x20000; i += 4)
> + regmap_write(mixer->engine.regs, i, 0);
> +
> + /* Enable the mixer */
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
> + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
> +
> + /* Initialize blender */
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
> + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
> + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
> + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
> + SUN8I_MIXER_BLEND_MODE_DEF);
> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
> + SUN8I_MIXER_BLEND_CK_CTL_DEF);
> +
> + regmap_write(mixer->engine.regs,
> + SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
> + 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->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
> + mixer->cfg->vi_num);
> +
> + return 0;
> +
> + clk_disable_unprepare(mixer->mod_clk);
This line cannot be reached.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [next] | [standalone]
| From | icenowy@aosc.io |
|---|---|
| Date | 2017-05-04 19:00 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDsgF-6aT-7@gated-at.bofh.it> |
| In reply to | #1635719 |
在 2017-05-05 00:50,icenowy@aosc.io 写道:
> 在 2017-05-04 21:05,Maxime Ripard 写道:
>> On Thu, May 04, 2017 at 07:48:53PM +0800, Icenowy Zheng wrote:
>>> Allwinner have a new "Display Engine 2.0" in their new SoCs, which
>>> comes
>>> with mixers to do graphic processing and feed data to TCON, like the
>>> old
>>> backends and frontends.
>>>
>>> Add support for the mixer on Allwinner V3s SoC; it's the simplest
>>> one.
>>>
>>> Currently a lot of functions are still missing -- more investigations
>>> are needed to gain enough information for them.
>>>
>>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
>>> ---
>>> Changes in v6:
>>> - Rebased on wens's multi-pipeline patchset.
>>> Changes in v5:
>>> - Changed some code alignment.
>>> - Request real 32-bit DMA (prepare for 64-bit SoCs).
>>> Changes in v4:
>>> - Killed some dead code according to Jernej.
>>>
>>> drivers/gpu/drm/sun4i/Kconfig | 10 +
>>> drivers/gpu/drm/sun4i/Makefile | 3 +
>>> drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
>>> drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
>>> drivers/gpu/drm/sun4i/sun8i_mixer.c | 394
>>> ++++++++++++++++++++++++++++++++++++
>>> drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
>>> 6 files changed, 720 insertions(+)
>>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
>>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
>>> 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 5a8227f37cc4..15557484520d 100644
>>> --- a/drivers/gpu/drm/sun4i/Kconfig
>>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>>> @@ -22,3 +22,13 @@ config DRM_SUN4I_BACKEND
>>> original Allwinner Display Engine, which has a backend to
>>> do some alpha blending and feed graphics to TCON. If M is
>>> selected the module will be called sun4i-backend.
>>> +
>>> +config DRM_SUN4I_SUN8I_MIXER
>>
>> DRM_SUN8I_MIXER?
>>
>>> + tristate "Support for Allwinner Display Engine 2.0 Mixer"
>>> + depends on DRM_SUN4I
>>> + default MACH_SUN8I
>>> + help
>>> + Choose this option if you have an Allwinner SoC with the
>>> + Allwinner Display Engine 2.0, which has a mixer to do some
>>> + graphics mixture and feed graphics to TCON, If M is
>>> + selected the module will be called sun8i-mixer.
>>> diff --git a/drivers/gpu/drm/sun4i/Makefile
>>> b/drivers/gpu/drm/sun4i/Makefile
>>> index a08df56759e3..a876c6b3027c 100644
>>> --- a/drivers/gpu/drm/sun4i/Makefile
>>> +++ b/drivers/gpu/drm/sun4i/Makefile
>>> @@ -8,7 +8,10 @@ sun4i-tcon-y += sun4i_crtc.o
>>>
>>> sun4i-backend-y += sun4i_backend.o sun4i_layer.o
>>>
>>> +sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
>>> +
>>> obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>>> obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
>>> +obj-$(CONFIG_DRM_SUN4I_SUN8I_MIXER) += sun8i-mixer.o
>>> obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>>> obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c
>>> b/drivers/gpu/drm/sun4i/sun8i_layer.c
>>> new file mode 100644
>>> index 000000000000..48f33d8e013b
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
>>> @@ -0,0 +1,140 @@
>>> +/*
>>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>>> + *
>>> + * Based on sun4i_layer.h, which is:
>>> + * Copyright (C) 2015 Free Electrons
>>> + * Copyright (C) 2015 NextThing Co
>>> + *
>>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>>> + *
>>> + * 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/drm_atomic_helper.h>
>>> +#include <drm/drm_plane_helper.h>
>>> +#include <drm/drmP.h>
>>> +
>>> +#include "sun8i_layer.h"
>>> +#include "sun8i_mixer.h"
>>> +
>>> +struct sun8i_plane_desc {
>>> + enum drm_plane_type type;
>>> + const uint32_t *formats;
>>> + uint32_t nformats;
>>> +};
>>> +
>>> +static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
>>> + struct drm_plane_state *state)
>>> +{
>>> + return 0;
>>> +}
>>
>> This isn't needed.
>>
>>> +static void sun8i_mixer_layer_atomic_disable(struct drm_plane
>>> *plane,
>>> + struct drm_plane_state *old_state)
>>> +{
>>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>>> + struct sun8i_mixer *mixer = layer->mixer;
>>> +
>>> + sun8i_mixer_layer_enable(mixer, layer->id, false);
>>> +}
>>> +
>>> +static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
>>> + struct drm_plane_state *old_state)
>>> +{
>>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>>> + struct sun8i_mixer *mixer = layer->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 sun8i_mixer_layer_helper_funcs
>>> = {
>>> + .atomic_check = sun8i_mixer_layer_atomic_check,
>>> + .atomic_disable = sun8i_mixer_layer_atomic_disable,
>>> + .atomic_update = sun8i_mixer_layer_atomic_update,
>>> +};
>>> +
>>> +static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
>>> + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>>> + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>>> + .destroy = drm_plane_cleanup,
>>> + .disable_plane = drm_atomic_helper_disable_plane,
>>> + .reset = drm_atomic_helper_plane_reset,
>>> + .update_plane = drm_atomic_helper_update_plane,
>>> +};
>>> +
>>> +static const uint32_t sun8i_mixer_layer_formats[] = {
>>> + DRM_FORMAT_RGB888,
>>> + DRM_FORMAT_XRGB8888,
>>> +};
>>> +
>>> +static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
>>> + {
>>> + .type = DRM_PLANE_TYPE_PRIMARY,
>>> + .formats = sun8i_mixer_layer_formats,
>>> + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>>> + },
>>> +};
>>> +
>>> +static struct sun8i_layer *sun8i_layer_init_one(struct drm_device
>>> *drm,
>>> + struct sun8i_mixer *mixer,
>>> + const struct sun8i_plane_desc *plane)
>>> +{
>>> + struct sun8i_layer *layer;
>>> + int ret;
>>> +
>>> + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
>>> + if (!layer)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + /* possible crtcs are set later */
>>> + ret = drm_universal_plane_init(drm, &layer->plane, 0,
>>> + &sun8i_mixer_layer_funcs,
>>> + plane->formats, plane->nformats,
>>> + plane->type, NULL);
>>> + if (ret) {
>>> + dev_err(drm->dev, "Couldn't initialize layer\n");
>>> + return ERR_PTR(ret);
>>> + }
>>> +
>>> + drm_plane_helper_add(&layer->plane,
>>> + &sun8i_mixer_layer_helper_funcs);
>>> + layer->mixer = mixer;
>>> +
>>> + return layer;
>>> +}
>>> +
>>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>>> + struct sunxi_engine *engine)
>>> +{
>>> + struct drm_plane **planes;
>>> + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
>>> + int i;
>>> +
>>> + planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
>>> + sizeof(*planes), GFP_KERNEL);
>>> + if (!planes)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>>> + const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
>>> + struct sun8i_layer *layer;
>>> +
>>> + layer = sun8i_layer_init_one(drm, mixer, 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;
>>> + planes[i] = &layer->plane;
>>> + };
>>> +
>>> + return planes;
>>> +}
>>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h
>>> b/drivers/gpu/drm/sun4i/sun8i_layer.h
>>> new file mode 100644
>>> index 000000000000..e5eccd27cff0
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
>>> @@ -0,0 +1,36 @@
>>> +/*
>>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>>> + *
>>> + * Based on sun4i_layer.h, which is:
>>> + * Copyright (C) 2015 Free Electrons
>>> + * Copyright (C) 2015 NextThing Co
>>> + *
>>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>>> + *
>>> + * 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_LAYER_H_
>>> +#define _SUN8I_LAYER_H_
>>> +
>>> +struct sunxi_engine;
>>> +
>>> +struct sun8i_layer {
>>> + struct drm_plane plane;
>>> + struct sun4i_drv *drv;
>>> + struct sun8i_mixer *mixer;
>>> + int id;
>>> +};
>>> +
>>> +static inline struct sun8i_layer *
>>> +plane_to_sun8i_layer(struct drm_plane *plane)
>>> +{
>>> + return container_of(plane, struct sun8i_layer, plane);
>>> +}
>>> +
>>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>>> + struct sunxi_engine *engine);
>>> +#endif /* _SUN8I_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..e216b84d5bb2
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>> @@ -0,0 +1,394 @@
>>> +/*
>>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
>>> + *
>>> + * 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/dma-mapping.h>
>>> +#include <linux/reset.h>
>>> +#include <linux/of_device.h>
>>> +
>>> +#include "sun4i_drv.h"
>>> +#include "sun8i_mixer.h"
>>> +#include "sun8i_layer.h"
>>> +#include "sunxi_engine.h"
>>> +
>>> +void sun8i_mixer_commit(struct sunxi_engine *engine)
>>> +{
>>> + DRM_DEBUG_DRIVER("Committing changes\n");
>>> +
>>> + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>>> + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>>> +}
>>
>> This function can be static
>>
>>> +
>>> +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->engine.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->engine.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->engine.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 one too.
>
> It's called from sun8i_layer.c, so it cannot be static.
>
>>
>>> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>>> + u32 format, u32 *mode)
>>> +{
>>> + switch (format) {
>>> + 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;
>>> +
>>> + 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->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
>>> + SUN8I_MIXER_SIZE(state->crtc_w,
>>> + state->crtc_h));
>>> + DRM_DEBUG_DRIVER("Updating blender size\n");
>>> + regmap_write(mixer->engine.regs,
>>> + SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
>>> + SUN8I_MIXER_SIZE(state->crtc_w,
>>> + state->crtc_h));
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
>>> + SUN8I_MIXER_SIZE(state->crtc_w,
>>> + state->crtc_h));
>>> + DRM_DEBUG_DRIVER("Updating channel size\n");
>>> + regmap_write(mixer->engine.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->engine.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->engine.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->engine.regs,
>>> + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>>> + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>>
>> X and Y are fixed point numbers. You want to keep only the higher 16
>> bits there.
>
> Do you mean "lower 16 bits"? Thus should I (x & 0xffff) or ((u16)x) ?
Oh sorry, it's 16.16 ...
I'm misled by the sun4i_backend driver.
May I soon send out a patch to fix the sun4i_backend?
>
> P.S. The negative coordinates are broken, how should I deal with it? or
> is the coordinates promised to be not negative?
>
>>
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +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->engine.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->engine.regs,
>>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +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;
>>> + /* 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];
>>> +
>>> + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>>> +
>>> + regmap_write(mixer->engine.regs,
>>> + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>>> + lower_32_bits(paddr));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct sunxi_engine_ops sun8i_engine_ops = {
>>> + .commit = sun8i_mixer_commit,
>>> + .layers_init = sun8i_layers_init,
>>> +};
>>> +
>>> +static struct regmap_config sun8i_mixer_regmap_config = {
>>> + .reg_bits = 32,
>>> + .val_bits = 32,
>>> + .reg_stride = 4,
>>> + .max_register = 0xbfffc, /* 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;
>>> +
>>> + /*
>>> + * The mixer uses single 32-bit register to store memory
>>> + * addresses, so that it cannot deal with 64-bit memory
>>> + * addresses.
>>> + * Restrict the DMA mask so that the mixer won't be
>>> + * allocated some memory that is too high.
>>> + */
>>> + ret = dma_set_mask(dev, DMA_BIT_MASK(32));
>>> + if (ret) {
>>> + dev_err(dev, "Cannot do 32-bit DMA.\n");
>>> + return ret;
>>> + }
>>> +
>>> + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>>> + if (!mixer)
>>> + return -ENOMEM;
>>> + dev_set_drvdata(dev, mixer);
>>> + mixer->engine.ops = &sun8i_engine_ops;
>>> + mixer->engine.node = dev->of_node;
>>> +
>>> + 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->engine.regs = devm_regmap_init_mmio(dev, regs,
>>> + &sun8i_mixer_regmap_config);
>>> + if (IS_ERR(mixer->engine.regs)) {
>>> + dev_err(dev, "Couldn't create the mixer regmap\n");
>>> + return PTR_ERR(mixer->engine.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);
>>> +
>>> + list_add_tail(&mixer->engine.list, &drv->engine_list);
>>
>> You didn't call INIT_LIST_HEAD on that list.
>>
>>> +
>>> + /* Reset the registers */
>>> + for (i = 0x0; i < 0x20000; i += 4)
>>> + regmap_write(mixer->engine.regs, i, 0);
>>> +
>>> + /* Enable the mixer */
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
>>> + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>>> +
>>> + /* Initialize blender */
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>>> + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>>> + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
>>> + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
>>> + SUN8I_MIXER_BLEND_MODE_DEF);
>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
>>> + SUN8I_MIXER_BLEND_CK_CTL_DEF);
>>> +
>>> + regmap_write(mixer->engine.regs,
>>> + SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
>>> + 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->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
>>> + mixer->cfg->vi_num);
>>> +
>>> + return 0;
>>> +
>>> + clk_disable_unprepare(mixer->mod_clk);
>>
>> This line cannot be reached.
>>
>> Maxime
[toc] | [prev] | [next] | [standalone]
| From | icenowy@aosc.io |
|---|---|
| Date | 2017-05-04 19:20 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDsA2-6wT-7@gated-at.bofh.it> |
| In reply to | #1635891 |
在 2017-05-05 00:57,icenowy@aosc.io 写道:
> 在 2017-05-05 00:50,icenowy@aosc.io 写道:
>> 在 2017-05-04 21:05,Maxime Ripard 写道:
>>> On Thu, May 04, 2017 at 07:48:53PM +0800, Icenowy Zheng wrote:
>>>> Allwinner have a new "Display Engine 2.0" in their new SoCs, which
>>>> comes
>>>> with mixers to do graphic processing and feed data to TCON, like the
>>>> old
>>>> backends and frontends.
>>>>
>>>> Add support for the mixer on Allwinner V3s SoC; it's the simplest
>>>> one.
>>>>
>>>> Currently a lot of functions are still missing -- more
>>>> investigations
>>>> are needed to gain enough information for them.
>>>>
>>>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
>>>> ---
>>>> Changes in v6:
>>>> - Rebased on wens's multi-pipeline patchset.
>>>> Changes in v5:
>>>> - Changed some code alignment.
>>>> - Request real 32-bit DMA (prepare for 64-bit SoCs).
>>>> Changes in v4:
>>>> - Killed some dead code according to Jernej.
>>>>
>>>> drivers/gpu/drm/sun4i/Kconfig | 10 +
>>>> drivers/gpu/drm/sun4i/Makefile | 3 +
>>>> drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
>>>> drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
>>>> drivers/gpu/drm/sun4i/sun8i_mixer.c | 394
>>>> ++++++++++++++++++++++++++++++++++++
>>>> drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
>>>> 6 files changed, 720 insertions(+)
>>>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
>>>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
>>>> 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 5a8227f37cc4..15557484520d 100644
>>>> --- a/drivers/gpu/drm/sun4i/Kconfig
>>>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>>>> @@ -22,3 +22,13 @@ config DRM_SUN4I_BACKEND
>>>> original Allwinner Display Engine, which has a backend to
>>>> do some alpha blending and feed graphics to TCON. If M is
>>>> selected the module will be called sun4i-backend.
>>>> +
>>>> +config DRM_SUN4I_SUN8I_MIXER
>>>
>>> DRM_SUN8I_MIXER?
>>>
>>>> + tristate "Support for Allwinner Display Engine 2.0 Mixer"
>>>> + depends on DRM_SUN4I
>>>> + default MACH_SUN8I
>>>> + help
>>>> + Choose this option if you have an Allwinner SoC with the
>>>> + Allwinner Display Engine 2.0, which has a mixer to do some
>>>> + graphics mixture and feed graphics to TCON, If M is
>>>> + selected the module will be called sun8i-mixer.
>>>> diff --git a/drivers/gpu/drm/sun4i/Makefile
>>>> b/drivers/gpu/drm/sun4i/Makefile
>>>> index a08df56759e3..a876c6b3027c 100644
>>>> --- a/drivers/gpu/drm/sun4i/Makefile
>>>> +++ b/drivers/gpu/drm/sun4i/Makefile
>>>> @@ -8,7 +8,10 @@ sun4i-tcon-y += sun4i_crtc.o
>>>>
>>>> sun4i-backend-y += sun4i_backend.o sun4i_layer.o
>>>>
>>>> +sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
>>>> +
>>>> obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>>>> obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
>>>> +obj-$(CONFIG_DRM_SUN4I_SUN8I_MIXER) += sun8i-mixer.o
>>>> obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>>>> obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>>>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c
>>>> b/drivers/gpu/drm/sun4i/sun8i_layer.c
>>>> new file mode 100644
>>>> index 000000000000..48f33d8e013b
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
>>>> @@ -0,0 +1,140 @@
>>>> +/*
>>>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>>>> + *
>>>> + * Based on sun4i_layer.h, which is:
>>>> + * Copyright (C) 2015 Free Electrons
>>>> + * Copyright (C) 2015 NextThing Co
>>>> + *
>>>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>>>> + *
>>>> + * 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/drm_atomic_helper.h>
>>>> +#include <drm/drm_plane_helper.h>
>>>> +#include <drm/drmP.h>
>>>> +
>>>> +#include "sun8i_layer.h"
>>>> +#include "sun8i_mixer.h"
>>>> +
>>>> +struct sun8i_plane_desc {
>>>> + enum drm_plane_type type;
>>>> + const uint32_t *formats;
>>>> + uint32_t nformats;
>>>> +};
>>>> +
>>>> +static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
>>>> + struct drm_plane_state *state)
>>>> +{
>>>> + return 0;
>>>> +}
>>>
>>> This isn't needed.
>>>
>>>> +static void sun8i_mixer_layer_atomic_disable(struct drm_plane
>>>> *plane,
>>>> + struct drm_plane_state *old_state)
>>>> +{
>>>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>>>> + struct sun8i_mixer *mixer = layer->mixer;
>>>> +
>>>> + sun8i_mixer_layer_enable(mixer, layer->id, false);
>>>> +}
>>>> +
>>>> +static void sun8i_mixer_layer_atomic_update(struct drm_plane
>>>> *plane,
>>>> + struct drm_plane_state *old_state)
>>>> +{
>>>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>>>> + struct sun8i_mixer *mixer = layer->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 sun8i_mixer_layer_helper_funcs
>>>> = {
>>>> + .atomic_check = sun8i_mixer_layer_atomic_check,
>>>> + .atomic_disable = sun8i_mixer_layer_atomic_disable,
>>>> + .atomic_update = sun8i_mixer_layer_atomic_update,
>>>> +};
>>>> +
>>>> +static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
>>>> + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>>>> + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>>>> + .destroy = drm_plane_cleanup,
>>>> + .disable_plane = drm_atomic_helper_disable_plane,
>>>> + .reset = drm_atomic_helper_plane_reset,
>>>> + .update_plane = drm_atomic_helper_update_plane,
>>>> +};
>>>> +
>>>> +static const uint32_t sun8i_mixer_layer_formats[] = {
>>>> + DRM_FORMAT_RGB888,
>>>> + DRM_FORMAT_XRGB8888,
>>>> +};
>>>> +
>>>> +static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
>>>> + {
>>>> + .type = DRM_PLANE_TYPE_PRIMARY,
>>>> + .formats = sun8i_mixer_layer_formats,
>>>> + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>>>> + },
>>>> +};
>>>> +
>>>> +static struct sun8i_layer *sun8i_layer_init_one(struct drm_device
>>>> *drm,
>>>> + struct sun8i_mixer *mixer,
>>>> + const struct sun8i_plane_desc *plane)
>>>> +{
>>>> + struct sun8i_layer *layer;
>>>> + int ret;
>>>> +
>>>> + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
>>>> + if (!layer)
>>>> + return ERR_PTR(-ENOMEM);
>>>> +
>>>> + /* possible crtcs are set later */
>>>> + ret = drm_universal_plane_init(drm, &layer->plane, 0,
>>>> + &sun8i_mixer_layer_funcs,
>>>> + plane->formats, plane->nformats,
>>>> + plane->type, NULL);
>>>> + if (ret) {
>>>> + dev_err(drm->dev, "Couldn't initialize layer\n");
>>>> + return ERR_PTR(ret);
>>>> + }
>>>> +
>>>> + drm_plane_helper_add(&layer->plane,
>>>> + &sun8i_mixer_layer_helper_funcs);
>>>> + layer->mixer = mixer;
>>>> +
>>>> + return layer;
>>>> +}
>>>> +
>>>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>>>> + struct sunxi_engine *engine)
>>>> +{
>>>> + struct drm_plane **planes;
>>>> + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
>>>> + int i;
>>>> +
>>>> + planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) +
>>>> 1,
>>>> + sizeof(*planes), GFP_KERNEL);
>>>> + if (!planes)
>>>> + return ERR_PTR(-ENOMEM);
>>>> +
>>>> + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>>>> + const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
>>>> + struct sun8i_layer *layer;
>>>> +
>>>> + layer = sun8i_layer_init_one(drm, mixer, 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;
>>>> + planes[i] = &layer->plane;
>>>> + };
>>>> +
>>>> + return planes;
>>>> +}
>>>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h
>>>> b/drivers/gpu/drm/sun4i/sun8i_layer.h
>>>> new file mode 100644
>>>> index 000000000000..e5eccd27cff0
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
>>>> @@ -0,0 +1,36 @@
>>>> +/*
>>>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>>>> + *
>>>> + * Based on sun4i_layer.h, which is:
>>>> + * Copyright (C) 2015 Free Electrons
>>>> + * Copyright (C) 2015 NextThing Co
>>>> + *
>>>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>>>> + *
>>>> + * 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_LAYER_H_
>>>> +#define _SUN8I_LAYER_H_
>>>> +
>>>> +struct sunxi_engine;
>>>> +
>>>> +struct sun8i_layer {
>>>> + struct drm_plane plane;
>>>> + struct sun4i_drv *drv;
>>>> + struct sun8i_mixer *mixer;
>>>> + int id;
>>>> +};
>>>> +
>>>> +static inline struct sun8i_layer *
>>>> +plane_to_sun8i_layer(struct drm_plane *plane)
>>>> +{
>>>> + return container_of(plane, struct sun8i_layer, plane);
>>>> +}
>>>> +
>>>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>>>> + struct sunxi_engine *engine);
>>>> +#endif /* _SUN8I_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..e216b84d5bb2
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>>>> @@ -0,0 +1,394 @@
>>>> +/*
>>>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
>>>> + *
>>>> + * 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/dma-mapping.h>
>>>> +#include <linux/reset.h>
>>>> +#include <linux/of_device.h>
>>>> +
>>>> +#include "sun4i_drv.h"
>>>> +#include "sun8i_mixer.h"
>>>> +#include "sun8i_layer.h"
>>>> +#include "sunxi_engine.h"
>>>> +
>>>> +void sun8i_mixer_commit(struct sunxi_engine *engine)
>>>> +{
>>>> + DRM_DEBUG_DRIVER("Committing changes\n");
>>>> +
>>>> + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>>>> + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>>>> +}
>>>
>>> This function can be static
>>>
>>>> +
>>>> +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->engine.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->engine.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->engine.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 one too.
>>
>> It's called from sun8i_layer.c, so it cannot be static.
>>
>>>
>>>> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>>>> + u32 format, u32 *mode)
>>>> +{
>>>> + switch (format) {
>>>> + 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;
>>>> +
>>>> + 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->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
>>>> + SUN8I_MIXER_SIZE(state->crtc_w,
>>>> + state->crtc_h));
>>>> + DRM_DEBUG_DRIVER("Updating blender size\n");
>>>> + regmap_write(mixer->engine.regs,
>>>> + SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
>>>> + SUN8I_MIXER_SIZE(state->crtc_w,
>>>> + state->crtc_h));
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
>>>> + SUN8I_MIXER_SIZE(state->crtc_w,
>>>> + state->crtc_h));
>>>> + DRM_DEBUG_DRIVER("Updating channel size\n");
>>>> + regmap_write(mixer->engine.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->engine.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->engine.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->engine.regs,
>>>> + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>>>> + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>>>
>>> X and Y are fixed point numbers. You want to keep only the higher 16
>>> bits there.
>>
>> Do you mean "lower 16 bits"? Thus should I (x & 0xffff) or ((u16)x) ?
>
> Oh sorry, it's 16.16 ...
Went wrong again... it's surely not 16.16 .
crtc_* is integer, and src_* is 16.16 .
>
> I'm misled by the sun4i_backend driver.
>
> May I soon send out a patch to fix the sun4i_backend?
>
>>
>> P.S. The negative coordinates are broken, how should I deal with it?
>> or
>> is the coordinates promised to be not negative?
But surely we should deal with the failure of negative values.
I think I should hack the update_layer_buffer function to deal with the
failure of negative coordinates. -- The status of negative coordinate is
that the display size is croped, but at (0,0) the pixel displayed is
still
the original (0,0), not the (-crtc_x, -crtc_y).
>>
>>>
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +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->engine.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->engine.regs,
>>>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>>>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +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;
>>>> + /* 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];
>>>> +
>>>> + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>>>> +
>>>> + regmap_write(mixer->engine.regs,
>>>> + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>>>> + lower_32_bits(paddr));
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static const struct sunxi_engine_ops sun8i_engine_ops = {
>>>> + .commit = sun8i_mixer_commit,
>>>> + .layers_init = sun8i_layers_init,
>>>> +};
>>>> +
>>>> +static struct regmap_config sun8i_mixer_regmap_config = {
>>>> + .reg_bits = 32,
>>>> + .val_bits = 32,
>>>> + .reg_stride = 4,
>>>> + .max_register = 0xbfffc, /* 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;
>>>> +
>>>> + /*
>>>> + * The mixer uses single 32-bit register to store memory
>>>> + * addresses, so that it cannot deal with 64-bit memory
>>>> + * addresses.
>>>> + * Restrict the DMA mask so that the mixer won't be
>>>> + * allocated some memory that is too high.
>>>> + */
>>>> + ret = dma_set_mask(dev, DMA_BIT_MASK(32));
>>>> + if (ret) {
>>>> + dev_err(dev, "Cannot do 32-bit DMA.\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>>>> + if (!mixer)
>>>> + return -ENOMEM;
>>>> + dev_set_drvdata(dev, mixer);
>>>> + mixer->engine.ops = &sun8i_engine_ops;
>>>> + mixer->engine.node = dev->of_node;
>>>> +
>>>> + 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->engine.regs = devm_regmap_init_mmio(dev, regs,
>>>> + &sun8i_mixer_regmap_config);
>>>> + if (IS_ERR(mixer->engine.regs)) {
>>>> + dev_err(dev, "Couldn't create the mixer regmap\n");
>>>> + return PTR_ERR(mixer->engine.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);
>>>> +
>>>> + list_add_tail(&mixer->engine.list, &drv->engine_list);
>>>
>>> You didn't call INIT_LIST_HEAD on that list.
>>>
>>>> +
>>>> + /* Reset the registers */
>>>> + for (i = 0x0; i < 0x20000; i += 4)
>>>> + regmap_write(mixer->engine.regs, i, 0);
>>>> +
>>>> + /* Enable the mixer */
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
>>>> + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>>>> +
>>>> + /* Initialize blender */
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>>>> + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>>>> + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
>>>> + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
>>>> + SUN8I_MIXER_BLEND_MODE_DEF);
>>>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
>>>> + SUN8I_MIXER_BLEND_CK_CTL_DEF);
>>>> +
>>>> + regmap_write(mixer->engine.regs,
>>>> + SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
>>>> + 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->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
>>>> + mixer->cfg->vi_num);
>>>> +
>>>> + return 0;
>>>> +
>>>> + clk_disable_unprepare(mixer->mod_clk);
>>>
>>> This line cannot be reached.
>>>
>>> Maxime
[toc] | [prev] | [next] | [standalone]
| From | icenowy@aosc.io |
|---|---|
| Date | 2017-05-04 19:00 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDsgF-6aT-11@gated-at.bofh.it> |
| In reply to | #1635719 |
在 2017-05-04 21:05,Maxime Ripard 写道:
> On Thu, May 04, 2017 at 07:48:53PM +0800, Icenowy Zheng wrote:
>> Allwinner have a new "Display Engine 2.0" in their new SoCs, which
>> comes
>> with mixers to do graphic processing and feed data to TCON, like the
>> old
>> backends and frontends.
>>
>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> Currently a lot of functions are still missing -- more investigations
>> are needed to gain enough information for them.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
>> ---
>> Changes in v6:
>> - Rebased on wens's multi-pipeline patchset.
>> Changes in v5:
>> - Changed some code alignment.
>> - Request real 32-bit DMA (prepare for 64-bit SoCs).
>> Changes in v4:
>> - Killed some dead code according to Jernej.
>>
>> drivers/gpu/drm/sun4i/Kconfig | 10 +
>> drivers/gpu/drm/sun4i/Makefile | 3 +
>> drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
>> drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
>> drivers/gpu/drm/sun4i/sun8i_mixer.c | 394
>> ++++++++++++++++++++++++++++++++++++
>> drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
>> 6 files changed, 720 insertions(+)
>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
>> 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 5a8227f37cc4..15557484520d 100644
>> --- a/drivers/gpu/drm/sun4i/Kconfig
>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>> @@ -22,3 +22,13 @@ config DRM_SUN4I_BACKEND
>> original Allwinner Display Engine, which has a backend to
>> do some alpha blending and feed graphics to TCON. If M is
>> selected the module will be called sun4i-backend.
>> +
>> +config DRM_SUN4I_SUN8I_MIXER
>
> DRM_SUN8I_MIXER?
>
>> + tristate "Support for Allwinner Display Engine 2.0 Mixer"
>> + depends on DRM_SUN4I
>> + default MACH_SUN8I
>> + help
>> + Choose this option if you have an Allwinner SoC with the
>> + Allwinner Display Engine 2.0, which has a mixer to do some
>> + graphics mixture and feed graphics to TCON, If M is
>> + selected the module will be called sun8i-mixer.
>> diff --git a/drivers/gpu/drm/sun4i/Makefile
>> b/drivers/gpu/drm/sun4i/Makefile
>> index a08df56759e3..a876c6b3027c 100644
>> --- a/drivers/gpu/drm/sun4i/Makefile
>> +++ b/drivers/gpu/drm/sun4i/Makefile
>> @@ -8,7 +8,10 @@ sun4i-tcon-y += sun4i_crtc.o
>>
>> sun4i-backend-y += sun4i_backend.o sun4i_layer.o
>>
>> +sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
>> +
>> obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>> obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
>> +obj-$(CONFIG_DRM_SUN4I_SUN8I_MIXER) += sun8i-mixer.o
>> obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>> obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c
>> b/drivers/gpu/drm/sun4i/sun8i_layer.c
>> new file mode 100644
>> index 000000000000..48f33d8e013b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
>> @@ -0,0 +1,140 @@
>> +/*
>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>> + *
>> + * Based on sun4i_layer.h, which is:
>> + * Copyright (C) 2015 Free Electrons
>> + * Copyright (C) 2015 NextThing Co
>> + *
>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>> + *
>> + * 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/drm_atomic_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +#include <drm/drmP.h>
>> +
>> +#include "sun8i_layer.h"
>> +#include "sun8i_mixer.h"
>> +
>> +struct sun8i_plane_desc {
>> + enum drm_plane_type type;
>> + const uint32_t *formats;
>> + uint32_t nformats;
>> +};
>> +
>> +static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
>> + struct drm_plane_state *state)
>> +{
>> + return 0;
>> +}
>
> This isn't needed.
>
>> +static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
>> + struct drm_plane_state *old_state)
>> +{
>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>> + struct sun8i_mixer *mixer = layer->mixer;
>> +
>> + sun8i_mixer_layer_enable(mixer, layer->id, false);
>> +}
>> +
>> +static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
>> + struct drm_plane_state *old_state)
>> +{
>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>> + struct sun8i_mixer *mixer = layer->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 sun8i_mixer_layer_helper_funcs =
>> {
>> + .atomic_check = sun8i_mixer_layer_atomic_check,
>> + .atomic_disable = sun8i_mixer_layer_atomic_disable,
>> + .atomic_update = sun8i_mixer_layer_atomic_update,
>> +};
>> +
>> +static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
>> + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>> + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>> + .destroy = drm_plane_cleanup,
>> + .disable_plane = drm_atomic_helper_disable_plane,
>> + .reset = drm_atomic_helper_plane_reset,
>> + .update_plane = drm_atomic_helper_update_plane,
>> +};
>> +
>> +static const uint32_t sun8i_mixer_layer_formats[] = {
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_XRGB8888,
>> +};
>> +
>> +static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
>> + {
>> + .type = DRM_PLANE_TYPE_PRIMARY,
>> + .formats = sun8i_mixer_layer_formats,
>> + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>> + },
>> +};
>> +
>> +static struct sun8i_layer *sun8i_layer_init_one(struct drm_device
>> *drm,
>> + struct sun8i_mixer *mixer,
>> + const struct sun8i_plane_desc *plane)
>> +{
>> + struct sun8i_layer *layer;
>> + int ret;
>> +
>> + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
>> + if (!layer)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + /* possible crtcs are set later */
>> + ret = drm_universal_plane_init(drm, &layer->plane, 0,
>> + &sun8i_mixer_layer_funcs,
>> + plane->formats, plane->nformats,
>> + plane->type, NULL);
>> + if (ret) {
>> + dev_err(drm->dev, "Couldn't initialize layer\n");
>> + return ERR_PTR(ret);
>> + }
>> +
>> + drm_plane_helper_add(&layer->plane,
>> + &sun8i_mixer_layer_helper_funcs);
>> + layer->mixer = mixer;
>> +
>> + return layer;
>> +}
>> +
>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>> + struct sunxi_engine *engine)
>> +{
>> + struct drm_plane **planes;
>> + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
>> + int i;
>> +
>> + planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
>> + sizeof(*planes), GFP_KERNEL);
>> + if (!planes)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>> + const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
>> + struct sun8i_layer *layer;
>> +
>> + layer = sun8i_layer_init_one(drm, mixer, 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;
>> + planes[i] = &layer->plane;
>> + };
>> +
>> + return planes;
>> +}
>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h
>> b/drivers/gpu/drm/sun4i/sun8i_layer.h
>> new file mode 100644
>> index 000000000000..e5eccd27cff0
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
>> @@ -0,0 +1,36 @@
>> +/*
>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>> + *
>> + * Based on sun4i_layer.h, which is:
>> + * Copyright (C) 2015 Free Electrons
>> + * Copyright (C) 2015 NextThing Co
>> + *
>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>> + *
>> + * 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_LAYER_H_
>> +#define _SUN8I_LAYER_H_
>> +
>> +struct sunxi_engine;
>> +
>> +struct sun8i_layer {
>> + struct drm_plane plane;
>> + struct sun4i_drv *drv;
>> + struct sun8i_mixer *mixer;
>> + int id;
>> +};
>> +
>> +static inline struct sun8i_layer *
>> +plane_to_sun8i_layer(struct drm_plane *plane)
>> +{
>> + return container_of(plane, struct sun8i_layer, plane);
>> +}
>> +
>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>> + struct sunxi_engine *engine);
>> +#endif /* _SUN8I_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..e216b84d5bb2
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> @@ -0,0 +1,394 @@
>> +/*
>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
>> + *
>> + * 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/dma-mapping.h>
>> +#include <linux/reset.h>
>> +#include <linux/of_device.h>
>> +
>> +#include "sun4i_drv.h"
>> +#include "sun8i_mixer.h"
>> +#include "sun8i_layer.h"
>> +#include "sunxi_engine.h"
>> +
>> +void sun8i_mixer_commit(struct sunxi_engine *engine)
>> +{
>> + DRM_DEBUG_DRIVER("Committing changes\n");
>> +
>> + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>> + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>> +}
>
> This function can be static
>
>> +
>> +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->engine.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->engine.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->engine.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 one too.
>
>> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>> + u32 format, u32 *mode)
>> +{
>> + switch (format) {
>> + 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;
>> +
>> + 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->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
>> + SUN8I_MIXER_SIZE(state->crtc_w,
>> + state->crtc_h));
>> + DRM_DEBUG_DRIVER("Updating blender size\n");
>> + regmap_write(mixer->engine.regs,
>> + SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
>> + SUN8I_MIXER_SIZE(state->crtc_w,
>> + state->crtc_h));
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
>> + SUN8I_MIXER_SIZE(state->crtc_w,
>> + state->crtc_h));
>> + DRM_DEBUG_DRIVER("Updating channel size\n");
>> + regmap_write(mixer->engine.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->engine.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->engine.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->engine.regs,
>> + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>> + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>
> X and Y are fixed point numbers. You want to keep only the higher 16
> bits there.
>
>> +
>> + return 0;
>> +}
>> +
>> +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->engine.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->engine.regs,
>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>> +
>> + return 0;
>> +}
>> +
>> +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;
>> + /* 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];
>> +
>> + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>> +
>> + regmap_write(mixer->engine.regs,
>> + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>> + lower_32_bits(paddr));
>> +
>> + return 0;
>> +}
>> +
>> +static const struct sunxi_engine_ops sun8i_engine_ops = {
>> + .commit = sun8i_mixer_commit,
>> + .layers_init = sun8i_layers_init,
>> +};
>> +
>> +static struct regmap_config sun8i_mixer_regmap_config = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> + .max_register = 0xbfffc, /* 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;
>> +
>> + /*
>> + * The mixer uses single 32-bit register to store memory
>> + * addresses, so that it cannot deal with 64-bit memory
>> + * addresses.
>> + * Restrict the DMA mask so that the mixer won't be
>> + * allocated some memory that is too high.
>> + */
>> + ret = dma_set_mask(dev, DMA_BIT_MASK(32));
>> + if (ret) {
>> + dev_err(dev, "Cannot do 32-bit DMA.\n");
>> + return ret;
>> + }
>> +
>> + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>> + if (!mixer)
>> + return -ENOMEM;
>> + dev_set_drvdata(dev, mixer);
>> + mixer->engine.ops = &sun8i_engine_ops;
>> + mixer->engine.node = dev->of_node;
>> +
>> + 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->engine.regs = devm_regmap_init_mmio(dev, regs,
>> + &sun8i_mixer_regmap_config);
>> + if (IS_ERR(mixer->engine.regs)) {
>> + dev_err(dev, "Couldn't create the mixer regmap\n");
>> + return PTR_ERR(mixer->engine.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);
>> +
>> + list_add_tail(&mixer->engine.list, &drv->engine_list);
>
> You didn't call INIT_LIST_HEAD on that list.
So didn't the sun4i_backend driver...
I think the mixer->engine.list only means an item in the
engine_list, and the drv->engine_list is initialized in the
sun4i_drv source code.
>
>> +
>> + /* Reset the registers */
>> + for (i = 0x0; i < 0x20000; i += 4)
>> + regmap_write(mixer->engine.regs, i, 0);
>> +
>> + /* Enable the mixer */
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
>> + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>> +
>> + /* Initialize blender */
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>> + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>> + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
>> + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
>> + SUN8I_MIXER_BLEND_MODE_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
>> + SUN8I_MIXER_BLEND_CK_CTL_DEF);
>> +
>> + regmap_write(mixer->engine.regs,
>> + SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
>> + 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->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
>> + mixer->cfg->vi_num);
>> +
>> + return 0;
>> +
>> + clk_disable_unprepare(mixer->mod_clk);
>
> This line cannot be reached.
>
> Maxime
[toc] | [prev] | [next] | [standalone]
| From | Chen-Yu Tsai <wens@csie.org> |
|---|---|
| Date | 2017-05-05 05:50 +0200 |
| Subject | Re: [linux-sunxi] Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDCpH-4Ds-3@gated-at.bofh.it> |
| In reply to | #1635892 |
On Fri, May 5, 2017 at 12:52 AM, <icenowy@aosc.io> wrote: > 在 2017-05-04 21:05,Maxime Ripard 写道: >> >> On Thu, May 04, 2017 at 07:48:53PM +0800, Icenowy Zheng wrote: >>> >>> Allwinner have a new "Display Engine 2.0" in their new SoCs, which comes >>> with mixers to do graphic processing and feed data to TCON, like the old >>> backends and frontends. >>> >>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one. >>> >>> Currently a lot of functions are still missing -- more investigations >>> are needed to gain enough information for them. >>> >>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io> >>> --- >>> Changes in v6: >>> - Rebased on wens's multi-pipeline patchset. >>> Changes in v5: >>> - Changed some code alignment. >>> - Request real 32-bit DMA (prepare for 64-bit SoCs). >>> Changes in v4: >>> - Killed some dead code according to Jernej. >>> >>> drivers/gpu/drm/sun4i/Kconfig | 10 + >>> drivers/gpu/drm/sun4i/Makefile | 3 + >>> drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++ >>> drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++ >>> drivers/gpu/drm/sun4i/sun8i_mixer.c | 394 >>> ++++++++++++++++++++++++++++++++++++ >>> drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++ >>> 6 files changed, 720 insertions(+) >>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c >>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h >>> 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/sun8i_mixer.c >>> b/drivers/gpu/drm/sun4i/sun8i_mixer.c >>> new file mode 100644 >>> index 000000000000..e216b84d5bb2 >>> --- /dev/null >>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c >>> @@ -0,0 +1,394 @@ [...] >>> + list_add_tail(&mixer->engine.list, &drv->engine_list); >> >> >> You didn't call INIT_LIST_HEAD on that list. > > > So didn't the sun4i_backend driver... > > I think the mixer->engine.list only means an item in the > engine_list, and the drv->engine_list is initialized in the > sun4i_drv source code. I read [1] that if the item is subsequently added to a list, you could omit the INIT_LIST_HEAD call. Makes sense, though you have to be sure you aren't doing anything else with the list element. ChenYu [1] https://isis.poly.edu/kulesh/stuff/src/klist/
[toc] | [prev] | [next] | [standalone]
| From | icenowy@aosc.io |
|---|---|
| Date | 2017-05-04 19:00 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDsgF-6aT-9@gated-at.bofh.it> |
| In reply to | #1635719 |
在 2017-05-04 21:05,Maxime Ripard 写道:
> On Thu, May 04, 2017 at 07:48:53PM +0800, Icenowy Zheng wrote:
>> Allwinner have a new "Display Engine 2.0" in their new SoCs, which
>> comes
>> with mixers to do graphic processing and feed data to TCON, like the
>> old
>> backends and frontends.
>>
>> Add support for the mixer on Allwinner V3s SoC; it's the simplest one.
>>
>> Currently a lot of functions are still missing -- more investigations
>> are needed to gain enough information for them.
>>
>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
>> ---
>> Changes in v6:
>> - Rebased on wens's multi-pipeline patchset.
>> Changes in v5:
>> - Changed some code alignment.
>> - Request real 32-bit DMA (prepare for 64-bit SoCs).
>> Changes in v4:
>> - Killed some dead code according to Jernej.
>>
>> drivers/gpu/drm/sun4i/Kconfig | 10 +
>> drivers/gpu/drm/sun4i/Makefile | 3 +
>> drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
>> drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
>> drivers/gpu/drm/sun4i/sun8i_mixer.c | 394
>> ++++++++++++++++++++++++++++++++++++
>> drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
>> 6 files changed, 720 insertions(+)
>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
>> create mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
>> 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 5a8227f37cc4..15557484520d 100644
>> --- a/drivers/gpu/drm/sun4i/Kconfig
>> +++ b/drivers/gpu/drm/sun4i/Kconfig
>> @@ -22,3 +22,13 @@ config DRM_SUN4I_BACKEND
>> original Allwinner Display Engine, which has a backend to
>> do some alpha blending and feed graphics to TCON. If M is
>> selected the module will be called sun4i-backend.
>> +
>> +config DRM_SUN4I_SUN8I_MIXER
>
> DRM_SUN8I_MIXER?
>
>> + tristate "Support for Allwinner Display Engine 2.0 Mixer"
>> + depends on DRM_SUN4I
>> + default MACH_SUN8I
>> + help
>> + Choose this option if you have an Allwinner SoC with the
>> + Allwinner Display Engine 2.0, which has a mixer to do some
>> + graphics mixture and feed graphics to TCON, If M is
>> + selected the module will be called sun8i-mixer.
>> diff --git a/drivers/gpu/drm/sun4i/Makefile
>> b/drivers/gpu/drm/sun4i/Makefile
>> index a08df56759e3..a876c6b3027c 100644
>> --- a/drivers/gpu/drm/sun4i/Makefile
>> +++ b/drivers/gpu/drm/sun4i/Makefile
>> @@ -8,7 +8,10 @@ sun4i-tcon-y += sun4i_crtc.o
>>
>> sun4i-backend-y += sun4i_backend.o sun4i_layer.o
>>
>> +sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
>> +
>> obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
>> obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
>> +obj-$(CONFIG_DRM_SUN4I_SUN8I_MIXER) += sun8i-mixer.o
>> obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
>> obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c
>> b/drivers/gpu/drm/sun4i/sun8i_layer.c
>> new file mode 100644
>> index 000000000000..48f33d8e013b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
>> @@ -0,0 +1,140 @@
>> +/*
>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>> + *
>> + * Based on sun4i_layer.h, which is:
>> + * Copyright (C) 2015 Free Electrons
>> + * Copyright (C) 2015 NextThing Co
>> + *
>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>> + *
>> + * 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/drm_atomic_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +#include <drm/drmP.h>
>> +
>> +#include "sun8i_layer.h"
>> +#include "sun8i_mixer.h"
>> +
>> +struct sun8i_plane_desc {
>> + enum drm_plane_type type;
>> + const uint32_t *formats;
>> + uint32_t nformats;
>> +};
>> +
>> +static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
>> + struct drm_plane_state *state)
>> +{
>> + return 0;
>> +}
>
> This isn't needed.
>
>> +static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
>> + struct drm_plane_state *old_state)
>> +{
>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>> + struct sun8i_mixer *mixer = layer->mixer;
>> +
>> + sun8i_mixer_layer_enable(mixer, layer->id, false);
>> +}
>> +
>> +static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
>> + struct drm_plane_state *old_state)
>> +{
>> + struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
>> + struct sun8i_mixer *mixer = layer->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 sun8i_mixer_layer_helper_funcs =
>> {
>> + .atomic_check = sun8i_mixer_layer_atomic_check,
>> + .atomic_disable = sun8i_mixer_layer_atomic_disable,
>> + .atomic_update = sun8i_mixer_layer_atomic_update,
>> +};
>> +
>> +static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
>> + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>> + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>> + .destroy = drm_plane_cleanup,
>> + .disable_plane = drm_atomic_helper_disable_plane,
>> + .reset = drm_atomic_helper_plane_reset,
>> + .update_plane = drm_atomic_helper_update_plane,
>> +};
>> +
>> +static const uint32_t sun8i_mixer_layer_formats[] = {
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_XRGB8888,
>> +};
>> +
>> +static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
>> + {
>> + .type = DRM_PLANE_TYPE_PRIMARY,
>> + .formats = sun8i_mixer_layer_formats,
>> + .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
>> + },
>> +};
>> +
>> +static struct sun8i_layer *sun8i_layer_init_one(struct drm_device
>> *drm,
>> + struct sun8i_mixer *mixer,
>> + const struct sun8i_plane_desc *plane)
>> +{
>> + struct sun8i_layer *layer;
>> + int ret;
>> +
>> + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
>> + if (!layer)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + /* possible crtcs are set later */
>> + ret = drm_universal_plane_init(drm, &layer->plane, 0,
>> + &sun8i_mixer_layer_funcs,
>> + plane->formats, plane->nformats,
>> + plane->type, NULL);
>> + if (ret) {
>> + dev_err(drm->dev, "Couldn't initialize layer\n");
>> + return ERR_PTR(ret);
>> + }
>> +
>> + drm_plane_helper_add(&layer->plane,
>> + &sun8i_mixer_layer_helper_funcs);
>> + layer->mixer = mixer;
>> +
>> + return layer;
>> +}
>> +
>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>> + struct sunxi_engine *engine)
>> +{
>> + struct drm_plane **planes;
>> + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
>> + int i;
>> +
>> + planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
>> + sizeof(*planes), GFP_KERNEL);
>> + if (!planes)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
>> + const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
>> + struct sun8i_layer *layer;
>> +
>> + layer = sun8i_layer_init_one(drm, mixer, 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;
>> + planes[i] = &layer->plane;
>> + };
>> +
>> + return planes;
>> +}
>> diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h
>> b/drivers/gpu/drm/sun4i/sun8i_layer.h
>> new file mode 100644
>> index 000000000000..e5eccd27cff0
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
>> @@ -0,0 +1,36 @@
>> +/*
>> + * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
>> + *
>> + * Based on sun4i_layer.h, which is:
>> + * Copyright (C) 2015 Free Electrons
>> + * Copyright (C) 2015 NextThing Co
>> + *
>> + * Maxime Ripard <maxime.ripard@free-electrons.com>
>> + *
>> + * 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_LAYER_H_
>> +#define _SUN8I_LAYER_H_
>> +
>> +struct sunxi_engine;
>> +
>> +struct sun8i_layer {
>> + struct drm_plane plane;
>> + struct sun4i_drv *drv;
>> + struct sun8i_mixer *mixer;
>> + int id;
>> +};
>> +
>> +static inline struct sun8i_layer *
>> +plane_to_sun8i_layer(struct drm_plane *plane)
>> +{
>> + return container_of(plane, struct sun8i_layer, plane);
>> +}
>> +
>> +struct drm_plane **sun8i_layers_init(struct drm_device *drm,
>> + struct sunxi_engine *engine);
>> +#endif /* _SUN8I_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..e216b84d5bb2
>> --- /dev/null
>> +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
>> @@ -0,0 +1,394 @@
>> +/*
>> + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
>> + *
>> + * 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/dma-mapping.h>
>> +#include <linux/reset.h>
>> +#include <linux/of_device.h>
>> +
>> +#include "sun4i_drv.h"
>> +#include "sun8i_mixer.h"
>> +#include "sun8i_layer.h"
>> +#include "sunxi_engine.h"
>> +
>> +void sun8i_mixer_commit(struct sunxi_engine *engine)
>> +{
>> + DRM_DEBUG_DRIVER("Committing changes\n");
>> +
>> + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
>> + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
>> +}
>
> This function can be static
>
>> +
>> +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->engine.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->engine.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->engine.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 one too.
It's called from sun8i_layer.c, so it cannot be static.
>
>> +static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
>> + u32 format, u32 *mode)
>> +{
>> + switch (format) {
>> + 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;
>> +
>> + 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->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
>> + SUN8I_MIXER_SIZE(state->crtc_w,
>> + state->crtc_h));
>> + DRM_DEBUG_DRIVER("Updating blender size\n");
>> + regmap_write(mixer->engine.regs,
>> + SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
>> + SUN8I_MIXER_SIZE(state->crtc_w,
>> + state->crtc_h));
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
>> + SUN8I_MIXER_SIZE(state->crtc_w,
>> + state->crtc_h));
>> + DRM_DEBUG_DRIVER("Updating channel size\n");
>> + regmap_write(mixer->engine.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->engine.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->engine.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->engine.regs,
>> + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>> + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>
> X and Y are fixed point numbers. You want to keep only the higher 16
> bits there.
Do you mean "lower 16 bits"? Thus should I (x & 0xffff) or ((u16)x) ?
P.S. The negative coordinates are broken, how should I deal with it? or
is the coordinates promised to be not negative?
>
>> +
>> + return 0;
>> +}
>> +
>> +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->engine.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->engine.regs,
>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
>> + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
>> +
>> + return 0;
>> +}
>> +
>> +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;
>> + /* 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];
>> +
>> + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
>> +
>> + regmap_write(mixer->engine.regs,
>> + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
>> + lower_32_bits(paddr));
>> +
>> + return 0;
>> +}
>> +
>> +static const struct sunxi_engine_ops sun8i_engine_ops = {
>> + .commit = sun8i_mixer_commit,
>> + .layers_init = sun8i_layers_init,
>> +};
>> +
>> +static struct regmap_config sun8i_mixer_regmap_config = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> + .max_register = 0xbfffc, /* 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;
>> +
>> + /*
>> + * The mixer uses single 32-bit register to store memory
>> + * addresses, so that it cannot deal with 64-bit memory
>> + * addresses.
>> + * Restrict the DMA mask so that the mixer won't be
>> + * allocated some memory that is too high.
>> + */
>> + ret = dma_set_mask(dev, DMA_BIT_MASK(32));
>> + if (ret) {
>> + dev_err(dev, "Cannot do 32-bit DMA.\n");
>> + return ret;
>> + }
>> +
>> + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
>> + if (!mixer)
>> + return -ENOMEM;
>> + dev_set_drvdata(dev, mixer);
>> + mixer->engine.ops = &sun8i_engine_ops;
>> + mixer->engine.node = dev->of_node;
>> +
>> + 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->engine.regs = devm_regmap_init_mmio(dev, regs,
>> + &sun8i_mixer_regmap_config);
>> + if (IS_ERR(mixer->engine.regs)) {
>> + dev_err(dev, "Couldn't create the mixer regmap\n");
>> + return PTR_ERR(mixer->engine.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);
>> +
>> + list_add_tail(&mixer->engine.list, &drv->engine_list);
>
> You didn't call INIT_LIST_HEAD on that list.
>
>> +
>> + /* Reset the registers */
>> + for (i = 0x0; i < 0x20000; i += 4)
>> + regmap_write(mixer->engine.regs, i, 0);
>> +
>> + /* Enable the mixer */
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
>> + SUN8I_MIXER_GLOBAL_CTL_RT_EN);
>> +
>> + /* Initialize blender */
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
>> + SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
>> + SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
>> + SUN8I_MIXER_BLEND_BKCOLOR_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
>> + SUN8I_MIXER_BLEND_MODE_DEF);
>> + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
>> + SUN8I_MIXER_BLEND_CK_CTL_DEF);
>> +
>> + regmap_write(mixer->engine.regs,
>> + SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
>> + 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->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
>> + mixer->cfg->vi_num);
>> +
>> + return 0;
>> +
>> + clk_disable_unprepare(mixer->mod_clk);
>
> This line cannot be reached.
>
> Maxime
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2017-05-05 14:40 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDKGC-1J7-17@gated-at.bofh.it> |
| In reply to | #1635901 |
[Multipart message — attachments visible in raw view] — view raw
On Fri, May 05, 2017 at 12:50:51AM +0800, icenowy@aosc.io wrote:
> > > +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->engine.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->engine.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->engine.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 one too.
>
> It's called from sun8i_layer.c, so it cannot be static.
Fair enough.
> > > + /* Set base coordinates */
> > > + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> > > + state->crtc_x, state->crtc_y);
> > > + regmap_write(mixer->engine.regs,
> > > + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> > > + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> >
> > X and Y are fixed point numbers. You want to keep only the higher 16
> > bits there.
>
> Do you mean "lower 16 bits"? Thus should I (x & 0xffff) or ((u16)x) ?
Nevermind, I got confused with src_x and src_y.
> P.S. The negative coordinates are broken, how should I deal with it? or
> is the coordinates promised to be not negative?
Adjust the buffer base address, and use a shorter line. You have such
an example in the sun4i code.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [next] | [standalone]
| From | Icenowy Zheng <icenowy@aosc.io> |
|---|---|
| Date | 2017-05-05 14:40 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tDKGD-1J7-23@gated-at.bofh.it> |
| In reply to | #1636354 |
于 2017年5月5日 GMT+08:00 下午8:36:18, Maxime Ripard <maxime.ripard@free-electrons.com> 写到:
>On Fri, May 05, 2017 at 12:50:51AM +0800, icenowy@aosc.io wrote:
>> > > +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->engine.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->engine.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->engine.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 one too.
>>
>> It's called from sun8i_layer.c, so it cannot be static.
>
>Fair enough.
>
>> > > + /* Set base coordinates */
>> > > + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
>> > > + state->crtc_x, state->crtc_y);
>> > > + regmap_write(mixer->engine.regs,
>> > > + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
>> > > + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
>> >
>> > X and Y are fixed point numbers. You want to keep only the higher
>16
>> > bits there.
>>
>> Do you mean "lower 16 bits"? Thus should I (x & 0xffff) or ((u16)x) ?
>
>Nevermind, I got confused with src_x and src_y.
>
>> P.S. The negative coordinates are broken, how should I deal with it?
>or
>> is the coordinates promised to be not negative?
>
>Adjust the buffer base address, and use a shorter line. You have such
>an example in the sun4i code.
Are they these two lines:
```
paddr += (state->src_x >> 16) * bpp;
paddr += (state->src_y >> 16) * fb->pitches[0];
```
I think I copied them here, so I don't need to mind this problem any more, right?
>
>Maxime
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2017-05-09 22:30 +0200 |
| Subject | Re: [PATCH v6 08/13] drm/sun4i: add support for Allwinner DE2 mixers |
| Message-ID | <tFjVD-6MU-11@gated-at.bofh.it> |
| In reply to | #1636357 |
[Multipart message — attachments visible in raw view] — view raw
On Fri, May 05, 2017 at 08:39:31PM +0800, Icenowy Zheng wrote:
> >> > > + /* Set base coordinates */
> >> > > + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
> >> > > + state->crtc_x, state->crtc_y);
> >> > > + regmap_write(mixer->engine.regs,
> >> > > + SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
> >> > > + SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
> >> >
> >> > X and Y are fixed point numbers. You want to keep only the higher
> >16
> >> > bits there.
> >>
> >> Do you mean "lower 16 bits"? Thus should I (x & 0xffff) or ((u16)x) ?
> >
> >Nevermind, I got confused with src_x and src_y.
> >
> >> P.S. The negative coordinates are broken, how should I deal with it?
> >or
> >> is the coordinates promised to be not negative?
> >
> >Adjust the buffer base address, and use a shorter line. You have such
> >an example in the sun4i code.
>
> Are they these two lines:
> ```
> paddr += (state->src_x >> 16) * bpp;
> paddr += (state->src_y >> 16) * fb->pitches[0];
> ```
>
> I think I copied them here, so I don't need to mind this problem any
> more, right?
Hmmm, yes, probably. That's pretty easy to test anyway, you just need
to set up a plane with a negative base coordinate.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web