* [PATCH v5 00/11] Initial Allwinner Display Engine 2.0 Support
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
This patchset is the initial patchset for Allwinner DE2 support.
It contains the support of clocks in DE2 and the mixers in DE2.
The SoC used to develop this patchset is V3s, as V3s is the simplest
one of the SoCs that have DE2.
(Allwinner V3s features only one mixer, although its clock control
unit contains support for second mixer's clock; and its only video
output is RGB LCD, which is already supported in our TCON driver)
The last patch is only a testing patch, it shouldn't be merged; and
for the patch to be really usable, the RFC fix of the TCON driver [1]
is needed.
No HDMI, TV encoder or other internal bridges' support is included
in this patchset, which makes it currently not usable on H3. (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 (11):
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 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 | 31 ++
.../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 | 10 +-
drivers/gpu/drm/sun4i/sun4i_backend.c | 68 ++--
drivers/gpu/drm/sun4i/sun4i_backend.h | 13 +-
drivers/gpu/drm/sun4i/sun4i_crtc.c | 32 +-
drivers/gpu/drm/sun4i/sun4i_crtc.h | 5 +-
drivers/gpu/drm/sun4i/sun4i_drv.c | 4 +-
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 | 7 +-
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 | 390 +++++++++++++++++++++
drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 ++++++++
drivers/gpu/drm/sun4i/sunxi_engine.h | 91 +++++
include/dt-bindings/clock/sun8i-de2.h | 18 +
include/dt-bindings/reset/sun8i-de2.h | 14 +
27 files changed, 1387 insertions(+), 80 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
^ permalink raw reply
* [PATCH v5 01/11] dt-bindings: add binding for the Allwinner DE2 CCU
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
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-h8G6r0blFSE@public.gmane.org>
---
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 | 31 ++++++++++++++++++++++
include/dt-bindings/clock/sun8i-de2.h | 18 +++++++++++++
include/dt-bindings/reset/sun8i-de2.h | 14 ++++++++++
3 files changed, 63 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..15a60bd7dcf3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sun8i-de2.txt
@@ -0,0 +1,31 @@
+Allwinner Display Engine 2.0 Clock Control Binding
+--------------------------------------------------
+
+Required properties :
+- compatible: must contain one of the following compatibles:
+ - "allwinner,sun8i-a83t-de2-clk"
+ - "allwinner,sun50i-a64-de2-clk"
+ - "allwinner,sun50i-h5-de2-clk"
+
+- reg: Must contain the registers base address and length
+- clocks: phandle to the clocks feeding the display engine subsystem.
+ Three are needed:
+ - "mod": the display engine module clock
+ - "bus": the bus clock for the whole display engine subsystem
+- clock-names: Must contain the clock names described just above
+- resets: phandle to the reset control for the display engine subsystem.
+- #clock-cells : must contain 1
+- #reset-cells : must contain 1
+
+Example:
+de2_clocks: clock@1000000 {
+ compatible = "allwinner,sun50i-a64-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-h8G6r0blFSE@public.gmane.org>
+ *
+ * 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-h8G6r0blFSE@public.gmane.org>
+ *
+ * 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
^ permalink raw reply related
* [PATCH v5 02/11] clk: sunxi-ng: add support for DE2 CCU
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
The "Display Engine 2.0" in Allwinner newer SoCs contains a clock
management unit for its subunits, like the DE CCU in A80.
Add a sunxi-ng style driver for it.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
Changes in v5:
- Removed dt-bindings headers (they're now in patch 1).
Changes in v4:
- Fixed the inconsistence between mixer_div clocks' number and real clock.
Changes in v2:
- Rename sunxi-de2-ccu to sun8i-de2-ccu.
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 +++++
4 files changed, 252 insertions(+)
create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-de2.c
create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-de2.h
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 64088e599404..2e4d804fbf61 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -140,6 +140,11 @@ config SUN8I_V3S_CCU
default MACH_SUN8I
depends on MACH_SUN8I || COMPILE_TEST
+config SUN8I_DE2_CCU
+ bool "Support for the Allwinner SoCs DE2 CCU"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_GATE
+
config SUN9I_A80_CCU
bool "Support for the Allwinner A80 CCU"
select SUNXI_CCU_DIV
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 0ec02fe14c50..be616279450e 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o
obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o
obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o
obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o
+obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o
obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o
obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o
obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
new file mode 100644
index 000000000000..adb2c344692a
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2017 Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "ccu_common.h"
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_reset.h"
+
+#include "ccu-sun8i-de2.h"
+
+static SUNXI_CCU_GATE(bus_mixer0_clk, "bus-mixer0", "bus-de",
+ 0x04, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_mixer1_clk, "bus-mixer1", "bus-de",
+ 0x04, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_wb_clk, "bus-wb", "bus-de",
+ 0x04, BIT(2), 0);
+
+static SUNXI_CCU_GATE(mixer0_clk, "mixer0", "mixer0-div",
+ 0x00, BIT(0), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(mixer1_clk, "mixer1", "mixer1-div",
+ 0x00, BIT(1), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div",
+ 0x00, BIT(2), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
+ CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
+ CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
+ CLK_SET_RATE_PARENT);
+
+static struct ccu_common *sunxi_de2_clks[] = {
+ &mixer0_clk.common,
+ &mixer1_clk.common,
+ &wb_clk.common,
+
+ &bus_mixer0_clk.common,
+ &bus_mixer1_clk.common,
+ &bus_wb_clk.common,
+
+ &mixer0_div_clk.common,
+ &mixer1_div_clk.common,
+ &wb_div_clk.common,
+};
+
+static struct clk_hw_onecell_data sunxi_de2_hw_clks = {
+ .hws = {
+ [CLK_MIXER0] = &mixer0_clk.common.hw,
+ [CLK_MIXER1] = &mixer1_clk.common.hw,
+ [CLK_WB] = &wb_clk.common.hw,
+
+ [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw,
+ [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw,
+ [CLK_BUS_WB] = &bus_wb_clk.common.hw,
+
+ [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw,
+ [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw,
+ [CLK_WB_DIV] = &wb_div_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
+ [RST_MIXER0] = { 0x08, BIT(0) },
+ /*
+ * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
+ * only RST_WB is exported here.
+ */
+ [RST_WB] = { 0x08, BIT(2) },
+};
+
+static struct ccu_reset_map sun50i_a64_de2_resets[] = {
+ [RST_MIXER0] = { 0x08, BIT(0) },
+ [RST_MIXER1] = { 0x08, BIT(1) },
+ [RST_WB] = { 0x08, BIT(2) },
+};
+
+static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
+ .ccu_clks = sunxi_de2_clks,
+ .num_ccu_clks = ARRAY_SIZE(sunxi_de2_clks),
+
+ .hw_clks = &sunxi_de2_hw_clks,
+
+ .resets = sun8i_a83t_de2_resets,
+ .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets),
+};
+
+static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
+ .ccu_clks = sunxi_de2_clks,
+ .num_ccu_clks = ARRAY_SIZE(sunxi_de2_clks),
+
+ .hw_clks = &sunxi_de2_hw_clks,
+
+ .resets = sun50i_a64_de2_resets,
+ .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets),
+};
+
+static int sunxi_de2_clk_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct clk *bus_clk, *mod_clk;
+ struct reset_control *rstc;
+ void __iomem *reg;
+ const struct sunxi_ccu_desc *ccu_desc;
+ int ret;
+
+ ccu_desc = of_device_get_match_data(&pdev->dev);
+ if (!ccu_desc)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(bus_clk)) {
+ ret = PTR_ERR(bus_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
+ return ret;
+ }
+
+ mod_clk = devm_clk_get(&pdev->dev, "mod");
+ if (IS_ERR(mod_clk)) {
+ ret = PTR_ERR(mod_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Couldn't get mod clk: %d\n", ret);
+ return ret;
+ }
+
+ rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(rstc)) {
+ ret = PTR_ERR(bus_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Couldn't get reset control: %d\n", ret);
+ return ret;
+ }
+
+ /* The clocks need to be enabled for us to access the registers */
+ ret = clk_prepare_enable(bus_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(mod_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret);
+ return ret;
+ }
+
+ /* The reset control needs to be asserted for the controls to work */
+ ret = reset_control_deassert(rstc);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Couldn't deassert reset control: %d\n", ret);
+ goto err_disable_clk;
+ }
+
+ ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
+ if (ret)
+ goto err_assert_reset;
+
+ return 0;
+
+err_assert_reset:
+ reset_control_assert(rstc);
+err_disable_clk:
+ clk_disable_unprepare(bus_clk);
+ return ret;
+}
+
+static const struct of_device_id sunxi_de2_clk_ids[] = {
+ {
+ .compatible = "allwinner,sun8i-a83t-de2-clk",
+ .data = &sun8i_a83t_de2_clk_desc,
+ },
+ {
+ .compatible = "allwinner,sun50i-h5-de2-clk",
+ .data = &sun50i_a64_de2_clk_desc,
+ },
+ /*
+ * The Allwinner A64 SoC needs some bit to be poke in syscon to make
+ * DE2 really working.
+ * So there's currently no A64 compatible here.
+ * H5 shares the same reset line with A64, so here H5 is using the
+ * clock description of A64.
+ */
+ { }
+};
+
+static struct platform_driver sunxi_de2_clk_driver = {
+ .probe = sunxi_de2_clk_probe,
+ .driver = {
+ .name = "sunxi-de2-clks",
+ .of_match_table = sunxi_de2_clk_ids,
+ },
+};
+builtin_platform_driver(sunxi_de2_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.h b/drivers/clk/sunxi-ng/ccu-sun8i-de2.h
new file mode 100644
index 000000000000..530c006e0ae9
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN8I_DE2_H_
+#define _CCU_SUN8I_DE2_H_
+
+#include <dt-bindings/clock/sun8i-de2.h>
+#include <dt-bindings/reset/sun8i-de2.h>
+
+/* Intermediary clock dividers are not exported */
+#define CLK_MIXER0_DIV 3
+#define CLK_MIXER1_DIV 4
+#define CLK_WB_DIV 5
+
+#define CLK_NUMBER (CLK_WB + 1)
+
+#endif /* _CCU_SUN8I_DE2_H_ */
--
2.12.2
^ permalink raw reply related
* [PATCH v5 03/11] dt-bindings: add bindings for DE2 on V3s SoC
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
Allwinner V3s SoC have a display engine which have a different pipeline
with older SoCs.
Add document for it (new compatibles and the new "mixer" part).
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes in v4:
- Removed the refactor at TCON chapter.
Changes in v3:
- Remove the description of having a BE directly as allwinner,pipeline.
.../bindings/display/sunxi/sun4i-drm.txt | 29 ++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index 57a8d0610062..7da80e26d61b 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -31,6 +31,7 @@ Required properties:
* allwinner,sun6i-a31-tcon
* allwinner,sun6i-a31s-tcon
* allwinner,sun8i-a33-tcon
+ * allwinner,sun8i-v3s-tcon
- reg: base address and size of memory-mapped region
- interrupts: interrupt associated to this IP
- clocks: phandles to the clocks feeding the TCON. Three are needed:
@@ -52,7 +53,7 @@ Required properties:
second the block connected to the TCON channel 1 (usually the TV
encoder)
-On SoCs other than the A33, there is one more clock required:
+On SoCs other than the A33 and V3s, there is one more clock required:
- 'tcon-ch1': The clock driving the TCON channel 1
DRC
@@ -138,6 +139,26 @@ Required properties:
Documentation/devicetree/bindings/media/video-interfaces.txt. The
first port should be the input endpoints, the second one the outputs
+Display Engine 2.0 Mixer
+------------------------
+
+The DE2 mixer have many functionalities, currently only layer blending is
+supported.
+
+Required properties:
+ - compatible: value must be one of:
+ * allwinner,sun8i-v3s-de2-mixer
+ - reg: base address and size of the memory-mapped region.
+ - clocks: phandles to the clocks feeding the frontend and backend
+ * bus: the backend interface clock
+ * ram: the backend DRAM clock
+ - clock-names: the clock names mentioned above
+ - resets: phandles to the reset controllers driving the backend
+
+- ports: A ports node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt. The
+ first port should be the input endpoints, the second one the output
+
Display Engine Pipeline
-----------------------
@@ -152,9 +173,13 @@ Required properties:
* allwinner,sun6i-a31-display-engine
* allwinner,sun6i-a31s-display-engine
* allwinner,sun8i-a33-display-engine
+ * allwinner,sun8i-v3s-display-engine
- allwinner,pipelines: list of phandle to the display engine
- frontends available.
+ pipeline entry point. For SoCs with original DE (currently
+ all SoCs supported by display engine except V3s), this
+ phandle should be a display frontend; for SoCs with DE2,
+ this phandle should be a mixer.
Example:
--
2.12.2
^ permalink raw reply related
* [PATCH v5 04/11] drm/sun4i: return only planes for layers created
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
As we are going to add support for the Allwinner DE2 Mixer in sun4i-drm
driver, we will finally have two types of layers.
Each layer is bound to a drm_plane that is CRTC-specific, so we create
them when initializing CRTC (calling sun4i_layers_init, which will be
generalized in next patch). The drm_plane's will be used when creating
CRTC, but the CRTC initialization code do not care other properties of
the layer, so we let the sun4i_layers_init function return drm_plane's
only.
As we have no need to trace the layers after the CRTC is properly
created, we drop the layers pointer in sun4i_crtc struct.
Doing these things makes the CRTC code independent to the type of layer
(the sun4i_layers_init function name is still hardcoded and will be
changed in the next patch), so that we can finally gain support for the
mixer in DE2, which will has different layers.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_crtc.c | 23 ++++++++++++-----------
drivers/gpu/drm/sun4i/sun4i_crtc.h | 1 -
drivers/gpu/drm/sun4i/sun4i_layer.c | 18 ++++++++++--------
drivers/gpu/drm/sun4i/sun4i_layer.h | 4 ++--
4 files changed, 24 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 3c876c3a356a..708b3543d4e9 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -139,6 +139,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
struct sun4i_tcon *tcon)
{
struct sun4i_crtc *scrtc;
+ struct drm_plane **planes;
struct drm_plane *primary = NULL, *cursor = NULL;
int ret, i;
@@ -149,22 +150,22 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
scrtc->tcon = tcon;
/* Create our layers */
- scrtc->layers = sun4i_layers_init(drm, scrtc->backend);
- if (IS_ERR(scrtc->layers)) {
+ planes = sun4i_layers_init(drm, scrtc);
+ if (IS_ERR(planes)) {
dev_err(drm->dev, "Couldn't create the planes\n");
return NULL;
}
/* find primary and cursor planes for drm_crtc_init_with_planes */
- for (i = 0; scrtc->layers[i]; i++) {
- struct sun4i_layer *layer = scrtc->layers[i];
+ for (i = 0; planes[i]; i++) {
+ struct drm_plane *plane = planes[i];
- switch (layer->plane.type) {
+ switch (plane->type) {
case DRM_PLANE_TYPE_PRIMARY:
- primary = &layer->plane;
+ primary = plane;
break;
case DRM_PLANE_TYPE_CURSOR:
- cursor = &layer->plane;
+ cursor = plane;
break;
default:
break;
@@ -188,12 +189,12 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
1);
/* Set possible_crtcs to this crtc for overlay planes */
- for (i = 0; scrtc->layers[i]; i++) {
+ for (i = 0; planes[i]; i++) {
uint32_t possible_crtcs = BIT(drm_crtc_index(&scrtc->crtc));
- struct sun4i_layer *layer = scrtc->layers[i];
+ struct drm_plane *plane = planes[i];
- if (layer->plane.type == DRM_PLANE_TYPE_OVERLAY)
- layer->plane.possible_crtcs = possible_crtcs;
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ plane->possible_crtcs = possible_crtcs;
}
return scrtc;
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.h b/drivers/gpu/drm/sun4i/sun4i_crtc.h
index 230cb8f0d601..4dae3508424a 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.h
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.h
@@ -19,7 +19,6 @@ struct sun4i_crtc {
struct sun4i_backend *backend;
struct sun4i_tcon *tcon;
- struct sun4i_layer **layers;
};
static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index f26bde5b9117..e1f03e1cc0ac 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -16,6 +16,7 @@
#include <drm/drmP.h>
#include "sun4i_backend.h"
+#include "sun4i_crtc.h"
#include "sun4i_layer.h"
struct sun4i_plane_desc {
@@ -128,15 +129,16 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
return layer;
}
-struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
- struct sun4i_backend *backend)
+struct drm_plane **sun4i_layers_init(struct drm_device *drm,
+ struct sun4i_crtc *crtc)
{
- struct sun4i_layer **layers;
+ struct drm_plane **planes;
+ struct sun4i_backend *backend = crtc->backend;
int i;
- layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
- sizeof(*layers), GFP_KERNEL);
- if (!layers)
+ planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
+ sizeof(*planes), GFP_KERNEL);
+ if (!planes)
return ERR_PTR(-ENOMEM);
/*
@@ -178,8 +180,8 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe));
layer->id = i;
- layers[i] = layer;
+ planes[i] = &layer->plane;
};
- return layers;
+ return planes;
}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index 4be1f0919df2..5ea5c994d6ea 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -26,7 +26,7 @@ plane_to_sun4i_layer(struct drm_plane *plane)
return container_of(plane, struct sun4i_layer, plane);
}
-struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
- struct sun4i_backend *backend);
+struct drm_plane **sun4i_layers_init(struct drm_device *drm,
+ struct sun4i_crtc *crtc);
#endif /* _SUN4I_LAYER_H_ */
--
2.12.2
^ permalink raw reply related
* [PATCH v5 05/11] drm/sun4i: abstract a engine type
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
As we are going to add support for the Allwinner DE2 engine in sun4i-drm
driver, we will finally have two types of display engines -- the DE1
backend and the DE2 mixer. They both do some display blending and feed
graphics data to TCON, so I choose to call them both "engine" here.
Abstract the engine type to a new struct with an ops struct, which contains
functions that should be called outside the engine-specified code (in
TCON, CRTC or TV Encoder code).
A dedicated Kconfig option is also added to control whether
sun4i-backend-specified code (sun4i_backend.c and sun4i_layer.c) should
be built. As we removed the codes in CRTC code that directly call the
layer code, we can now extract the layer part and combine it with the
backend part into a new module, sun4i-backend.ko.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
Changes in v5:
- Really made a sunxi_engine struct type, and moved ops pointer
into it.
- Added checked ops wrappers.
- Changed the second parameter of layers_init from crtc to engine.
Changes in v4:
- Comments to tag the color correction functions as optional.
- Check before calling the optional functions.
- Change layers_init to satisfy new PATCH v4 04/11.
drivers/gpu/drm/sun4i/Kconfig | 10 ++++
drivers/gpu/drm/sun4i/Makefile | 6 ++-
drivers/gpu/drm/sun4i/sun4i_backend.c | 68 ++++++++++++++------------
drivers/gpu/drm/sun4i/sun4i_backend.h | 13 +++--
drivers/gpu/drm/sun4i/sun4i_crtc.c | 11 ++---
drivers/gpu/drm/sun4i/sun4i_crtc.h | 4 +-
drivers/gpu/drm/sun4i/sun4i_drv.h | 2 +-
drivers/gpu/drm/sun4i/sun4i_layer.c | 8 ++-
drivers/gpu/drm/sun4i/sun4i_layer.h | 5 +-
drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 +-
drivers/gpu/drm/sun4i/sun4i_tv.c | 9 ++--
drivers/gpu/drm/sun4i/sunxi_engine.h | 91 +++++++++++++++++++++++++++++++++++
12 files changed, 169 insertions(+), 60 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sunxi_engine.h
diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..5a8227f37cc4 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,13 @@ config DRM_SUN4I
Choose this option if you have an Allwinner SoC with a
Display Engine. If M is selected the module will be called
sun4i-drm.
+
+config DRM_SUN4I_BACKEND
+ tristate "Support for Allwinner A10 Display Engine Backend"
+ depends on DRM_SUN4I
+ default DRM_SUN4I
+ help
+ Choose this option if you have an Allwinner SoC with the
+ 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.
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 59b757350a1f..1db1068b9be1 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -5,9 +5,11 @@ sun4i-tcon-y += sun4i_tcon.o
sun4i-tcon-y += sun4i_rgb.o
sun4i-tcon-y += sun4i_dotclock.o
sun4i-tcon-y += sun4i_crtc.o
-sun4i-tcon-y += sun4i_layer.o
+
+sun4i-backend-y += sun4i_layer.o
+sun4i-backend-y += sun4i_backend.o
obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
-obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
+obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index d660741ba475..360ab046121b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -23,6 +23,8 @@
#include "sun4i_backend.h"
#include "sun4i_drv.h"
+#include "sun4i_layer.h"
+#include "sunxi_engine.h"
static const u32 sunxi_rgb2yuv_coef[12] = {
0x00000107, 0x00000204, 0x00000064, 0x00000108,
@@ -30,41 +32,38 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
};
-void sun4i_backend_apply_color_correction(struct sun4i_backend *backend)
+static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine)
{
int i;
DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");
/* Set color correction */
- regmap_write(backend->regs, SUN4I_BACKEND_OCCTL_REG,
+ regmap_write(engine->regs, SUN4I_BACKEND_OCCTL_REG,
SUN4I_BACKEND_OCCTL_ENABLE);
for (i = 0; i < 12; i++)
- regmap_write(backend->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
+ regmap_write(engine->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
sunxi_rgb2yuv_coef[i]);
}
-EXPORT_SYMBOL(sun4i_backend_apply_color_correction);
-void sun4i_backend_disable_color_correction(struct sun4i_backend *backend)
+static void sun4i_backend_disable_color_correction(struct sunxi_engine *engine)
{
DRM_DEBUG_DRIVER("Disabling color correction\n");
/* Disable color correction */
- regmap_update_bits(backend->regs, SUN4I_BACKEND_OCCTL_REG,
+ regmap_update_bits(engine->regs, SUN4I_BACKEND_OCCTL_REG,
SUN4I_BACKEND_OCCTL_ENABLE, 0);
}
-EXPORT_SYMBOL(sun4i_backend_disable_color_correction);
-void sun4i_backend_commit(struct sun4i_backend *backend)
+static void sun4i_backend_commit(struct sunxi_engine *engine)
{
DRM_DEBUG_DRIVER("Committing changes\n");
- regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
+ regmap_write(engine->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS |
SUN4I_BACKEND_REGBUFFCTL_LOADCTL);
}
-EXPORT_SYMBOL(sun4i_backend_commit);
void sun4i_backend_layer_enable(struct sun4i_backend *backend,
int layer, bool enable)
@@ -78,10 +77,9 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend,
else
val = 0;
- regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
SUN4I_BACKEND_MODCTL_LAY_EN(layer), val);
}
-EXPORT_SYMBOL(sun4i_backend_layer_enable);
static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
u32 format, u32 *mode)
@@ -141,33 +139,33 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
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(backend->regs, SUN4I_BACKEND_DISSIZE_REG,
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_DISSIZE_REG,
SUN4I_BACKEND_DISSIZE(state->crtc_w,
state->crtc_h));
}
/* Set the line width */
DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
+ regmap_write(backend->engine.regs,
+ SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
fb->pitches[0] * 8);
/* Set height and width */
DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
state->crtc_w, state->crtc_h);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
SUN4I_BACKEND_LAYSIZE(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(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
SUN4I_BACKEND_LAYCOOR(state->crtc_x,
state->crtc_y));
return 0;
}
-EXPORT_SYMBOL(sun4i_backend_update_layer_coord);
int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
int layer, struct drm_plane *plane)
@@ -182,7 +180,7 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
interlaced = plane->state->crtc->state->adjusted_mode.flags
& DRM_MODE_FLAG_INTERLACE;
- regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
SUN4I_BACKEND_MODCTL_ITLMOD_EN,
interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
@@ -196,12 +194,12 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
return ret;
}
- regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer),
+ regmap_update_bits(backend->engine.regs,
+ SUN4I_BACKEND_ATTCTL_REG1(layer),
SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
return 0;
}
-EXPORT_SYMBOL(sun4i_backend_update_layer_formats);
int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
int layer, struct drm_plane *plane)
@@ -229,19 +227,19 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
/* Write the 32 lower bits of the address (in bits) */
lo_paddr = paddr << 3;
DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
+ regmap_write(backend->engine.regs,
+ SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
lo_paddr);
/* And the upper bits */
hi_paddr = paddr >> 29;
DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr);
- regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
+ regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer),
SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr));
return 0;
}
-EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
static int sun4i_backend_init_sat(struct device *dev) {
struct sun4i_backend *backend = dev_get_drvdata(dev);
@@ -288,6 +286,13 @@ static int sun4i_backend_free_sat(struct device *dev) {
return 0;
}
+static const struct sunxi_engine_ops sun4i_backend_engine_ops = {
+ .commit = sun4i_backend_commit,
+ .layers_init = sun4i_layers_init,
+ .apply_color_correction = sun4i_backend_apply_color_correction,
+ .disable_color_correction = sun4i_backend_disable_color_correction,
+};
+
static struct regmap_config sun4i_backend_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
@@ -310,18 +315,19 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
if (!backend)
return -ENOMEM;
dev_set_drvdata(dev, backend);
- drv->backend = backend;
+ drv->engine = &backend->engine;
+ drv->engine->ops = &sun4i_backend_engine_ops;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
- backend->regs = devm_regmap_init_mmio(dev, regs,
- &sun4i_backend_regmap_config);
- if (IS_ERR(backend->regs)) {
+ backend->engine.regs = devm_regmap_init_mmio(dev, regs,
+ &sun4i_backend_regmap_config);
+ if (IS_ERR(backend->engine.regs)) {
dev_err(dev, "Couldn't create the backend0 regmap\n");
- return PTR_ERR(backend->regs);
+ return PTR_ERR(backend->engine.regs);
}
backend->reset = devm_reset_control_get(dev, NULL);
@@ -371,14 +377,14 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
/* Reset the registers */
for (i = 0x800; i < 0x1000; i += 4)
- regmap_write(backend->regs, i, 0);
+ regmap_write(backend->engine.regs, i, 0);
/* Disable registers autoloading */
- regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_REGBUFFCTL_REG,
SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS);
/* Enable the backend */
- regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
SUN4I_BACKEND_MODCTL_DEBE_EN |
SUN4I_BACKEND_MODCTL_START_CTL);
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h
index 83e63cc702b4..a92f50eca948 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.h
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.h
@@ -17,6 +17,8 @@
#include <linux/regmap.h>
#include <linux/reset.h>
+#include "sunxi_engine.h"
+
#define SUN4I_BACKEND_MODCTL_REG 0x800
#define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29)
#define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28)
@@ -139,7 +141,7 @@
#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p)))
struct sun4i_backend {
- struct regmap *regs;
+ struct sunxi_engine engine;
struct reset_control *reset;
@@ -151,10 +153,11 @@ struct sun4i_backend {
struct reset_control *sat_reset;
};
-void sun4i_backend_apply_color_correction(struct sun4i_backend *backend);
-void sun4i_backend_disable_color_correction(struct sun4i_backend *backend);
-
-void sun4i_backend_commit(struct sun4i_backend *backend);
+static inline struct sun4i_backend *
+engine_to_sun4i_backend(struct sunxi_engine *engine)
+{
+ return container_of(engine, struct sun4i_backend, engine);
+}
void sun4i_backend_layer_enable(struct sun4i_backend *backend,
int layer, bool enable);
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 708b3543d4e9..f8c70439d1e2 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,10 +25,9 @@
#include <video/videomode.h>
-#include "sun4i_backend.h"
#include "sun4i_crtc.h"
#include "sun4i_drv.h"
-#include "sun4i_layer.h"
+#include "sunxi_engine.h"
#include "sun4i_tcon.h"
static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
@@ -56,7 +55,7 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
DRM_DEBUG_DRIVER("Committing plane changes\n");
- sun4i_backend_commit(scrtc->backend);
+ sunxi_engine_commit(scrtc->engine);
if (event) {
crtc->state->event = NULL;
@@ -135,7 +134,7 @@ static const struct drm_crtc_funcs sun4i_crtc_funcs = {
};
struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
- struct sun4i_backend *backend,
+ struct sunxi_engine *engine,
struct sun4i_tcon *tcon)
{
struct sun4i_crtc *scrtc;
@@ -146,11 +145,11 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
if (!scrtc)
return ERR_PTR(-ENOMEM);
- scrtc->backend = backend;
+ scrtc->engine = engine;
scrtc->tcon = tcon;
/* Create our layers */
- planes = sun4i_layers_init(drm, scrtc);
+ planes = sunxi_engine_layers_init(drm, engine);
if (IS_ERR(planes)) {
dev_err(drm->dev, "Couldn't create the planes\n");
return NULL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.h b/drivers/gpu/drm/sun4i/sun4i_crtc.h
index 4dae3508424a..bf0ce36eb518 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.h
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.h
@@ -17,7 +17,7 @@ struct sun4i_crtc {
struct drm_crtc crtc;
struct drm_pending_vblank_event *event;
- struct sun4i_backend *backend;
+ struct sunxi_engine *engine;
struct sun4i_tcon *tcon;
};
@@ -27,7 +27,7 @@ static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc *crtc)
}
struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
- struct sun4i_backend *backend,
+ struct sunxi_engine *engine,
struct sun4i_tcon *tcon);
#endif /* _SUN4I_CRTC_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 5df50126ff52..dee8e88e99b0 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -17,7 +17,7 @@
#include <linux/regmap.h>
struct sun4i_drv {
- struct sun4i_backend *backend;
+ struct sunxi_engine *engine;
struct sun4i_tcon *tcon;
struct drm_fbdev_cma *fbdev;
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index e1f03e1cc0ac..ab33e4d06782 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -11,12 +11,10 @@
*/
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc.h>
#include <drm/drm_plane_helper.h>
#include <drm/drmP.h>
#include "sun4i_backend.h"
-#include "sun4i_crtc.h"
#include "sun4i_layer.h"
struct sun4i_plane_desc {
@@ -130,10 +128,10 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
}
struct drm_plane **sun4i_layers_init(struct drm_device *drm,
- struct sun4i_crtc *crtc)
+ struct sunxi_engine *engine)
{
struct drm_plane **planes;
- struct sun4i_backend *backend = crtc->backend;
+ struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
int i;
planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
@@ -175,7 +173,7 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
i ? "overlay" : "primary", plane->pipe);
- regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
+ regmap_update_bits(engine->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe));
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index 5ea5c994d6ea..004b7cfe8ffb 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -13,6 +13,8 @@
#ifndef _SUN4I_LAYER_H_
#define _SUN4I_LAYER_H_
+struct sunxi_engine;
+
struct sun4i_layer {
struct drm_plane plane;
struct sun4i_drv *drv;
@@ -27,6 +29,5 @@ plane_to_sun4i_layer(struct drm_plane *plane)
}
struct drm_plane **sun4i_layers_init(struct drm_device *drm,
- struct sun4i_crtc *crtc);
-
+ struct sunxi_engine *engine);
#endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 9a83a85529ac..e4fef0639656 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -459,7 +459,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
goto err_free_dotclock;
}
- tcon->crtc = sun4i_crtc_init(drm, drv->backend, tcon);
+ tcon->crtc = sun4i_crtc_init(drm, drv->engine, tcon);
if (IS_ERR(tcon->crtc)) {
dev_err(dev, "Couldn't create our CRTC\n");
ret = PTR_ERR(tcon->crtc);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 49c49431a053..e598594ccea2 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -22,10 +22,10 @@
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
-#include "sun4i_backend.h"
#include "sun4i_crtc.h"
#include "sun4i_drv.h"
#include "sun4i_tcon.h"
+#include "sunxi_engine.h"
#define SUN4I_TVE_EN_REG 0x000
#define SUN4I_TVE_EN_DAC_MAP_MASK GENMASK(19, 4)
@@ -353,7 +353,6 @@ static void sun4i_tv_disable(struct drm_encoder *encoder)
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
struct sun4i_tcon *tcon = crtc->tcon;
- struct sun4i_backend *backend = crtc->backend;
DRM_DEBUG_DRIVER("Disabling the TV Output\n");
@@ -362,7 +361,8 @@ static void sun4i_tv_disable(struct drm_encoder *encoder)
regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
SUN4I_TVE_EN_ENABLE,
0);
- sun4i_backend_disable_color_correction(backend);
+
+ sunxi_engine_disable_color_correction(crtc->engine);
}
static void sun4i_tv_enable(struct drm_encoder *encoder)
@@ -370,11 +370,10 @@ static void sun4i_tv_enable(struct drm_encoder *encoder)
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
struct sun4i_tcon *tcon = crtc->tcon;
- struct sun4i_backend *backend = crtc->backend;
DRM_DEBUG_DRIVER("Enabling the TV Output\n");
- sun4i_backend_apply_color_correction(backend);
+ sunxi_engine_apply_color_correction(crtc->engine);
regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
SUN4I_TVE_EN_ENABLE,
diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h b/drivers/gpu/drm/sun4i/sunxi_engine.h
new file mode 100644
index 000000000000..85790d2f5f62
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sunxi_engine.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUNXI_ENGINE_H_
+#define _SUNXI_ENGINE_H_
+
+struct sun4i_crtc;
+struct drm_plane;
+struct drm_device;
+
+struct sunxi_engine;
+
+struct sunxi_engine_ops {
+ void (*commit)(struct sunxi_engine *engine);
+ struct drm_plane **(*layers_init)(struct drm_device *drm,
+ struct sunxi_engine *engine);
+
+ void (*apply_color_correction)(struct sunxi_engine *engine);
+ void (*disable_color_correction)(struct sunxi_engine *engine);
+};
+
+/**
+ * struct sunxi_engine - the common parts of an engine for sun4i-drm driver
+ * @ops: the operations of the engine
+ * @regs: the regmap of the engine
+ */
+struct sunxi_engine {
+ const struct sunxi_engine_ops *ops;
+
+ struct regmap *regs;
+};
+
+/**
+ * sunxi_engine_commit() - commit all changes of the engine
+ * @engine: pointer to the engine
+ */
+static inline void
+sunxi_engine_commit(struct sunxi_engine *engine)
+{
+ if (engine->ops && engine->ops->commit)
+ engine->ops->commit(engine);
+}
+
+/**
+ * sunxi_engine_layers_init() - Create planes (layers) for the engine
+ * @drm: pointer to the drm_device for which planes will be created
+ * @engine: pointer to the engine
+ */
+static inline struct drm_plane **
+sunxi_engine_layers_init(struct drm_device *drm, struct sunxi_engine *engine)
+{
+ if (engine->ops && engine->ops->layers_init)
+ return engine->ops->layers_init(drm, engine);
+ return ERR_PTR(-ENOSYS);
+}
+
+/**
+ * sunxi_engine_apply_color_correction - Apply the RGB2YUV color correction
+ * @engine: pointer to the engine
+ *
+ * This functionality is optional for an engine, however, if the engine is
+ * intended to be used with TV Encoder, the output will be incorrect
+ * without the color correction, due to TV Encoder expects the engine to
+ * output directly YUV signal.
+ */
+static inline void
+sunxi_engine_apply_color_correction(struct sunxi_engine *engine)
+{
+ if (engine->ops && engine->ops->apply_color_correction)
+ engine->ops->apply_color_correction(engine);
+}
+
+/**
+ * sunxi_engine_disable_color_correction - Disable the color space correction
+ * @engine: pointer to the engine
+ *
+ * This function is paired with apply_color_correction().
+ */
+static inline void
+sunxi_engine_disable_color_correction(struct sunxi_engine *engine)
+{
+ if (engine->ops && engine->ops->disable_color_correction)
+ engine->ops->disable_color_correction(engine);
+}
+#endif /* _SUNXI_ENGINE_H_ */
--
2.12.2
^ permalink raw reply related
* [PATCH v5 06/11] drm/sun4i: add support for Allwinner DE2 mixers
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
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-h8G6r0blFSE@public.gmane.org>
---
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 | 4 +
drivers/gpu/drm/sun4i/sun8i_layer.c | 140 +++++++++++++
drivers/gpu/drm/sun4i/sun8i_layer.h | 36 ++++
drivers/gpu/drm/sun4i/sun8i_mixer.c | 390 ++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/sun4i/sun8i_mixer.h | 137 +++++++++++++
6 files changed, 717 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 1db1068b9be1..0d67d6e80fd6 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,7 +9,11 @@ sun4i-tcon-y += sun4i_crtc.o
sun4i-backend-y += sun4i_layer.o
sun4i-backend-y += sun4i_backend.o
+sun8i-mixer-y += sun8i_layer.o
+sun8i-mixer-y += sun8i_mixer.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-h8G6r0blFSE@public.gmane.org>
+ *
+ * Based on sun4i_layer.h, which is:
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#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-h8G6r0blFSE@public.gmane.org>
+ *
+ * Based on sun4i_layer.h, which is:
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_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..70b71345e4e3
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2017 Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
+ *
+ * Based on sun4i_backend.c, which is:
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/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);
+ drv->engine = &mixer->engine;
+ drv->engine->ops = &sun8i_engine_ops;
+
+ 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);
+
+ /* 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);
+
+ 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-h8G6r0blFSE@public.gmane.org>");
+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-h8G6r0blFSE@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_MIXER_H_
+#define _SUN8I_MIXER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "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
^ permalink raw reply related
* [PATCH v5 07/11] drm/sun4i: Add compatible string for V3s display engine
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
Allwinner V3s features the new "Display Engine 2.0", which can now also
be driven with our subdrivers in sun4i-drm.
Add the compatible string for in sun4i_drv.c, in order to make the
display engine and its components probed.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_drv.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 8ddd72cd5873..68c0b754cdb5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -294,6 +294,7 @@ static const struct of_device_id sun4i_drv_of_table[] = {
{ .compatible = "allwinner,sun6i-a31-display-engine" },
{ .compatible = "allwinner,sun6i-a31s-display-engine" },
{ .compatible = "allwinner,sun8i-a33-display-engine" },
+ { .compatible = "allwinner,sun8i-v3s-display-engine" },
{ }
};
MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
--
2.12.2
^ permalink raw reply related
* [PATCH v5 08/11] drm/sun4i: tcon: add support for V3s TCON
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
Allwinner V3s SoC features a TCON without channel 1.
Add support for it.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
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 68c0b754cdb5..736b28e47281 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -176,7 +176,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 e4fef0639656..c8e695d5776b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -533,11 +533,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
^ permalink raw reply related
* [PATCH v5 09/11] ARM: dts: sun8i: add DE2 nodes for V3s SoC
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
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-h8G6r0blFSE@public.gmane.org>
---
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
^ permalink raw reply related
* [PATCH v5 10/11] ARM: dts: sun8i: add pinmux for LCD pins of V3s SoC
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
Allwinner V3s SoC features a set of pins that have functionality of RGB
LCD, the pins are at different pin ban than other SoCs.
Add pinctrl node for them.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
arch/arm/boot/dts/sun8i-v3s.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 0a895179d8ae..a37d68b227bc 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -297,6 +297,15 @@
function = "i2c0";
};
+ lcd_rgb666_pins: lcd_rgb666@0 {
+ pins = "PE0", "PE1", "PE2", "PE3", "PE4",
+ "PE5", "PE6", "PE7", "PE8", "PE9",
+ "PE10", "PE11", "PE12", "PE13", "PE14",
+ "PE15", "PE16", "PE17", "PE18", "PE19",
+ "PE23", "PE24";
+ function = "lcd";
+ };
+
uart0_pins_a: uart0@0 {
pins = "PB8", "PB9";
function = "uart0";
--
2.12.2
^ permalink raw reply related
* [PATCH v5 11/11] [DO NOT MERGE] ARM: dts: sun8i: enable LCD panel of Lichee Pi Zero
From: Icenowy Zheng @ 2017-04-23 10:37 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Jernej Skrabec
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20170423103754.50012-1-icenowy-h8G6r0blFSE@public.gmane.org>
A 480x272 QiaoDian QD43003C0-40-7LED panel is available from Lichee Pi.
This commit connects this panel to Lichee Pi Zero.
Lichee Pi also provides a 800x480 panel without accurate model number,
so do not merge this patch. It will finally come as device tree overlay.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts | 36 +++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
index 387fc2aa546d..7ae72bf63cd0 100644
--- a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
+++ b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
@@ -75,6 +75,28 @@
gpios = <&pio 6 2 GPIO_ACTIVE_LOW>; /* PG2 */
};
};
+
+ panel: panel {
+ compatible = "qiaodian,qd43003c0-40", "simple-panel";
+ enable-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* Should be backlight */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel_input: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_out_lcd>;
+ };
+ };
+ };
+};
+
+&de {
+ status = "okay";
};
&mmc0 {
@@ -86,6 +108,20 @@
status = "okay";
};
+&tcon0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_rgb666_pins>;
+ status = "okay";
+
+};
+
+&tcon0_out {
+ tcon0_out_lcd: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&panel_input>;
+ };
+};
+
&uart0 {
pinctrl-0 = <&uart0_pins_a>;
pinctrl-names = "default";
--
2.12.2
^ permalink raw reply related
* [PATCH 0/2] Lattice MachXO2 Passive SPI FPGA Manager support
From: Paolo Pisati @ 2017-04-23 15:20 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Alan Tull, Moritz Fischer
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-fpga-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Hi all,
this series adds support for the Lattice MachXO2 FPGA chip, programmed
over Slave SPI.
Tested on my raspberry pi3 + bugblat's pif2 fpga hat.
Paolo Pisati (2):
dt: bindings: fpga: add lattice machxo2 slave spi binding description
fpga: lattice machxo2: Add Lattice MachXO2 support
.../bindings/fpga/lattice-machxo2-spi.txt | 29 +++
drivers/fpga/Kconfig | 7 +
drivers/fpga/Makefile | 1 +
drivers/fpga/machxo2-spi.c | 199 +++++++++++++++++++++
4 files changed, 236 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt
create mode 100644 drivers/fpga/machxo2-spi.c
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 1/2] dt: bindings: fpga: add lattice machxo2 slave spi binding description
From: Paolo Pisati @ 2017-04-23 15:20 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Alan Tull, Moritz Fischer
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-fpga-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1492960845-342-1-git-send-email-p.pisati-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Add dt binding documentation details for Lattice MachXO2 FPGA configuration
over Slave SPI interface.
Signed-off-by: Paolo Pisati <p.pisati-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
.../bindings/fpga/lattice-machxo2-spi.txt | 29 ++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt
diff --git a/Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt b/Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt
new file mode 100644
index 0000000..c3ef26bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt
@@ -0,0 +1,29 @@
+Lattice MachXO2 Slave SPI FPGA Manager
+
+Lattice MachXO2 FPGAs support a method of loading the bitstream over
+'slave SPI' interface.
+
+See 'MachXO2ProgrammingandConfigurationUsageGuide.pdf' on www.latticesemi.com
+
+Required properties:
+- compatible: should contain "lattice,machxo2-slave-spi"
+- reg: spi chip select of the FPGA
+
+Example for full FPGA configuration:
+
+ fpga-region0 {
+ compatible = "fpga-region";
+ fpga-mgr = <&fpga_mgr_spi>;
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ };
+
+ spi1: spi@2000 {
+ ...
+
+ fpga_mgr_spi: fpga-mgr@0 {
+ compatible = "lattice,machxo2-slave-spi";
+ spi-max-frequency = <60000000>;
+ reg = <0>;
+ };
+ };
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 2/2] fpga: lattice machxo2: Add Lattice MachXO2 support
From: Paolo Pisati @ 2017-04-23 15:20 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Alan Tull, Moritz Fischer
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-fpga-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1492960845-342-1-git-send-email-p.pisati-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Add support for the Lattice MachXO2 FPGA chip in Slave SPI configuration.
Signed-off-by: Paolo Pisati <p.pisati-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
drivers/fpga/Kconfig | 7 ++
drivers/fpga/Makefile | 1 +
drivers/fpga/machxo2-spi.c | 199 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 207 insertions(+)
create mode 100644 drivers/fpga/machxo2-spi.c
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index c81cb7d..cce135b 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -26,6 +26,13 @@ config FPGA_MGR_ICE40_SPI
help
FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
+config FPGA_MGR_MACHXO2_SPI
+ tristate "Lattice MachXO2 SPI"
+ depends on SPI
+ help
+ FPGA manager driver support for Lattice MachXO2 configuration
+ over slave SPI interface.
+
config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA || COMPILE_TEST
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index c6f5d74..cdab1fe 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers
obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
+obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
new file mode 100644
index 0000000..5ee56bd
--- /dev/null
+++ b/drivers/fpga/machxo2-spi.c
@@ -0,0 +1,199 @@
+/**
+ * Lattice MachXO2 Slave SPI Driver
+ *
+ * Copyright (C) 2017 Paolo Pisati <p.pisati-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Manage Lattice FPGA firmware that is loaded over SPI using
+ * the slave serial configuration interface.
+ */
+
+#include <linux/delay.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+
+
+/* MachXO2 Programming Guide - sysCONFIG Programming Commands */
+
+#define ISC_ENABLE 0x000008c6
+#define ISC_ERASE 0x0000040e
+#define ISC_PROGRAMDONE 0x0000005e
+#define LSC_CHECKBUSY 0x000000f0
+#define LSC_INITADDRESS 0x00000046
+#define LSC_PROGINCRNV 0x01000070
+#define LSC_REFRESH 0x00000079
+
+/*
+ * Max CCLK in Slave SPI mode according to 'MachXO2 Family Data
+ * Sheet' sysCONFIG Port Timing Specifications (3-36)
+ */
+#define MACHXO2_MAX_SPEED 66000000
+
+#define MACHXO2_LOW_DELAY 5 /* us */
+#define MACHXO2_HIGH_DELAY 200 /* us */
+
+#define MACHXO2_OP_SIZE sizeof(uint32_t)
+#define MACHXO2_PAGE_SIZE 16
+#define MACHXO2_BUF_SIZE (MACHXO2_OP_SIZE + MACHXO2_PAGE_SIZE)
+
+
+static int waituntilnotbusy(struct spi_device *spi)
+{
+ uint8_t rx, busyflag = 0x80;
+ uint32_t checkbusy = LSC_CHECKBUSY;
+
+ do {
+ if (spi_write_then_read(spi, &checkbusy, MACHXO2_OP_SIZE,
+ &rx, sizeof(rx)))
+ return -EIO;
+ } while (rx & busyflag);
+ return 0;
+}
+
+static enum fpga_mgr_states machxo2_spi_state(struct fpga_manager *mgr)
+{
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int machxo2_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ struct spi_device *spi = mgr->priv;
+ uint32_t enable = ISC_ENABLE;
+ uint32_t erase = ISC_ERASE;
+ uint32_t initaddr = LSC_INITADDRESS;
+
+ if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ dev_err(&mgr->dev,
+ "Partial reconfiguration is not supported\n");
+ return -ENOTSUPP;
+ }
+
+ if (spi_write(spi, &enable, MACHXO2_OP_SIZE))
+ goto fail;
+ udelay(MACHXO2_LOW_DELAY);
+ if (spi_write(spi, &erase, MACHXO2_OP_SIZE))
+ goto fail;
+ waituntilnotbusy(spi);
+ if (spi_write(spi, &initaddr, MACHXO2_OP_SIZE))
+ goto fail;
+ return 0;
+
+fail:
+ dev_err(&mgr->dev, "Error during FPGA init.\n");
+ return -EIO;
+}
+
+static int machxo2_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ struct spi_device *spi = mgr->priv;
+ uint32_t progincr = LSC_PROGINCRNV;
+ uint8_t payload[MACHXO2_BUF_SIZE];
+ int i;
+
+ if (count % MACHXO2_PAGE_SIZE != 0) {
+ dev_err(&mgr->dev, "Malformed payload.\n");
+ return -EINVAL;
+ }
+
+ memcpy(payload, &progincr, MACHXO2_OP_SIZE);
+ for (i = 0; i < count; i += MACHXO2_PAGE_SIZE) {
+ memcpy(&payload[MACHXO2_OP_SIZE], &buf[i], MACHXO2_PAGE_SIZE);
+ if (spi_write(spi, payload, MACHXO2_BUF_SIZE)) {
+ dev_err(&mgr->dev, "Error loading the bitstream.\n");
+ return -EIO;
+ }
+ udelay(MACHXO2_HIGH_DELAY);
+ }
+
+ return 0;
+}
+
+static int machxo2_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct spi_device *spi = mgr->priv;
+ uint32_t progdone = ISC_PROGRAMDONE;
+ uint32_t refresh = LSC_REFRESH;
+
+ if (spi_write(spi, &progdone, MACHXO2_OP_SIZE))
+ goto fail;
+ /* yep, LSC_REFRESH is 3 bytes long actually */
+ if (spi_write(spi, &refresh, MACHXO2_OP_SIZE-1))
+ goto fail;
+ return 0;
+
+fail:
+ dev_err(&mgr->dev, "Refresh failed.\n");
+ return -EIO;
+}
+
+static const struct fpga_manager_ops machxo2_ops = {
+ .state = machxo2_spi_state,
+ .write_init = machxo2_write_init,
+ .write = machxo2_write,
+ .write_complete = machxo2_write_complete,
+};
+
+static int machxo2_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ int ret = 0;
+
+ if (spi->max_speed_hz > MACHXO2_MAX_SPEED) {
+ dev_err(dev, "Speed is too high\n");
+ return -EINVAL;
+ }
+
+ ret = fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
+ &machxo2_ops, spi);
+ if (ret)
+ dev_err(dev, "Unable to register FPGA manager");
+
+ return ret;
+}
+
+static int machxo2_spi_remove(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+
+ fpga_mgr_unregister(dev);
+ return 0;
+}
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "lattice,machxo2-slave-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_match);
+
+static const struct spi_device_id lattice_ids[] = {
+ { "machxo2-slave-spi", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, lattice_ids);
+
+static struct spi_driver machxo2_spi_driver = {
+ .driver = {
+ .name = "machxo2-slave-spi",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_match),
+ },
+ .probe = machxo2_spi_probe,
+ .remove = machxo2_spi_remove,
+ .id_table = lattice_ids,
+};
+
+module_spi_driver(machxo2_spi_driver)
+
+MODULE_AUTHOR("Paolo Pisati <p.pisati-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("Load Lattice FPGA firmware over SPI");
+MODULE_LICENSE("GPL v2");
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [04/13] watchdog: lantiq: access boot cause register through regmap
From: Guenter Roeck @ 2017-04-23 15:48 UTC (permalink / raw)
To: Hauke Mehrtens
Cc: ralf-6z/3iImG2C8G8FEW9MqTrA, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg,
john-Pj+rj9U5foFAfugRpC6u6w, linux-spi-u79uwXL29TY76Z2rM5mHXA,
hauke.mehrtens-ral2JQCrhuEAvxtiuMwx3w
In-Reply-To: <20170417192942.32219-5-hauke-5/S+JYg5SzeELgA04lAiVw@public.gmane.org>
On Mon, Apr 17, 2017 at 09:29:33PM +0200, Hauke Mehrtens wrote:
> This patch avoids accessing the function ltq_reset_cause() and directly
> accesses the register given over the syscon interface. The syscon
> interface will be implemented for the xway SoCs for the falcon SoCs the
> ltq_reset_cause() function never worked, because a wrong offset was used.
>
> Signed-off-by: Hauke Mehrtens <hauke-5/S+JYg5SzeELgA04lAiVw@public.gmane.org>
Acked-by: Guenter Roeck <linux-cYGBoTPqujPR7s880joybQ@public.gmane.org>
> ---
> drivers/watchdog/lantiq_wdt.c | 47 +++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 43 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c
> index e0823677d8c1..0e349ad03fdf 100644
> --- a/drivers/watchdog/lantiq_wdt.c
> +++ b/drivers/watchdog/lantiq_wdt.c
> @@ -17,9 +17,14 @@
> #include <linux/uaccess.h>
> #include <linux/clk.h>
> #include <linux/io.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
>
> #include <lantiq_soc.h>
>
> +#define LTQ_RST_CAUSE_WDT_XRX BIT(31)
> +#define LTQ_RST_CAUSE_WDT_FALCON 0x02
> +
> /*
> * Section 3.4 of the datasheet
> * The password sequence protects the WDT control register from unintended
> @@ -186,6 +191,40 @@ static struct miscdevice ltq_wdt_miscdev = {
> .fops = <q_wdt_fops,
> };
>
> +static void ltq_set_wdt_bootstatus(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct regmap *rcu_regmap;
> + u32 status_reg_offset;
> + u32 val;
> + int err;
> +
> + rcu_regmap = syscon_regmap_lookup_by_phandle(np,
> + "lantiq,rcu-syscon");
> + if (IS_ERR_OR_NULL(rcu_regmap))
> + return;
> +
> + err = of_property_read_u32_index(np, "lantiq,rcu-syscon", 1,
> + &status_reg_offset);
> + if (err) {
> + dev_err(&pdev->dev, "Failed to get RCU reg offset\n");
> + return;
> + }
> +
> + err = regmap_read(rcu_regmap, status_reg_offset, &val);
> + if (err)
> + return;
> +
> + /* find out if the watchdog caused the last reboot */
> + if (of_device_is_compatible(np, "lantiq,wdt-xrx100")) {
> + if (val & LTQ_RST_CAUSE_WDT_XRX)
> + ltq_wdt_bootstatus = WDIOF_CARDRESET;
> + } else if (of_device_is_compatible(np, "lantiq,wdt-falcon")) {
> + if ((val & 0x7) == LTQ_RST_CAUSE_WDT_FALCON)
> + ltq_wdt_bootstatus = WDIOF_CARDRESET;
> + }
> +}
> +
> static int
> ltq_wdt_probe(struct platform_device *pdev)
> {
> @@ -205,9 +244,7 @@ ltq_wdt_probe(struct platform_device *pdev)
> ltq_io_region_clk_rate = clk_get_rate(clk);
> clk_put(clk);
>
> - /* find out if the watchdog caused the last reboot */
> - if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
> - ltq_wdt_bootstatus = WDIOF_CARDRESET;
> + ltq_set_wdt_bootstatus(pdev);
>
> dev_info(&pdev->dev, "Init done\n");
> return misc_register(<q_wdt_miscdev);
> @@ -222,7 +259,9 @@ ltq_wdt_remove(struct platform_device *pdev)
> }
>
> static const struct of_device_id ltq_wdt_match[] = {
> - { .compatible = "lantiq,wdt" },
> + { .compatible = "lantiq,wdt"},
> + { .compatible = "lantiq,wdt-xrx100"},
> + { .compatible = "lantiq,wdt-falcon"},
> {},
> };
> MODULE_DEVICE_TABLE(of, ltq_wdt_match);
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2] spi-imx: Implements handling of the SPI_READY mode flag.
From: Jonas Gorski @ 2017-04-23 18:43 UTC (permalink / raw)
To: Leif Middelschulte
Cc: Mark Brown, linux-spi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20170423111837.19460-1-Leif.Middelschulte-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Hi,
On 23 April 2017 at 13:18, Leif Middelschulte
<leif.middelschulte-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> From: Leif Middelschulte <leif.middelschulte-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> This patch implements consideration of the SPI_READY mode flag as
> defined in spi.h. It extends the device tree bindings to support
> the values defined by the reference manual for the DRCTL field.
>
> Thus supporting edge-triggered and level-triggered bursts.
>
> Signed-off-by: Leif Middelschulte <Leif.Middelschulte-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> .../devicetree/bindings/spi/fsl-imx-cspi.txt | 5 +++++
> drivers/spi/spi-imx.c | 23 ++++++++++++++++++++--
> 2 files changed, 26 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
> index 8bc95e2fc47f..890b3ff3325f 100644
> --- a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
> +++ b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
You are modifying device tree documentation, you need to CC the
appropriate mailing list as well (as I now did).
> @@ -23,6 +23,10 @@ See the clock consumer binding,
> Obsolete properties:
> - fsl,spi-num-chipselects : Contains the number of the chipselect
>
> +Optional properties:
> +- fsl,spi-drctl: Integer, representing the value of DRCTL. Note that to
> +enable the DRCTL consideration, the SPI_READY mode-flag needs to be set.
Maybe document the valid values here as well? Also spi-drctl isn't
really a nice name, maybe something human understandable that doesn't
require looking into the datasheet?
> +
> Example:
>
> ecspi@70010000 {
> @@ -35,4 +39,5 @@ ecspi@70010000 {
> <&gpio3 25 0>; /* GPIO3_25 */
> dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
> dma-names = "rx", "tx";
> + fsl,spi-drctl = <1>;
> };
> diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
> index 9a7c62f471dc..647a4bf18705 100644
> --- a/drivers/spi/spi-imx.c
> +++ b/drivers/spi/spi-imx.c
> @@ -95,6 +95,7 @@ struct spi_imx_data {
> unsigned int spi_bus_clk;
>
> unsigned int bytes_per_word;
> + unsigned int spi_drctl;
>
> unsigned int count;
> void (*tx)(struct spi_imx_data *);
> @@ -246,6 +247,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
> #define MX51_ECSPI_CTRL_XCH (1 << 2)
> #define MX51_ECSPI_CTRL_SMC (1 << 3)
> #define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
> +#define MX51_ECSPI_CTRL_DRCTL(drctl) ((drctl) << 16)
> #define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
> #define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
> #define MX51_ECSPI_CTRL_CS(cs) ((cs) << 18)
> @@ -355,6 +357,12 @@ static int mx51_ecspi_config(struct spi_device *spi,
> */
> ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
>
> + /*
> + * Enable SPI_RDY handling (falling edge/level triggered).
> + */
> + if (spi->mode & SPI_READY)
> + ctrl |= MX51_ECSPI_CTRL_DRCTL(spi_imx->spi_drctl);
> +
> /* set clock speed */
> ctrl |= mx51_ecspi_clkdiv(spi_imx, config->speed_hz, &clk);
> spi_imx->spi_bus_clk = clk;
> @@ -1173,7 +1181,7 @@ static int spi_imx_probe(struct platform_device *pdev)
> struct spi_master *master;
> struct spi_imx_data *spi_imx;
> struct resource *res;
> - int i, ret, irq;
> + int i, ret, irq, spi_drctl;
>
> if (!np && !mxc_platform_info) {
> dev_err(&pdev->dev, "can't get the platform data\n");
> @@ -1181,6 +1189,15 @@ static int spi_imx_probe(struct platform_device *pdev)
> }
>
> master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
> + ret = of_property_read_u32(np, "fsl,spi-drctl", &spi_drctl);
> + if ((ret < 0) || (spi_drctl == 0x3)) {
> + // '11' is reserved
Don't use C99 comments, use /* */ comments.
> + spi_drctl = 0;
Here you say '11' is reserved and force 0 if set
> + } else {
> + // only the values '00', '01' and '11' are valid
And here you say '11' is valid. So which is it? Did you mean '10'?
> + spi_drctl &= 0x3;
Also with this code flow if fsl,spi-drctl is set to <7>, it will cause
spi_drctl set to '11' / 0x3.
Maybe it would be easier to do
if (ret < 0 || spi_drctl >= 0x3)
spi_drctl = 0;
to only accept valid values. Assuming '11' is the invalid one and not '10'.
Regards
Jonas
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 6/9] staging: ccree: add FIPS support
From: Stephan Müller @ 2017-04-23 18:57 UTC (permalink / raw)
To: Gilad Ben-Yossef
Cc: Herbert Xu, David S. Miller, Rob Herring, Mark Rutland,
Greg Kroah-Hartman, devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
linux-crypto-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Linux kernel mailing list,
Gilad Ben-Yossef, Binoy Jayan, Ofir Drang, Stuart Yoder
In-Reply-To: <CAOtvUMcOib2xr=THj1hy_uPtVhRHgJ2-4__mUBb5VwANQ1GA_A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Am Sonntag, 23. April 2017, 11:48:58 CEST schrieb Gilad Ben-Yossef:
Hi Gilad,
> I do wonder if there is value in alternate behavior of stopping crypto
> API on FIPS error rather than a panic though. I will try to get an
> explanation why we do it this way.
In FIPS, all crypto function must cease if a self test fails. This can be done
by instrumenting the crypto API calls with a check to a global flag or by
simply terminating the entire "FIPS module".
The panic() is the simplest approach to meet that requirement.
Ciao
Stephan
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] iio: adc: add driver for the ti-adc084s021 chip
From: Jonathan Cameron @ 2017-04-23 19:13 UTC (permalink / raw)
To: Peter Meerwald-Stadler, Mårten Lindahl
Cc: lars-Qo5EllUWu/uELgA04lAiVw, linux-iio-u79uwXL29TY76Z2rM5mHXA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA, Mårten Lindahl
In-Reply-To: <alpine.DEB.2.20.1704212149070.2470-M0QeDd4q1oXQbIPoIc8EuQ@public.gmane.org>
On 21/04/17 21:19, Peter Meerwald-Stadler wrote:
>
>> From: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
>
> comments below
A few more from me. Laptop battery gone flat so not so thorough on top few lines!
Jonathan
>
>> This adds support for the Texas Instruments ADC084S021 ADC chip.
>>
>> Signed-off-by: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
>> ---
>> .../devicetree/bindings/iio/adc/ti-adc084s021.txt | 25 ++
>> drivers/iio/adc/Kconfig | 12 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/ti-adc084s021.c | 342 +++++++++++++++++++++
>> 4 files changed, 380 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/ti-adc084s021.txt
>> create mode 100644 drivers/iio/adc/ti-adc084s021.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/ti-adc084s021.txt b/Documentation/devicetree/bindings/iio/adc/ti-adc084s021.txt
>> new file mode 100644
>> index 0000000..921eb46
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/ti-adc084s021.txt
>> @@ -0,0 +1,25 @@
>> +* Texas Instruments' ADC084S021
>> +
>> +Required properties:
>> + - compatible : Must be "ti,adc084s021"
>> + - reg : SPI chip select number for the device
>> + - vref-supply : The regulator supply for ADC reference voltage
>> + - spi-max-frequency : Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt
>> +
>> +Optional properties:
>> + - spi-cpol : SPI inverse clock polarity, as per spi-bus bindings
>> + - spi-cpha : SPI shifted clock phase (CPHA), as per spi-bus bindings
>> + - spi-cs-high : SPI chip select active high, as per spi-bus bindings
>> +
>> +
>> +Example:
>> +adc@0 {
>> + compatible = "ti,adc084s021";
>> + reg = <0>;
>> + vref-supply = <&adc_vref>;
>> + spi-cpol;
>> + spi-cpha;
>> + spi-cs-high;
>> + spi-max-frequency = <16000000>;
>> + pl022,com-mode = <0x2>; /* DMA */
>
> what is this?
>
>> +};
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index dedae7a..13141e5 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -560,6 +560,18 @@ config TI_ADC0832
>> This driver can also be built as a module. If so, the module will be
>> called ti-adc0832.
>>
>> +config TI_ADC084S021
>> + tristate "Texas Instruments ADC084S021"
>> + depends on SPI
>> + select IIO_BUFFER
>> + select IIO_TRIGGERED_BUFFER
>> + help
>> + If you say yes here you get support for Texas Instruments ADC084S021
>> + chips.
>> +
>> + This driver can also be built as a module. If so, the module will be
>> + called ti-adc084s021.
>> +
>> config TI_ADC12138
>> tristate "Texas Instruments ADC12130/ADC12132/ADC12138"
>> depends on SPI
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index d001262..b1a6158 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -51,6 +51,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>> obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>> +obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
>> obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
>> obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
>> obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
>> diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
>> new file mode 100644
>> index 0000000..4f33b91
>> --- /dev/null
>> +++ b/drivers/iio/adc/ti-adc084s021.c
>> @@ -0,0 +1,342 @@
>> +/**
>> + * Copyright (C) 2017 Axis Communications AB
>> + *
>> + * Driver for Texas Instruments' ADC084S021 ADC chip.
>> + * Datasheets can be found here:
>> + * http://www.ti.com/lit/ds/symlink/adc084s021.pdf
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/module.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/events.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#define MODULE_NAME "adc084s021"
>> +#define DRIVER_VERSION "1.0"
>
> is only used once at the very end...
>
>> +#define ADC_RESOLUTION 8
Put it inline...
>> +#define ADC_N_CHANNELS 4
Belongs inline. No additional info provided by having it here.
>
> we want a consistent prefix, such as ADC084S021_
>
>> +
>> +struct adc084s021_configuration {
>> + const struct iio_chan_spec *channels;
>> + u8 num_channels;
>
> no need for u8, perhaps unsigned?
>
>> +};
>> +
>> +struct adc084s021 {
>> + struct spi_device *spi;
>> + struct spi_message message;
>> + struct spi_transfer spi_trans[2];
>> + struct regulator *reg;
>> + struct mutex lock;
>> + /*
>> + * DMA (thus cache coherency maintenance) requires the
>> + * transfer buffers to live in their own cache lines.
>> + */
>> + union {
>> + u8 tx_buf[2];
>> + u8 rx_buf[2];
>> + } ____cacheline_aligned;
>> + u8 cur_adc_values[ADC_N_CHANNELS];
>> +};
>> +
>> +/**
>> + * Event triggered when value changes on a channel
>> + */
>> +static const struct iio_event_spec adc084s021_event = {
>> + .type = IIO_EV_TYPE_CHANGE,
>> + .dir = IIO_EV_DIR_NONE,
>> +};
Not the intent of that type of event at all.
>> +
>> +/**
>> + * Channel specification
>> + */
>> +#define ADC084S021_VOLTAGE_CHANNEL(num) \
>> + { \
>> + .type = IIO_VOLTAGE, \
>> + .channel = (num), \
>> + .address = (num << 3), \
>
> parenthesis should be around (num)
>
>> + .indexed = 1, \
>> + .scan_index = num, \
>
> parenthesis?
>
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .realbits = 8, \
>> + .storagebits = 32, \
>> + .shift = 24 - ((num << 3)), \
>
> no need for (( )) around the expression, parenthesis should be around num
>
> the shift doesn't make sense, you are shifting in
>
> adc084s021_adc_conversion() already and return just 8 bits (so storagebits
> should be 8)?
>
> you could/should use realbits = 8, storagebits = 16, shift = 4 and
> endianness = IIO_BE if I read Figure 1 of the datasheet correctly
>
>> + }, \
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
>
> likely this is missing _SCALE
> IIO expects data to be returned in millivolt, see
> Documentation/ABI/testing/sysfs-bus-iio
>
>> + .event_spec = &adc084s021_event, \
>> + .num_event_specs = 1, \
>
> ARRAY_SIZE(&adc084s021_event) to be extensible?
>
>> + }
>> +
>> +static const struct iio_chan_spec adc084s021_channels[] = {
>> + ADC084S021_VOLTAGE_CHANNEL(0),
>> + ADC084S021_VOLTAGE_CHANNEL(1),
>> + ADC084S021_VOLTAGE_CHANNEL(2),
>> + ADC084S021_VOLTAGE_CHANNEL(3),
>> + IIO_CHAN_SOFT_TIMESTAMP(4),
>> +};
>> +
>> +static const struct adc084s021_configuration adc084s021_config[] = {
>> + { adc084s021_channels, ARRAY_SIZE(adc084s021_channels) },
>
> for just one configuration, this is not really needed; so you plan/forsee
> more chips being added soonish?
>
>> +};
>> +
>> +/**
>> + * Read an ADC channel and return its value.
>> + *
>> + * @adc: The ADC SPI data.
>> + * @channel: The IIO channel data structure.
>> + */
>> +static int adc084s021_adc_conversion(struct adc084s021 *adc,
>> + struct iio_chan_spec const *channel)
>> +{
>> + u16 value;
>
> value should be u8, but is not really needed
>
>> + int ret;
>> +
>> + mutex_lock(&adc->lock);
>> + adc->tx_buf[0] = channel->address;
>> +
>> + /* Do the transfer */
>> + ret = spi_sync(adc->spi, &adc->message);
>> +
>
> no newline here please
>
>> + if (ret < 0) {
>> + mutex_unlock(&adc->lock);
>> + return ret;
>> + }
>> +
>> + value = (adc->rx_buf[0] << 4) | (adc->rx_buf[1] >> 4);
>
> I recommend using __be16 for rx_buf and
> ret = (be16_to_cpu(adc->rx_buf) >> 4) & 0xff;
>
>> + mutex_unlock(&adc->lock);
>> +
>> + dev_dbg(&adc->spi->dev, "value 0x%02X on channel %d\n",
>> + value, channel->channel);
>> + return value;
>> +}
>> +
>> +/**
>> + * Make a readout of requested IIO channel info.
>
> no need to document this, it is found in every IIO driver...
>
>> + *
>> + * @indio_dev: The industrial I/O device.
>> + * @channel: The IIO channel data structure.
>> + * @val: First element of value (integer).
>> + * @val2: Second element of value (fractional).
>> + * @mask: The info_mask to read.
>> + */
>> +static int adc084s021_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *channel, int *val,
>> + int *val2, long mask)
>> +{
>> + struct adc084s021 *adc = iio_priv(indio_dev);
>> + int retval;
>
> how about using ret everywhere?
Agreed.
>
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>
> probably want iio_device_claim_direct_mode() so to not interfere with
> buffered accessBorderline case as all you will do is queue up additional spi transfers.
At least after you have dropped the unusual events stuff.
Arguably this will mean sysfs reads could make the buffered data flow
less deterministic though so maybe on claiming direct mode (which
prevents it running concurrently with buffered capture.
>
>> + retval = adc084s021_adc_conversion(adc, channel);
>> + if (retval < 0)
>> + return retval;
>> +
>> + *val = retval;
>> + return IIO_VAL_INT;
>> +
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +/**
>> + * Read enabled ADC channels and push data to the buffer.
>> + *
>> + * @irq: The interrupt number (not used).
>> + * @pollfunc: Pointer to the poll func.
>> + */
>> +static irqreturn_t adc084s021_trigger_handler(int irq, void *pollfunc)
>> +{
>> + struct iio_poll_func *pf = pollfunc;
>> + struct iio_dev *indio_dev = pf->indio_dev;
>> + struct adc084s021 *adc = iio_priv(indio_dev);
>> + u8 *data;
>> + s64 timestamp;
>> + int value, scan_index;
>> +
>> + data = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
>
> pre-allocate buffer with maximum size statically; allocation is
> potentially expensive; don't forget space for the timestamp and padding...
>
>> + if (!data) {
>> + iio_trigger_notify_done(indio_dev->trig);
>> + return IRQ_NONE;
>> + }
>> +
>> + timestamp = iio_get_time_ns(indio_dev);
>> +
>> + for_each_set_bit(scan_index, indio_dev->active_scan_mask,
>> + indio_dev->masklength) {
>> + const struct iio_chan_spec *channel =
>> + &indio_dev->channels[scan_index];
>> + value = adc084s021_adc_conversion(adc, channel);
>
> lock is taken and released for each channel, probably want to do it just
> once?
>
>> + data[scan_index] = value;
>> +
>> + /*
>> + * Compare read data to previous read. If it differs send
>> + * event notification for affected channel.
>> + */
>> + if (adc->cur_adc_values[scan_index] != (u8)value) {
>
> cur_adc_values is not initialized (probably set to 0);
> so on first read, should the notification be sent?
? This is 'unusual' to say the least. Why the events given the data
is available via the buffer. If you really want to do this stuff, it
ought to be in userspace.
Are you trying to emulate the filtering input does?
>
>> + adc->cur_adc_values[scan_index] = (u8)value;
>> + iio_push_event(indio_dev,
>> + IIO_EVENT_CODE(IIO_VOLTAGE, 0,
>> + IIO_NO_MOD, IIO_EV_DIR_NONE,
>> + IIO_EV_TYPE_CHANGE,
>> + channel->channel, 0, 0),
>> + timestamp);
>> + dev_dbg(&indio_dev->dev,
>> + "new value on ch%d: 0x%02X (ts %llu)\n",
>> + channel->channel, value, timestamp);
>> + }
>> + }
>> +
>> + iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
>> + iio_trigger_notify_done(indio_dev->trig);
>> + kfree(data);
blank line here please.
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static const struct iio_info adc084s021_info = {
>> + .read_raw = adc084s021_read_raw,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +/**
>> + * Create and register ADC IIO device for SPI.
>
> comment is rather pointless
>
>> + */
>> +static int adc084s021_probe(struct spi_device *spi)
>> +{
>> + struct iio_dev *indio_dev;
>> + struct adc084s021 *adc;
>> + int config = spi_get_device_id(spi)->driver_data;
>> + int retval;
>
> ret maybe
>
>> +
>> + /* Allocate an Industrial I/O device */
>
> obviously...
:) Yeah, clear out any comments that don't tell us much.
>
>> + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
>> + if (!indio_dev) {
>> + dev_err(&spi->dev, "Failed to allocate IIO device\n");
>> + return -ENOMEM;
>> + }
>> +
>> + adc = iio_priv(indio_dev);
>> + adc->spi = spi;
>> + spi->bits_per_word = ADC_RESOLUTION;
This surprised me somewhat as I was expecting an odd value for this
(and was going to ask if standard power of 2 options would work).
If it had been inline, this would have required slightly less review
time which I always like (particularly when crammed into an airline seat!)
>> +
>> + /* Update the SPI device with config and connect the iio dev */
>> + retval = spi_setup(spi);
>> + if (retval) {
>> + dev_err(&spi->dev, "Failed to update SPI device\n");
>> + return retval;
>> + }
>> + spi_set_drvdata(spi, indio_dev);
>> +
>> + /* Initiate the Industrial I/O device */
:>> + indio_dev->dev.parent = &spi->dev;
>> + indio_dev->dev.of_node = spi->dev.of_node;
>> + indio_dev->name = spi_get_device_id(spi)->name;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + indio_dev->info = &adc084s021_info;
>> + indio_dev->channels = adc084s021_config[config].channels;
>> + indio_dev->num_channels = adc084s021_config[config].num_channels;
>
> could do it directly as long there is just one configuration
That would certainly be preferable unless V2 is going to show up
with multiple options!
>
>> +
>> + /* Create SPI transfer for channel reads */
>> + adc->spi_trans[0].tx_buf = &adc->tx_buf[0];
>> + adc->spi_trans[0].len = 2;
>> + adc->spi_trans[0].speed_hz = spi->max_speed_hz;
>> + adc->spi_trans[0].bits_per_word = spi->bits_per_word;
>> + adc->spi_trans[1].rx_buf = &adc->rx_buf[0];
>> + adc->spi_trans[1].len = 2;
>> + adc->spi_trans[1].speed_hz = spi->max_speed_hz;
>> + adc->spi_trans[1].bits_per_word = spi->bits_per_word;
>> +
>> + /* Setup SPI message for channel reads */
>> + spi_message_init(&adc->message);
>> + spi_message_add_tail(&adc->spi_trans[0], &adc->message);
>> + spi_message_add_tail(&adc->spi_trans[1], &adc->message);
spi_init_with_transfers to save a bit of boilerplate.
>> +
>> + adc->reg = devm_regulator_get(&spi->dev, "vref");
>> + if (IS_ERR(adc->reg))
>> + return PTR_ERR(adc->reg);
>> +
>> + retval = regulator_enable(adc->reg);
>> + if (retval < 0)
>> + return retval;
Given we either have slow sysfs accesses or know we have buffered
access on going. Have you considered enabling and disabling
the regulator dynamically? The fact that this often makes sense is
why Mark and co from the regulators side of things haven't provided
a devm_regulator_enable... You would need a preenable and postdisable
to make sure it was on for buffered access.
>> +
>> + mutex_init(&adc->lock);
>> +
>> + /* Setup triggered buffer with pollfunction */
>> + retval = iio_triggered_buffer_setup(indio_dev, NULL,
>
> devm_()
I disagree. It would change the ordering wrt to the regulator_enable.
Now, obviously it won't actually matter, but it will make the code
less obviously correct and for the small burden of extra unwind
code I'd keep using the non devm version.
>
>> + adc084s021_trigger_handler, NULL);
>> + if (retval) {
>> + dev_err(&spi->dev, "Failed to setup triggered buffer\n");
>> + goto buffer_setup_failed;
>> + }
>> +
>> + retval = iio_device_register(indio_dev);
>> + if (retval) {
>> + dev_err(&spi->dev, "Failed to register IIO device\n");
Hmm. If we were to flesh out some error messages for the few
cases where there is no info provided in iio_device_register we could
probably clean out a fair bit of boiler plate reporting in drivers.
Ah well, one for the future!
>> + goto device_register_failed;
>> + }
>> +
>> + dev_info(&spi->dev, "probed!\n");
>
> no logging please, just outputs clutter
>
>> + return 0;
>> +
>> +device_register_failed:
>> + iio_triggered_buffer_cleanup(indio_dev);
>> +buffer_setup_failed:
>> + regulator_disable(adc->reg);
>> + return retval;
>> +}
>> +
>> +/**
>> + * Unregister ADC IIO device for SPI.
>> + */
>> +static int adc084s021_remove(struct spi_device *spi)
>> +{
>> + struct iio_dev *indio_dev = spi_get_drvdata(spi);
>> + struct adc084s021 *adc = iio_priv(indio_dev);
>> +
>> + iio_device_unregister(indio_dev);
>> + iio_triggered_buffer_cleanup(indio_dev);
>> + regulator_disable(adc->reg);
blank line here preferred (slightly!)
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id adc084s021_of_match[] = {
>> + { .compatible = "ti,adc084s021", },
>> + {},
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, adc084s021_of_match);
>> +
>> +static const struct spi_device_id adc084s021_id[] = {
>> + { MODULE_NAME, 0},
>> + {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(spi, adc084s021_id);
>> +
>> +static struct spi_driver adc084s021_driver = {
>> + .driver = {
>> + .name = MODULE_NAME,
>> + .of_match_table = of_match_ptr(adc084s021_of_match),
>> + },
>> + .probe = adc084s021_probe,
>> + .remove = adc084s021_remove,
>> + .id_table = adc084s021_id,
>> +};
>> +
Convention often says to not bother with a blank line here or before
the MODULE_DEVICE_TABLE above. Gives a visual indication that these macros
are being passed the structures.
(really minor point!)
>> +module_spi_driver(adc084s021_driver);
>> +
>> +MODULE_AUTHOR("Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>");
>> +MODULE_DESCRIPTION("Texas Instruments ADC084S021");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_VERSION(DRIVER_VERSION);
>>
>
^ permalink raw reply
* [PATCH v3] spi-imx: Implements handling of the SPI_READY mode flag.
From: Leif Middelschulte @ 2017-04-23 19:19 UTC (permalink / raw)
To: broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Leif Middelschulte
From: Leif Middelschulte <leif.middelschulte-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
This patch implements consideration of the SPI_READY mode flag as
defined in spi.h. It extends the device tree bindings to support
the values defined by the reference manual for the DRCTL field.
Thus supporting edge-triggered and level-triggered bursts.
Signed-off-by: Leif Middelschulte <Leif.Middelschulte-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
.../devicetree/bindings/spi/fsl-imx-cspi.txt | 7 +++++++
drivers/spi/spi-imx.c | 20 ++++++++++++++++++--
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
index 8bc95e2fc47f..31b5b21598ff 100644
--- a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
+++ b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
@@ -23,6 +23,12 @@ See the clock consumer binding,
Obsolete properties:
- fsl,spi-num-chipselects : Contains the number of the chipselect
+Optional properties:
+- fsl,spi-rdy-drctl: Integer, representing the value of DRCTL, the register
+controlling the SPI_READY handling. Note that to enable the DRCTL consideration,
+the SPI_READY mode-flag needs to be set too.
+Valid values are: 0 (disabled), 1 (edge-triggered burst) and 2 (level-triggered burst).
+
Example:
ecspi@70010000 {
@@ -35,4 +41,5 @@ ecspi@70010000 {
<&gpio3 25 0>; /* GPIO3_25 */
dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
dma-names = "rx", "tx";
+ fsl,spi-rdy-drctl = <1>;
};
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 9a7c62f471dc..b402530a7a9a 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -95,6 +95,7 @@ struct spi_imx_data {
unsigned int spi_bus_clk;
unsigned int bytes_per_word;
+ unsigned int spi_drctl;
unsigned int count;
void (*tx)(struct spi_imx_data *);
@@ -246,6 +247,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
#define MX51_ECSPI_CTRL_XCH (1 << 2)
#define MX51_ECSPI_CTRL_SMC (1 << 3)
#define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
+#define MX51_ECSPI_CTRL_DRCTL(drctl) ((drctl) << 16)
#define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
#define MX51_ECSPI_CTRL_CS(cs) ((cs) << 18)
@@ -355,6 +357,12 @@ static int mx51_ecspi_config(struct spi_device *spi,
*/
ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
+ /*
+ * Enable SPI_RDY handling (falling edge/level triggered).
+ */
+ if (spi->mode & SPI_READY)
+ ctrl |= MX51_ECSPI_CTRL_DRCTL(spi_imx->spi_drctl);
+
/* set clock speed */
ctrl |= mx51_ecspi_clkdiv(spi_imx, config->speed_hz, &clk);
spi_imx->spi_bus_clk = clk;
@@ -1173,7 +1181,7 @@ static int spi_imx_probe(struct platform_device *pdev)
struct spi_master *master;
struct spi_imx_data *spi_imx;
struct resource *res;
- int i, ret, irq;
+ int i, ret, irq, spi_drctl;
if (!np && !mxc_platform_info) {
dev_err(&pdev->dev, "can't get the platform data\n");
@@ -1181,6 +1189,12 @@ static int spi_imx_probe(struct platform_device *pdev)
}
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
+ ret = of_property_read_u32(np, "fsl,spi-rdy-drctl", &spi_drctl);
+ if ((ret < 0) || (spi_drctl >= 0x3)) {
+ /* '11' is reserved */
+ spi_drctl = 0;
+ }
+
if (!master)
return -ENOMEM;
@@ -1216,7 +1230,9 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx))
- spi_imx->bitbang.master->mode_bits |= SPI_LOOP;
+ spi_imx->bitbang.master->mode_bits |= SPI_LOOP | SPI_READY;
+
+ spi_imx->spi_drctl = spi_drctl;
init_completion(&spi_imx->xfer_done);
--
2.12.2
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH V4 1/9] PM / OPP: Allow OPP table to be used for power-domains
From: Kevin Hilman @ 2017-04-23 22:07 UTC (permalink / raw)
To: Viresh Kumar
Cc: Sudeep Holla, Nishanth Menon, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-pm-u79uwXL29TY76Z2rM5mHXA, Viresh Kumar, Rafael Wysocki,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
rnayak-sgV2jX0FEOL9JmXXK+q4OQ, Stephen Boyd,
lina.iyer-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <20170420095248.GJ5436@vireshk-i7>
Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> writes:
> On 20-04-17, 10:43, Sudeep Holla wrote:
>> Just that the term performance is closely related to frequency, it needs
>> to be explicit on what *exactly* it means. As it stands now,
>> it can be used for OPP as I explain which controls both but as you
>> clarify that's not what it's designed for.
>
> We are talking about active states of a power domain here and
> *performance* is the best word I got.
>
> And yes we can still have
> frequency as a configurable here, just that current platforms don't
> have it.
It's not that your platforms don't have frequency, it's just that it's
hidden by firmware on an M3 in your particular case.
DT is meant to model hardware, and surely what the M3 is managing is
frequency and/or voltage (IOW, an OPP).
The way I see it, the problem you're trying to solve is complicated just
because you don't know the exat freq and/or voltage because they are
hiddent behind the firware, and all you have control over is an index.
What if you drop the introduction of the new domain-performance-state
and just stick with OPPs (and phandles to them), and for cases where you
don't know the exact freq/volage pairs, just use indexes and comment
what they refer to:
operating-points = <
/*
* NOTE: actual frequency and voltages are managed by
* firmware and are hidden from HLOS, so we simply use index
* here to select the OPP
*/
1 1
2 2
3 3
>;
Since selecting the OPP is up to the power-domain driver implementation,
this should be fine, IMO.
This would mean just updating the doc to reflect the "relaxing" of these
fields to reflect indexes in cases where the exact freq/voltages are not
known.
IMO, this would be much simpler, as it avoids adding a new property and
continues to use teriminology that people are already familiar with
around OPPs.
Kevin
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/5] arm64: renesas: r8a7796: Add external audio clocks
From: Kuninori Morimoto @ 2017-04-23 23:56 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Simon Horman, Magnus Damm, Yoshihiro Shimoda, Rob Herring,
Mark Rutland, linux-renesas-soc, devicetree, linux-arm-kernel
In-Reply-To: <1492779321-23939-2-git-send-email-geert+renesas@glider.be>
Hi Geert
> Add the external audio clocks as zero Hz fixed-frequency clocks.
> Boards that provide these clocks should override them.
>
> Based on commit 623197b90c7aa97c ("arm64: renesas: r8a7795: Sound SSI
> PIO support").
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
Acked-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> arch/arm64/boot/dts/renesas/r8a7796.dtsi | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
> index 2ec1ed5f499165ad..101cd41d693a7ab5 100644
> --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
> @@ -120,6 +120,29 @@
> clock-frequency = <0>;
> };
>
> + /*
> + * The external audio clocks are configured as 0 Hz fixed frequency
> + * clocks by default.
> + * Boards that provide audio clocks should override them.
> + */
> + audio_clk_a: audio_clk_a {
> + compatible = "fixed-clock";
> + #clock-cells = <0>;
> + clock-frequency = <0>;
> + };
> +
> + audio_clk_b: audio_clk_b {
> + compatible = "fixed-clock";
> + #clock-cells = <0>;
> + clock-frequency = <0>;
> + };
> +
> + audio_clk_c: audio_clk_c {
> + compatible = "fixed-clock";
> + #clock-cells = <0>;
> + clock-frequency = <0>;
> + };
> +
> /* External CAN clock - to be overridden by boards that provide it */
> can_clk: can {
> compatible = "fixed-clock";
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v3] ARM: dts: at91: sama5d2: add m_can nodes
From: Wenyou Yang @ 2017-04-24 1:12 UTC (permalink / raw)
To: Nicolas.Ferre, Alexandre Belloni, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Russell King
Cc: devicetree, Wenyou Yang, Oliver Hartkopp, linux-kernel, linux-can,
Quentin Schulz, Wenyou Yang, linux-arm-kernel
Add nodes to support the Controller Area Network(M_CAN) on SAMA5D2.
The version of M_CAN IP core is 3.1.0 (CREL = 0x31040730).
As said in SAMA5D2 datasheet, the CAN clock is recommended to use
frequencies of 20, 40 or 80 MHz. To achieve these frequencies,
PMC GCLK3 must select the UPLLCK(480 MHz) as source clock and
divide by 24, 12, or 6. So, the "assigned-clock-rates" property
has three options: 20000000, 40000000, and 80000000.
The "assigned-clock-parents" property should be referred to utmi
fixedly.
The MSBs [bits 31:16] of the CAN Message RAM for CAN0 and CAN1 are
default configured in 0x00200000. To avoid conflict with SRAM map
for PM, change them to 0x00210000 in the AT91Bootstrap via setting
the CAN Memories Address-based Register(SFR_CAN) of SFR.
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
The patch is tested on SAMA5D2 Xplained and based on the patch set,
1. [PATCH v4 1/7] can: m_can: Disabled Interrupt Line 1
http://marc.info/?l=linux-can&m=149165343604033&w=2
Changes in v3:
- Add Tested-by tag.
- Change the number of Rx Rx Buffers, Tx Buffers and Tx Event FIFO
to maximum.
Changes in v2:
- Configures 10 TX Event FIFO elements and 10 TX Buffers/FIFO slots,
because the TXE FIFO is needed to be configured.
- Configure the offset of Message RAM for CAN1 followed from CAN0's.
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 24 +++++++++++++
arch/arm/boot/dts/sama5d2.dtsi | 56 +++++++++++++++++++++++++++++
2 files changed, 80 insertions(+)
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 9f7f8a7d8ff9..2f19b08dc226 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -257,6 +257,12 @@
status = "okay";
};
+ can0: can@f8054000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_can0_default>;
+ status = "okay";
+ };
+
uart3: serial@fc008000 {
atmel,use-dma-rx;
atmel,use-dma-tx;
@@ -321,6 +327,18 @@
bias-disable;
};
+ pinctrl_can0_default: can0_default {
+ pinmux = <PIN_PC10__CANTX0>,
+ <PIN_PC11__CANRX0>;
+ bias-disable;
+ };
+
+ pinctrl_can1_default: can1_default {
+ pinmux = <PIN_PC26__CANTX1>,
+ <PIN_PC27__CANRX1>;
+ bias-disable;
+ };
+
pinctrl_charger_chglev: charger_chglev {
pinmux = <PIN_PA12__GPIO>;
bias-disable;
@@ -468,6 +486,12 @@
};
};
+
+ can1: can@fc050000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_can1_default>;
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 22332be72140..7e00fa21373e 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -762,6 +762,18 @@
atmel,clk-output-range = <0 83000000>;
};
+ can0_clk: can0_clk {
+ #clock-cells = <0>;
+ reg = <56>;
+ atmel,clk-output-range = <0 83000000>;
+ };
+
+ can1_clk: can1_clk {
+ #clock-cells = <0>;
+ reg = <57>;
+ atmel,clk-output-range = <0 83000000>;
+ };
+
classd_clk: classd_clk {
#clock-cells = <0>;
reg = <59>;
@@ -890,6 +902,18 @@
#clock-cells = <0>;
reg = <55>;
};
+
+ can0_gclk: can0_gclk {
+ #clock-cells = <0>;
+ reg = <56>;
+ atmel,clk-output-range = <0 80000000>;
+ };
+
+ can1_gclk: can1_gclk {
+ #clock-cells = <0>;
+ reg = <57>;
+ atmel,clk-output-range = <0 80000000>;
+ };
};
};
@@ -1144,6 +1168,22 @@
clocks = <&clk32k>;
};
+ can0: can@f8054000 {
+ compatible = "bosch,m_can";
+ reg = <0xf8054000 0x4000>, <0x210000 0x4000>;
+ reg-names = "m_can", "message_ram";
+ interrupts = <56 IRQ_TYPE_LEVEL_HIGH 7>,
+ <64 IRQ_TYPE_LEVEL_HIGH 7>;
+ interrupt-names = "int0", "int1";
+ clocks = <&can0_clk>, <&can0_gclk>;
+ clock-names = "hclk", "cclk";
+ assigned-clocks = <&can0_gclk>;
+ assigned-clock-parents = <&utmi>;
+ assigned-clock-rates = <40000000>;
+ bosch,mram-cfg = <0x0 0 0 64 0 0 32 32>;
+ status = "disabled";
+ };
+
spi1: spi@fc000000 {
compatible = "atmel,at91rm9200-spi";
reg = <0xfc000000 0x100>;
@@ -1305,6 +1345,22 @@
status = "okay";
};
+ can1: can@fc050000 {
+ compatible = "bosch,m_can";
+ reg = <0xfc050000 0x4000>, <0x210000 0x4000>;
+ reg-names = "m_can", "message_ram";
+ interrupts = <57 IRQ_TYPE_LEVEL_HIGH 7>,
+ <65 IRQ_TYPE_LEVEL_HIGH 7>;
+ interrupt-names = "int0", "int1";
+ clocks = <&can1_clk>, <&can1_gclk>;
+ clock-names = "hclk", "cclk";
+ assigned-clocks = <&can1_gclk>;
+ assigned-clock-parents = <&utmi>;
+ assigned-clock-rates = <40000000>;
+ bosch,mram-cfg = <0x1100 0 0 64 0 0 32 32>;
+ status = "disabled";
+ };
+
chipid@fc069000 {
compatible = "atmel,sama5d2-chipid";
reg = <0xfc069000 0x8>;
--
2.11.0
^ permalink raw reply related
* 12224 devicetree
From: arywhite007-/E1597aS9LQAvxtiuMwx3w @ 2017-04-24 2:10 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA
[-- Attachment #1: 9062578892.zip --]
[-- Type: application/zip, Size: 1691 bytes --]
^ permalink raw reply
* RE: [PATCH 1/2 v2] dt-bindings: qoriq-clock: Add coreclk
From: Andy Tang @ 2017-04-24 3:14 UTC (permalink / raw)
To: mturquette@baylibre.com, sboyd@codeaurora.org
Cc: robh+dt@kernel.org, mark.rutland@arm.com,
linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, Scott Wood
In-Reply-To: <DB6PR0402MB283701D478A1678D28894C77F30A0@DB6PR0402MB2837.eurprd04.prod.outlook.com>
Does anyone give me a clue why this patch set can't be responded after so long time?
Thanks,
Andy
-----Original Message-----
From: Andy Tang
Sent: Monday, April 17, 2017 9:37 AM
To: 'mturquette@baylibre.com' <mturquette@baylibre.com>; 'sboyd@codeaurora.org' <sboyd@codeaurora.org>
Cc: 'robh+dt@kernel.org' <robh+dt@kernel.org>; 'mark.rutland@arm.com' <mark.rutland@arm.com>; 'linux-clk@vger.kernel.org' <linux-clk@vger.kernel.org>; 'devicetree@vger.kernel.org' <devicetree@vger.kernel.org>; 'linux-kernel@vger.kernel.org' <linux-kernel@vger.kernel.org>; 'linux-arm-kernel@lists.infradead.org' <linux-arm-kernel@lists.infradead.org>; 'Scott Wood' <oss@buserror.net>
Subject: RE: [PATCH 1/2 v2] dt-bindings: qoriq-clock: Add coreclk
Hi Stephen and Michael,
This patch set has been pending for more than two months since it was first sent.
I have not received any response from you until now.
Could you give some comments on it?
Regards,
Andy
-----Original Message-----
From: Andy Tang
Sent: Wednesday, April 05, 2017 2:16 PM
To: mturquette@baylibre.com; sboyd@codeaurora.org
Cc: robh+dt@kernel.org; mark.rutland@arm.com; linux-clk@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; Scott Wood <oss@buserror.net>
Subject: RE: [PATCH 1/2 v2] dt-bindings: qoriq-clock: Add coreclk
Hello
Do you have any comments on this patch set which was acked by Rob?
Regards,
Andy
> -----Original Message-----
> From: Yuantian Tang [mailto:andy.tang@nxp.com]
> Sent: Monday, March 20, 2017 10:37 AM
> To: mturquette@baylibre.com
> Cc: sboyd@codeaurora.org; robh+dt@kernel.org; mark.rutland@arm.com;
> linux-clk@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; Scott
> Wood <oss@buserror.net>; Andy Tang <andy.tang@nxp.com>
> Subject: [PATCH 1/2 v2] dt-bindings: qoriq-clock: Add coreclk
>
> From: Scott Wood <oss@buserror.net>
>
> ls1012a has separate input root clocks for core PLLs versus the
> platform PLL, with the latter described as sysclk in the hw docs.
> Update the qoriq-clock binding to allow a second input clock, named
> "coreclk". If present, this clock will be used for the core PLLs.
>
> Signed-off-by: Scott Wood <oss@buserror.net>
> Signed-off-by: Tang Yuantian <andy.tang@nxp.com>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
> v2:
> -- change the author to Scott
> Documentation/devicetree/bindings/clock/qoriq-clock.txt | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/clock/qoriq-clock.txt
> b/Documentation/devicetree/bindings/clock/qoriq-clock.txt
> index aa3526f..119cafd 100644
> --- a/Documentation/devicetree/bindings/clock/qoriq-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/qoriq-clock.txt
> @@ -56,6 +56,11 @@ Optional properties:
> - clocks: If clock-frequency is not specified, sysclk may be provided
> as an input clock. Either clock-frequency or clocks must be
> provided.
> + A second input clock, called "coreclk", may be provided if
> + core PLLs are based on a different input clock from the
> + platform PLL.
> +- clock-names: Required if a coreclk is present. Valid names are
> + "sysclk" and "coreclk".
>
> 2. Clock Provider
>
> @@ -72,6 +77,7 @@ second cell is the clock index for the specified type.
> 2 hwaccel index (n in CLKCGnHWACSR)
> 3 fman 0 for fm1, 1 for fm2
> 4 platform pll 0=pll, 1=pll/2, 2=pll/3, 3=pll/4
> + 5 coreclk must be 0
>
> 3. Example
>
> --
> 2.1.0.27.g96db324
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox