* [PATCH 1/2] mmc: mediatek: Fix module autoload
From: Ulf Hansson @ 2016-10-21 8:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476720825-30442-1-git-send-email-javier@osg.samsung.com>
On 17 October 2016 at 18:13, Javier Martinez Canillas
<javier@osg.samsung.com> wrote:
> If the driver is built as a module, autoload won't work because the module
> alias information is not filled. So user-space can't match the registered
> device with the corresponding module.
>
> Export the module alias information using the MODULE_DEVICE_TABLE() macro.
>
> Before this patch:
>
> $ modinfo drivers/mmc/host/mtk-sd.ko | grep alias
> $
>
> After this patch:
>
> $ modinfo drivers/mmc/host/mtk-sd.ko | grep alias
> alias: of:N*T*Cmediatek,mt8135-mmcC*
> alias: of:N*T*Cmediatek,mt8135-mmc
>
> Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
Thanks, applied for next!
Kind regards
Uffe
> ---
>
> drivers/mmc/host/mtk-sd.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 84e9afcb5c09..86af0b199a54 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -1713,6 +1713,7 @@ static const struct of_device_id msdc_of_ids[] = {
> { .compatible = "mediatek,mt8135-mmc", },
> {}
> };
> +MODULE_DEVICE_TABLE(of, msdc_of_ids);
>
> static struct platform_driver mt_msdc_driver = {
> .probe = msdc_drv_probe,
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v2] mmc: sunxi: Prevent against null dereference for vmmc
From: Ulf Hansson @ 2016-10-21 8:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161019133304.22915-1-maxime.ripard@free-electrons.com>
On 19 October 2016 at 15:33, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> VMMC is an optional regulator, which means that mmc_regulator_get_supply
> will only return an error in case of a deferred probe, but not when the
> regulator is not set in the DT.
>
> However, the sunxi driver assumes that VMMC is always there, and doesn't
> check the value of the regulator pointer before using it, which obviously
> leads to a (close to) null pointer dereference.
>
> Add proper checks to prevent that.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Thanks, applied for next!
Kind regards
Uffe
>
> ---
> Changes from v1:
> - remove redundant error message
> ---
> drivers/mmc/host/sunxi-mmc.c | 15 ++++++++++-----
> 1 file changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
> index c0a5c676d0e8..b1d1303389a7 100644
> --- a/drivers/mmc/host/sunxi-mmc.c
> +++ b/drivers/mmc/host/sunxi-mmc.c
> @@ -822,10 +822,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> break;
>
> case MMC_POWER_UP:
> - host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
> - ios->vdd);
> - if (host->ferror)
> - return;
> + if (!IS_ERR(mmc->supply.vmmc)) {
> + host->ferror = mmc_regulator_set_ocr(mmc,
> + mmc->supply.vmmc,
> + ios->vdd);
> + if (host->ferror)
> + return;
> + }
>
> if (!IS_ERR(mmc->supply.vqmmc)) {
> host->ferror = regulator_enable(mmc->supply.vqmmc);
> @@ -847,7 +850,9 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> case MMC_POWER_OFF:
> dev_dbg(mmc_dev(mmc), "power off!\n");
> sunxi_mmc_reset_host(host);
> - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> + if (!IS_ERR(mmc->supply.vmmc))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> +
> if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
> regulator_disable(mmc->supply.vqmmc);
> host->vqmmc_enabled = false;
> --
> 2.9.3
>
^ permalink raw reply
* [PATCH v2 2/2] arm64/numa: fix incorrect log for memory-less node
From: Hanjun Guo @ 2016-10-21 8:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037636-10077-1-git-send-email-guohanjun@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
When booting on NUMA system with memory-less node (no
memory dimm on this memory controller), the print
for setup_node_data() is incorrect:
NUMA: Initmem setup node 2 [mem 0x00000000-0xffffffffffffffff]
It can be fixed by printing [mem 0x00000000-0x00000000] when
end_pfn is 0, but print <memory-less node> will be more useful.
Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ganapatrao Kulkarni <gkulkarni@caviumnetworks.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Yisheng Xie <xieyisheng1@huawei.com>
---
arch/arm64/mm/numa.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 9a71d06..4b32168 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -223,8 +223,11 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
void *nd;
int tnid;
- pr_info("Initmem setup node %d [mem %#010Lx-%#010Lx]\n",
- nid, start_pfn << PAGE_SHIFT, (end_pfn << PAGE_SHIFT) - 1);
+ if (start_pfn < end_pfn)
+ pr_info("Initmem setup node %d [mem %#010Lx-%#010Lx]\n", nid,
+ start_pfn << PAGE_SHIFT, (end_pfn << PAGE_SHIFT) - 1);
+ else
+ pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
nd_pa = memblock_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
nd = __va(nd_pa);
--
1.7.12.4
^ permalink raw reply related
* [PATCH v2 1/2] arm64/numa: fix pcpu_cpu_distance() to get correct CPU proximity
From: Hanjun Guo @ 2016-10-21 8:13 UTC (permalink / raw)
To: linux-arm-kernel
From: Yisheng Xie <xieyisheng1@huawei.com>
The pcpu_build_alloc_info() function group CPUs according to their
proximity, by call callback function @cpu_distance_fn from different
ARCHs.
For arm64 the callback of @cpu_distance_fn is
pcpu_cpu_distance(from, to)
-> node_distance(from, to)
The @from and @to for function node_distance() should be nid.
However, pcpu_cpu_distance() in arch/arm64/mm/numa.c just past the
cpu id for @from and @to, and didn't convert to numa node id.
For this incorrect cpu proximity get from ARCH, it may cause each CPU
in one group and make group_cnt out of bound:
setup_per_cpu_areas()
pcpu_embed_first_chunk()
pcpu_build_alloc_info()
in pcpu_build_alloc_info, since cpu_distance_fn will return
REMOTE_DISTANCE if we pass cpu ids (0,1,2...), so
cpu_distance_fn(cpu, tcpu) > LOCAL_DISTANCE will wrongly be ture.
This may results in triggering the BUG_ON(unit != nr_units) later:
[ 0.000000] kernel BUG at mm/percpu.c:1916!
[ 0.000000] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 0.000000] Modules linked in:
[ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.9.0-rc1-00003-g14155ca-dirty #26
[ 0.000000] Hardware name: Hisilicon Hi1616 Evaluation Board (DT)
[ 0.000000] task: ffff000008d6e900 task.stack: ffff000008d60000
[ 0.000000] PC is at pcpu_embed_first_chunk+0x420/0x704
[ 0.000000] LR is at pcpu_embed_first_chunk+0x3bc/0x704
[ 0.000000] pc : [<ffff000008c754f4>] lr : [<ffff000008c75490>] pstate: 800000c5
[ 0.000000] sp : ffff000008d63eb0
[ 0.000000] x29: ffff000008d63eb0 [ 0.000000] x28: 0000000000000000
[ 0.000000] x27: 0000000000000040 [ 0.000000] x26: ffff8413fbfcef00
[ 0.000000] x25: 0000000000000042 [ 0.000000] x24: 0000000000000042
[ 0.000000] x23: 0000000000001000 [ 0.000000] x22: 0000000000000046
[ 0.000000] x21: 0000000000000001 [ 0.000000] x20: ffff000008cb3bc8
[ 0.000000] x19: ffff8413fbfcf570 [ 0.000000] x18: 0000000000000000
[ 0.000000] x17: ffff000008e49ae0 [ 0.000000] x16: 0000000000000003
[ 0.000000] x15: 000000000000001e [ 0.000000] x14: 0000000000000004
[ 0.000000] x13: 0000000000000000 [ 0.000000] x12: 000000000000006f
[ 0.000000] x11: 00000413fbffff00 [ 0.000000] x10: 0000000000000004
[ 0.000000] x9 : 0000000000000000 [ 0.000000] x8 : 0000000000000001
[ 0.000000] x7 : ffff8413fbfcf63c [ 0.000000] x6 : ffff000008d65d28
[ 0.000000] x5 : ffff000008d65e50 [ 0.000000] x4 : 0000000000000000
[ 0.000000] x3 : ffff000008cb3cc8 [ 0.000000] x2 : 0000000000000040
[ 0.000000] x1 : 0000000000000040 [ 0.000000] x0 : 0000000000000000
[...]
[ 0.000000] Call trace:
[ 0.000000] Exception stack(0xffff000008d63ce0 to 0xffff000008d63e10)
[ 0.000000] 3ce0: ffff8413fbfcf570 0001000000000000 ffff000008d63eb0 ffff000008c754f4
[ 0.000000] 3d00: ffff000008d63d50 ffff0000081af210 00000413fbfff010 0000000000001000
[ 0.000000] 3d20: ffff000008d63d50 ffff0000081af220 00000413fbfff010 0000000000001000
[ 0.000000] 3d40: 00000413fbfcef00 0000000000000004 ffff000008d63db0 ffff0000081af390
[ 0.000000] 3d60: 00000413fbfcef00 0000000000001000 0000000000000000 0000000000001000
[ 0.000000] 3d80: 0000000000000000 0000000000000040 0000000000000040 ffff000008cb3cc8
[ 0.000000] 3da0: 0000000000000000 ffff000008d65e50 ffff000008d65d28 ffff8413fbfcf63c
[ 0.000000] 3dc0: 0000000000000001 0000000000000000 0000000000000004 00000413fbffff00
[ 0.000000] 3de0: 000000000000006f 0000000000000000 0000000000000004 000000000000001e
[ 0.000000] 3e00: 0000000000000003 ffff000008e49ae0
[ 0.000000] [<ffff000008c754f4>] pcpu_embed_first_chunk+0x420/0x704
[ 0.000000] [<ffff000008c6658c>] setup_per_cpu_areas+0x38/0xc8
[ 0.000000] [<ffff000008c608d8>] start_kernel+0x10c/0x390
[ 0.000000] [<ffff000008c601d8>] __primary_switched+0x5c/0x64
[ 0.000000] Code: b8018660 17ffffd7 6b16037f 54000080 (d4210000)
[ 0.000000] ---[ end trace 0000000000000000 ]---
[ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
Fix by getting cpu's node id with early_cpu_to_node() then pass it
to node_distance() as the original intention.
Fixes: 7af3a0a99252 ("arm64/numa: support HAVE_SETUP_PER_CPU_AREA")
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Zhen Lei <thunder.leizhen@huawei.com>
---
arch/arm64/mm/numa.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 778a985..9a71d06 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -147,7 +147,7 @@ static int __init early_cpu_to_node(int cpu)
static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
{
- return node_distance(from, to);
+ return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
}
static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
--
1.7.12.4
^ permalink raw reply related
* [PATCH v5 3/7] drm: sunxi: add DE2 HDMI support
From: Jean-Francois Moine @ 2016-10-21 8:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1477142934.git.moinejf@free.fr>
This patch adds a HDMI driver to the DE2 based Allwinner's SoCs
as A83T and H3.
Audio and video are supported.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
.../devicetree/bindings/display/sunxi/hdmi.txt | 52 ++
drivers/gpu/drm/sunxi/Kconfig | 8 +
drivers/gpu/drm/sunxi/Makefile | 2 +
drivers/gpu/drm/sunxi/de2_hdmi.c | 396 +++++++++
drivers/gpu/drm/sunxi/de2_hdmi.h | 40 +
drivers/gpu/drm/sunxi/de2_hdmi_io.c | 927 +++++++++++++++++++++
drivers/gpu/drm/sunxi/de2_hdmi_io.h | 25 +
7 files changed, 1450 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.c
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.h
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.c
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.h
diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..0558c07
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,52 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+ - compatible : should be
+ "allwinner,sun8i-a83t-hdmi" or
+ "allwinner,sun8i-h3-hdmi"
+ - clocks : phandles to the HDMI clocks as described in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+ "gate" : bus gate
+ "clock" : streaming clock
+ "ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : must be
+ "hdmi0" and "hdmi1"
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+ as defined in Documentation/devicetree/bindings/graph.txt.
+
+Example:
+
+ hdmi: hdmi at 01ee0000 {
+ compatible = "allwinner,sun8i-a83t-hdmi";
+ reg = <0x01ee0000 0x20000>;
+ clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+ <&ccu CLK_HDMI_DDC>;
+ clock-names = "gate", "clock", "ddc-clock";
+ resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+ reset-names = "hdmi0", "hdmi1";
+ ...
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port at 0 { /* video */
+ reg = <0>;
+ hdmi_lcd1: endpoint {
+ remote-endpoint = <&lcd1_hdmi>;
+ };
+ };
+ port at 1 { /* audio */
+ reg = <1>;
+ hdmi_i2s2: endpoint {
+ remote-endpoint = <&i2s2_hdmi>;
+ };
+ };
+ };
diff --git a/drivers/gpu/drm/sunxi/Kconfig b/drivers/gpu/drm/sunxi/Kconfig
index 56bde2e..4c82153 100644
--- a/drivers/gpu/drm/sunxi/Kconfig
+++ b/drivers/gpu/drm/sunxi/Kconfig
@@ -19,3 +19,11 @@ config DRM_SUNXI_DE2
Choose this option if your Allwinner chipset has the DE2 interface
as the A64, A83T and H3. If M is selected the module will be called
sunxi-de2-drm.
+
+config DRM_SUNXI_DE2_HDMI
+ tristate "Support for DE2 HDMI"
+ depends on DRM_SUNXI_DE2
+ select SND_SOC_SUNXI_HDMI if SND_SOC
+ help
+ Choose this option if you use want HDMI on DE2.
+ If M is selected the module will be called sunxi-de2-hdmi.
diff --git a/drivers/gpu/drm/sunxi/Makefile b/drivers/gpu/drm/sunxi/Makefile
index 62220cb..a268069 100644
--- a/drivers/gpu/drm/sunxi/Makefile
+++ b/drivers/gpu/drm/sunxi/Makefile
@@ -3,5 +3,7 @@
#
sunxi-de2-drm-objs := de2_drv.o de2_de.o de2_crtc.o de2_plane.o
+sunxi-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
obj-$(CONFIG_DRM_SUNXI_DE2) += sunxi-de2-drm.o
+obj-$(CONFIG_DRM_SUNXI_DE2_HDMI) += sunxi-de2-hdmi.o
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi.c b/drivers/gpu/drm/sunxi/de2_hdmi.c
new file mode 100644
index 0000000..a2324cc
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi.c
@@ -0,0 +1,396 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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 <linux/module.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.h"
+#include "de2_hdmi_io.h"
+
+static const struct of_device_id de2_hdmi_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-hdmi",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-hdmi",
+ .data = (void *) SOC_H3 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, de2_hdmi_dt_ids);
+
+#define conn_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, connector)
+
+#define enc_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, encoder)
+
+#define codec_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, codec)
+
+#if IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)
+/* --- audio functions --- */
+static int de2_hdmi_set_audio_input(struct device *dev,
+ int enable,
+ unsigned sample_rate,
+ unsigned sample_bit)
+{
+ struct de2_hdmi_priv *priv = codec_to_priv(dev_get_drvdata(dev));
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_audio(priv, sample_rate, sample_bit);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+#endif
+
+/* --- encoder functions --- */
+
+static void de2_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ priv->cea_mode = drm_match_cea_mode(mode);
+
+ DRM_DEBUG_DRIVER("cea_mode %d\n", priv->cea_mode);
+
+ clk_set_rate(priv->clk, mode->clock * 1000);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_mode(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_on(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_encoder_helper_funcs de2_hdmi_encoder_helper_funcs = {
+ .mode_set = de2_hdmi_encoder_mode_set,
+ .enable = de2_hdmi_encoder_enable,
+ .disable = de2_hdmi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs de2_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* --- connector functions --- */
+static int de2_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int cea_mode = drm_match_cea_mode(mode);
+
+ if (hdmi_io_mode_valid(cea_mode) < 0)
+ return MODE_NOMODE;
+
+ if (connector->eld[0])
+ priv->codec.eld = connector->eld; /* audio parameters */
+
+ return MODE_OK;
+}
+
+static enum drm_connector_status de2_hdmi_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_get_hpd(priv);
+ mutex_unlock(&priv->mutex);
+
+ return ret ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int read_edid_block(void *data, u8 *buf,
+ unsigned int blk, size_t length)
+{
+ struct de2_hdmi_priv *priv = data;
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_ddc_read(priv,
+ blk / 2, (blk & 1) ? 128 : 0,
+ length, buf);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ struct edid *edid;
+ int n;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ priv->codec.eld = NULL;
+
+ edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+ if (!edid) {
+ dev_warn(priv->dev, "failed to read EDID\n");
+ if (!connector->cmdline_mode.specified)
+ return 0;
+
+ return drm_add_modes_noedid(connector,
+ connector->cmdline_mode.xres,
+ connector->cmdline_mode.yres);
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+
+ drm_edid_to_eld(connector, edid);
+
+ kfree(edid);
+
+ DRM_DEBUG_DRIVER("%s EDID ok %d modes\n",
+ connector->eld[0] ? "HDMI" : "DVI", n);
+
+ return n;
+}
+
+static const
+struct drm_connector_helper_funcs de2_hdmi_connector_helper_funcs = {
+ .get_modes = de2_hdmi_connector_get_modes,
+ .mode_valid = de2_hdmi_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs de2_hdmi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = de2_hdmi_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void de2_hdmi_cleanup(struct de2_hdmi_priv *priv)
+{
+ clk_disable_unprepare(priv->clk_ddc);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ if (!IS_ERR_OR_NULL(priv->rstc1))
+ reset_control_assert(priv->rstc1);
+ if (!IS_ERR_OR_NULL(priv->rstc0))
+ reset_control_assert(priv->rstc0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = data;
+ struct de2_hdmi_priv *priv = codec_to_priv(dev_get_drvdata(dev));
+ struct drm_encoder *encoder = &priv->encoder;
+ struct drm_connector *connector = &priv->connector;
+ int ret;
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ /* if no CRTC, delay */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ /* HDMI init */
+ ret = reset_control_deassert(priv->rstc0);
+ if (ret)
+ goto err;
+ ret = reset_control_deassert(priv->rstc1);
+ if (ret)
+ goto err;
+ if (!IS_ERR(priv->gate)) {
+ ret = clk_prepare_enable(priv->gate);
+ if (ret)
+ goto err;
+ }
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->clk_ddc);
+ if (ret)
+ goto err;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_init(priv);
+ hdmi_io_reset(priv); /* hpd reset */
+ mutex_unlock(&priv->mutex);
+
+ /* encoder init */
+ ret = drm_encoder_init(drm, encoder, &de2_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ goto err;
+
+ drm_encoder_helper_add(encoder, &de2_hdmi_encoder_helper_funcs);
+
+ /* connector init */
+ ret = drm_connector_init(drm, connector,
+ &de2_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ goto err_connector;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ drm_connector_helper_add(connector,
+ &de2_hdmi_connector_helper_funcs);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+
+err_connector:
+ drm_encoder_cleanup(encoder);
+err:
+ de2_hdmi_cleanup(priv);
+ DRM_DEBUG_DRIVER("err %d\n", ret);
+ return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct de2_hdmi_priv *priv = codec_to_priv(dev_get_drvdata(dev));
+
+ drm_connector_cleanup(&priv->connector);
+ drm_encoder_cleanup(&priv->encoder);
+ de2_hdmi_cleanup(priv);
+}
+
+static const struct component_ops de2_hdmi_ops = {
+ .bind = de2_hdmi_bind,
+ .unbind = de2_hdmi_unbind,
+};
+
+static int de2_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct de2_hdmi_priv *priv;
+ struct resource *res;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, &priv->codec);
+ priv->dev = dev;
+
+ mutex_init(&priv->mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -ENXIO;
+ }
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(priv->mmio);
+ }
+
+ priv->gate = devm_clk_get(dev, "gate"); /* optional */
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "hdmi clock err %d\n",
+ (int) PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+ priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+ if (IS_ERR(priv->clk_ddc)) {
+ dev_err(dev, "hdmi-ddc clock err %d\n",
+ (int) PTR_ERR(priv->clk_ddc));
+ return PTR_ERR(priv->clk_ddc);
+ }
+
+ priv->rstc0 = devm_reset_control_get(dev, "hdmi0");
+ if (IS_ERR(priv->rstc0)) {
+ dev_err(dev, "reset controller err %d\n",
+ (int) PTR_ERR(priv->rstc0));
+ return PTR_ERR(priv->rstc0);
+ }
+
+ priv->rstc1 = devm_reset_control_get(dev, "hdmi1");
+ if (IS_ERR(priv->rstc1)) {
+ dev_err(dev, "reset controller err %d\n",
+ (int) PTR_ERR(priv->rstc1));
+ return PTR_ERR(priv->rstc1);
+ }
+
+ priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+ &pdev->dev)->data;
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)) {
+ priv->codec.set_audio_input = de2_hdmi_set_audio_input;
+ sunxi_hdmi_codec_register(dev);
+ }
+
+ return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+ if (IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI))
+ sunxi_hdmi_codec_unregister(&pdev->dev);
+ component_del(&pdev->dev, &de2_hdmi_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_hdmi_driver = {
+ .probe = de2_hdmi_probe,
+ .remove = de2_hdmi_remove,
+ .driver = {
+ .name = "sunxi-de2-hdmi",
+ .of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+ },
+};
+
+module_platform_driver(de2_hdmi_driver);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi.h b/drivers/gpu/drm/sunxi/de2_hdmi.h
new file mode 100644
index 0000000..915d609
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi.h
@@ -0,0 +1,40 @@
+#ifndef __DE2_HDMI_H__
+#define __DE2_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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 <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+#include <sound/sunxi_hdmi.h>
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct de2_hdmi_priv {
+ struct device *dev;
+ void __iomem *mmio;
+
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct clk *clk;
+ struct clk *clk_ddc;
+ struct clk *gate;
+ struct reset_control *rstc0;
+ struct reset_control *rstc1;
+
+ struct mutex mutex;
+ u8 soc_type;
+ u8 cea_mode;
+ struct sunxi_hdmi_codec codec;
+};
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi_io.c b/drivers/gpu/drm/sunxi/de2_hdmi_io.c
new file mode 100644
index 0000000..127b220
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi_io.c
@@ -0,0 +1,927 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * 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.
+ */
+
+/*
+ * The HDMI controller in the A83T and H3 seems to be a
+ * Synopsys DesignWare HDMI controller.
+ * The PHYs are unknown.
+ * Documentation:
+ * https://linux-sunxi.org/DWC_HDMI_Controller
+ * https://www.synopsys.com/dw/doc.php/ds/c/dwc_hdmi_tx_csds.pdf
+ */
+
+#include <linux/hdmi.h>
+
+#include "de2_hdmi.h"
+#include "de2_hdmi_io.h"
+
+/* DW registers (obfuscated addresses) */
+
+/* Interrupt Registers */
+#define R_0100_HDMI_IH_FC_STAT0 0x0010
+#define R_0101_HDMI_IH_FC_STAT1 0x0011
+#define R_0102_HDMI_IH_FC_STAT2 0x8010
+#define R_0103_HDMI_IH_AS_STAT0 0x8011
+#define R_0104_HDMI_IH_PHY_STAT0 0x0012
+#define R_0105_HDMI_IH_I2CM_STAT0 0x0013
+#define R_0106_HDMI_IH_CEC_STAT0 0x8012
+#define R_0107_HDMI_IH_VP_STAT0 0x8013
+#define R_0108_HDMI_IH_I2CMPHY_STAT0 0x4010
+#define R_01ff_HDMI_IH_MUTE 0xf01f
+
+/* Video Sample Registers */
+#define R_0200_HDMI_TX_INVID0 0x0800
+#define R_0201_HDMI_TX_INSTUFFING 0x0801
+#define R_0202_HDMI_TX_GYDATA0 0x8800
+#define R_0203_HDMI_TX_GYDATA1 0x8801
+#define R_0204_HDMI_TX_RCRDATA0 0x0802
+#define R_0205_HDMI_TX_RCRDATA1 0x0803
+#define R_0206_HDMI_TX_BCBDATA0 0x8802
+#define R_0207_HDMI_TX_BCBDATA1 0x8803
+
+/* Video Packetizer Registers */
+#define R_0801_HDMI_VP_PR_CD 0x0401
+#define R_0802_HDMI_VP_STUFF 0x8400
+#define R_0803_HDMI_VP_REMAP 0x8401
+#define R_0804_HDMI_VP_CONF 0x0402
+#define R_0807_HDMI_VP_MASK 0x8403
+
+/* Frame Composer Registers */
+#define R_1000_HDMI_FC_INVIDCONF 0x0040
+#define HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH 0x10
+#define R_1001_HDMI_FC_INHACTV0 0x0041
+#define R_1002_HDMI_FC_INHACTV1 0x8040
+#define R_1003_HDMI_FC_INHBLANK0 0x8041
+#define R_1004_HDMI_FC_INHBLANK1 0x0042
+#define R_1005_HDMI_FC_INVACTV0 0x0043
+#define R_1006_HDMI_FC_INVACTV1 0x8042
+#define R_1007_HDMI_FC_INVBLANK 0x8043
+#define R_1008_HDMI_FC_HSYNCINDELAY0 0x4040
+#define R_1009_HDMI_FC_HSYNCINDELAY1 0x4041
+#define R_100a_HDMI_FC_HSYNCINWIDTH1 0xc040
+#define R_100b_HDMI_FC_HSYNCINWIDTH1 0xc041
+#define R_100c_HDMI_FC_VSYNCINDELAY 0x4042
+#define R_100d_HDMI_FC_VSYNCINWIDTH 0x4043
+#define R_1011_HDMI_FC_CTRLDUR 0x0045
+#define R_1012_HDMI_FC_EXCTRLDUR 0x8044
+#define R_1013_HDMI_FC_EXCTRLSPAC 0x8045
+#define R_1014_HDMI_FC_CH0PREAM 0x0046
+#define R_1015_HDMI_FC_CH1PREAM 0x0047
+#define R_1016_HDMI_FC_CH2PREAM 0x8046
+#define R_1018_HDMI_FC_GCP 0x4044
+#define R_1019_HDMI_FC_AVICONF0 0x4045
+#define HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
+#define R_101a_HDMI_FC_AVICONF1 0xc044
+#define R_101b_HDMI_FC_AVICONF2 0xc045
+#define R_101c_HDMI_FC_AVIVID 0x4046
+#define R_1025_HDMI_FC_AUDICONF0 0x2043
+#define R_1026_HDMI_FC_AUDICONF1 0xa042
+#define R_1027_HDMI_FC_AUDICONF2 0xa043
+#define R_1028_HDMI_FC_AUDICONF3 0x6040
+#define R_1029_HDMI_FC_VSDIEEEID0 0x6041
+#define R_1030_HDMI_FC_VSDIEEEID1 0x2044
+#define R_1031_HDMI_FC_VSDIEEEID2 0x2045
+#define R_1032_HDMI_FC_VSDPAYLOAD0 0xa044
+#define R_1033_HDMI_FC_VSDPAYLOAD1 0xa045
+#define R_1034_HDMI_FC_VSDPAYLOAD2 0x2046
+#define R_1063_HDMI_FC_AUDSCONF 0xa049
+#define R_1065_HDMI_FC_AUDSV 0x204b
+#define R_1066_HDMI_FC_AUDSU 0xa04a
+#define R_1067_HDMI_FC_AUDSCHNLS0 0xa04b
+#define HDMI_FC_AUDSCHNLS0_CGMSA 0x30
+#define R_1068_HDMI_FC_AUDSCHNLS1 0x6048
+#define R_1069_HDMI_FC_AUDSCHNLS2 0x6049
+#define R_106a_HDMI_FC_AUDSCHNLS3 0xe048
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH1(v) (v << 4)
+#define R_106b_HDMI_FC_AUDSCHNLS4 0xe049
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH3(v) (v << 4)
+#define R_106c_HDMI_FC_AUDSCHNLS5 0x604a
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH1(v) (v << 4)
+#define R_106d_HDMI_FC_AUDSCHNLS6 0x604b
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH3(v) (v << 4)
+#define R_106e_HDMI_FC_AUDSCHNLS7 0xe04a
+#define R_106f_HDMI_FC_AUDSCHNLS8 0xe04b
+#define HDMI_FC_AUDSCHNLS8_WORDLENGTH(v) (v)
+#define R_10b3_HDMI_FC_DATAUTO0 0xb045
+#define R_10b4_HDMI_FC_DATAUTO1 0x3046
+#define R_10b5_HDMI_FC_DATAUTO2 0x3047
+#define R_10d2_HDMI_FC_MASK0 0x904c
+#define R_10d6_HDMI_FC_MASK1 0x904e
+#define R_10da_HDMI_FC_MASK2 0xd04c
+#define R_10e0_HDMI_FC_PRCONF 0x3048
+#define R_1103_HDMI_FC_GMD_CONF 0x8051
+#define R_1104_HDMI_FC_GMD_HB 0x0052
+#define R_1200_HDMI_FC_DBGFORCE 0x0840
+#define HDMI_FC_DBGFORCE_FORCEAUDIO BIT(4)
+#define HDMI_FC_DBGFORCE_FORCEVIDEO BIT(0)
+#define R_1219_HDMI_FC_DBGTMDS0 0x4845
+
+/* HDMI Source PHY Registers */
+#define R_3000_HDMI_PHY_CONF0 0x0240
+#define HDMI_PHY_CONF0_PDZ BIT(7)
+#define HDMI_PHY_CONF0_ENTMDS BIT(6)
+#define HDMI_PHY_CONF0_SPARECTRL BIT(5)
+#define HDMI_PHY_CONF0_GEN2_PDDQ BIT(4)
+#define HDMI_PHY_CONF0_GEN2_TXPWRON BIT(3)
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE BIT(2)
+#define HDMI_PHY_CONF0_SELDATAENPOL BIT(1)
+#define HDMI_PHY_CONF0_SELDIPIF BIT(0)
+#define R_3001_HDMI_PHY_TST0 0x0241
+#define HDMI_PHY_TST0_TSTCLR BIT(5)
+#define R_3005_HDMI_PHY_INT0 0x0243
+#define R_3006_HDMI_PHY_MASK0 0x8242
+
+/* HDMI Master PHY Registers */
+#define R_3020_HDMI_PHY_I2CM_SLAVE_ADDR 0x2240
+#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR 0x2241
+#define R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR 0xa240
+#define R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR 0xa241
+#define R_3026_HDMI_PHY_I2CM_OPERATION_ADDR 0xa242
+#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define R_3027_HDMI_PHY_I2CM_INT_ADDR 0xa243
+#define R_3028_HDMI_PHY_I2CM_CTLINT_ADDR 0x6240
+
+/* Audio Sampler Registers */
+#define R_3100_HDMI_AUD_CONF0 0x0250
+#define R_3101_HDMI_AUD_CONF1 0x0251
+#define R_3102_HDMI_AUD_INT 0x8250
+#define R_3103_HDMI_AUD_CONF2 0x8251
+#define R_3200_HDMI_AUD_N1 0x0a40
+#define R_3201_HDMI_AUD_N2 0x0a41
+#define R_3202_HDMI_AUD_N3 0x8a40
+#define R_3205_HDMI_AUD_CTS3 0x0a43
+#define R_3206_HDMI_AUD_INPUTCLKFS 0x8a42
+#define R_3302_HDMI_AUD_SPDIFINT 0x8a50
+
+/* Generic Parallel Audio Interface Registers */
+#define R_3506_HDMI_GP_POL 0x8272
+
+/* Main Controller Registers */
+#define R_4001_HDMI_MC_CLKDIS 0x0081
+#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE BIT(6)
+#define HDMI_MC_CLKDIS_AUDCLK_DISABLE BIT(3)
+#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE BIT(1)
+#define R_4002_HDMI_MC_SWRSTZ 0x8080
+#define R_4004_HDMI_MC_FLOWCTRL 0x0082
+#define R_4005_HDMI_MC_PHYRSTZ 0x0083
+#define HDMI_MC_PHYRSTZ_DEASSERT BIT(0)
+
+/* HDCP Encryption Engine Registers */
+#define R_5000_HDMI_A_HDCPCFG0 0x00c0
+#define R_5001_HDMI_A_HDCPCFG1 0x00c1
+#define HDMI_A_HDCPCFG1_PH2UPSHFTENC BIT(2)
+#define HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE BIT(1)
+#define HDMI_A_HDCPCFG1_SWRESET BIT(0)
+#define R_5008_HDMI_A_APIINTMSK 0x40c0
+#define R_5009_HDMI_A_VIDPOLCFG 0x40c1
+#define HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH BIT(4)
+
+/* CEC Engine Registers */
+#define R_7d02_HDMI_CEC_MASK 0x86f0
+
+/* I2C Master Registers (E-DDC) */
+#define R_7e00_HDMI_I2CM_SLAVE 0x0ee0
+#define R_7e01_HDMI_I2CM_ADDRESS 0x0ee1
+#define R_7e03_HDMI_I2CM_DATAI 0x8ee1
+#define R_7e04_HDMI_I2CM_OPERATION 0x0ee2
+#define HDMI_I2CM_OPERATION_DDC_READ 0x02
+#define R_7e05_HDMI_I2CM_INT 0x0ee3
+#define R_7e06_HDMI_I2CM_CTLINT 0x8ee2
+#define R_7e07_HDMI_I2CM_DIV 0x8ee3
+#define R_7e08_HDMI_I2CM_SEGADDR 0x4ee0
+#define R_7e09_HDMI_I2CM_SOFTRSTZ 0x4ee1
+#define R_7e0a_HDMI_I2CM_SEGPTR 0xcee0
+#define R_7e0b_HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0xcee1
+#define R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0d_HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x4ee3
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+#define R_7e0f_HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0xcee3
+#define R_7e10_HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x0ee4
+#define R_7e11_HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x0ee5
+
+/*
+ * [0] = vic (CEA Video ID)
+ * [1] used in hdmi_phy_set / hdmi_io_audio
+ * [2..16] used in hdmi_io_video_mode
+ */
+static const struct para_tab {
+ u32 para[17];
+} ptbl[] = {
+ {{ 6, 1, 1, 1, 5, 3, 0, 1, 4, 0, 0, 160, 20, 38, 124, 240, 22}},
+ {{ 21, 11, 1, 1, 5, 3, 1, 1, 2, 0, 0, 160, 32, 24, 126, 32, 24}},
+ {{ 2, 11, 0, 0, 2, 6, 1, 0, 9, 0, 0, 208, 138, 16, 62, 224, 45}},
+ {{ 17, 11, 0, 0, 2, 5, 2, 0, 5, 0, 0, 208, 144, 12, 64, 64, 49}},
+ {{ 19, 4, 0, 0x60, 5, 5, 2, 2, 5, 1, 0, 0, 188, 184, 40, 208, 30}},
+ {{ 4, 4, 0, 0x60, 5, 5, 2, 1, 5, 0, 0, 0, 114, 110, 40, 208, 30}},
+ {{ 20, 4, 0, 0x61, 7, 5, 4, 2, 2, 2, 0, 128, 208, 16, 44, 56, 22}},
+ {{ 5, 4, 0, 0x60, 7, 5, 4, 1, 2, 0, 0, 128, 24, 88, 44, 56, 22}},
+ {{ 31, 2, 0, 0x60, 7, 5, 4, 2, 4, 2, 0, 128, 208, 16, 44, 56, 45}},
+ {{ 16, 2, 0, 0x60, 7, 5, 4, 1, 4, 0, 0, 128, 24, 88, 44, 56, 45}},
+ {{ 32, 4, 0, 0x60, 7, 5, 4, 3, 4, 2, 0, 128, 62, 126, 44, 56, 45}},
+ {{ 33, 4, 0, 0, 7, 5, 4, 2, 4, 2, 0, 128, 208, 16, 44, 56, 45}},
+ {{ 34, 4, 0, 0, 7, 5, 4, 1, 4, 0, 0, 128, 24, 88, 44, 56, 45}},
+#if 0
+ {{160, 2, 0, 0x60, 7, 5, 8, 3, 4, 1, 0, 128, 62, 126, 44, 157, 45}},
+ {{147, 2, 0, 0x60, 5, 5, 5, 2, 5, 1, 0, 0, 188, 184, 40, 190, 30}},
+ {{132, 2, 0, 0x60, 5, 5, 5, 1, 5, 0, 0, 0, 114, 110, 40, 160, 30}},
+ {{257, 1, 0, 0x60, 15, 10, 8, 2, 8, 0, 0, 0, 48, 176, 88, 112, 90}},
+ {{258, 1, 0, 0x60, 15, 10, 8, 5, 8, 4, 0, 0, 160, 32, 88, 112, 90}},
+#endif
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)
+/* HDMI_FC_AUDSCHNLS7 values */
+static const struct pcm_sf {
+ u32 sf;
+ unsigned char cs_sf;
+} sf[] = {
+ {44100, 0x00},
+ {48000, 0x02},
+ {96000, 0x0a},
+ {192000,0x0e},
+ {22050, 0x04},
+ {24000, 0x06},
+ {32000, 0x03},
+ {88200, 0x08},
+ {768000,0x09},
+ {176400,0x0c},
+};
+
+static const struct {
+ int rate;
+ unsigned short n1, n2;
+} n_table[] = {
+ {32000, 3072, 4096},
+ {44100, 4704, 6272},
+ {88200, 4704*2, 6272*2},
+ {176400,4704*4, 6272*4},
+ {48000, 5120, 6144},
+ {96000, 5120*2, 6144*2},
+ {192000,5120*4, 6144*4},
+};
+#endif
+
+static inline void hdmi_writeb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_writel(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(data, priv->mmio + addr);
+}
+
+static inline u8 hdmi_readb(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readb_relaxed(priv->mmio + addr);
+}
+
+static inline u32 hdmi_readl(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readl_relaxed(priv->mmio + addr);
+}
+
+static void hdmi_lock(struct de2_hdmi_priv *priv)
+{
+ hdmi_writeb(priv, 0x10010, 0x45);
+ hdmi_writeb(priv, 0x10011, 0x45);
+ hdmi_writeb(priv, 0x10012, 0x52);
+ hdmi_writeb(priv, 0x10013, 0x54);
+}
+static void hdmi_unlock(struct de2_hdmi_priv *priv)
+{
+ hdmi_writeb(priv, 0x10010, 0x52);
+ hdmi_writeb(priv, 0x10011, 0x54);
+ hdmi_writeb(priv, 0x10012, 0x41);
+ hdmi_writeb(priv, 0x10013, 0x57);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+ u8 clkdis = priv->soc_type == SOC_H3 ?
+ ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+ hdmi_lock(priv);
+
+ /* software reset */
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0x00);
+ udelay(2);
+
+ /* mask all interrupts */
+ hdmi_writeb(priv, R_01ff_HDMI_IH_MUTE, 0x00);
+ hdmi_writeb(priv, R_0807_HDMI_VP_MASK, 0xff);
+ hdmi_writeb(priv, R_10d2_HDMI_FC_MASK0, 0xff);
+ hdmi_writeb(priv, R_10d6_HDMI_FC_MASK1, 0xff);
+ hdmi_writeb(priv, R_10da_HDMI_FC_MASK2, 0xff);
+ hdmi_writeb(priv, R_3102_HDMI_AUD_INT, 0xff);
+ hdmi_writeb(priv, R_3302_HDMI_AUD_SPDIFINT, 0xff);
+ hdmi_writeb(priv, R_3506_HDMI_GP_POL, 0xff);
+ hdmi_writeb(priv, R_5008_HDMI_A_APIINTMSK, 0xff);
+ hdmi_writeb(priv, R_7d02_HDMI_CEC_MASK, 0xff);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0xff);
+ hdmi_writeb(priv, R_7e06_HDMI_I2CM_CTLINT, 0xff);
+
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x1e);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1, 0x00);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE |
+ HDMI_A_HDCPCFG1_SWRESET);
+ hdmi_writeb(priv, R_5000_HDMI_A_HDCPCFG0, 0x00);
+ hdmi_writeb(priv, R_5009_HDMI_A_VIDPOLCFG,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_0100_HDMI_IH_FC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0101_HDMI_IH_FC_STAT1, 0xff);
+ hdmi_writeb(priv, R_0102_HDMI_IH_FC_STAT2, 0xff);
+ hdmi_writeb(priv, R_0103_HDMI_IH_AS_STAT0, 0xff);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, 0xff);
+ hdmi_writeb(priv, R_0106_HDMI_IH_CEC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0107_HDMI_IH_VP_STAT0, 0xff);
+}
+
+static void hdmi_phy_init_a83t(struct de2_hdmi_priv *priv)
+{
+ hdmi_inner_init(priv);
+
+ hdmi_writeb(priv, 0x10000, 0x01);
+ hdmi_writeb(priv, 0x10001, 0x00);
+ hdmi_writeb(priv, 0x10002, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, 0x10003, 0x00);
+ hdmi_writeb(priv, 0x10007, 0xa0);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ,
+ HDMI_MC_PHYRSTZ_DEASSERT);
+ udelay(1);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3006_HDMI_PHY_MASK0, 0xf0);
+ hdmi_writeb(priv, R_3027_HDMI_PHY_I2CM_INT_ADDR, 0xff);
+ hdmi_writeb(priv, R_3028_HDMI_PHY_I2CM_CTLINT_ADDR, 0xff);
+ hdmi_writeb(priv, R_0104_HDMI_IH_PHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_0108_HDMI_IH_I2CMPHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR);
+ hdmi_writeb(priv, R_3020_HDMI_PHY_I2CM_SLAVE_ADDR,
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, 0x00);
+}
+
+static void hdmi_phy_init_h3(struct de2_hdmi_priv *priv)
+{
+ int to_cnt;
+ u32 tmp;
+
+ hdmi_writel(priv, 0x10020, 0);
+ hdmi_writel(priv, 0x10020, 1 << 0);
+ udelay(5);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 16));
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 1));
+ udelay(10);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 2));
+ udelay(5);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 3));
+ usleep_range(40, 50);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 19));
+ usleep_range(100, 120);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 18));
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (7 << 4));
+
+ to_cnt = 10;
+ while (1) {
+ if (hdmi_readl(priv, 0x10038) & 0x80)
+ break;
+ usleep_range(200, 250);
+ if (--to_cnt == 0) {
+ pr_warn("hdmi phy init timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (0xf << 8));
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 7));
+
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x80084343);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | 0x02000000);
+ msleep(100);
+ tmp = hdmi_readl(priv, 0x10038);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ ((tmp >> 11) & 0x3f));
+ hdmi_writel(priv, 0x10020, 0x01ff0f7f);
+ hdmi_writel(priv, 0x10024, 0x80639000);
+ hdmi_writel(priv, 0x10028, 0x0f81c405);
+
+ hdmi_inner_init(priv);
+}
+
+void hdmi_io_init(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_phy_init_h3(priv);
+ else
+ hdmi_phy_init_a83t(priv);
+}
+
+static int get_vid(u32 id)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(ptbl); i++) {
+ if (id == ptbl[i].para[0])
+ return i;
+ }
+
+ return -1;
+}
+
+static void hdmi_i2cm_write(struct de2_hdmi_priv *priv,
+ int addr, u8 valh, u8 vall)
+{
+ hdmi_writeb(priv, R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+ hdmi_writeb(priv, R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR, valh);
+ hdmi_writeb(priv, R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR, vall);
+ hdmi_writeb(priv, R_3026_HDMI_PHY_I2CM_OPERATION_ADDR,
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+ usleep_range(2000, 2500);
+}
+
+static int hdmi_phy_set_a83t(struct de2_hdmi_priv *priv, int i)
+{
+ switch (ptbl[i].para[1]) {
+ case 1:
+ hdmi_i2cm_write(priv, 0x06, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0f);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x2b);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_i2cm_write(priv, 0x06, 0x04, 0xa0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0a);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x21);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x29);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080P */
+ hdmi_i2cm_write(priv, 0x06, 0x05, 0x40);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x05);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x02, 0xb5);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ case 11: /* 480P/576P */
+ hdmi_i2cm_write(priv, 0x06, 0x01,
+ ptbl[i].para[2] ? 0xe3 : 0xe0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x10, 0x08, 0xda);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x03, 0x18);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ default:
+ return -1;
+ }
+ hdmi_i2cm_write(priv, 0x1e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x13, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x17, 0x00, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+
+ return 0;
+}
+
+static int hdmi_phy_set_h3(struct de2_hdmi_priv *priv, int i)
+{
+ u32 tmp;
+
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) & ~0xf000);
+ switch (ptbl[i].para[1]) {
+ case 1:
+ hdmi_writel(priv, 0x1002c, 0x31dc5fc0); /* or 0x30dc5fc0 ? */
+ hdmi_writel(priv, 0x10030, 0x800863c0);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(200);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ if (tmp < 0x3d)
+ tmp += 2;
+ else
+ tmp = 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ msleep(100);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063b000);
+ hdmi_writel(priv, 0x10028, 0x0f8246b5);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x80084381);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063a800);
+ hdmi_writel(priv, 0x10028, 0x0f81c485);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080P */
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x80084343);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063b000);
+ hdmi_writel(priv, 0x10028, 0x0f81c405);
+ break;
+ case 11: /* 480P/576P */
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x8008430a);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063b000);
+ hdmi_writel(priv, 0x10028, 0x0f81c405);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_writel(priv, 0x10020,
+ hdmi_readl(priv, 0x10020) | (0x0f << 12));
+#if 0
+ else
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0);
+#endif
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_writel(priv, 0x10020,
+ hdmi_readl(priv, 0x10020) & ~(0x0f << 12));
+#if 0
+ else
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+#endif
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)
+/* start audio */
+int hdmi_io_audio(struct de2_hdmi_priv *priv,
+ int sample_rate, int sample_bit)
+{
+ int id = get_vid(priv->cea_mode); /* ptbl index */
+ unsigned int i, n;
+
+ if (id < 0)
+ return id;
+
+ /* (2 channels) */
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+
+ hdmi_writeb(priv, R_1065_HDMI_FC_AUDSV, ~0x11);
+
+ hdmi_writeb(priv, R_1066_HDMI_FC_AUDSU, 0x00);
+ hdmi_writeb(priv, R_1067_HDMI_FC_AUDSCHNLS0,
+ HDMI_FC_AUDSCHNLS0_CGMSA);
+ hdmi_writeb(priv, R_1068_HDMI_FC_AUDSCHNLS1, 0x00);
+ hdmi_writeb(priv, R_1069_HDMI_FC_AUDSCHNLS2, 0x01);
+ hdmi_writeb(priv, R_106a_HDMI_FC_AUDSCHNLS3,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH0(2) |
+ HDMI_FC_AUDSCHNLS3_OIEC_CH1(4));
+ hdmi_writeb(priv, R_106b_HDMI_FC_AUDSCHNLS4,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH2(6) |
+ HDMI_FC_AUDSCHNLS4_OIEC_CH3(8));
+ hdmi_writeb(priv, R_106c_HDMI_FC_AUDSCHNLS5,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH0(1) |
+ HDMI_FC_AUDSCHNLS5_OIEC_CH1(3));
+ hdmi_writeb(priv, R_106d_HDMI_FC_AUDSCHNLS6,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH2(5) |
+ HDMI_FC_AUDSCHNLS6_OIEC_CH3(7));
+ hdmi_writeb(priv, R_106e_HDMI_FC_AUDSCHNLS7, 0x01);
+ for (i = 0; i < ARRAY_SIZE(sf); i++) {
+ if (sample_rate == sf[i].sf) {
+ hdmi_writeb(priv, R_106e_HDMI_FC_AUDSCHNLS7,
+ sf[i].cs_sf);
+ break;
+ }
+ }
+ hdmi_writeb(priv, R_106f_HDMI_FC_AUDSCHNLS8,
+ (sample_bit == 16) ? HDMI_FC_AUDSCHNLS8_WORDLENGTH(2) :
+ (sample_bit == 24 ? HDMI_FC_AUDSCHNLS8_WORDLENGTH(11) :
+ HDMI_FC_AUDSCHNLS8_WORDLENGTH(0)));
+
+ hdmi_writeb(priv, R_3101_HDMI_AUD_CONF1, sample_bit);
+
+ n = 6272;
+ for (i = 0; i < ARRAY_SIZE(n_table); i++) {
+ if (sample_rate == n_table[i].rate) {
+ if (ptbl[id].para[1] == 1)
+ n = n_table[i].n1;
+ else
+ n = n_table[i].n2;
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_3200_HDMI_AUD_N1, n);
+ hdmi_writeb(priv, R_3201_HDMI_AUD_N2, n >> 8);
+ hdmi_writeb(priv, R_3202_HDMI_AUD_N3, n >> 16);
+ hdmi_writeb(priv, R_3205_HDMI_AUD_CTS3, 0x00);
+ hdmi_writeb(priv, R_3206_HDMI_AUD_INPUTCLKFS, 0x04);
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0x00); /* layout0 */
+ hdmi_writeb(priv, R_1025_HDMI_FC_AUDICONF0, 2 * 16); /* 2 channels */
+ hdmi_writeb(priv, R_1026_HDMI_FC_AUDICONF1, 0x00);
+ hdmi_writeb(priv, R_1027_HDMI_FC_AUDICONF2, 0x00);
+ hdmi_writeb(priv, R_1028_HDMI_FC_AUDICONF3, 0x00);
+
+ hdmi_writeb(priv, R_3103_HDMI_AUD_CONF2, 0x00); /* PCM only */
+
+ hdmi_writeb(priv, R_3100_HDMI_AUD_CONF0, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE);
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0xf7);
+ usleep_range(100, 120);
+ hdmi_writeb(priv, R_3100_HDMI_AUD_CONF0, 0xaf);
+ usleep_range(100, 120);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ return 0;
+}
+#endif
+
+
+/* initialize */
+int hdmi_io_video_mode(struct de2_hdmi_priv *priv)
+{
+ int i = get_vid(priv->cea_mode); /* ptbl index */
+ u8 avi_d2; /* AVI InfoFrame Data Byte 2 */
+
+ if (i < 0)
+ return i;
+
+ /* colorimetry and aspect ratio */
+ switch (priv->cea_mode) {
+ case 2: /* 480P */
+ case 6: /* 1440x480I */
+ case 17: /* 576P */
+ case 21: /* 1440x576I */
+ avi_d2 = (HDMI_COLORIMETRY_ITU_601 << 6) |
+ (HDMI_PICTURE_ASPECT_4_3 << 4) | 0x08;
+ break;
+ default:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_709 << 6) |
+ (HDMI_PICTURE_ASPECT_16_9 << 4) | 0x08;
+ break;
+ }
+ if (priv->soc_type == SOC_H3) {
+ if (hdmi_phy_set_h3(priv, i) != 0)
+ return -1;
+ hdmi_inner_init(priv);
+ } else {
+ hdmi_io_init(priv);
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+ hdmi_writeb(priv, R_1219_HDMI_FC_DBGTMDS0, 0x00);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF, ptbl[i].para[3] |
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+ hdmi_writeb(priv, 0x10001, ptbl[i].para[3] < 0x60 ? 0x03 : 0x00);
+ hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, ptbl[i].para[4]);
+ hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH, ptbl[i].para[5]);
+ hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, ptbl[i].para[6]);
+ hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, ptbl[i].para[7]);
+ hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY, ptbl[i].para[8]);
+ hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, ptbl[i].para[9]);
+ hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, ptbl[i].para[10]);
+ hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, ptbl[i].para[11]);
+ hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, ptbl[i].para[12]);
+ hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, ptbl[i].para[13]);
+ hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH1, ptbl[i].para[14]);
+ hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, ptbl[i].para[15]);
+ hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK, ptbl[i].para[16]);
+ hdmi_writeb(priv, R_1011_HDMI_FC_CTRLDUR, 12);
+ hdmi_writeb(priv, R_1012_HDMI_FC_EXCTRLDUR, 32);
+ hdmi_writeb(priv, R_1013_HDMI_FC_EXCTRLSPAC, 1);
+ hdmi_writeb(priv, R_1014_HDMI_FC_CH0PREAM, 0x0b);
+ hdmi_writeb(priv, R_1015_HDMI_FC_CH1PREAM, 0x16);
+ hdmi_writeb(priv, R_1016_HDMI_FC_CH2PREAM, 0x21);
+ hdmi_writeb(priv, R_10e0_HDMI_FC_PRCONF, ptbl[i].para[2] ? 0x21 : 0x10);
+ hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD, ptbl[i].para[2] ? 0x41 : 0x40);
+ hdmi_writeb(priv, R_0802_HDMI_VP_STUFF, 0x07);
+ hdmi_writeb(priv, R_0803_HDMI_VP_REMAP, 0x00);
+ hdmi_writeb(priv, R_0804_HDMI_VP_CONF, 0x47);
+ hdmi_writeb(priv, R_0200_HDMI_TX_INVID0, 0x01);
+ hdmi_writeb(priv, R_0201_HDMI_TX_INSTUFFING, 0x07);
+ hdmi_writeb(priv, R_0202_HDMI_TX_GYDATA0, 0x00);
+ hdmi_writeb(priv, R_0203_HDMI_TX_GYDATA1, 0x00);
+ hdmi_writeb(priv, R_0204_HDMI_TX_RCRDATA0, 0x00);
+ hdmi_writeb(priv, R_0205_HDMI_TX_RCRDATA1, 0x00);
+ hdmi_writeb(priv, R_0206_HDMI_TX_BCBDATA0, 0x00);
+ hdmi_writeb(priv, R_0207_HDMI_TX_BCBDATA1, 0x00);
+
+ if (priv->codec.eld) { /* if audio/HDMI */
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x08);
+ hdmi_writeb(priv, R_1031_HDMI_FC_VSDIEEEID2, 0x00);
+ hdmi_writeb(priv, R_1030_HDMI_FC_VSDIEEEID1,
+ HDMI_IEEE_OUI >> 8);
+ hdmi_writeb(priv, R_1029_HDMI_FC_VSDIEEEID0,
+ HDMI_IEEE_OUI & 0xff);
+ hdmi_writeb(priv, R_1032_HDMI_FC_VSDPAYLOAD0, 0x00);
+ hdmi_writeb(priv, R_1033_HDMI_FC_VSDPAYLOAD1, 0x00);
+ hdmi_writeb(priv, R_1034_HDMI_FC_VSDPAYLOAD2, 0x00);
+ hdmi_writeb(priv, R_10b4_HDMI_FC_DATAUTO1, 0x01);
+ hdmi_writeb(priv, R_10b5_HDMI_FC_DATAUTO2, 0x11);
+ hdmi_writeb(priv, R_1018_HDMI_FC_GCP, 0x00);
+ hdmi_writeb(priv, R_1104_HDMI_FC_GMD_HB, 0x00);
+ hdmi_writeb(priv, R_1103_HDMI_FC_GMD_CONF, 0x11);
+
+ hdmi_lock(priv);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF,
+ hdmi_readb(priv, R_1000_HDMI_FC_INVIDCONF) | 0x08);
+ hdmi_unlock(priv);
+
+ /* AVI */
+ hdmi_writeb(priv, R_1019_HDMI_FC_AVICONF0,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN);
+ hdmi_writeb(priv, R_101a_HDMI_FC_AVICONF1, avi_d2);
+ hdmi_writeb(priv, R_101b_HDMI_FC_AVICONF2, 0x08);
+ hdmi_writeb(priv, R_101c_HDMI_FC_AVIVID, priv->cea_mode);
+ }
+
+ hdmi_writeb(priv, R_4004_HDMI_MC_FLOWCTRL, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ if (priv->soc_type != SOC_H3) {
+ if (hdmi_phy_set_a83t(priv, i) != 0)
+ return -1;
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+ return 0;
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char off,
+ int nbyte, char *pbuf)
+{
+ unsigned to_cnt;
+ u8 reg;
+ int ret = 0;
+
+ hdmi_lock(priv);
+ hdmi_writeb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ, 0x00);
+ to_cnt = 50;
+ while (!(hdmi_readb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ) & 0x01)) {
+ udelay(10);
+ if (--to_cnt == 0) { /* wait for 500us for timeout */
+ pr_warn("hdmi ddc reset timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_7e07_HDMI_I2CM_DIV, 0x05);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0x08);
+ hdmi_writeb(priv, R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR, 0xd8);
+ hdmi_writeb(priv, R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR, 0xfe);
+
+ while (nbyte > 0) {
+ hdmi_writeb(priv, R_7e00_HDMI_I2CM_SLAVE, 0xa0 >> 1);
+ hdmi_writeb(priv, R_7e01_HDMI_I2CM_ADDRESS, off);
+ hdmi_writeb(priv, R_7e08_HDMI_I2CM_SEGADDR, 0x60 >> 1);
+ hdmi_writeb(priv, R_7e0a_HDMI_I2CM_SEGPTR, pointer);
+ hdmi_writeb(priv, R_7e04_HDMI_I2CM_OPERATION,
+ HDMI_I2CM_OPERATION_DDC_READ);
+
+ to_cnt = 200; /* timeout 100ms */
+ while (1) {
+ reg = hdmi_readb(priv, R_0105_HDMI_IH_I2CM_STAT0);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, reg);
+ if (reg & 0x02) {
+ *pbuf++ = hdmi_readb(priv, R_7e03_HDMI_I2CM_DATAI);
+ break;
+ }
+ if (reg & 0x01) {
+ pr_warn("hdmi ddc read error\n");
+ ret = -1;
+ break;
+ }
+ if (--to_cnt == 0) {
+ if (!ret) {
+ pr_warn("hdmi ddc read timeout\n");
+ ret = -1;
+ }
+ break;
+ }
+ usleep_range(800, 1000);
+ }
+ if (ret)
+ break;
+ nbyte--;
+ off++;
+ }
+ hdmi_unlock(priv);
+
+ return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+ int ret;
+
+ hdmi_lock(priv);
+
+ if (priv->soc_type == SOC_H3)
+ ret = hdmi_readl(priv, 0x10038) & 0x80000;
+ else
+ ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+ hdmi_unlock(priv);
+
+ return ret != 0;
+}
+
+void hdmi_io_reset(struct de2_hdmi_priv *priv)
+{
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE);
+}
+
+int hdmi_io_mode_valid(int cea_mode)
+{
+ return get_vid(cea_mode);
+}
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi_io.h b/drivers/gpu/drm/sunxi/de2_hdmi_io.h
new file mode 100644
index 0000000..2577f0b
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi_io.h
@@ -0,0 +1,25 @@
+#ifndef __DE2_HDMI_IO_H__
+#define __DE2_HDMI_IO_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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.
+ */
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+int hdmi_io_audio(struct de2_hdmi_priv *priv,
+ int sample_rate, int sample_bit);
+int hdmi_io_video_mode(struct de2_hdmi_priv *priv);
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char offset,
+ int nbyte, char *pbuf);
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv);
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_reset(struct de2_hdmi_priv *priv);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_IO_H__ */
--
2.10.1
^ permalink raw reply related
* [PATCH] kernel: irq: fix build failure
From: Thomas Gleixner @ 2016-10-21 8:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021120020.52b3fa35@canb.auug.org.au>
On Fri, 21 Oct 2016, Stephen Rothwell wrote:
> On Thu, 20 Oct 2016 14:55:45 +0200 (CEST) Thomas Gleixner <tglx@linutronix.de> wrote:
> > I know. This is under discussion with the driver folks as we are not going
> > to blindly export stuff just because someone slapped a irq_set_parent()
> > into the code w/o knowing why.
>
> Do we have any idea if a resolution is close. This was first reported
> in linux-next in September 14/15. :-(
Grr. Yes. As much as I hate it, I'll go and export it for now. Should be
able to get it into rc2.
Thanks,
tglx
^ permalink raw reply
* [PATCH v8 3/3] ARM: dts: imx6qdl-icore: Add FEC support
From: Jagan Teki @ 2016-10-21 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037153-20484-1-git-send-email-jteki@openedev.com>
From: Jagan Teki <jagan@amarulasolutions.com>
Add FEC support for Engicam i.CoreM6 dql modules.
Observed similar 'eth0: link is not ready' issue which was
discussed in [1] due rmii mode with external ref_clk, so added
clock node along with the properties mentioned by Shawn in [2]
FEC link log:
------------
$ ifconfig eth0 up
[ 27.905187] SMSC LAN8710/LAN8720 2188000.ethernet:00: attached PHY driver
[SMSC LAN8710/LAN8720] (mii_bus:phy_addr=2188000.ethernet:00, irq=-1)
[ 27.918982] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[1] https://patchwork.kernel.org/patch/3491061/
[2] https://patchwork.kernel.org/patch/3490511/
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v8:
- s/117/IMX6QDL_CLK_ENET in &fec/clocks
- Add rmii_clk fixed-clock node instead of adding container node like clock
Changes for v7:
- none
Changes for v6:
- none
Changes for v5:
- new patch
arch/arm/boot/dts/imx6qdl-icore.dtsi | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
index 0a1b60a..023839a 100644
--- a/arch/arm/boot/dts/imx6qdl-icore.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -74,6 +74,12 @@
regulator-boot-on;
regulator-always-on;
};
+
+ rmii_clk: clock-rmii-clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>; /* 25MHz for example */
+ };
};
&can1 {
@@ -93,6 +99,15 @@
assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>;
};
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ clocks = <&clks IMX6QDL_CLK_ENET>, <&clks IMX6QDL_CLK_ENET>, <&rmii_clk>;
+ phy-mode = "rmii";
+ status = "okay";
+};
+
&gpmi {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpmi_nand>;
@@ -150,6 +165,22 @@
};
&iomuxc {
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x1b0b1
+ MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0
+ MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1b0b0
+ MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1b0b0
+ MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x1b0b0
+ MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__GPIO1_IO23 0x1b0b0
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
+ >;
+ };
+
pinctrl_flexcan1: flexcan1grp {
fsl,pins = <
MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x1b020
--
2.7.4
^ permalink raw reply related
* [PATCH v8 2/3] ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
From: Jagan Teki @ 2016-10-21 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037153-20484-1-git-send-email-jteki@openedev.com>
From: Jagan Teki <jagan@amarulasolutions.com>
i.CoreM6 DualLite/Solo modules are system on module solutions manufactured
by Engicam with following characteristics:
CPU NXP i.MX6 DL, 800MHz
RAM 1GB, 32, 64 bit, DDR3-800/1066
NAND SLC,512MB
Power supply Single 5V
MAX LCD RES FULLHD
and more info at
http://www.engicam.com/en/products/embedded/som/sodimm/i-core-m6s-dl-d-q
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v8:
- none
Changes for v7:
- none
Changes for v6:
- none
Changes for v5:
- none
Changes for v4:
- new patch
Changes for v3:
- Use compatible as engicam,imx6-icore instead of fsl,imx6-icore
Changes for v2:
- s/oaky/okay/g
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/imx6dl-icore.dts | 59 ++++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6dl-icore.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 89a3eea..8f8b14b 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -340,6 +340,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
imx6dl-gw552x.dtb \
imx6dl-gw553x.dtb \
imx6dl-hummingboard.dtb \
+ imx6dl-icore.dtb \
imx6dl-nit6xlite.dtb \
imx6dl-nitrogen6x.dtb \
imx6dl-phytec-pbab01.dtb \
diff --git a/arch/arm/boot/dts/imx6dl-icore.dts b/arch/arm/boot/dts/imx6dl-icore.dts
new file mode 100644
index 0000000..aec332c
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-icore.dts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "imx6dl.dtsi"
+#include "imx6qdl-icore.dtsi"
+
+/ {
+ model = "Engicam i.CoreM6 DualLite/Solo Starter Kit";
+ compatible = "engicam,imx6-icore", "fsl,imx6dl";
+};
+
+&can1 {
+ status = "okay";
+};
+
+&can2 {
+ status = "okay";
+};
--
2.7.4
^ permalink raw reply related
* [PATCH v8 1/3] ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
From: Jagan Teki @ 2016-10-21 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037153-20484-1-git-send-email-jteki@openedev.com>
From: Jagan Teki <jagan@amarulasolutions.com>
i.CoreM6 Quad/Dual modules are system on module solutions manufactured
by Engicam with following characteristics:
CPU NXP i.MX6 DQ, 800MHz
RAM 1GB, 32, 64 bit, DDR3-800/1066
NAND SLC,512MB
Power supply Single 5V
MAX LCD RES FULLHD
and more info at
http://www.engicam.com/en/products/embedded/som/sodimm/i-core-m6s-dl-d-q
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v8:
- Squash usb host and otg patches
- Hyphen instead of underscore should be used in regulator node
names for usb host and otg patches.
- Followed fixed regulator name as following schema:
reg_xxx: regulator-xxx {
----
};
Changes for v7:
- none
Changes for v6:
- none
Changes for v5:
- removed fsl,legacy-bch-geometry property from gpmi node
Changes for v4:
- new patch
Changes for v3:
- Use compatible as engicam,imx6-icore instead of fsl,imx6-icore
- Update IOMUX value for can1 and can2 nodes
- Added reg_3p3v for can1 and can2 nodes
Changes for v2:
- s/oaky/okay/g
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/imx6q-icore.dts | 59 +++++++++
arch/arm/boot/dts/imx6qdl-icore.dtsi | 234 +++++++++++++++++++++++++++++++++++
3 files changed, 294 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6q-icore.dts
create mode 100644 arch/arm/boot/dts/imx6qdl-icore.dtsi
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index befcd26..89a3eea 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -381,6 +381,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
imx6q-gw553x.dtb \
imx6q-h100.dtb \
imx6q-hummingboard.dtb \
+ imx6q-icore.dtb \
imx6q-icore-rqs.dtb \
imx6q-marsboard.dtb \
imx6q-nitrogen6x.dtb \
diff --git a/arch/arm/boot/dts/imx6q-icore.dts b/arch/arm/boot/dts/imx6q-icore.dts
new file mode 100644
index 0000000..025f543
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-icore.dts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "imx6q.dtsi"
+#include "imx6qdl-icore.dtsi"
+
+/ {
+ model = "Engicam i.CoreM6 Quad/Dual Starter Kit";
+ compatible = "engicam,imx6-icore", "fsl,imx6q";
+};
+
+&can1 {
+ status = "okay";
+};
+
+&can2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
new file mode 100644
index 0000000..0a1b60a
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ memory {
+ reg = <0x10000000 0x80000000>;
+ };
+
+ reg_3p3v: regulator-3p3v {
+ compatible = "regulator-fixed";
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ reg_usb_h1_vbus: regulator-usb-h1-vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb_h1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator-usb-otg-vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+};
+
+&can1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_flexcan1>;
+ xceiver-supply = <®_3p3v>;
+};
+
+&can2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_flexcan2>;
+ xceiver-supply = <®_3p3v>;
+};
+
+&clks {
+ assigned-clocks = <&clks IMX6QDL_CLK_LVDS2_SEL>;
+ assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>;
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ nand-on-flash-bbt;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+};
+
+&uart4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart4>;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <®_usb_h1_vbus>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <®_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usdhc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc1>;
+ cd-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
+ no-1-8-v;
+ status = "okay";
+};
+
+&iomuxc {
+ pinctrl_flexcan1: flexcan1grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x1b020
+ MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x1b020
+ >;
+ };
+
+ pinctrl_flexcan2: flexcan2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x1b020
+ MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x1b020
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpmi-nand {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0
+ >;
+ };
+
+ pinctrl_uart4: uart4grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc1: usdhc1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17070
+ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10070
+ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17070
+ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17070
+ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17070
+ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17070
+ >;
+ };
+};
--
2.7.4
^ permalink raw reply related
* [PATCH v8 0/3] ARM: dts: imx6q: Add Engicam i.CoreM6 dts
From: Jagan Teki @ 2016-10-21 8:05 UTC (permalink / raw)
To: linux-arm-kernel
From: Jagan Teki <jagan@amarulasolutions.com>
This is series add dts support for Engicam I.Core M6 qdl modules. just
rebased on top of linux-next.
Jagan Teki (3):
ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
ARM: dts: imx6qdl-icore: Add FEC support
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/imx6dl-icore.dts | 59 ++++++++
arch/arm/boot/dts/imx6q-icore.dts | 59 ++++++++
arch/arm/boot/dts/imx6qdl-icore.dtsi | 265 +++++++++++++++++++++++++++++++++++
4 files changed, 385 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6dl-icore.dts
create mode 100644 arch/arm/boot/dts/imx6q-icore.dts
create mode 100644 arch/arm/boot/dts/imx6qdl-icore.dtsi
--
2.7.4
^ permalink raw reply
* [PATCH v7 0/5] ARM: dts: imx6q: Add Engicam i.CoreM6 dts
From: Jagan Teki @ 2016-10-21 7:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021013546.GF30578@tiger>
On Fri, Oct 21, 2016 at 7:05 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> On Tue, Oct 18, 2016 at 05:30:28PM +0530, Jagan Teki wrote:
>> Hi Shawn,
>>
>> On Fri, Oct 14, 2016 at 2:57 PM, Jagan Teki <jteki@openedev.com> wrote:
>> > This is series add dts support for Engicam I.Core M6 qdl modules. just
>> > rebased on top of linux-next of previous set[1].
>> >
>> > [1] http://www.spinics.net/lists/kernel/msg2358233.html
>> >
>> > Jagan Teki (5):
>> > ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
>> > ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
>> > ARM: dts: imx6qdl-icore: Add usbhost support
>> > ARM: dts: imx6qdl-icore: Add usbotg support
>> > ARM: dts: imx6qdl-icore: Add FEC support
>> >
>> > arch/arm/boot/dts/Makefile | 2 +
>> > arch/arm/boot/dts/imx6dl-icore.dts | 59 ++++++++
>> > arch/arm/boot/dts/imx6q-icore.dts | 59 ++++++++
>> > arch/arm/boot/dts/imx6qdl-icore.dtsi | 271 +++++++++++++++++++++++++++++++++++
>> > 4 files changed, 391 insertions(+)
>> > create mode 100644 arch/arm/boot/dts/imx6dl-icore.dts
>> > create mode 100644 arch/arm/boot/dts/imx6q-icore.dts
>> > create mode 100644 arch/arm/boot/dts/imx6qdl-icore.dtsi
>>
>> Can you pick this series?
>
> Sorry for the delay. The first two patches look good, and I had a
> couple of small comments on the other 3. Also, for the initial board
> support submission like this, you do not need to split it per device
> support. That said, please squash the series into one patch when
> resending.
Sent v8 by squash the usb host and otg with first patch and second and
third one keep separated as one is new board dts and other one though
it is FEC support it also have a rmii_clk addition which is kind of
new compared to other qdl dtsi files. So please let me know if have
any more comments.
thanks!
--
Jagan Teki
Free Software Engineer | www.openedev.com
U-Boot, Linux | Upstream Maintainer
Hyderabad, India.
^ permalink raw reply
* [PATCH] clk: rockchip: add 533.25MHz to rk3399 clock rates table
From: Heiko Stuebner @ 2016-10-21 7:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477022620-8143-1-git-send-email-zhengxing@rock-chips.com>
Am Freitag, 21. Oktober 2016, 12:03:40 CEST schrieb Xing Zheng:
> We need to get the accurate 533.25MHz for the DP display.
>
> Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
applied to my clk-branch for 4.10
Thanks
Heiko
^ permalink raw reply
* [PATCH v5 2/7] ASoC: sunxi: Add a simple HDMI CODEC
From: Jean-Francois Moine @ 2016-10-21 7:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1477142934.git.moinejf@free.fr>
Allwinner's SoCs include support for both audio and video on HDMI.
This patch defines a simple audio CODEC which may be used in sunxi
HDMI video drivers.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
include/sound/sunxi_hdmi.h | 23 +++++++++
sound/soc/codecs/Kconfig | 9 ++++
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/sunxi-hdmi.c | 106 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 140 insertions(+)
create mode 100644 include/sound/sunxi_hdmi.h
create mode 100644 sound/soc/codecs/sunxi-hdmi.c
diff --git a/include/sound/sunxi_hdmi.h b/include/sound/sunxi_hdmi.h
new file mode 100644
index 0000000..0986bb9
--- /dev/null
+++ b/include/sound/sunxi_hdmi.h
@@ -0,0 +1,23 @@
+#ifndef __SUNXI_HDMI_H__
+#define __SUNXI_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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.
+ */
+
+struct sunxi_hdmi_codec {
+ u8 *eld;
+ int (*set_audio_input)(struct device *dev,
+ int enable,
+ unsigned sample_rate,
+ unsigned sample_bit);
+};
+
+int sunxi_hdmi_codec_register(struct device *dev);
+void sunxi_hdmi_codec_unregister(struct device *dev);
+
+#endif /* __SUNXI_HDMI_H__ */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c67667b..53385b1 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -131,6 +131,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_STA529 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_STI_SAS
+ select SND_SOC_SUNXI_HDMI
select SND_SOC_TAS2552 if I2C
select SND_SOC_TAS5086 if I2C
select SND_SOC_TAS571X if I2C
@@ -793,6 +794,14 @@ config SND_SOC_STAC9766
config SND_SOC_STI_SAS
tristate "codec Audio support for STI SAS codec"
+config SND_SOC_SUNXI_HDMI
+ tristate "Allwinner sunxi HDMI Support"
+ default m if DRM_SUNXI_DE2_HDMI=m
+ default y if DRM_SUNXI_DE2_HDMI=y
+ select SND_PCM_ELD
+ help
+ Enable HDMI audio output
+
config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 958cd49..35804eb 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -139,6 +139,7 @@ snd-soc-sta350-objs := sta350.o
snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-sti-sas-objs := sti-sas.o
+snd-soc-sunxi-hdmi-objs := sunxi-hdmi.o
snd-soc-tas5086-objs := tas5086.o
snd-soc-tas571x-objs := tas571x.o
snd-soc-tas5720-objs := tas5720.o
@@ -359,6 +360,7 @@ obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
+obj-$(CONFIG_SND_SOC_SUNXI_HDMI) += snd-soc-sunxi-hdmi.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
diff --git a/sound/soc/codecs/sunxi-hdmi.c b/sound/soc/codecs/sunxi-hdmi.c
new file mode 100644
index 0000000..0d08676
--- /dev/null
+++ b/sound/soc/codecs/sunxi-hdmi.c
@@ -0,0 +1,106 @@
+/*
+ * Allwinner HDMI codec
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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 <linux/module.h>
+#include <sound/soc.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/pcm_params.h>
+
+#include "sound/sunxi_hdmi.h"
+
+#define SUNXI_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int sunxi_hdmi_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sunxi_hdmi_codec *priv = dev_get_drvdata(dai->dev);
+ u8 *eld;
+
+ eld = priv->eld;
+ if (!eld)
+ return -ENODEV;
+
+ return snd_pcm_hw_constraint_eld(runtime, eld);
+}
+
+static int sunxi_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sunxi_hdmi_codec *priv = dev_get_drvdata(dai->dev);
+ unsigned sample_bit;
+
+ if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+ sample_bit = 16;
+ else
+ sample_bit = 24;
+
+ return priv->set_audio_input(dai->dev, true,
+ params_rate(params),
+ sample_bit);
+}
+
+static void sunxi_hdmi_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sunxi_hdmi_codec *priv = dev_get_drvdata(dai->dev);
+
+ priv->set_audio_input(dai->dev, false, 0, 0);
+}
+
+static const struct snd_soc_dai_ops sunxi_hdmi_codec_ops = {
+ .startup = sunxi_hdmi_codec_startup,
+ .hw_params = sunxi_hdmi_hw_params,
+ .shutdown = sunxi_hdmi_codec_shutdown,
+};
+
+static struct snd_soc_dai_driver sunxi_hdmi_codec = {
+ .name = "hdmi", /* must be the name of the node in the DT */
+ .playback = {
+ .stream_name = "HDMI Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .formats = SUNXI_HDMI_FORMATS,
+ },
+ .ops = &sunxi_hdmi_codec_ops,
+};
+
+static const struct snd_soc_codec_driver sunxi_hdmi_codec_drv = {
+ .ignore_pmdown_time = true,
+};
+
+/* functions called from the HDMI video driver */
+int sunxi_hdmi_codec_register(struct device *dev)
+{
+ return snd_soc_register_codec(dev, &sunxi_hdmi_codec_drv,
+ &sunxi_hdmi_codec, 1);
+}
+EXPORT_SYMBOL_GPL(sunxi_hdmi_codec_register);
+
+void sunxi_hdmi_codec_unregister(struct device *dev)
+{
+ snd_soc_unregister_codec(dev);
+}
+EXPORT_SYMBOL_GPL(sunxi_hdmi_codec_unregister);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner HDMI CODEC");
+MODULE_LICENSE("GPL");
--
2.10.1
^ permalink raw reply related
* [PATCH v5 1/7] drm: sunxi: Add a basic DRM driver for Allwinner DE2
From: Jean-Francois Moine @ 2016-10-21 7:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1477142934.git.moinejf@free.fr>
Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
engine, DE2.
This patch adds a DRM video driver for this device.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
.../bindings/display/sunxi/sunxi-de2.txt | 83 +++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/sunxi/Kconfig | 21 +
drivers/gpu/drm/sunxi/Makefile | 7 +
drivers/gpu/drm/sunxi/de2_crtc.c | 475 +++++++++++++++++
drivers/gpu/drm/sunxi/de2_crtc.h | 63 +++
drivers/gpu/drm/sunxi/de2_de.c | 591 +++++++++++++++++++++
drivers/gpu/drm/sunxi/de2_drm.h | 47 ++
drivers/gpu/drm/sunxi/de2_drv.c | 378 +++++++++++++
drivers/gpu/drm/sunxi/de2_plane.c | 119 +++++
11 files changed, 1787 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
create mode 100644 drivers/gpu/drm/sunxi/Kconfig
create mode 100644 drivers/gpu/drm/sunxi/Makefile
create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
diff --git a/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
new file mode 100644
index 0000000..f9cd67a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
@@ -0,0 +1,83 @@
+Allwinner sunxi Display Engine 2 subsystem
+==========================================
+
+The sunxi DE2 subsystem contains a display controller (DE2),
+one or two LCD controllers (TCON) and their external interfaces.
+
+Display controller
+==================
+
+Required properties:
+
+- compatible: value should be one of the following
+ "allwinner,sun8i-a83t-display-engine"
+ "allwinner,sun8i-h3-display-engine"
+
+- clocks: must include clock specifiers corresponding to entries in the
+ clock-names property.
+
+- clock-names: must contain
+ "gate": for DE activation
+ "clock": DE clock
+
+- resets: phandle to the reset of the device
+
+- ports: phandle's to the LCD ports
+
+LCD controller
+==============
+
+Required properties:
+
+- compatible: value should be one of the following
+ "allwinner,sun8i-a83t-lcd"
+
+- clocks: must include clock specifiers corresponding to entries in the
+ clock-names property.
+
+- clock-names: must contain
+ "gate": for LCD activation
+ "clock": pixel clock
+
+- resets: phandle to the reset of the device
+
+- port: port node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+
+ de: de-controller at 01000000 {
+ compatible = "allwinner,sun8i-h3-display-engine";
+ ...
+ clocks = <&&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
+ clock-names = "gate", "clock";
+ resets = <&ccu RST_BUS_DE>;
+ ports = <&lcd0_p>;
+ };
+
+ lcd0: lcd-controller at 01c0c000 {
+ compatible = "allwinner,sun8i-a83t-lcd";
+ ...
+ clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
+ clock-names = "gate", "clock";
+ resets = <&ccu RST_BUS_TCON0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ lcd0_p: port {
+ lcd0_ep: endpoint {
+ remote-endpoint = <&hdmi_ep>;
+ };
+ };
+ };
+
+ hdmi: hdmi at 01ee0000 {
+ ...
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port {
+ type = "video";
+ hdmi_ep: endpoint {
+ remote-endpoint = <&lcd0_ep>;
+ };
+ };
+ };
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 483059a..afd576f 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -187,6 +187,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
source "drivers/gpu/drm/sun4i/Kconfig"
+source "drivers/gpu/drm/sunxi/Kconfig"
+
source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 25c7204..120d0bf 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-y += omapdrm/
obj-$(CONFIG_DRM_SUN4I) += sun4i/
+obj-$(CONFIG_DRM_SUNXI) += sunxi/
obj-y += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
diff --git a/drivers/gpu/drm/sunxi/Kconfig b/drivers/gpu/drm/sunxi/Kconfig
new file mode 100644
index 0000000..56bde2e
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/Kconfig
@@ -0,0 +1,21 @@
+#
+# Allwinner Video configuration
+#
+
+config DRM_SUNXI
+ tristate "DRM Support for Allwinner Video"
+ depends on DRM && OF
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select DRM_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_GEM_CMA_HELPER
+ help
+ Choose this option if you have a Allwinner chipset.
+
+config DRM_SUNXI_DE2
+ tristate "Support for Allwinner Video with DE2 interface"
+ depends on DRM_SUNXI
+ help
+ Choose this option if your Allwinner chipset has the DE2 interface
+ as the A64, A83T and H3. If M is selected the module will be called
+ sunxi-de2-drm.
diff --git a/drivers/gpu/drm/sunxi/Makefile b/drivers/gpu/drm/sunxi/Makefile
new file mode 100644
index 0000000..62220cb
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Allwinner's sun8i DRM device driver
+#
+
+sunxi-de2-drm-objs := de2_drv.o de2_de.o de2_crtc.o de2_plane.o
+
+obj-$(CONFIG_DRM_SUNXI_DE2) += sunxi-de2-drm.o
diff --git a/drivers/gpu/drm/sunxi/de2_crtc.c b/drivers/gpu/drm/sunxi/de2_crtc.c
new file mode 100644
index 0000000..dae0fab
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_crtc.c
@@ -0,0 +1,475 @@
+/*
+ * Allwinner DRM driver - DE2 CRTC
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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 <linux/component.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <asm/io.h>
+#include <linux/of_irq.h>
+
+#include "de2_drm.h"
+#include "de2_crtc.h"
+
+/* I/O map */
+
+struct tcon {
+ u32 gctl;
+#define TCON_GCTL_TCON_En BIT(31)
+ u32 gint0;
+#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)
+#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
+ u32 gint1;
+ u32 dum0[13];
+ u32 tcon0_ctl; /* 0x40 */
+#define TCON0_CTL_TCON_En BIT(31)
+ u32 dum1[19];
+ u32 tcon1_ctl; /* 0x90 */
+#define TCON1_CTL_TCON_En BIT(31)
+#define TCON1_CTL_Interlace_En BIT(20)
+#define TCON1_CTL_Start_Delay_SHIFT 4
+#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
+ u32 basic0; /* XI/YI */
+ u32 basic1; /* LS_XO/LS_YO */
+ u32 basic2; /* XO/YO */
+ u32 basic3; /* HT/HBP */
+ u32 basic4; /* VT/VBP */
+ u32 basic5; /* HSPW/VSPW */
+ u32 dum2;
+ u32 ps_sync; /* 0xb0 */
+ u32 dum3[15];
+ u32 io_pol; /* 0xf0 */
+#define TCON1_IO_POL_IO0_inv BIT(24)
+#define TCON1_IO_POL_IO1_inv BIT(25)
+#define TCON1_IO_POL_IO2_inv BIT(26)
+ u32 io_tri;
+ u32 dum4[2];
+
+ u32 ceu_ctl; /* 0x100 */
+#define TCON_CEU_CTL_ceu_en BIT(31)
+ u32 dum5[3];
+ u32 ceu_rr;
+ u32 ceu_rg;
+ u32 ceu_rb;
+ u32 ceu_rc;
+ u32 ceu_gr;
+ u32 ceu_gg;
+ u32 ceu_gb;
+ u32 ceu_gc;
+ u32 ceu_br;
+ u32 ceu_bg;
+ u32 ceu_bb;
+ u32 ceu_bc;
+ u32 ceu_rv;
+ u32 ceu_gv;
+ u32 ceu_bv;
+ u32 dum6[45];
+
+ u32 mux_ctl; /* 0x200 */
+ u32 dum7[63];
+
+ u32 fill_ctl; /* 0x300 */
+ u32 fill_start0;
+ u32 fill_end0;
+ u32 fill_data0;
+};
+
+#define XY(x, y) (((x) << 16) | (y))
+
+#define tcon_read(base, member) \
+ readl_relaxed(base + offsetof(struct tcon, member))
+#define tcon_write(base, member, data) \
+ writel_relaxed(data, base + offsetof(struct tcon, member))
+
+/* vertical blank functions */
+static void de2_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (event) {
+ crtc->state->event = NULL;
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+static irqreturn_t de2_lcd_irq(int irq, void *dev_id)
+{
+ struct lcd *lcd = (struct lcd *) dev_id;
+ u32 isr;
+
+ isr = tcon_read(lcd->mmio, gint0);
+
+ drm_crtc_handle_vblank(&lcd->crtc);
+
+ tcon_write(lcd->mmio, gint0, isr & ~TCON_GINT0_TCON1_Vb_Int_Flag);
+
+ return IRQ_HANDLED;
+}
+
+int de2_enable_vblank(struct drm_device *drm, unsigned crtc)
+{
+ struct priv *priv = drm->dev_private;
+ struct lcd *lcd = priv->lcds[crtc];
+
+ tcon_write(lcd->mmio, gint0,
+ tcon_read(lcd->mmio, gint0) |
+ TCON_GINT0_TCON1_Vb_Int_En);
+ return 0;
+}
+
+void de2_disable_vblank(struct drm_device *drm, unsigned crtc)
+{
+ struct priv *priv = drm->dev_private;
+ struct lcd *lcd = priv->lcds[crtc];
+
+ tcon_write(lcd->mmio, gint0,
+ tcon_read(lcd->mmio, gint0) &
+ ~TCON_GINT0_TCON1_Vb_Int_En);
+}
+
+/* panel functions */
+static void de2_set_frame_timings(struct lcd *lcd)
+{
+ struct drm_crtc *crtc = &lcd->crtc;
+ const struct drm_display_mode *mode = &crtc->mode;
+ int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;
+ int start_delay;
+ u32 data;
+
+ data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
+ tcon_write(lcd->mmio, basic0, data);
+ tcon_write(lcd->mmio, basic1, data);
+ tcon_write(lcd->mmio, basic2, data);
+ tcon_write(lcd->mmio, basic3,
+ XY(mode->htotal - 1,
+ mode->htotal - mode->hsync_start - 1));
+ tcon_write(lcd->mmio, basic4,
+ XY(mode->vtotal * (3 - interlace),
+ mode->vtotal - mode->vsync_start - 1));
+ tcon_write(lcd->mmio, basic5,
+ XY(mode->hsync_end - mode->hsync_start - 1,
+ mode->vsync_end - mode->vsync_start - 1));
+
+ tcon_write(lcd->mmio, ps_sync, XY(1, 1));
+
+ data = TCON1_IO_POL_IO2_inv;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ data |= TCON1_IO_POL_IO0_inv;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ data |= TCON1_IO_POL_IO1_inv;
+ tcon_write(lcd->mmio, io_pol, data);
+
+ tcon_write(lcd->mmio, ceu_ctl,
+ tcon_read(lcd->mmio, ceu_ctl) & ~TCON_CEU_CTL_ceu_en);
+
+ data = tcon_read(lcd->mmio, tcon1_ctl);
+ if (interlace == 2)
+ data |= TCON1_CTL_Interlace_En;
+ else
+ data &= ~TCON1_CTL_Interlace_En;
+ tcon_write(lcd->mmio, tcon1_ctl, data);
+
+ tcon_write(lcd->mmio, fill_ctl, 0);
+ tcon_write(lcd->mmio, fill_start0, mode->vtotal + 1);
+ tcon_write(lcd->mmio, fill_end0, mode->vtotal);
+ tcon_write(lcd->mmio, fill_data0, 0);
+
+ start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
+ if (start_delay > 31)
+ start_delay = 31;
+ data = tcon_read(lcd->mmio, tcon1_ctl);
+ data &= ~TCON1_CTL_Start_Delay_MASK;
+ data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
+ tcon_write(lcd->mmio, tcon1_ctl, data);
+
+ tcon_write(lcd->mmio, io_tri, 0x0fffffff);
+}
+
+static void de2_crtc_enable(struct drm_crtc *crtc)
+{
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct drm_display_mode *mode = &crtc->mode;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ clk_set_rate(lcd->clk, mode->clock * 1000);
+
+ de2_set_frame_timings(lcd);
+
+ tcon_write(lcd->mmio, tcon1_ctl,
+ tcon_read(lcd->mmio, tcon1_ctl) | TCON1_CTL_TCON_En);
+
+ de2_de_panel_init(lcd->priv, lcd->num, mode);
+
+ drm_mode_debug_printmodeline(mode);
+}
+
+static void de2_crtc_disable(struct drm_crtc *crtc)
+{
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ unsigned long flags;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ tcon_write(lcd->mmio, tcon1_ctl,
+ tcon_read(lcd->mmio, tcon1_ctl) & ~TCON1_CTL_TCON_En);
+
+ if (crtc->state->event && !crtc->state->active) {
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+ crtc->state->event = NULL;
+ }
+}
+
+static const struct drm_crtc_funcs de2_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {
+ .atomic_flush = de2_atomic_flush,
+ .enable = de2_crtc_enable,
+ .disable = de2_crtc_disable,
+};
+
+static void de2_tcon_init(struct lcd *lcd)
+{
+ tcon_write(lcd->mmio, tcon0_ctl,
+ tcon_read(lcd->mmio, tcon0_ctl) & ~TCON0_CTL_TCON_En);
+ tcon_write(lcd->mmio, tcon1_ctl,
+ tcon_read(lcd->mmio, tcon1_ctl) & ~TCON1_CTL_TCON_En);
+ tcon_write(lcd->mmio, gctl,
+ tcon_read(lcd->mmio, gctl) & ~TCON_GCTL_TCON_En);
+
+ /* disable/ack interrupts */
+ tcon_write(lcd->mmio, gint0, 0);
+}
+
+static void de2_tcon_enable(struct lcd *lcd)
+{
+ tcon_write(lcd->mmio, gctl,
+ tcon_read(lcd->mmio, gctl) | TCON_GCTL_TCON_En);
+}
+
+static int de2_crtc_init(struct drm_device *drm, struct lcd *lcd)
+{
+ struct drm_crtc *crtc = &lcd->crtc;
+ int ret;
+
+ ret = de2_plane_init(drm, lcd);
+ if (ret < 0)
+ return ret;
+
+ drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, crtc,
+ &lcd->planes[DE2_PRIMARY_PLANE],
+ &lcd->planes[DE2_CURSOR_PLANE],
+ &de2_crtc_funcs, NULL);
+ if (ret < 0)
+ return ret;
+
+ de2_tcon_enable(lcd);
+
+ de2_de_enable(lcd->priv, lcd->num);
+
+ return 0;
+}
+
+/*
+ * device init
+ */
+static int de2_lcd_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm = data;
+ struct priv *priv = drm->dev_private;
+ struct lcd *lcd = dev_get_drvdata(dev);
+ int ret;
+
+ lcd->priv = priv;
+
+ /* (only 2 LCDs) */
+ lcd->crtc_idx = priv->lcds[0] ? 1 : 0;
+ priv->lcds[lcd->crtc_idx] = lcd;
+
+ ret = de2_crtc_init(drm, lcd);
+ if (ret < 0) {
+ dev_err(dev, "failed to init the crtc\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void de2_lcd_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lcd *lcd = platform_get_drvdata(pdev);
+
+ if (lcd->mmio) {
+ if (lcd->priv)
+ de2_de_disable(lcd->priv, lcd->num);
+ tcon_write(lcd->mmio, gctl,
+ tcon_read(lcd->mmio, gctl) & ~TCON_GCTL_TCON_En);
+ }
+}
+
+static const struct component_ops de2_lcd_ops = {
+ .bind = de2_lcd_bind,
+ .unbind = de2_lcd_unbind,
+};
+
+static int de2_lcd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node, *tmp, *parent, *port;
+ struct lcd *lcd;
+ struct resource *res;
+ int id, irq, ret;
+
+ id = of_alias_get_id(np, "lcd");
+ if (id < 0) {
+ dev_err(dev, "no alias for lcd\n");
+ id = 0;
+ }
+ lcd = devm_kzalloc(dev, sizeof *lcd, GFP_KERNEL);
+ if (!lcd) {
+ dev_err(dev, "failed to allocate private data\n");
+ return -ENOMEM;
+ }
+ dev_set_drvdata(dev, lcd);
+ lcd->dev = dev;
+ lcd->num = id;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -EINVAL;
+ }
+
+ lcd->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lcd->mmio)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(lcd->mmio);
+ }
+
+ snprintf(lcd->name, sizeof(lcd->name), "sunxi-lcd%d", id);
+
+ /* possible CRTCs */
+ parent = np;
+ tmp = of_get_child_by_name(np, "ports");
+ if (tmp)
+ parent = tmp;
+ port = of_get_child_by_name(parent, "port");
+ of_node_put(tmp);
+ if (!port) {
+ dev_err(dev, "no port node\n");
+ return -ENXIO;
+ }
+ lcd->crtc.port = port;
+
+ lcd->gate = devm_clk_get(dev, "gate"); /* optional */
+
+ lcd->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(lcd->clk)) {
+ dev_err(dev, "video clock err %d\n", (int) PTR_ERR(lcd->clk));
+ ret = PTR_ERR(lcd->clk);
+ goto err;
+ }
+
+ lcd->rstc = devm_reset_control_get_optional(dev, NULL);
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0 || irq == NO_IRQ) {
+ dev_err(dev, "unable to get irq lcd %d\n", id);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!IS_ERR(lcd->rstc)) {
+ ret = reset_control_deassert(lcd->rstc);
+ if (ret) {
+ dev_err(dev, "reset deassert err %d\n", ret);
+ goto err;
+ }
+ }
+
+ if (!IS_ERR(lcd->gate)) {
+ ret = clk_prepare_enable(lcd->gate);
+ if (ret)
+ goto err2;
+ }
+
+ ret = clk_prepare_enable(lcd->clk);
+ if (ret)
+ goto err2;
+
+ de2_tcon_init(lcd);
+
+ ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
+ lcd->name, lcd);
+ if (ret < 0) {
+ dev_err(dev, "unable to request irq %d\n", irq);
+ goto err2;
+ }
+
+ return component_add(dev, &de2_lcd_ops);
+
+err2:
+ if (!IS_ERR_OR_NULL(lcd->rstc))
+ reset_control_assert(lcd->rstc);
+ clk_disable_unprepare(lcd->gate);
+ clk_disable_unprepare(lcd->clk);
+err:
+ of_node_put(lcd->crtc.port);
+ return ret;
+}
+
+static int de2_lcd_remove(struct platform_device *pdev)
+{
+ struct lcd *lcd = platform_get_drvdata(pdev);
+
+ component_del(&pdev->dev, &de2_lcd_ops);
+
+ if (!IS_ERR_OR_NULL(lcd->rstc))
+ reset_control_assert(lcd->rstc);
+ clk_disable_unprepare(lcd->gate);
+ clk_disable_unprepare(lcd->clk);
+ of_node_put(lcd->crtc.port);
+
+ return 0;
+}
+
+static const struct of_device_id de2_lcd_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-lcd", },
+ { }
+};
+
+struct platform_driver de2_lcd_platform_driver = {
+ .probe = de2_lcd_probe,
+ .remove = de2_lcd_remove,
+ .driver = {
+ .name = "sunxi-de2-lcd",
+ .of_match_table = of_match_ptr(de2_lcd_ids),
+ },
+};
diff --git a/drivers/gpu/drm/sunxi/de2_crtc.h b/drivers/gpu/drm/sunxi/de2_crtc.h
new file mode 100644
index 0000000..efbe45d
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_crtc.h
@@ -0,0 +1,63 @@
+#ifndef __DE2_CRTC_H__
+#define __DE2_CRTC_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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 <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drm_plane_helper.h>
+
+struct priv;
+
+enum de2_plane2 {
+ DE2_PRIMARY_PLANE,
+ DE2_CURSOR_PLANE,
+ DE2_VI_PLANE,
+ DE2_N_PLANES,
+};
+struct lcd {
+ void __iomem *mmio;
+
+ struct device *dev;
+ struct drm_crtc crtc;
+ struct priv *priv; /* DRM/DE private data */
+
+ short num; /* LCD number in hardware */
+ short crtc_idx; /* CRTC index in drm */
+
+ struct clk *clk;
+ struct clk *gate;
+ struct reset_control *rstc;
+
+ char name[16];
+
+ struct drm_pending_vblank_event *event;
+
+ struct drm_plane planes[DE2_N_PLANES];
+};
+
+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
+
+/* in de2_de.c */
+void de2_de_enable(struct priv *priv, int lcd_num);
+void de2_de_disable(struct priv *priv, int lcd_num);
+void de2_de_hw_init(struct priv *priv, int lcd_num);
+void de2_de_panel_init(struct priv *priv, int lcd_num,
+ struct drm_display_mode *mode);
+void de2_de_plane_disable(struct priv *priv,
+ int lcd_num, int plane_ix);
+void de2_de_plane_update(struct priv *priv,
+ int lcd_num, int plane_ix,
+ struct drm_plane_state *state,
+ struct drm_plane_state *old_state);
+
+/* in de2_plane.c */
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
+
+#endif /* __DE2_CRTC_H__ */
diff --git a/drivers/gpu/drm/sunxi/de2_de.c b/drivers/gpu/drm/sunxi/de2_de.c
new file mode 100644
index 0000000..0d8cb62
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_de.c
@@ -0,0 +1,591 @@
+/*
+ * ALLWINNER DRM driver - Display Engine 2
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * 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 <asm/io.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "de2_drm.h"
+#include "de2_crtc.h"
+
+static DEFINE_SPINLOCK(de_lock);
+
+#define DE_CLK_RATE_A83T 504000000 /* pll-de */
+#define DE_CLK_RATE_H3 432000000 /* de */
+
+/* I/O map */
+
+#define DE_MOD_REG 0x0000 /* 1 bit per LCD */
+#define DE_GATE_REG 0x0004
+#define DE_RESET_REG 0x0008
+#define DE_DIV_REG 0x000c /* 4 bits per LCD */
+#define DE_SEL_REG 0x0010
+
+#define DE_MUX0_BASE 0x00100000
+#define DE_MUX1_BASE 0x00200000
+
+/* MUX registers (addr / MUX base) */
+#define DE_MUX_GLB_REGS 0x00000 /* global control */
+#define DE_MUX_BLD_REGS 0x01000 /* alpha blending */
+#define DE_MUX_CHAN_REGS 0x02000 /* VI/UI overlay channels */
+#define DE_MUX_CHAN_SZ 0x1000 /* size of a channel */
+#define DE_MUX_VSU_REGS 0x20000 /* VSU */
+#define DE_MUX_GSU1_REGS 0x30000 /* GSUs */
+#define DE_MUX_GSU2_REGS 0x40000
+#define DE_MUX_GSU3_REGS 0x50000
+#define DE_MUX_FCE_REGS 0xa0000 /* FCE */
+#define DE_MUX_BWS_REGS 0xa2000 /* BWS */
+#define DE_MUX_LTI_REGS 0xa4000 /* LTI */
+#define DE_MUX_PEAK_REGS 0xa6000 /* PEAK */
+#define DE_MUX_ASE_REGS 0xa8000 /* ASE */
+#define DE_MUX_FCC_REGS 0xaa000 /* FCC */
+#define DE_MUX_DCSC_REGS 0xb0000 /* DCSC/SMBL */
+
+/* global control */
+struct de_glb {
+ u32 ctl;
+#define DE_MUX_GLB_CTL_rt_en BIT(0)
+#define DE_MUX_GLB_CTL_finish_irq_en BIT(4)
+#define DE_MUX_GLB_CTL_rtwb_port BIT(12)
+ u32 status;
+ u32 dbuff;
+ u32 size;
+};
+
+/* alpha blending */
+struct de_bld {
+ u32 fcolor_ctl; /* 00 */
+ struct {
+ u32 fcolor;
+ u32 insize;
+ u32 offset;
+ u32 dum;
+ } attr[4];
+ u32 dum0[15]; /* (end of clear offset) */
+ u32 route; /* 80 */
+ u32 premultiply;
+ u32 bkcolor;
+ u32 output_size;
+ u32 bld_mode[4];
+ u32 dum1[4];
+ u32 ck_ctl; /* b0 */
+ u32 ck_cfg;
+ u32 dum2[2];
+ u32 ck_max[4]; /* c0 */
+ u32 dum3[4];
+ u32 ck_min[4]; /* e0 */
+ u32 dum4[3];
+ u32 out_ctl; /* fc */
+};
+
+/* VI channel */
+struct de_vi {
+ struct {
+ u32 attr;
+#define VI_CFG_ATTR_en BIT(0)
+#define VI_CFG_ATTR_fcolor_en BIT(4)
+#define VI_CFG_ATTR_fmt_SHIFT 8
+#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
+#define VI_CFG_ATTR_ui_sel BIT(15)
+#define VI_CFG_ATTR_top_down BIT(23)
+ u32 size;
+ u32 coord;
+#define VI_N_PLANES 3
+ u32 pitch[VI_N_PLANES];
+ u32 top_laddr[VI_N_PLANES];
+ u32 bot_laddr[VI_N_PLANES];
+ } cfg[4];
+ u32 fcolor[4]; /* c0 */
+ u32 top_haddr[VI_N_PLANES]; /* d0 */
+ u32 bot_haddr[VI_N_PLANES]; /* dc */
+ u32 ovl_size[2]; /* e8 */
+ u32 hori[2]; /* f0 */
+ u32 vert[2]; /* f8 */
+};
+
+/* UI channel */
+struct de_ui {
+ struct {
+ u32 attr;
+#define UI_CFG_ATTR_en BIT(0)
+#define UI_CFG_ATTR_alpmod_SHIFT 1
+#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)
+#define UI_CFG_ATTR_fcolor_en BIT(4)
+#define UI_CFG_ATTR_fmt_SHIFT 8
+#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
+#define UI_CFG_ATTR_top_down BIT(23)
+#define UI_CFG_ATTR_alpha_SHIFT 24
+#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)
+ u32 size;
+ u32 coord;
+ u32 pitch;
+ u32 top_laddr;
+ u32 bot_laddr;
+ u32 fcolor;
+ u32 dum;
+ } cfg[4]; /* 00 */
+ u32 top_haddr; /* 80 */
+ u32 bot_haddr;
+ u32 ovl_size; /* 88 */
+};
+
+/* coordinates and sizes */
+#define XY(x, y) (((y) << 16) | (x))
+#define WH(w, h) (((h - 1) << 16) | (w - 1))
+
+/* UI video formats */
+#define DE2_FORMAT_ARGB_8888 0
+#define DE2_FORMAT_BGRA_8888 3
+#define DE2_FORMAT_XRGB_8888 4
+#define DE2_FORMAT_RGB_888 8
+#define DE2_FORMAT_BGR_888 9
+
+/* VI video formats */
+#define DE2_FORMAT_YUV422_I_YVYU 1 /* Y-V-Y-U */
+#define DE2_FORMAT_YUV422_I_UYVY 2 /* U-Y-V-Y */
+#define DE2_FORMAT_YUV422_I_YUYV 3 /* Y-U-Y-V */
+#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
+#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
+
+#define glb_read(base, member) \
+ readl_relaxed(base + offsetof(struct de_glb, member))
+#define glb_write(base, member, data) \
+ writel_relaxed(data, base + offsetof(struct de_glb, member))
+#define bld_read(base, member) \
+ readl_relaxed(base + offsetof(struct de_bld, member))
+#define bld_write(base, member, data) \
+ writel_relaxed(data, base + offsetof(struct de_bld, member))
+#define ui_read(base, member) \
+ readl_relaxed(base + offsetof(struct de_ui, member))
+#define ui_write(base, member, data) \
+ writel_relaxed(data, base + offsetof(struct de_ui, member))
+#define vi_read(base, member) \
+ readl_relaxed(base + offsetof(struct de_vi, member))
+#define vi_write(base, member, data) \
+ writel_relaxed(data, base + offsetof(struct de_vi, member))
+
+static const struct {
+ char chan;
+ char layer;
+ char pipe;
+} plane2layer[DE2_N_PLANES] = {
+ [DE2_PRIMARY_PLANE] = {0, 0, 0},
+ [DE2_CURSOR_PLANE] = {1, 0, 1},
+ [DE2_VI_PLANE] = {0, 1, 0},
+};
+
+static inline void de_write(struct priv *priv, int reg, u32 data)
+{
+ writel_relaxed(data, priv->mmio + reg);
+}
+
+static inline u32 de_read(struct priv *priv, int reg)
+{
+ return readl_relaxed(priv->mmio + reg);
+}
+
+static void de_lcd_select(struct priv *priv,
+ int lcd_num,
+ void __iomem *mux_o)
+{
+ u32 data;
+
+ /* select the LCD */
+ data = de_read(priv, DE_SEL_REG);
+ data &= ~1;
+ de_write(priv, DE_SEL_REG, data);
+
+ /* double register switch */
+ glb_write(mux_o + DE_MUX_GLB_REGS, dbuff, 1);
+}
+
+void de2_de_plane_update(struct priv *priv,
+ int lcd_num, int plane_ix,
+ struct drm_plane_state *state,
+ struct drm_plane_state *old_state)
+{
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_gem_cma_object *gem;
+ void __iomem *mux_o = priv->mmio;
+ void __iomem *chan_o;
+ u32 size = WH(state->crtc_w, state->crtc_h);
+ u32 coord;
+ u32 screen_size;
+ u32 data, fcolor;
+ u32 ui_sel, alpha_glob;
+ int chan, layer, x, y;
+ unsigned fmt;
+ unsigned long flags;
+
+ chan = plane2layer[plane_ix].chan;
+ layer = plane2layer[plane_ix].layer;
+
+ mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
+ chan_o = mux_o;
+ chan_o += DE_MUX_CHAN_REGS + DE_MUX_CHAN_SZ * chan;
+
+ x = state->crtc_x >= 0 ? state->crtc_x : 0;
+ y = state->crtc_y >= 0 ? state->crtc_y : 0;
+ coord = XY(x, y);
+
+ /* handle the cursor move */
+ if (plane_ix == DE2_CURSOR_PLANE
+ && fb == old_state->fb) {
+ spin_lock_irqsave(&de_lock, flags);
+ de_lcd_select(priv, lcd_num, mux_o);
+ if (chan == 0)
+ vi_write(chan_o, cfg[layer].coord, coord);
+ else
+ ui_write(chan_o, cfg[layer].coord, coord);
+ spin_unlock_irqrestore(&de_lock, flags);
+ return;
+ }
+
+ gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+ ui_sel = alpha_glob = 0;
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_ARGB8888:
+ fmt = DE2_FORMAT_ARGB_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_BGRA8888:
+ fmt = DE2_FORMAT_BGRA_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ fmt = DE2_FORMAT_XRGB_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |
+ (0xff << UI_CFG_ATTR_alpha_SHIFT);
+ break;
+ case DRM_FORMAT_RGB888:
+ fmt = DE2_FORMAT_RGB_888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_BGR888:
+ fmt = DE2_FORMAT_BGR_888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_YUYV:
+ fmt = DE2_FORMAT_YUV422_I_YUYV;
+ break;
+ case DRM_FORMAT_YVYU:
+ fmt = DE2_FORMAT_YUV422_I_YVYU;
+ break;
+ case DRM_FORMAT_YUV422:
+ fmt = DE2_FORMAT_YUV422_P;
+ break;
+ case DRM_FORMAT_YUV420:
+ fmt = DE2_FORMAT_YUV420_P;
+ break;
+ case DRM_FORMAT_UYVY:
+ fmt = DE2_FORMAT_YUV422_I_UYVY;
+ break;
+ default:
+ pr_err("format %.4s not yet treated\n",
+ (char *) &fb->pixel_format);
+ return;
+ }
+
+ spin_lock_irqsave(&de_lock, flags);
+
+ screen_size = plane_ix == DE2_PRIMARY_PLANE ?
+ size :
+ glb_read(mux_o + DE_MUX_GLB_REGS, size);
+
+ /* prepare the activation of alpha blending (1 bit per plane) */
+ fcolor = bld_read(mux_o + DE_MUX_BLD_REGS, fcolor_ctl)
+ | (0x100 << plane2layer[plane_ix].pipe);
+
+ de_lcd_select(priv, lcd_num, mux_o);
+
+ if (chan == 0) { /* VI channel */
+ int i;
+
+ data = VI_CFG_ATTR_en | (fmt << VI_CFG_ATTR_fmt_SHIFT) |
+ ui_sel;
+ vi_write(chan_o, cfg[layer].attr, data);
+ vi_write(chan_o, cfg[layer].size, size);
+ vi_write(chan_o, cfg[layer].coord, coord);
+ for (i = 0; i < VI_N_PLANES; i++) {
+ vi_write(chan_o, cfg[layer].pitch[i],
+ fb->pitches[i] ? fb->pitches[i] :
+ fb->pitches[0]);
+ vi_write(chan_o, cfg[layer].top_laddr[i],
+ gem->paddr + fb->offsets[i]);
+ vi_write(chan_o, fcolor[layer], 0xff000000);
+ }
+ if (layer == 0)
+ vi_write(chan_o, ovl_size[0], screen_size);
+
+ } else { /* UI channel */
+ data = UI_CFG_ATTR_en | (fmt << UI_CFG_ATTR_fmt_SHIFT) |
+ alpha_glob;
+ ui_write(chan_o, cfg[layer].attr, data);
+ ui_write(chan_o, cfg[layer].size, size);
+ ui_write(chan_o, cfg[layer].coord, coord);
+ ui_write(chan_o, cfg[layer].pitch, fb->pitches[0]);
+ ui_write(chan_o, cfg[layer].top_laddr,
+ gem->paddr + fb->offsets[0]);
+ if (layer == 0)
+ ui_write(chan_o, ovl_size, screen_size);
+ }
+ bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl, fcolor);
+
+ spin_unlock_irqrestore(&de_lock, flags);
+}
+
+void de2_de_plane_disable(struct priv *priv,
+ int lcd_num, int plane_ix)
+{
+ void __iomem *mux_o = priv->mmio;
+ void __iomem *chan_o;
+ u32 fcolor;
+ int chan, layer, chan_disable = 0;
+ unsigned long flags;
+
+ chan = plane2layer[plane_ix].chan;
+ layer = plane2layer[plane_ix].layer;
+
+ mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
+ chan_o = mux_o;
+ chan_o += DE_MUX_CHAN_REGS + DE_MUX_CHAN_SZ * chan;
+
+ /* (only 2 layers) */
+ if (chan == 0) {
+ if (vi_read(chan_o, cfg[1 - layer].attr) == 0)
+ chan_disable = 1;
+ } else {
+ if (ui_read(chan_o, cfg[1 - layer].attr) == 0)
+ chan_disable = 1;
+ }
+
+ spin_lock_irqsave(&de_lock, flags);
+
+ fcolor = bld_read(mux_o + DE_MUX_BLD_REGS, fcolor_ctl);
+
+ de_lcd_select(priv, lcd_num, mux_o);
+
+ if (chan == 0)
+ vi_write(chan_o, cfg[layer].attr, 0);
+ else
+ ui_write(chan_o, cfg[layer].attr, 0);
+
+ if (chan_disable)
+ bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl,
+ fcolor & ~(0x100 << plane2layer[plane_ix].pipe));
+
+ spin_unlock_irqrestore(&de_lock, flags);
+}
+
+void de2_de_panel_init(struct priv *priv, int lcd_num,
+ struct drm_display_mode *mode)
+{
+ void __iomem *mux_o = priv->mmio;
+ u32 size = WH(mode->hdisplay, mode->vdisplay);
+ unsigned i;
+ unsigned long flags;
+
+ mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
+
+ DRM_DEBUG_DRIVER("%dx%d\n", mode->hdisplay, mode->vdisplay);
+
+ spin_lock_irqsave(&de_lock, flags);
+
+ de_lcd_select(priv, lcd_num, mux_o);
+
+ glb_write(mux_o + DE_MUX_GLB_REGS, size, size);
+
+ /* set alpha blending */
+ for (i = 0; i < 4; i++) {
+ bld_write(mux_o + DE_MUX_BLD_REGS, attr[i].fcolor, 0xff000000);
+ bld_write(mux_o + DE_MUX_BLD_REGS, attr[i].insize, size);
+ }
+ bld_write(mux_o + DE_MUX_BLD_REGS, output_size, size);
+ bld_write(mux_o + DE_MUX_BLD_REGS, out_ctl,
+ mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0);
+
+ spin_unlock_irqrestore(&de_lock, flags);
+}
+
+void de2_de_enable(struct priv *priv, int lcd_num)
+{
+ void __iomem *mux_o = priv->mmio;
+ unsigned chan, i;
+ u32 size = WH(1920, 1080);
+ u32 data;
+ unsigned long flags;
+
+ DRM_DEBUG_DRIVER("lcd %d\n", lcd_num);
+
+ de_write(priv, DE_RESET_REG,
+ de_read(priv, DE_RESET_REG) |
+ (lcd_num == 0 ? 1 : 4));
+ data = 1 << lcd_num; /* 1 bit / lcd */
+ de_write(priv, DE_GATE_REG,
+ de_read(priv, DE_GATE_REG) | data);
+ de_write(priv, DE_MOD_REG,
+ de_read(priv, DE_MOD_REG) | data);
+
+ mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
+
+ spin_lock_irqsave(&de_lock, flags);
+
+ /* select the LCD */
+ data = de_read(priv, DE_SEL_REG);
+ if (lcd_num == 0)
+ data &= ~1;
+ else
+ data |= 1;
+ de_write(priv, DE_SEL_REG, data);
+
+ /* start init */
+ glb_write(mux_o + DE_MUX_GLB_REGS, ctl,
+ DE_MUX_GLB_CTL_rt_en | DE_MUX_GLB_CTL_rtwb_port);
+ glb_write(mux_o + DE_MUX_GLB_REGS, status, 0);
+ glb_write(mux_o + DE_MUX_GLB_REGS, dbuff, 1); /* dble reg switch */
+ glb_write(mux_o + DE_MUX_GLB_REGS, size, size);
+
+ /* clear the VI/UI channels */
+ for (chan = 0; chan < 4; chan++) {
+ void __iomem *chan_o = mux_o + DE_MUX_CHAN_REGS +
+ DE_MUX_CHAN_SZ * chan;
+
+ memset_io(chan_o, 0, chan == 0 ?
+ sizeof(struct de_vi) : sizeof(struct de_ui));
+
+ /* only 1 VI and 1 UI in lcd1 */
+ if (chan == 2 && lcd_num == 1)
+ break;
+ }
+
+ /* clear and set alpha blending */
+ memset_io(mux_o + DE_MUX_BLD_REGS, 0, offsetof(struct de_bld, dum0));
+ bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl, 0x00000101);
+ /* fcolor for primary */
+
+ /* prepare route for planes */
+ data = 0;
+ for (i = 0; i < DE2_N_PLANES; i++)
+ data |= plane2layer[i].chan << (plane2layer[i].pipe * 4);
+ bld_write(mux_o + DE_MUX_BLD_REGS, route, data);
+
+ bld_write(mux_o + DE_MUX_BLD_REGS, premultiply, 0);
+ bld_write(mux_o + DE_MUX_BLD_REGS, bkcolor, 0xff000000);
+ bld_write(mux_o + DE_MUX_BLD_REGS, bld_mode[0], 0x03010301);
+ /* SRCOVER */
+ bld_write(mux_o + DE_MUX_BLD_REGS, bld_mode[1], 0x03010301);
+ bld_write(mux_o + DE_MUX_BLD_REGS, out_ctl, 0);
+
+ /* disable the enhancements */
+ writel_relaxed(0, mux_o + DE_MUX_VSU_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_GSU1_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_GSU2_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_GSU3_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_FCE_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_BWS_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_LTI_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_PEAK_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_ASE_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_FCC_REGS);
+ writel_relaxed(0, mux_o + DE_MUX_DCSC_REGS);
+
+ spin_unlock_irqrestore(&de_lock, flags);
+}
+
+void de2_de_disable(struct priv *priv, int lcd_num)
+{
+ u32 data;
+
+ data = ~(1 << lcd_num);
+ de_write(priv, DE_MOD_REG,
+ de_read(priv, DE_MOD_REG) & data);
+ de_write(priv, DE_GATE_REG,
+ de_read(priv, DE_GATE_REG) & data);
+ de_write(priv, DE_RESET_REG,
+ de_read(priv, DE_RESET_REG) & data);
+}
+
+int de2_de_init(struct priv *priv, struct device *dev)
+{
+ struct resource *res;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ res = platform_get_resource(to_platform_device(dev),
+ IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -EINVAL;
+ }
+
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(priv->mmio);
+ }
+
+ priv->gate = devm_clk_get(dev, "gate"); /* optional */
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "video clock err %d\n", (int) PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->rstc = devm_reset_control_get_optional(dev, NULL);
+
+ if (!IS_ERR(priv->rstc)) {
+ ret = reset_control_deassert(priv->rstc);
+ if (ret) {
+ dev_err(dev, "reset deassert err %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!IS_ERR(priv->gate)) {
+ ret = clk_prepare_enable(priv->gate);
+ if (ret)
+ goto err_gate;
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err_enable;
+ if (priv->soc_type == SOC_A83T)
+ clk_set_rate(priv->clk, DE_CLK_RATE_A83T);
+ else
+ clk_set_rate(priv->clk, DE_CLK_RATE_H3);
+
+ /* set the A83T clock divider = 500 / 250 */
+ if (priv->soc_type == SOC_A83T)
+ de_write(priv, DE_DIV_REG,
+ 0x00000011); /* div = 2 for both LCDs */
+
+ return 0;
+
+err_enable:
+ clk_disable_unprepare(priv->gate);
+err_gate:
+ if (!IS_ERR(priv->rstc))
+ reset_control_assert(priv->rstc);
+ return ret;
+}
+
+void de2_de_cleanup(struct priv *priv)
+{
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ if (!IS_ERR(priv->rstc))
+ reset_control_assert(priv->rstc);
+}
diff --git a/drivers/gpu/drm/sunxi/de2_drm.h b/drivers/gpu/drm/sunxi/de2_drm.h
new file mode 100644
index 0000000..7bb966c
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_drm.h
@@ -0,0 +1,47 @@
+#ifndef __DE2_DRM_H__
+#define __DE2_DRM_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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 <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_cma_helper.h>
+
+struct lcd;
+
+#define N_LCDS 2
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct priv {
+ void __iomem *mmio;
+ struct clk *clk;
+ struct clk *gate;
+ struct reset_control *rstc;
+
+ int soc_type;
+
+ struct drm_fbdev_cma *fbdev;
+
+ struct lcd *lcds[N_LCDS];
+};
+
+/* in de2_crtc.c */
+int de2_enable_vblank(struct drm_device *drm, unsigned crtc);
+void de2_disable_vblank(struct drm_device *drm, unsigned crtc);
+extern struct platform_driver de2_lcd_platform_driver;
+
+/* in de2_de.c */
+int de2_de_init(struct priv *priv, struct device *dev);
+void de2_de_cleanup(struct priv *priv);
+
+#endif /* __DE2_DRM_H__ */
diff --git a/drivers/gpu/drm/sunxi/de2_drv.c b/drivers/gpu/drm/sunxi/de2_drv.c
new file mode 100644
index 0000000..5daa15c
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_drv.c
@@ -0,0 +1,378 @@
+/*
+ * Allwinner DRM driver - DE2 DRM driver
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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 <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+#include <drm/drm_of.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "de2_drm.h"
+
+#define DRIVER_NAME "sunxi-de2"
+#define DRIVER_DESC "Allwinner DRM DE2"
+#define DRIVER_DATE "20161001"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static struct of_device_id de2_drm_of_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-display-engine",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-display-engine",
+ .data = (void *) SOC_H3 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, de2_drm_of_match);
+
+static void de2_fb_output_poll_changed(struct drm_device *drm)
+{
+ struct priv *priv = drm->dev_private;
+
+ if (priv->fbdev)
+ drm_fbdev_cma_hotplug_event(priv->fbdev);
+}
+
+static const struct drm_mode_config_funcs de2_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .output_poll_changed = de2_fb_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+/*
+ * DRM operations:
+ */
+static void de2_lastclose(struct drm_device *drm)
+{
+ struct priv *priv = drm->dev_private;
+
+ if (priv->fbdev)
+ drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static const struct file_operations de2_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static struct drm_driver de2_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
+ DRIVER_ATOMIC,
+ .lastclose = de2_lastclose,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
+ .enable_vblank = de2_enable_vblank,
+ .disable_vblank = de2_disable_vblank,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .fops = &de2_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * Power management
+ */
+static int de2_pm_suspend(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ drm_kms_helper_poll_disable(drm);
+ return 0;
+}
+
+static int de2_pm_resume(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ drm_kms_helper_poll_enable(drm);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops de2_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(de2_pm_suspend, de2_pm_resume)
+};
+
+/*
+ * Platform driver
+ */
+
+static int de2_drm_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ struct priv *priv;
+ int ret;
+
+ drm = drm_dev_alloc(&de2_drm_driver, dev);
+ if (!drm)
+ return -ENOMEM;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev, "failed to allocate private area\n");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ dev_set_drvdata(dev, drm);
+ drm->dev_private = priv;
+
+ drm_mode_config_init(drm);
+ drm->mode_config.min_width = 32; /* needed for cursor */
+ drm->mode_config.min_height = 32;
+ drm->mode_config.max_width = 1920;
+ drm->mode_config.max_height = 1080;
+ drm->mode_config.funcs = &de2_mode_config_funcs;
+
+ drm->irq_enabled = true;
+
+ /* initialize the display engine */
+ priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
+ ret = de2_de_init(priv, dev);
+ if (ret)
+ goto out2;
+
+ /* start the subdevices */
+ ret = component_bind_all(dev, drm);
+ if (ret < 0)
+ goto out2;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret < 0)
+ goto out3;
+
+ DRM_DEBUG_DRIVER("%d crtcs %d connectors\n",
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ if (ret < 0)
+ dev_warn(dev, "failed to initialize vblank\n");
+
+ drm_mode_config_reset(drm);
+
+ priv->fbdev = drm_fbdev_cma_init(drm,
+ 32, /* bpp */
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+ if (IS_ERR(priv->fbdev)) {
+ ret = PTR_ERR(priv->fbdev);
+ priv->fbdev = NULL;
+ goto out4;
+ }
+
+ drm_kms_helper_poll_init(drm);
+
+ return 0;
+
+out4:
+ drm_dev_unregister(drm);
+out3:
+ component_unbind_all(dev, drm);
+out2:
+ kfree(priv);
+out1:
+ drm_dev_unref(drm);
+ return ret;
+}
+
+static void de2_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct priv *priv = drm->dev_private;
+
+ if (priv)
+ drm_fbdev_cma_fini(priv->fbdev);
+ drm_kms_helper_poll_fini(drm);
+
+ drm_dev_unregister(drm);
+ drm_vblank_cleanup(drm);
+
+ drm_mode_config_cleanup(drm);
+
+ component_unbind_all(dev, drm);
+
+ if (priv) {
+ de2_de_cleanup(priv);
+ kfree(priv);
+ }
+
+ drm_dev_unref(drm);
+}
+
+static const struct component_master_ops de2_drm_comp_ops = {
+ .bind = de2_drm_bind,
+ .unbind = de2_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int de2_drm_add_components(struct device *dev,
+ int (*compare_of)(struct device *, void *),
+ const struct component_master_ops *m_ops)
+{
+ struct device_node *ep, *port, *remote;
+ struct component_match *match = NULL;
+ int i;
+
+ if (!dev->of_node)
+ return -EINVAL;
+
+ /* bind the CRTCs */
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(dev->of_node, "ports", i);
+ if (!port)
+ break;
+
+ if (!of_device_is_available(port->parent)) {
+ of_node_put(port);
+ continue;
+ }
+
+ component_match_add(dev, &match, compare_of, port->parent);
+ of_node_put(port);
+ }
+
+ if (i == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+ if (!match) {
+ dev_err(dev, "no available port\n");
+ return -ENODEV;
+ }
+
+ /* bind the encoders/connectors */
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(dev->of_node, "ports", i);
+ if (!port)
+ break;
+
+ if (!of_device_is_available(port->parent)) {
+ of_node_put(port);
+ continue;
+ }
+
+ for_each_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ continue;
+ }
+ if (!of_device_is_available(remote->parent)) {
+ dev_warn(dev,
+ "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
+ }
+
+ component_match_add(dev, &match, compare_of, remote);
+ of_node_put(remote);
+ }
+ of_node_put(port);
+ }
+
+ return component_master_add_with_match(dev, m_ops, match);
+}
+
+static int de2_drm_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = de2_drm_add_components(&pdev->dev,
+ compare_of,
+ &de2_drm_comp_ops);
+ if (ret == -EINVAL)
+ ret = -ENXIO;
+ return ret;
+}
+
+static int de2_drm_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &de2_drm_comp_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_drm_platform_driver = {
+ .probe = de2_drm_probe,
+ .remove = de2_drm_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &de2_pm_ops,
+ .of_match_table = de2_drm_of_match,
+ },
+};
+
+static int __init de2_drm_init(void)
+{
+ int ret;
+
+/* uncomment to activate the drm traces at startup time */
+/* drm_debug = DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS |
+ DRM_UT_PRIME | DRM_UT_ATOMIC; */
+
+ DRM_DEBUG_DRIVER("\n");
+
+ ret = platform_driver_register(&de2_lcd_platform_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = platform_driver_register(&de2_drm_platform_driver);
+ if (ret < 0)
+ platform_driver_unregister(&de2_lcd_platform_driver);
+
+ return ret;
+}
+
+static void __exit de2_drm_fini(void)
+{
+ platform_driver_unregister(&de2_lcd_platform_driver);
+ platform_driver_unregister(&de2_drm_platform_driver);
+}
+
+module_init(de2_drm_init);
+module_exit(de2_drm_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sunxi/de2_plane.c b/drivers/gpu/drm/sunxi/de2_plane.c
new file mode 100644
index 0000000..b338684
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_plane.c
@@ -0,0 +1,119 @@
+/*
+ * Allwinner DRM driver - DE2 planes
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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/drm_crtc_helper.h>
+
+#include "de2_drm.h"
+#include "de2_crtc.h"
+
+/* plane formats */
+static const uint32_t ui_formats[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+};
+
+static const uint32_t vi_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_YUV422,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+};
+
+static void de2_plane_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_crtc *crtc = old_state->crtc;
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ int plane_num = plane - lcd->planes;
+
+ de2_de_plane_disable(lcd->priv, lcd->num, plane_num);
+}
+
+static void de2_plane_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_crtc *crtc = state->crtc;
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct drm_framebuffer *fb = state->fb;
+ int plane_num = plane - lcd->planes;
+
+ if (!crtc || !fb) {
+ DRM_DEBUG_DRIVER("no crtc/fb\n");
+ return;
+ }
+
+ de2_de_plane_update(lcd->priv, lcd->num, plane_num,
+ state, old_state);
+}
+
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+ .atomic_disable = de2_plane_disable,
+ .atomic_update = de2_plane_update,
+};
+
+static const struct drm_plane_funcs plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int de2_one_plane_init(struct drm_device *drm,
+ struct drm_plane *plane,
+ int type, int possible_crtcs,
+ const uint32_t *formats,
+ int nformats)
+{
+ int ret;
+
+ ret = drm_universal_plane_init(drm, plane, possible_crtcs,
+ &plane_funcs,
+ formats, nformats, type, NULL);
+ if (ret >= 0)
+ drm_plane_helper_add(plane, &plane_helper_funcs);
+
+ return ret;
+}
+
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
+{
+ int ret, possible_crtcs = 1 << lcd->crtc_idx;
+
+ ret = de2_one_plane_init(drm, &lcd->planes[DE2_PRIMARY_PLANE],
+ DRM_PLANE_TYPE_PRIMARY, possible_crtcs,
+ ui_formats, ARRAY_SIZE(ui_formats));
+ if (ret >= 0)
+ ret = de2_one_plane_init(drm, &lcd->planes[DE2_CURSOR_PLANE],
+ DRM_PLANE_TYPE_CURSOR, possible_crtcs,
+ ui_formats, ARRAY_SIZE(ui_formats));
+ if (ret >= 0)
+ ret = de2_one_plane_init(drm, &lcd->planes[DE2_VI_PLANE],
+ DRM_PLANE_TYPE_OVERLAY, possible_crtcs,
+ vi_formats, ARRAY_SIZE(vi_formats));
+ if (ret < 0)
+ dev_err(lcd->dev, "Couldn't initialize the planes err %d\n",
+ ret);
+
+ return ret;
+}
--
2.10.1
^ permalink raw reply related
* [PATCH 2/2] ARM: imx: init enet MAC address from ocotp
From: Octavian Purdila @ 2016-10-21 7:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477034301-26579-1-git-send-email-octavian.purdila@nxp.com>
From: Fugang Duan <b38611@freescale.com>
The FEC driver advertises the following order of getting it's MAC
address: module parameters or kernel command line first, then device
tree, then fuse / flash, then mac registers set by bootloader, then
random MAC address.
This patch add support for fuse MAC address for imx6/7 by updating the
device tree fec entry with a MAC address from OCOTP, if none is set.
Signed-off-by: Fugang Duan <B38611@freescale.com>
[Octavian: move to ocotp file, squash imx7d fix]
Signed-off-by: Octavian Purdila <octavian.purdila@nxp.com>
---
arch/arm/mach-imx/common.h | 1 +
arch/arm/mach-imx/mach-imx6q.c | 12 +++++--
arch/arm/mach-imx/mach-imx6sl.c | 11 +++++--
arch/arm/mach-imx/mach-imx6sx.c | 1 +
arch/arm/mach-imx/mach-imx7d.c | 1 +
arch/arm/mach-imx/ocotp.c | 73 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index fc78d4d0..7609dfe 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -108,6 +108,7 @@ u32 imx_ocotp_read(u32 offset);
int imx6_set_lpm(enum mxc_cpu_pwr_mode mode);
void imx6_set_int_mem_clk_lpm(bool enable);
void imx6sl_set_wait_clk(bool enter);
+void ocotp_enet_mac_init(const char *enet_compat);
int imx_mmdc_get_ddr_type(void);
void imx_cpu_die(unsigned int cpu);
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 51f6c19..823b5ba 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -31,6 +31,7 @@
#include <linux/micrel_phy.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/of_net.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/system_misc.h>
@@ -262,6 +263,13 @@ static void __init imx6q_axi_init(void)
}
}
+static inline void imx6q_enet_init(void)
+{
+ ocotp_enet_mac_init("fsl,imx6q-fec");
+ imx6q_enet_phy_init();
+ imx6q_1588_init();
+}
+
static void __init imx6q_init_machine(void)
{
struct device *parent;
@@ -276,14 +284,12 @@ static void __init imx6q_init_machine(void)
if (parent == NULL)
pr_warn("failed to initialize soc device\n");
- imx6q_enet_phy_init();
-
of_platform_default_populate(NULL, NULL, parent);
imx_ocotp_init("fsl,imx6q-ocotp");
+ imx6q_enet_init();
imx_anatop_init();
cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
- imx6q_1588_init();
imx6q_axi_init();
}
diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c
index 01558df..1c2863f 100644
--- a/arch/arm/mach-imx/mach-imx6sl.c
+++ b/arch/arm/mach-imx/mach-imx6sl.c
@@ -19,7 +19,7 @@
#include "common.h"
#include "cpuidle.h"
-static void __init imx6sl_fec_init(void)
+static void __init imx6sl_fec_clk_init(void)
{
struct regmap *gpr;
@@ -30,9 +30,14 @@ static void __init imx6sl_fec_init(void)
IMX6SL_GPR1_FEC_CLOCK_MUX2_SEL_MASK, 0);
regmap_update_bits(gpr, IOMUXC_GPR1,
IMX6SL_GPR1_FEC_CLOCK_MUX1_SEL_MASK, 0);
- } else {
+ } else
pr_err("failed to find fsl,imx6sl-iomux-gpr regmap\n");
- }
+}
+
+static inline void imx6sl_fec_init(void)
+{
+ imx6sl_fec_clk_init();
+ ocotp_enet_mac_init("fsl,imx6sl-fec");
}
static void __init imx6sl_init_late(void)
diff --git a/arch/arm/mach-imx/mach-imx6sx.c b/arch/arm/mach-imx/mach-imx6sx.c
index acb73c1..e502b55 100644
--- a/arch/arm/mach-imx/mach-imx6sx.c
+++ b/arch/arm/mach-imx/mach-imx6sx.c
@@ -60,6 +60,7 @@ static void __init imx6sx_enet_clk_sel(void)
static inline void imx6sx_enet_init(void)
{
+ ocotp_enet_mac_init("fsl,imx6sx-fec");
imx6sx_enet_phy_init();
imx6sx_enet_clk_sel();
}
diff --git a/arch/arm/mach-imx/mach-imx7d.c b/arch/arm/mach-imx/mach-imx7d.c
index c29c771..29cde14 100644
--- a/arch/arm/mach-imx/mach-imx7d.c
+++ b/arch/arm/mach-imx/mach-imx7d.c
@@ -81,6 +81,7 @@ static void __init imx7d_enet_clk_sel(void)
static inline void imx7d_enet_init(void)
{
+ ocotp_enet_mac_init("fsl,imx7d-fec");
imx7d_enet_phy_init();
imx7d_enet_clk_sel();
}
diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c
index e73f0e8..5c5806a 100644
--- a/arch/arm/mach-imx/ocotp.c
+++ b/arch/arm/mach-imx/ocotp.c
@@ -11,6 +11,10 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_net.h>
+#include <linux/slab.h>
+
+#include "hardware.h"
static void __iomem *ocotp_base;
@@ -38,3 +42,72 @@ u32 imx_ocotp_read(u32 offset)
return readl_relaxed(ocotp_base + offset);
}
+
+#define OCOTP_MAC_OFF (cpu_is_imx7d() ? 0x640 : 0x620)
+#define OCOTP_MACn(n) (OCOTP_MAC_OFF + (n) * 0x10)
+
+void __init ocotp_enet_mac_init(const char *enet_compat)
+{
+ struct device_node *enet_np, *from = NULL;
+ struct property *newmac;
+ u32 macaddr_low;
+ u32 macaddr_high = 0;
+ u32 macaddr1_high = 0;
+ u8 *macaddr;
+ int i, id;
+
+ for (i = 0; i < 2; i++) {
+ enet_np = of_find_compatible_node(from, NULL, enet_compat);
+ if (!enet_np)
+ return;
+
+ from = enet_np;
+
+ if (of_get_mac_address(enet_np))
+ goto put_enet_node;
+
+ id = of_alias_get_id(enet_np, "ethernet");
+ if (id < 0)
+ id = i;
+
+ macaddr_low = imx_ocotp_read(OCOTP_MACn(1));
+ if (id)
+ macaddr1_high = imx_ocotp_read(OCOTP_MACn(2));
+ else
+ macaddr_high = imx_ocotp_read(OCOTP_MACn(0));
+
+ newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL);
+ if (!newmac)
+ goto put_enet_node;
+
+ newmac->value = newmac + 1;
+ newmac->length = 6;
+ newmac->name = kstrdup("local-mac-address", GFP_KERNEL);
+ if (!newmac->name) {
+ kfree(newmac);
+ goto put_enet_node;
+ }
+
+ macaddr = newmac->value;
+ if (id) {
+ macaddr[5] = (macaddr_low >> 16) & 0xff;
+ macaddr[4] = (macaddr_low >> 24) & 0xff;
+ macaddr[3] = macaddr1_high & 0xff;
+ macaddr[2] = (macaddr1_high >> 8) & 0xff;
+ macaddr[1] = (macaddr1_high >> 16) & 0xff;
+ macaddr[0] = (macaddr1_high >> 24) & 0xff;
+ } else {
+ macaddr[5] = macaddr_high & 0xff;
+ macaddr[4] = (macaddr_high >> 8) & 0xff;
+ macaddr[3] = (macaddr_high >> 16) & 0xff;
+ macaddr[2] = (macaddr_high >> 24) & 0xff;
+ macaddr[1] = macaddr_low & 0xff;
+ macaddr[0] = (macaddr_low >> 8) & 0xff;
+ }
+
+ of_update_property(enet_np, newmac);
+
+put_enet_node:
+ of_node_put(enet_np);
+ }
+}
--
2.7.4
^ permalink raw reply related
* [PATCH 1/2] ARM: imx: add ocotp support in machine code
From: Octavian Purdila @ 2016-10-21 7:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477034301-26579-1-git-send-email-octavian.purdila@nxp.com>
Since we will have the need to access OCOTP fuse registers from
multiple places move the ocotp initialization and register access code
to a new file and provide interfaces for it.
Signed-off-by: Octavian Purdila <octavian.purdila@nxp.com>
---
arch/arm/mach-imx/Kconfig | 5 +++++
arch/arm/mach-imx/Makefile | 1 +
arch/arm/mach-imx/common.h | 2 ++
arch/arm/mach-imx/mach-imx6q.c | 20 ++------------------
arch/arm/mach-imx/mach-imx6sl.c | 1 +
arch/arm/mach-imx/mach-imx6sx.c | 1 +
arch/arm/mach-imx/mach-imx7d.c | 1 +
arch/arm/mach-imx/ocotp.c | 40 ++++++++++++++++++++++++++++++++++++++++
8 files changed, 53 insertions(+), 18 deletions(-)
create mode 100644 arch/arm/mach-imx/ocotp.c
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 0ac05a0..1fb36ee 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -58,6 +58,9 @@ config HAVE_IMX_SRC
def_bool y if SMP
select ARCH_HAS_RESET_CONTROLLER
+config HAVE_IMX_OCOTP
+ bool
+
config IMX_HAVE_IOMUX_V1
bool
@@ -492,6 +495,7 @@ config SOC_IMX6
select HAVE_IMX_SRC
select MFD_SYSCON
select PL310_ERRATA_769419 if CACHE_L2X0
+ select HAVE_IMX_OCOTP
config SOC_IMX6Q
bool "i.MX6 Quad/DualLite support"
@@ -537,6 +541,7 @@ config SOC_IMX7D
select HAVE_IMX_ANATOP
select HAVE_IMX_MMDC
select HAVE_IMX_SRC
+ select HAVE_IMX_OCOTP
help
This enables support for Freescale i.MX7 Dual processor.
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index cab1289..feef1cf 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
+obj-$(CONFIG_HAVE_IMX_OCOTP) += ocotp.o
ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
AFLAGS_headsmp.o :=-Wa,-march=armv7-a
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index c4436d9..fc78d4d0 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -103,6 +103,8 @@ void imx_gpc_hwirq_unmask(unsigned int hwirq);
void imx_anatop_init(void);
void imx_anatop_pre_suspend(void);
void imx_anatop_post_resume(void);
+void imx_ocotp_init(const char *ocotp_compat);
+u32 imx_ocotp_read(u32 offset);
int imx6_set_lpm(enum mxc_cpu_pwr_mode mode);
void imx6_set_int_mem_clk_lpm(bool enable);
void imx6sl_set_wait_clk(bool enter);
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 97fd251..51f6c19 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -280,6 +280,7 @@ static void __init imx6q_init_machine(void)
of_platform_default_populate(NULL, NULL, parent);
+ imx_ocotp_init("fsl,imx6q-ocotp");
imx_anatop_init();
cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
imx6q_1588_init();
@@ -294,22 +295,8 @@ static void __init imx6q_init_machine(void)
static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
{
- struct device_node *np;
- void __iomem *base;
u32 val;
- np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
- if (!np) {
- pr_warn("failed to find ocotp node\n");
- return;
- }
-
- base = of_iomap(np, 0);
- if (!base) {
- pr_warn("failed to map ocotp\n");
- goto put_node;
- }
-
/*
* SPEED_GRADING[1:0] defines the max speed of ARM:
* 2b'11: 1200000000Hz;
@@ -318,7 +305,7 @@ static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
* 2b'00: 792000000Hz;
* We need to set the max speed of ARM according to fuse map.
*/
- val = readl_relaxed(base + OCOTP_CFG3);
+ val = imx_ocotp_read(OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
val &= 0x3;
@@ -333,9 +320,6 @@ static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
if (dev_pm_opp_disable(cpu_dev, 852000000))
pr_warn("failed to disable 852 MHz OPP\n");
}
- iounmap(base);
-put_node:
- of_node_put(np);
}
static void __init imx6q_opp_init(void)
diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c
index 0408490..01558df 100644
--- a/arch/arm/mach-imx/mach-imx6sl.c
+++ b/arch/arm/mach-imx/mach-imx6sl.c
@@ -54,6 +54,7 @@ static void __init imx6sl_init_machine(void)
of_platform_default_populate(NULL, NULL, parent);
+ imx_ocotp_init("fsl,imx6sl-ocotp");
imx6sl_fec_init();
imx_anatop_init();
imx6sl_pm_init();
diff --git a/arch/arm/mach-imx/mach-imx6sx.c b/arch/arm/mach-imx/mach-imx6sx.c
index 7f52d9b..acb73c1 100644
--- a/arch/arm/mach-imx/mach-imx6sx.c
+++ b/arch/arm/mach-imx/mach-imx6sx.c
@@ -74,6 +74,7 @@ static void __init imx6sx_init_machine(void)
of_platform_default_populate(NULL, NULL, parent);
+ imx_ocotp_init("fsl,imx6sx-ocotp");
imx6sx_enet_init();
imx_anatop_init();
imx6sx_pm_init();
diff --git a/arch/arm/mach-imx/mach-imx7d.c b/arch/arm/mach-imx/mach-imx7d.c
index 26ca744..c29c771 100644
--- a/arch/arm/mach-imx/mach-imx7d.c
+++ b/arch/arm/mach-imx/mach-imx7d.c
@@ -93,6 +93,7 @@ static void __init imx7d_init_machine(void)
if (parent == NULL)
pr_warn("failed to initialize soc device\n");
+ imx_ocotp_init("fsl,imx7d-ocotp");
imx_anatop_init();
imx7d_enet_init();
}
diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c
new file mode 100644
index 0000000..e73f0e8
--- /dev/null
+++ b/arch/arm/mach-imx/ocotp.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+static void __iomem *ocotp_base;
+
+void __init imx_ocotp_init(const char *compat)
+{
+ struct device_node *ocotp_np;
+
+ ocotp_np = of_find_compatible_node(NULL, NULL, compat);
+ if (!ocotp_np) {
+ pr_warn("failed to find ocotp node\n");
+ return;
+ }
+
+ ocotp_base = of_iomap(ocotp_np, 0);
+ if (!ocotp_base)
+ pr_warn("failed to map ocotp\n");
+
+ of_node_put(ocotp_np);
+}
+
+u32 imx_ocotp_read(u32 offset)
+{
+ if (WARN_ON(!ocotp_base))
+ return 0;
+
+ return readl_relaxed(ocotp_base + offset);
+}
--
2.7.4
^ permalink raw reply related
* [PATCH 0/2] ARM: imx: init MAC address for ocotp
From: Octavian Purdila @ 2016-10-21 7:18 UTC (permalink / raw)
To: linux-arm-kernel
This patch series adds support for initializing the ethernet MAC
address for the OCOTP fuse configuration.
Fugang Duan (1):
ARM: imx: init enet MAC address from ocotp
Octavian Purdila (1):
ARM: imx: add ocotp support in machine code
arch/arm/mach-imx/Kconfig | 5 ++
arch/arm/mach-imx/Makefile | 1 +
arch/arm/mach-imx/common.h | 3 ++
arch/arm/mach-imx/mach-imx6q.c | 32 ++++--------
arch/arm/mach-imx/mach-imx6sl.c | 12 +++--
arch/arm/mach-imx/mach-imx6sx.c | 2 +
arch/arm/mach-imx/mach-imx7d.c | 2 +
arch/arm/mach-imx/ocotp.c | 113 ++++++++++++++++++++++++++++++++++++++++
8 files changed, 146 insertions(+), 24 deletions(-)
create mode 100644 arch/arm/mach-imx/ocotp.c
--
2.7.4
^ permalink raw reply
* [PATCH v2] ARM: shmobile: Consolidate R8A7743 and R8A779[234] machine definitions
From: Simon Horman @ 2016-10-21 7:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4b407584-21d4-5dc7-6866-6feff12f302f@cogentembedded.com>
On Thu, Oct 20, 2016 at 09:44:56PM +0300, Sergei Shtylyov wrote:
> Hello.
>
> On 10/20/2016 11:58 AM, Simon Horman wrote:
>
> >>The four SoCs use identical machine operations, consolidate them into
> >>two machine definitions in a single file.
> >>
> >>Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>Tested-by: Simon Horman <horms+renesas@verge.net.au>
> >>Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> >>---
> >>Changes since v1:
> >>
> >>- Rebased on top of Simon's latest devel branch, thus including R8A7743
> >> consolidation
> >>
> >> arch/arm/mach-shmobile/Makefile | 4 ----
> >> arch/arm/mach-shmobile/setup-r8a7743.c | 34 -------------------------------
> >> arch/arm/mach-shmobile/setup-r8a7792.c | 35 --------------------------------
> >> arch/arm/mach-shmobile/setup-r8a7793.c | 33 ------------------------------
> >> arch/arm/mach-shmobile/setup-r8a7794.c | 33 ------------------------------
> >> arch/arm/mach-shmobile/setup-rcar-gen2.c | 33 ++++++++++++++++++++++++++++++
> >> 6 files changed, 33 insertions(+), 139 deletions(-)
> >> delete mode 100644 arch/arm/mach-shmobile/setup-r8a7743.c
> >> delete mode 100644 arch/arm/mach-shmobile/setup-r8a7792.c
> >> delete mode 100644 arch/arm/mach-shmobile/setup-r8a7793.c
> >> delete mode 100644 arch/arm/mach-shmobile/setup-r8a7794.c
> >
> >Thanks for this Laurent, its a very nice diffstat.
> >I have queued it up for v4.10.
>
> I'm not seeing a new devel tag when fetching... :-(
Sorry about that. It should be there now.
^ permalink raw reply
* [PATCH] pwm: meson: Remove unneeded platform MODULE_ALIAS
From: Thierry Reding @ 2016-10-21 7:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476906599-14350-1-git-send-email-javier@osg.samsung.com>
On Wed, Oct 19, 2016 at 04:49:59PM -0300, Javier Martinez Canillas wrote:
> The Amlogic Meson is a DT-only platform, which means the devices are
> registered via OF and not using the legacy platform devices support.
>
> So there's no need to have a MODULE_ALIAS("platform:meson-pwm") since
> the reported uevent MODALIAS to user-space will always be the OF one.
>
> Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
> ---
>
> drivers/pwm/pwm-meson.c | 1 -
> 1 file changed, 1 deletion(-)
Applied, thanks.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161021/6e99fd32/attachment-0001.sig>
^ permalink raw reply
* [PATCH -next] pwm: meson: Fix missing spin_lock_init()
From: Thierry Reding @ 2016-10-21 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <57D41083.3060109@baylibre.com>
On Sat, Sep 10, 2016 at 03:54:11PM +0200, Neil Armstrong wrote:
> Le 10/09/2016 08:38, Wei Yongjun a ?crit :
> > From: Wei Yongjun <weiyongjun1@huawei.com>
> >
> > The driver allocates the spinlock but not initialize it.
> > Use spin_lock_init() on it to initialize it correctly.
> >
> > This is detected by Coccinelle semantic patch.
> >
> > Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
> > ---
> > drivers/pwm/pwm-meson.c | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
> > index bfbbe7f..778d088 100644
> > --- a/drivers/pwm/pwm-meson.c
> > +++ b/drivers/pwm/pwm-meson.c
> > @@ -465,6 +465,7 @@ static int meson_pwm_probe(struct platform_device *pdev)
> > if (IS_ERR(meson->base))
> > return PTR_ERR(meson->base);
> >
> > + spin_lock_init(&meson->lock);
> > meson->chip.dev = &pdev->dev;
> > meson->chip.ops = &meson_pwm_ops;
> > meson->chip.base = -1;
> >
>
> Thanks for the fix, but it was already posted earlier by Axel Lin <axel.lin@ingics.com>
>
> Thierry will decide which one to merge.
>
> Acked-by: Neil Armstrong <narmstrong@baylibre.com>
I've applied Axel's patch.
Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161021/c8c0227d/attachment.sig>
^ permalink raw reply
* [PATCH v3] clkdev: Detect errors in clk_hw_register_clkdev() for mass registration
From: Geert Uytterhoeven @ 2016-10-21 7:02 UTC (permalink / raw)
To: linux-arm-kernel
Unlike clk_register_clkdev(), clk_hw_register_clkdev() doesn't check for
passed error objects from a previous registration call. Hence the caller
of clk_hw_register_*() has to check for errors before calling
clk_hw_register_clkdev*().
Make clk_hw_register_clkdev() more similar to clk_register_clkdev() by
adding this error check, removing the burden from callers that do mass
registration.
Fixes: e4f1b49bda6d6aa2 ("clkdev: Add clk_hw based registration APIs")
Fixes: 944b9a41e004534f ("clk: ls1x: Migrate to clk_hw based OF and registration APIs")
Fixes: 44ce9a9ae977736f ("MIPS: TXx9: Convert to Common Clock Framework")
Fixes: f48d947a162dfa9d ("clk: clps711x: Migrate to clk_hw based OF and registration APIs")
Fixes: b4626a7f489238a5 ("CLK: Add Loongson1C clock support")
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
v3:
- Add more Fixes tags for drivers not checking errors in v4.9-rc1,
v2:
- Correct wrong function references
s/clkdev_{,hw_}create/clk_{,hw_}register_clkdev/
---
drivers/clk/clkdev.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 97ae60fa15849954..bb8a77a5985f8627 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -448,12 +448,20 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
*
* con_id or dev_id may be NULL as a wildcard, just as in the rest of
* clkdev.
+ *
+ * To make things easier for mass registration, we detect error clk_hws
+ * from a previous clk_hw_register_*() call, and return the error code for
+ * those. This is to permit this function to be called immediately
+ * after clk_hw_register_*().
*/
int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
const char *dev_id)
{
struct clk_lookup *cl;
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
--
1.9.1
^ permalink raw reply related
* [PATCH V6 00/10] dmaengine: qcom_hidma: add MSI interrupt support
From: Vinod Koul @ 2016-10-21 6:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <37baf7db-1e14-4027-9397-70649e8fa946@codeaurora.org>
On Thu, Oct 20, 2016 at 02:55:23PM -0700, Sinan Kaya wrote:
> Hi Vinod,
>
> On 10/20/2016 10:34 AM, Sinan Kaya wrote:
> > On 10/20/2016 9:43 AM, Vinod Koul wrote:
> >>> slave-dma git://git.infradead.org/users/vkoul/slave-dma.git (fetch)
> >>>> slave-dma git://git.infradead.org/users/vkoul/slave-dma.git (push)
> >> You seem to have missed topic/qcom which I pushed last night. next would have worked too!!
> >>
> >
> > OK. Let me pick that up and test.
> >
>
> It looks like patches were applied out of order.
When applying, if I get a conflict I try to skip them. Typically subsequent
patches fail, sometimes they apply. I think couple of them did in this case
It built for me, 0day also gave me success report, so unless we have bisect
regression, I would like rest of the patches on top of this please..
> -rw-rw-r-- 1 okaya users 8176 Oct 20 17:44 0010-dmaengine-qcom_hidma-add-MSI-support-for-interrupts.patch
> -rw-rw-r-- 1 okaya users 1175 Oct 20 17:44 0009-dmaengine-qcom_hidma-break-completion-processing-on-.patch
> -rw-rw-r-- 1 okaya users 4773 Oct 20 17:44 0008-dmaengine-qcom_hidma-protect-common-data-structures.patch
> -rw-rw-r-- 1 okaya users 2752 Oct 20 17:44 0007-dmaengine-qcom_hidma-add-a-common-API-to-setup-the-i.patch
> -rw-rw-r-- 1 okaya users 3225 Oct 20 17:44 0006-dmaengine-qcom_hidma-bring-out-interrupt-cause.patch
> -rw-rw-r-- 1 okaya users 4284 Oct 20 17:44 0005-dmaengine-qcom_hidma-make-pending_tre_count-atomic.patch
> -rw-rw-r-- 1 okaya users 1090 Oct 20 17:44 0004-dmaengine-qcom_hidma-configure-DMA-and-MSI-for-OF.patch
> -rw-rw-r-- 1 okaya users 959 Oct 20 17:44 0003-of-irq-make-of_msi_configure-accessible-from-modules.patch
> -rw-rw-r-- 1 okaya users 1558 Oct 20 17:44 0002-Documentation-DT-qcom_hidma-correct-spelling-mistake.patch
> -rw-rw-r-- 1 okaya users 1588 Oct 20 17:44 0001-Documentation-DT-qcom_hidma-update-binding-for-MSI.patch
>
> This is the commit order in topic/dma branch. I added <<< for the patches missing below.
>
> <<< 0010-dmaengine-qcom_hidma-add-MSI-support-for-interrupts.patch
> <<< 0009-dmaengine-qcom_hidma-break-completion-processing-on-.patch
>
> fc73796 dmaengine: qcom_hidma: break completion processing on error
>
> <<< 0008-dmaengine-qcom_hidma-protect-common-data-structures.patch
>
> d3eab50 dmaengine: qcom_hidma: add a common API to setup the interrupt
>
> <<< 0006-dmaengine-qcom_hidma-bring-out-interrupt-cause.patch
> <<< 0005-dmaengine-qcom_hidma-make-pending_tre_count-atomic.patch
>
> 9da0be8 dmaengine: qcom_hidma: configure DMA and MSI for OF
> 5282c18 of: irq: make of_msi_configure accessible from modules
> 13af1c8 Documentation: DT: qcom_hidma: correct spelling mistakes
> ef6661b Documentation: DT: qcom_hidma: update binding for MSI
>
> I also looked at the binary contents of the patches in topic/dma with what I posted
> on v6. They match excluding your Signed off lines.
--
~Vinod
^ permalink raw reply
* [RFC,v1,2/2] vfio/iommu-type1: set only stage 2 translation
From: Rick Song @ 2016-10-21 4:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477024764-79882-1-git-send-email-songwenjun@huawei.com>
Normally, VFIO should use only stage 2 translation of
iommu, to create the address mapping. If nesting translation
is disabled from VFIO core, enable iommu domain only stage 2
attribute, otherwise, enable iommu domain nesting attribute.
Signed-off-by: Rick Song <songwenjun@huawei.com>
---
drivers/vfio/vfio_iommu_type1.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 2ba1942..c0265fe 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -741,7 +741,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
struct vfio_group *group, *g;
struct vfio_domain *domain, *d;
struct bus_type *bus = NULL;
- int ret;
+ int attr, ret;
mutex_lock(&iommu->lock);
@@ -775,13 +775,22 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
goto out_free;
}
+ /*
+ * Set iommu nesting domain attribute if nesting translation
+ * is enabled from iommu vfio, otherwise set iommu only stage
+ * 2 domain attribute.
+ */
+ attr = 1;
if (iommu->nesting) {
- int attr = 1;
-
ret = iommu_domain_set_attr(domain->domain, DOMAIN_ATTR_NESTING,
&attr);
if (ret)
goto out_domain;
+ } else {
+ ret = iommu_domain_set_attr(domain->domain, DOMAIN_ATTR_S2,
+ &attr);
+ if (ret)
+ goto out_domain;
}
ret = iommu_attach_group(domain->domain, iommu_group);
--
1.9.1
^ permalink raw reply related
* [RFC,v1,1/2] iommu/arm-smmu: support s2 domain attribute
From: Rick Song @ 2016-10-21 4:39 UTC (permalink / raw)
To: linux-arm-kernel
Under some condition, user want to use the
only stage 2 translation ability, so smmu
driver need support setting only s2 domain
attribute.
Signed-off-by: Rick Song <songwenjun@huawei.com>
---
drivers/iommu/arm-smmu-v3.c | 12 ++++++++++++
include/linux/iommu.h | 1 +
2 files changed, 13 insertions(+)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 15c01c3..d3b19d2 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1854,6 +1854,18 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
mutex_lock(&smmu_domain->init_mutex);
switch (attr) {
+ case DOMAIN_ATTR_S2:
+ if (smmu_domain->smmu) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ if (*(int *)data)
+ smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+ else
+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+ break;
case DOMAIN_ATTR_NESTING:
if (smmu_domain->smmu) {
ret = -EPERM;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 436dc21..0b5a387 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -114,6 +114,7 @@ enum iommu_attr {
DOMAIN_ATTR_FSL_PAMU_ENABLE,
DOMAIN_ATTR_FSL_PAMUV1,
DOMAIN_ATTR_NESTING, /* two stages of translation */
+ DOMAIN_ATTR_S2, /* only stage 2 translation */
DOMAIN_ATTR_MAX,
};
--
1.9.1
^ permalink raw reply related
* [PATCH] clk: rockchip: add 533.25MHz to rk3399 clock rates table
From: Xing Zheng @ 2016-10-21 4:03 UTC (permalink / raw)
To: linux-arm-kernel
We need to get the accurate 533.25MHz for the DP display.
Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
---
drivers/clk/rockchip/clk-rk3399.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 2c7cba7..a87cb49 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -93,6 +93,7 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
RK3036_PLL_RATE( 676000000, 3, 169, 2, 1, 1, 0),
RK3036_PLL_RATE( 600000000, 1, 75, 3, 1, 1, 0),
RK3036_PLL_RATE( 594000000, 1, 99, 4, 1, 1, 0),
+ RK3036_PLL_RATE( 533250000, 8, 711, 4, 1, 1, 0),
RK3036_PLL_RATE( 504000000, 1, 63, 3, 1, 1, 0),
RK3036_PLL_RATE( 500000000, 6, 250, 2, 1, 1, 0),
RK3036_PLL_RATE( 408000000, 1, 68, 2, 2, 1, 0),
--
2.7.4
^ permalink raw reply related
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