All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-28 14:23     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-28 14:23 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

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-GANU6spQydw@public.gmane.org>
---
 drivers/gpu/drm/Kconfig           |   2 +
 drivers/gpu/drm/Makefile          |   1 +
 drivers/gpu/drm/sun8i/Kconfig     |  19 +
 drivers/gpu/drm/sun8i/Makefile    |   7 +
 drivers/gpu/drm/sun8i/de2_crtc.c  | 449 +++++++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_crtc.h  |  50 +++
 drivers/gpu/drm/sun8i/de2_drv.c   | 317 ++++++++++++++++
 drivers/gpu/drm/sun8i/de2_drv.h   |  48 +++
 drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
 9 files changed, 1627 insertions(+)
 create mode 100644 drivers/gpu/drm/sun8i/Kconfig
 create mode 100644 drivers/gpu/drm/sun8i/Makefile
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 95fc041..bb1bfbc 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
 
 source "drivers/gpu/drm/sun4i/Kconfig"
 
+source "drivers/gpu/drm/sun8i/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 883f3e7..3e1eaa0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-y			+= omapdrm/
 obj-$(CONFIG_DRM_SUN4I) += sun4i/
+obj-$(CONFIG_DRM_SUN8I) += sun8i/
 obj-y			+= tilcdc/
 obj-$(CONFIG_DRM_QXL) += qxl/
 obj-$(CONFIG_DRM_BOCHS) += bochs/
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
new file mode 100644
index 0000000..6940895
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -0,0 +1,19 @@
+#
+# Allwinner DE2 Video configuration
+#
+
+config DRM_SUN8I
+	bool
+
+config DRM_SUN8I_DE2
+	tristate "Support for Allwinner Video with DE2 interface"
+	depends on DRM && OF
+	depends on ARCH_SUNXI || COMPILE_TEST
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_KMS_HELPER
+	select DRM_SUN8I
+	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
+	  sun8i-de2-drm.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
new file mode 100644
index 0000000..f107919
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Allwinner's sun8i DRM device driver
+#
+
+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+
+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
new file mode 100644
index 0000000..4e94ccc
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.c
@@ -0,0 +1,449 @@
+/*
+ * Allwinner DRM driver - DE2 CRTC
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/component.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+
+#include "de2_drv.h"
+#include "de2_crtc.h"
+
+/* I/O map */
+
+#define TCON_GCTL_REG		0x00
+#define		TCON_GCTL_TCON_ENABLE BIT(31)
+#define TCON_GINT0_REG		0x04
+#define		TCON_GINT0_TCON1_Vb_Int_En BIT(30)
+#define		TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
+#define		TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
+#define TCON0_CTL_REG		0x40
+#define		TCON0_CTL_TCON_ENABLE BIT(31)
+#define TCON1_CTL_REG		0x90
+#define		TCON1_CTL_TCON_ENABLE BIT(31)
+#define		TCON1_CTL_INTERLACE_ENABLE BIT(20)
+#define		TCON1_CTL_Start_Delay_SHIFT 4
+#define		TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
+#define TCON1_BASIC0_REG	0x94	/* XI/YI */
+#define TCON1_BASIC1_REG	0x98	/* LS_XO/LS_YO */
+#define TCON1_BASIC2_REG	0x9c	/* XO/YO */
+#define TCON1_BASIC3_REG	0xa0	/* HT/HBP */
+#define TCON1_BASIC4_REG	0xa4	/* VT/VBP */
+#define TCON1_BASIC5_REG	0xa8	/* HSPW/VSPW */
+#define TCON1_PS_SYNC_REG	0xb0
+#define TCON1_IO_POL_REG	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)
+#define TCON1_IO_TRI_REG	0xf4
+#define TCON_CEU_CTL_REG	0x100
+#define		TCON_CEU_CTL_ceu_en BIT(31)
+#define	TCON1_FILL_CTL_REG	0x300
+#define TCON1_FILL_START0_REG	0x304
+#define TCON1_FILL_END0_REG	0x308
+#define TCON1_FILL_DATA0_REG	0x30c
+
+#define XY(x, y) (((x) << 16) | (y))
+
+#define andl_relaxed(addr, val) \
+	writel_relaxed(readl_relaxed(addr) & val, addr)
+#define orl_relaxed(addr, val) \
+	writel_relaxed(readl_relaxed(addr) | val, addr)
+
+/* 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 = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
+
+	drm_crtc_handle_vblank(&lcd->crtc);
+
+	writel_relaxed(isr &
+			~(TCON_GINT0_TCON1_Vb_Int_Flag |
+			  TCON_GINT0_TCON1_Vb_Line_Int_Flag),
+			lcd->mmio + TCON_GINT0_REG);
+
+	return IRQ_HANDLED;
+}
+
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+	struct priv *priv = drm_to_priv(drm);
+	struct lcd *lcd = priv->lcds[crtc_ix];
+
+	orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
+
+	return 0;
+}
+
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+	struct priv *priv = drm_to_priv(drm);
+	struct lcd *lcd = priv->lcds[crtc_ix];
+
+	andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
+}
+
+void de2_vblank_reset(struct lcd *lcd)
+{
+	drm_crtc_vblank_reset(&lcd->crtc);
+}
+
+/* frame functions */
+static int de2_crtc_set_clock(struct lcd *lcd, int rate)
+{
+	struct clk *parent_clk;
+	u32 parent_rate;
+	int ret;
+
+	/* determine and set the best rate for the parent clock (pll-video) */
+	if ((270000 * 2) % rate == 0)
+		parent_rate = 270000000;
+	else if (297000 % rate == 0)
+		parent_rate = 297000000;
+	else
+		return -EINVAL;			/* unsupported clock */
+
+	parent_clk = clk_get_parent(lcd->clk);
+
+	ret = clk_set_rate(parent_clk, parent_rate);
+	if (ret) {
+		dev_err(lcd->dev, "set parent rate failed %d\n", ret);
+		return ret;
+	}
+	ret = clk_set_rate(lcd->clk, rate * 1000);
+	if (ret) {
+		dev_err(lcd->dev, "set rate failed %d\n", ret);
+		return ret;
+	}
+
+	/* enable the clock */
+	reset_control_deassert(lcd->reset);
+	clk_prepare_enable(lcd->bus);
+	clk_prepare_enable(lcd->clk);
+
+	return ret;
+}
+
+static void de2_tcon_init(struct lcd *lcd)
+{
+	andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
+	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+
+	/* disable/ack interrupts */
+	writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
+}
+
+static void de2_tcon_enable(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;
+
+	orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
+
+	data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
+	writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
+	writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
+	writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
+	writel_relaxed(XY(mode->htotal - 1,
+			 mode->htotal - mode->hsync_start - 1),
+		      lcd->mmio + TCON1_BASIC3_REG);
+	writel_relaxed(XY(mode->vtotal * (3 - interlace),
+			 mode->vtotal - mode->vsync_start - 1),
+		      lcd->mmio + TCON1_BASIC4_REG);
+	writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
+			 mode->vsync_end - mode->vsync_start - 1),
+		      lcd->mmio + TCON1_BASIC5_REG);
+
+	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;
+	writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
+
+	andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
+
+	if (interlace == 2)
+		orl_relaxed(lcd->mmio + TCON1_CTL_REG,
+			    TCON1_CTL_INTERLACE_ENABLE);
+	else
+		andl_relaxed(lcd->mmio + TCON1_CTL_REG,
+			     ~TCON1_CTL_INTERLACE_ENABLE);
+
+	writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
+	writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
+	writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
+	writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
+
+	start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
+	if (start_delay > 31)
+		start_delay = 31;
+	data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
+	data &= ~TCON1_CTL_Start_Delay_MASK;
+	data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
+	writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
+
+	orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
+}
+
+static void de2_tcon_disable(struct lcd *lcd)
+{
+	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+}
+
+static void de2_crtc_enable(struct drm_crtc *crtc)
+{
+	struct lcd *lcd = crtc_to_lcd(crtc);
+	struct drm_display_mode *mode = &crtc->mode;
+
+	if (de2_crtc_set_clock(lcd, mode->clock) < 0)
+		return;
+	lcd->clk_enabled = true;
+
+	/* start the TCON and the DE */
+	de2_tcon_enable(lcd);
+	de2_de_enable(lcd);
+
+	/* turn on blanking interrupt */
+	drm_crtc_vblank_on(crtc);
+}
+
+static void de2_crtc_disable(struct drm_crtc *crtc,
+				struct drm_crtc_state *old_crtc_state)
+{
+	struct lcd *lcd = crtc_to_lcd(crtc);
+
+	if (!lcd->clk_enabled)
+		return;			/* already disabled */
+	lcd->clk_enabled = false;
+
+	de2_de_disable(lcd);
+
+	drm_crtc_vblank_off(crtc);
+
+	de2_tcon_disable(lcd);
+
+	clk_disable_unprepare(lcd->clk);
+	clk_disable_unprepare(lcd->bus);
+	reset_control_assert(lcd->reset);
+}
+
+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,
+	.atomic_disable	= de2_crtc_disable,
+};
+
+/* device init */
+static int de2_lcd_bind(struct device *dev, struct device *master,
+			void *data)
+{
+	struct drm_device *drm = data;
+	struct priv *priv = drm_to_priv(drm);
+	struct lcd *lcd = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &lcd->crtc;
+	int ret, i, crtc_ix;
+
+	lcd->priv = priv;
+
+	/* set the CRTC reference */
+	crtc_ix = drm_crtc_index(crtc);
+	if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
+		dev_err(drm->dev, "Bad crtc index");
+		return -ENOENT;
+	}
+	priv->lcds[crtc_ix] = lcd;
+
+	/* and the mixer index (DT port index in the DE) */
+	for (i = 0; ; i++) {
+		struct device_node *port;
+
+		port = of_parse_phandle(drm->dev->of_node, "ports", i);
+		if (!port)
+			break;
+		if (port == lcd->crtc.port) {
+			lcd->mixer = i;
+			break;
+		}
+	}
+
+	ret = de2_plane_init(drm, lcd);
+	if (ret < 0)
+		return ret;
+
+	drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
+
+	return drm_crtc_init_with_planes(drm, crtc,
+					 &lcd->planes[DE2_PRIMARY_PLANE],
+					 &lcd->planes[DE2_CURSOR_PLANE],
+					 &de2_crtc_funcs, NULL);
+}
+
+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->priv)
+		lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
+}
+
+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;
+
+	lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
+	if (!lcd)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, lcd);
+	lcd->dev = dev;
+	lcd->mixer = 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);
+	}
+
+	/* possible CRTC */
+	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->bus = devm_clk_get(dev, "bus");
+	if (IS_ERR(lcd->bus)) {
+		dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
+		ret = PTR_ERR(lcd->bus);
+		goto err;
+	}
+
+	lcd->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(lcd->clk)) {
+		ret = PTR_ERR(lcd->clk);
+		dev_err(dev, "get video clock err %d\n", ret);
+		goto err;
+	}
+
+	lcd->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(lcd->reset)) {
+		ret = PTR_ERR(lcd->reset);
+		dev_err(dev, "get reset err %d\n", ret);
+		goto err;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "unable to get irq\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	de2_tcon_init(lcd);		/* stop TCON and avoid interrupts */
+
+	ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
+				dev_name(dev), lcd);
+	if (ret < 0) {
+		dev_err(dev, "unable to request irq %d\n", irq);
+		goto err;
+	}
+
+	return component_add(dev, &de2_lcd_ops);
+
+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);
+
+	of_node_put(lcd->crtc.port);
+
+	return 0;
+}
+
+static const struct of_device_id de2_lcd_ids[] = {
+	{ .compatible = "allwinner,sun8i-a83t-tcon", },
+	{ }
+};
+
+struct platform_driver de2_lcd_platform_driver = {
+	.probe = de2_lcd_probe,
+	.remove = de2_lcd_remove,
+	.driver = {
+		.name = "sun8i-de2-tcon",
+		.of_match_table = of_match_ptr(de2_lcd_ids),
+	},
+};
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
new file mode 100644
index 0000000..c0d34a7
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.h
@@ -0,0 +1,50 @@
+#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 <drm/drm_plane_helper.h>
+
+struct clk;
+struct reset_control;
+struct priv;
+
+/* planes */
+#define DE2_PRIMARY_PLANE 0
+#define DE2_CURSOR_PLANE 1
+#define DE2_N_PLANES 5	/* number of planes - see plane_tb[] in de2_plane.c */
+
+struct lcd {
+	void __iomem *mmio;
+
+	struct device *dev;
+	struct drm_crtc crtc;
+
+	struct priv *priv;	/* DRM/DE private data */
+
+	u8 mixer;		/* LCD (mixer) number */
+	u8 delayed;		/* bitmap of planes with delayed update */
+
+	u8 clk_enabled;		/* used for error in crtc_enable */
+
+	struct clk *clk;
+	struct clk *bus;
+	struct reset_control *reset;
+
+	struct drm_plane planes[DE2_N_PLANES];
+};
+
+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
+
+/* in de2_plane.c */
+void de2_de_enable(struct lcd *lcd);
+void de2_de_disable(struct lcd *lcd);
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
+
+#endif /* __DE2_CRTC_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
new file mode 100644
index 0000000..f96babe
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.c
@@ -0,0 +1,317 @@
+/*
+ * Allwinner DRM driver - DE2 DRM driver
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <drm/drm_of.h>
+#include <linux/component.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "de2_drv.h"
+
+#define DRIVER_NAME	"sun8i-de2"
+#define DRIVER_DESC	"Allwinner DRM DE2"
+#define DRIVER_DATE	"20161101"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static const 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_to_priv(drm);
+
+	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_to_priv(drm);
+
+	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,
+};
+
+/*
+ * Platform driver
+ */
+
+static int de2_drm_bind(struct device *dev)
+{
+	struct drm_device *drm;
+	struct priv *priv;
+	struct resource *res;
+	struct lcd *lcd;
+	int i, ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	drm = &priv->drm;
+	dev_set_drvdata(dev, drm);
+
+	/* get the resources */
+	priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
+
+	res = platform_get_resource(to_platform_device(dev),
+				IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "failed to get memory resource\n");
+		ret = -EINVAL;
+		goto out1;
+	}
+
+	priv->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->mmio)) {
+		ret = PTR_ERR(priv->mmio);
+		dev_err(dev, "failed to map registers %d\n", ret);
+		goto out1;
+	}
+
+	priv->gate = devm_clk_get(dev, "bus");
+	if (IS_ERR(priv->gate)) {
+		ret = PTR_ERR(priv->gate);
+		dev_err(dev, "bus gate err %d\n", ret);
+		goto out1;
+	}
+
+	priv->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		dev_err(dev, "clock err %d\n", ret);
+		goto out1;
+	}
+
+	priv->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(priv->reset)) {
+		ret = PTR_ERR(priv->reset);
+		dev_err(dev, "reset err %d\n", ret);
+		goto out1;
+	}
+
+	mutex_init(&priv->mutex);	/* protect DE I/O accesses */
+
+	ret = drm_dev_init(drm, &de2_drm_driver, dev);
+	if (ret != 0) {
+		dev_err(dev, "dev_init failed %d\n", ret);
+		goto out1;
+	}
+
+	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;
+
+	/* start the subdevices */
+	ret = component_bind_all(dev, drm);
+	if (ret < 0)
+		goto out2;
+
+	/* initialize and disable vertical blanking on all CRTCs */
+	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+	if (ret < 0)
+		dev_warn(dev, "vblank_init failed %d\n", ret);
+
+	for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
+		lcd = priv->lcds[i];
+		if (lcd)
+			de2_vblank_reset(lcd);
+	}
+
+	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 out3;
+	}
+
+	drm_kms_helper_poll_init(drm);
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto out4;
+
+	return 0;
+
+out4:
+	drm_fbdev_cma_fini(priv->fbdev);
+out3:
+	component_unbind_all(dev, drm);
+out2:
+	drm_dev_unref(drm);
+out1:
+	kfree(priv);
+	return ret;
+}
+
+static void de2_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+	struct priv *priv = drm_to_priv(drm);
+
+	drm_dev_unregister(drm);
+
+	drm_fbdev_cma_fini(priv->fbdev);
+	drm_kms_helper_poll_fini(drm);
+	drm_vblank_cleanup(drm);
+	drm_mode_config_cleanup(drm);
+
+	component_unbind_all(dev, drm);
+
+	kfree(priv);
+}
+
+static const struct component_master_ops de2_drm_comp_ops = {
+	.bind = de2_drm_bind,
+	.unbind = de2_drm_unbind,
+};
+
+/*
+ * drm_of_component_probe() does:
+ * - bind of the ports (lcd-controller.port)
+ * - bind of the remote nodes (hdmi, tve..)
+ */
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	if (of_node_cmp(np->name, "port") == 0) {
+		np = of_get_parent(np);
+		of_node_put(np);
+	}
+	return dev->of_node == np;
+}
+
+static int de2_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = drm_of_component_probe(&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,
+		.of_match_table = de2_drm_of_match,
+	},
+};
+
+static int __init de2_drm_init(void)
+{
+	int ret;
+
+	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-GANU6spQydw@public.gmane.org>");
+MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
new file mode 100644
index 0000000..c42c30a
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.h
@@ -0,0 +1,48 @@
+#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 <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+struct drm_fbdev_cma;
+struct lcd;
+
+#define N_LCDS 2
+
+struct priv {
+	struct drm_device drm;
+	void __iomem *mmio;
+	struct clk *clk;
+	struct clk *gate;
+	struct reset_control *reset;
+
+	struct mutex mutex;	/* protect DE I/O access */
+	u8 soc_type;
+#define SOC_A83T 0
+#define SOC_H3 1
+	u8 started;		/* bitmap of started mixers */
+	u8 clean;		/* bitmap of clean mixers */
+
+	struct drm_fbdev_cma *fbdev;
+
+	struct lcd *lcds[N_LCDS]; /* CRTCs */
+};
+
+#define drm_to_priv(x) container_of(x, struct priv, drm)
+
+/* in de2_crtc.c */
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_vblank_reset(struct lcd *lcd);
+extern struct platform_driver de2_lcd_platform_driver;
+
+#endif /* __DE2_DRM_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
new file mode 100644
index 0000000..2fd72dc
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_plane.c
@@ -0,0 +1,734 @@
+/*
+ * Allwinner DRM driver - Display Engine 2
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ *	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 <linux/io.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "de2_drv.h"
+#include "de2_crtc.h"
+
+/* DE2 I/O map */
+
+#define DE2_MOD_REG 0x0000		/* 1 bit per LCD */
+#define DE2_GATE_REG 0x0004
+#define DE2_RESET_REG 0x0008
+#define DE2_DIV_REG 0x000c		/* 4 bits per LCD */
+#define DE2_SEL_REG 0x0010
+
+#define DE2_MIXER0_BASE 0x00100000	/* LCD 0 */
+#define DE2_MIXER1_BASE 0x00200000	/* LCD 1 */
+
+/* mixer registers (addr / mixer base) */
+#define MIXER_GLB_REGS	0x00000		/* global control */
+#define MIXER_BLD_REGS	0x01000		/* alpha blending */
+#define MIXER_CHAN_REGS 0x02000		/* VI/UI overlay channels */
+#define		MIXER_CHAN_SZ 0x1000	/* size of a channel */
+#define MIXER_VSU_REGS	0x20000		/* VSU */
+#define MIXER_GSU1_REGS 0x30000		/* GSUs */
+#define MIXER_GSU2_REGS 0x40000
+#define MIXER_GSU3_REGS 0x50000
+#define MIXER_FCE_REGS	0xa0000		/* FCE */
+#define MIXER_BWS_REGS	0xa2000		/* BWS */
+#define MIXER_LTI_REGS	0xa4000		/* LTI */
+#define MIXER_PEAK_REGS 0xa6000		/* PEAK */
+#define MIXER_ASE_REGS	0xa8000		/* ASE */
+#define MIXER_FCC_REGS	0xaa000		/* FCC */
+#define MIXER_DCSC_REGS 0xb0000		/* DCSC/SMBL */
+
+/* global control */
+#define MIXER_GLB_CTL_REG	0x00
+#define		MIXER_GLB_CTL_rt_en BIT(0)
+#define		MIXER_GLB_CTL_finish_irq_en BIT(4)
+#define		MIXER_GLB_CTL_rtwb_port BIT(12)
+#define MIXER_GLB_STATUS_REG	0x04
+#define MIXER_GLB_DBUFF_REG	0x08
+#define MIXER_GLB_SIZE_REG	0x0c
+
+/* alpha blending */
+#define MIXER_BLD_FCOLOR_CTL_REG	0x00
+#define		MIXER_BLD_FCOLOR_CTL_PEN(pipe)	(0x0100 << (pipe))
+#define	MIXER_BLD_ATTR_N 4		/* number of attribute blocks */
+#define	MIXER_BLD_ATTR_SIZE (4 * 4)	/* size of an attribute block */
+#define MIXER_BLD_ATTRx_FCOLOR(x)	(0x04 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_INSIZE(x)	(0x08 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_OFFSET(x)	(0x0c + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ROUTE_REG	0x80
+#define		MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
+#define MIXER_BLD_PREMULTIPLY_REG	0x84
+#define MIXER_BLD_BKCOLOR_REG	0x88
+#define MIXER_BLD_OUTPUT_SIZE_REG	0x8c
+#define MIXER_BLD_MODEx_REG(x)	(0x90 + 4 * (x))	/* x = 0..3 */
+#define		MIXER_BLD_MODE_SRCOVER	0x03010301
+#define MIXER_BLD_OUT_CTL_REG	0xfc
+
+/* VI channel (channel 0) */
+#define VI_CFG_N		4		/* number of layers */
+#define VI_CFG_SIZE		0x30		/* size of a layer */
+#define VI_CFGx_ATTR(l)		(0x00 + VI_CFG_SIZE * (l))
+#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)
+#define VI_CFGx_SIZE(l)		(0x04 + VI_CFG_SIZE * (l))
+#define VI_CFGx_COORD(l)	(0x08 + VI_CFG_SIZE * (l))
+#define VI_N_PLANES 3
+#define VI_CFGx_PITCHy(l, p)	(0x0c + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_FCOLORx(l)		(0xc0 + 4 * (l))
+#define VI_TOP_HADDRx(p)	(0xd0 + 4 * (p))
+#define VI_BOT_HADDRx(p)	(0xdc + 4 * (p))
+#define VI_OVL_SIZEx(n)		(0xe8 + 4 * (n))
+#define VI_HORI_DSx(n)		(0xf0 + 4 * (n))
+#define VI_VERT_DSx(n)		(0xf8 + 4 * (n))
+#define VI_SIZE			0x100
+
+/* UI channel (channels 1..3) */
+#define UI_CFG_N		4		/* number of layers */
+#define UI_CFG_SIZE		(8 * 4)		/* size of a layer */
+#define UI_CFGx_ATTR(l)		(0x00 + UI_CFG_SIZE * (l))
+#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)
+#define UI_CFGx_SIZE(l)		(0x04 + UI_CFG_SIZE * (l))
+#define UI_CFGx_COORD(l)	(0x08 + UI_CFG_SIZE * (l))
+#define UI_CFGx_PITCH(l)	(0x0c + UI_CFG_SIZE * (l))
+#define UI_CFGx_TOP_LADDR(l)	(0x10 + UI_CFG_SIZE * (l))
+#define UI_CFGx_BOT_LADDR(l)	(0x14 + UI_CFG_SIZE * (l))
+#define UI_CFGx_FCOLOR(l)	(0x18 + UI_CFG_SIZE * (l))
+#define UI_TOP_HADDR		0x80
+#define UI_BOT_HADDR		0x84
+#define UI_OVL_SIZE		0x88
+#define UI_SIZE			0x8c
+
+/* 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	/* YVYU */
+#define DE2_FORMAT_YUV422_I_UYVY 2	/* UYVY */
+#define DE2_FORMAT_YUV422_I_YUYV 3	/* YUYV */
+#define DE2_FORMAT_YUV422_P 6		/* YYYY UU VV planar */
+#define DE2_FORMAT_YUV420_P 10		/* YYYY U V planar */
+
+/* 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,
+};
+
+/*
+ * plane table
+ *
+ * The chosen channel/layer assignment of the planes respects
+ * the following constraints:
+ * - the cursor must be in a channel higher than the primary channel
+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
+ */
+static const struct {
+	u8 chan;
+	u8 layer;
+	u8 pipe;
+	u8 type;			/* plane type */
+	const uint32_t *formats;
+	u8 n_formats;
+} plane_tb[] = {
+	[DE2_PRIMARY_PLANE] = {		/* primary plane: channel 0 (VI) */
+		0, 0, 0,
+		DRM_PLANE_TYPE_PRIMARY,
+		ui_formats, ARRAY_SIZE(ui_formats),
+	},
+	[DE2_CURSOR_PLANE] = {		/* cursor: channel 1 (UI) */
+		1, 0, 1,
+		DRM_PLANE_TYPE_CURSOR,
+		ui_formats, ARRAY_SIZE(ui_formats),
+	},
+	{
+		0, 1, 0,		/* 1st overlay: channel 0, layer 1 */
+		DRM_PLANE_TYPE_OVERLAY,
+		vi_formats, ARRAY_SIZE(vi_formats),
+	},
+	{
+		0, 2, 0,		/* 2nd overlay: channel 0, layer 2 */
+		DRM_PLANE_TYPE_OVERLAY,
+		vi_formats, ARRAY_SIZE(vi_formats),
+	},
+	{
+		0, 3, 0,		/* 3rd overlay: channel 0, layer 3 */
+		DRM_PLANE_TYPE_OVERLAY,
+		vi_formats, ARRAY_SIZE(vi_formats),
+	},
+};
+
+static inline void andl_relaxed(void __iomem *addr, u32 val)
+{
+	writel_relaxed(readl_relaxed(addr) & val, addr);
+}
+
+static inline void orl_relaxed(void __iomem *addr, u32 val)
+{
+	writel_relaxed(readl_relaxed(addr) | val, addr);
+}
+
+/* alert the DE processor about changes in a mixer configuration */
+static void de2_mixer_select(struct priv *priv,
+			int mixer,
+			void __iomem *mixer_io)
+{
+	/* select the mixer */
+	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+
+	/* double register switch */
+	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+}
+
+/*
+ * cleanup a mixer
+ *
+ * This is needed only once after power on.
+ */
+static void de2_mixer_cleanup(struct priv *priv, int mixer,
+				u32 size)
+{
+	void __iomem *mixer_io = priv->mmio;
+	void __iomem *chan_io;
+	u32 data;
+	unsigned int i;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+	chan_io = mixer_io + MIXER_CHAN_REGS;
+
+	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+
+	writel_relaxed(MIXER_GLB_CTL_rt_en,
+			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+	/*
+	 * clear the VI/UI channels
+	 *	LCD0: 1 VI and 3 UIs
+	 *	LCD1: 1 VI and 1 UI
+	 */
+	memset_io(chan_io, 0, VI_SIZE);
+	memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
+	if (mixer == 0) {
+		memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
+		memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
+	}
+
+	/* alpha blending */
+	writel_relaxed(0x00000001 |		/* fcolor for primary */
+			MIXER_BLD_FCOLOR_CTL_PEN(0),
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+	for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
+		writel_relaxed(0xff000000,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
+		writel_relaxed(size,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
+		writel_relaxed(0,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
+	}
+	writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+
+	/* prepare the pipe route for the planes */
+	data = 0;
+	for (i = 0; i < DE2_N_PLANES; i++)
+		data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
+	writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
+
+	writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_PREMULTIPLY_REG);
+	writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_BKCOLOR_REG);
+	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_OUTPUT_SIZE_REG);
+	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
+	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
+
+	/* disable the enhancements */
+	writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
+	writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
+	writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
+	writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
+	writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
+	writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
+	writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
+	writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
+	writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
+	writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
+	writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
+}
+
+/* enable a mixer */
+static void de2_mixer_enable(struct lcd *lcd)
+{
+	struct priv *priv = lcd->priv;
+	void __iomem *mixer_io = priv->mmio;
+	struct drm_display_mode *mode = &lcd->crtc.mode;
+	u32 size = WH(mode->hdisplay, mode->vdisplay);
+	u32 data;
+	int mixer = lcd->mixer;
+	int i;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+	/* if not done yet, start the DE processor */
+	if (!priv->started) {
+		reset_control_deassert(priv->reset);
+		clk_prepare_enable(priv->gate);
+		clk_prepare_enable(priv->clk);
+	}
+	priv->started |= 1 << mixer;
+
+	/* set the A83T clock divider (500 / 2) = 250MHz */
+	if (priv->soc_type == SOC_A83T)
+		writel_relaxed(0x00000011, /* div = 2 for both LCDs */
+				priv->mmio + DE2_DIV_REG);
+
+	/* deassert the mixer and enable its clock */
+	orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
+	data = 1 << mixer;			/* 1 bit / lcd */
+	orl_relaxed(priv->mmio + DE2_GATE_REG, data);
+	orl_relaxed(priv->mmio + DE2_MOD_REG, data);
+
+	/* if not done yet, cleanup and enable */
+	if (!(priv->clean & (1 << mixer))) {
+		priv->clean |= 1 << mixer;
+		de2_mixer_cleanup(priv, mixer, size);
+		return;
+	}
+
+	/* enable */
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	writel_relaxed(MIXER_GLB_CTL_rt_en,
+			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+	/* set the size of the frame buffer */
+	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+	for (i = 0; i < MIXER_BLD_ATTR_N; i++)
+		writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+				MIXER_BLD_ATTRx_INSIZE(i));
+	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_OUTPUT_SIZE_REG);
+
+	writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+}
+
+/* enable a LCD (DE mixer) */
+void de2_de_enable(struct lcd *lcd)
+{
+	mutex_lock(&lcd->priv->mutex);
+
+	de2_mixer_enable(lcd);
+
+	mutex_unlock(&lcd->priv->mutex);
+}
+
+/* disable a LCD (DE mixer) */
+void de2_de_disable(struct lcd *lcd)
+{
+	struct priv *priv = lcd->priv;
+	void __iomem *mixer_io = priv->mmio;
+	int mixer = lcd->mixer;
+	u32 data;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+	mutex_lock(&priv->mutex);
+
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+
+	data = ~(1 << mixer);
+	andl_relaxed(priv->mmio + DE2_MOD_REG, data);
+	andl_relaxed(priv->mmio + DE2_GATE_REG, data);
+	andl_relaxed(priv->mmio + DE2_RESET_REG, data);
+
+	mutex_unlock(&priv->mutex);
+
+	/* if all mixers are disabled, stop the DE */
+	priv->started &= ~(1 << mixer);
+	if (!priv->started) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->gate);
+		reset_control_assert(priv->reset);
+	}
+}
+
+static void de2_vi_update(void __iomem *chan_io,
+			  struct drm_gem_cma_object *gem,
+			  int layer,
+			  unsigned int fmt,
+			  u32 ui_sel,
+			  u32 size,
+			  u32 coord,
+			  struct drm_framebuffer *fb,
+			  u32 screen_size)
+{
+	int i;
+
+	writel_relaxed(VI_CFG_ATTR_en |
+			(fmt << VI_CFG_ATTR_fmt_SHIFT) |
+			ui_sel,
+			chan_io + VI_CFGx_ATTR(layer));
+	writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
+	writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+	for (i = 0; i < VI_N_PLANES; i++) {
+		writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
+						fb->pitches[0],
+				chan_io + VI_CFGx_PITCHy(layer, i));
+		writel_relaxed(gem->paddr + fb->offsets[i],
+				chan_io + VI_CFGx_TOP_LADDRy(layer, i));
+	}
+	writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
+	if (layer == 0) {
+		writel_relaxed(screen_size,
+				chan_io + VI_OVL_SIZEx(0));
+	}
+}
+
+static void de2_ui_update(void __iomem *chan_io,
+			  struct drm_gem_cma_object *gem,
+			  int layer,
+			  unsigned int fmt,
+			  u32 alpha_glob,
+			  u32 size,
+			  u32 coord,
+			  struct drm_framebuffer *fb,
+			  u32 screen_size)
+{
+	writel_relaxed(UI_CFG_ATTR_en |
+			(fmt << UI_CFG_ATTR_fmt_SHIFT) |
+			alpha_glob,
+			chan_io + UI_CFGx_ATTR(layer));
+	writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
+	writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+	writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
+	writel_relaxed(gem->paddr + fb->offsets[0],
+			chan_io + UI_CFGx_TOP_LADDR(layer));
+	if (layer == 0)
+		writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
+}
+
+static void de2_plane_update(struct priv *priv, struct lcd *lcd,
+				int plane_num,
+				struct drm_plane_state *state,
+				struct drm_plane_state *old_state)
+{
+	void __iomem *mixer_io = priv->mmio;
+	void __iomem *chan_io;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	u32 size = WH(state->crtc_w, state->crtc_h);
+	u32 coord, screen_size;
+	u32 fcolor;
+	u32 ui_sel, alpha_glob;
+	int mixer = lcd->mixer;
+	int chan, layer, x, y;
+	unsigned int fmt;
+
+	chan = plane_tb[plane_num].chan;
+	layer = plane_tb[plane_num].layer;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_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);
+
+	/* if plane update was delayed, force a full update */
+	if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
+			(1 << plane_num)) {
+		priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
+							~(1 << plane_num);
+
+	/* handle plane move */
+	} else if (fb == old_state->fb) {
+		de2_mixer_select(priv, mixer, mixer_io);
+		if (chan == 0)
+			writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+		else
+			writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+		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("de2_plane_update: format %.4s not yet treated\n",
+			(char *) &fb->pixel_format);
+		return;
+	}
+
+	/* the overlay size is the one of the primary plane */
+	screen_size = plane_num == DE2_PRIMARY_PLANE ?
+		size :
+		readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+	/* prepare pipe enable */
+	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+				MIXER_BLD_FCOLOR_CTL_REG);
+	fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
+
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	if (chan == 0)				/* VI channel */
+		de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
+				fb, screen_size);
+	else					/* UI channel */
+		de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
+				fb, screen_size);
+	writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
+				MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static int vi_nb_layers(void __iomem *chan_io)
+{
+	int layer, n = 0;
+
+	for (layer = 0; layer < 4; layer++) {
+		if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
+			n++;
+	}
+
+	return n;
+}
+
+static int ui_nb_layers(void __iomem *chan_io)
+{
+	int layer, n = 0;
+
+	for (layer = 0; layer < 4; layer++) {
+		if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
+			n++;
+	}
+
+	return n;
+}
+
+static void de2_plane_disable(struct priv *priv,
+				int mixer, int plane_num)
+{
+	void __iomem *mixer_io = priv->mmio;
+	void __iomem *chan_io;
+	u32 fcolor;
+	int chan, layer, n;
+
+	chan = plane_tb[plane_num].chan;
+	layer = plane_tb[plane_num].layer;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
+
+	if (chan == 0)
+		n = vi_nb_layers(chan_io);
+	else
+		n = ui_nb_layers(chan_io);
+
+	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_FCOLOR_CTL_REG);
+
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	if (chan == 0)
+		writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
+	else
+		writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
+
+	/* disable the pipe if no more active layer */
+	if (n <= 1)
+		writel_relaxed(fcolor &
+			~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static void de2_drm_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 priv *priv = lcd->priv;
+	int plane_num = plane - lcd->planes;
+
+	/* if the crtc is disabled, mark update delayed */
+	if (!(priv->started & (1 << lcd->mixer))) {
+		lcd->delayed |= 1 << plane_num;
+		return;				/* mixer disabled */
+	}
+
+	mutex_lock(&priv->mutex);
+
+	de2_plane_update(priv, lcd, plane_num, state, old_state);
+
+	mutex_unlock(&priv->mutex);
+}
+
+static void de2_drm_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);
+	struct priv *priv = lcd->priv;
+	int plane_num = plane - lcd->planes;
+
+	if (!(priv->started & (1 << lcd->mixer)))
+		return;				/* mixer disabled */
+
+	mutex_lock(&priv->mutex);
+
+	de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
+
+	mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+	.atomic_update = de2_drm_plane_update,
+	.atomic_disable = de2_drm_plane_disable,
+};
+
+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 possible_crtcs,
+				int plane_num)
+{
+	int ret;
+
+	ret = drm_universal_plane_init(drm, plane, possible_crtcs,
+				&plane_funcs,
+				plane_tb[plane_num].formats,
+				plane_tb[plane_num].n_formats,
+				plane_tb[plane_num].type, NULL);
+	if (ret >= 0)
+		drm_plane_helper_add(plane, &plane_helper_funcs);
+
+	return ret;
+}
+
+/* initialize the planes */
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
+{
+	int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
+
+	n = ARRAY_SIZE(plane_tb);
+	if (n != DE2_N_PLANES) {
+		dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
+			__stringify(DE2_N_PLANES) "\n", n);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < n; i++) {
+		ret = de2_one_plane_init(drm, &lcd->planes[i],
+				possible_crtcs, i);
+		if (ret < 0) {
+			dev_err(lcd->dev, "plane init failed %d\n", ret);
+			break;
+		}
+	}
+
+	return ret;
+}
-- 
2.10.2

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

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

* [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
@ 2016-11-28 14:23     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-28 14:23 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
 drivers/gpu/drm/Kconfig           |   2 +
 drivers/gpu/drm/Makefile          |   1 +
 drivers/gpu/drm/sun8i/Kconfig     |  19 +
 drivers/gpu/drm/sun8i/Makefile    |   7 +
 drivers/gpu/drm/sun8i/de2_crtc.c  | 449 +++++++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_crtc.h  |  50 +++
 drivers/gpu/drm/sun8i/de2_drv.c   | 317 ++++++++++++++++
 drivers/gpu/drm/sun8i/de2_drv.h   |  48 +++
 drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
 9 files changed, 1627 insertions(+)
 create mode 100644 drivers/gpu/drm/sun8i/Kconfig
 create mode 100644 drivers/gpu/drm/sun8i/Makefile
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 95fc041..bb1bfbc 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
 
 source "drivers/gpu/drm/sun4i/Kconfig"
 
+source "drivers/gpu/drm/sun8i/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 883f3e7..3e1eaa0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-y			+= omapdrm/
 obj-$(CONFIG_DRM_SUN4I) += sun4i/
+obj-$(CONFIG_DRM_SUN8I) += sun8i/
 obj-y			+= tilcdc/
 obj-$(CONFIG_DRM_QXL) += qxl/
 obj-$(CONFIG_DRM_BOCHS) += bochs/
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
new file mode 100644
index 0000000..6940895
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -0,0 +1,19 @@
+#
+# Allwinner DE2 Video configuration
+#
+
+config DRM_SUN8I
+	bool
+
+config DRM_SUN8I_DE2
+	tristate "Support for Allwinner Video with DE2 interface"
+	depends on DRM && OF
+	depends on ARCH_SUNXI || COMPILE_TEST
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_KMS_HELPER
+	select DRM_SUN8I
+	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
+	  sun8i-de2-drm.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
new file mode 100644
index 0000000..f107919
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Allwinner's sun8i DRM device driver
+#
+
+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+
+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
new file mode 100644
index 0000000..4e94ccc
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.c
@@ -0,0 +1,449 @@
+/*
+ * 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 <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+
+#include "de2_drv.h"
+#include "de2_crtc.h"
+
+/* I/O map */
+
+#define TCON_GCTL_REG		0x00
+#define		TCON_GCTL_TCON_ENABLE BIT(31)
+#define TCON_GINT0_REG		0x04
+#define		TCON_GINT0_TCON1_Vb_Int_En BIT(30)
+#define		TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
+#define		TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
+#define TCON0_CTL_REG		0x40
+#define		TCON0_CTL_TCON_ENABLE BIT(31)
+#define TCON1_CTL_REG		0x90
+#define		TCON1_CTL_TCON_ENABLE BIT(31)
+#define		TCON1_CTL_INTERLACE_ENABLE BIT(20)
+#define		TCON1_CTL_Start_Delay_SHIFT 4
+#define		TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
+#define TCON1_BASIC0_REG	0x94	/* XI/YI */
+#define TCON1_BASIC1_REG	0x98	/* LS_XO/LS_YO */
+#define TCON1_BASIC2_REG	0x9c	/* XO/YO */
+#define TCON1_BASIC3_REG	0xa0	/* HT/HBP */
+#define TCON1_BASIC4_REG	0xa4	/* VT/VBP */
+#define TCON1_BASIC5_REG	0xa8	/* HSPW/VSPW */
+#define TCON1_PS_SYNC_REG	0xb0
+#define TCON1_IO_POL_REG	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)
+#define TCON1_IO_TRI_REG	0xf4
+#define TCON_CEU_CTL_REG	0x100
+#define		TCON_CEU_CTL_ceu_en BIT(31)
+#define	TCON1_FILL_CTL_REG	0x300
+#define TCON1_FILL_START0_REG	0x304
+#define TCON1_FILL_END0_REG	0x308
+#define TCON1_FILL_DATA0_REG	0x30c
+
+#define XY(x, y) (((x) << 16) | (y))
+
+#define andl_relaxed(addr, val) \
+	writel_relaxed(readl_relaxed(addr) & val, addr)
+#define orl_relaxed(addr, val) \
+	writel_relaxed(readl_relaxed(addr) | val, addr)
+
+/* 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 = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
+
+	drm_crtc_handle_vblank(&lcd->crtc);
+
+	writel_relaxed(isr &
+			~(TCON_GINT0_TCON1_Vb_Int_Flag |
+			  TCON_GINT0_TCON1_Vb_Line_Int_Flag),
+			lcd->mmio + TCON_GINT0_REG);
+
+	return IRQ_HANDLED;
+}
+
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+	struct priv *priv = drm_to_priv(drm);
+	struct lcd *lcd = priv->lcds[crtc_ix];
+
+	orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
+
+	return 0;
+}
+
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+	struct priv *priv = drm_to_priv(drm);
+	struct lcd *lcd = priv->lcds[crtc_ix];
+
+	andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
+}
+
+void de2_vblank_reset(struct lcd *lcd)
+{
+	drm_crtc_vblank_reset(&lcd->crtc);
+}
+
+/* frame functions */
+static int de2_crtc_set_clock(struct lcd *lcd, int rate)
+{
+	struct clk *parent_clk;
+	u32 parent_rate;
+	int ret;
+
+	/* determine and set the best rate for the parent clock (pll-video) */
+	if ((270000 * 2) % rate == 0)
+		parent_rate = 270000000;
+	else if (297000 % rate == 0)
+		parent_rate = 297000000;
+	else
+		return -EINVAL;			/* unsupported clock */
+
+	parent_clk = clk_get_parent(lcd->clk);
+
+	ret = clk_set_rate(parent_clk, parent_rate);
+	if (ret) {
+		dev_err(lcd->dev, "set parent rate failed %d\n", ret);
+		return ret;
+	}
+	ret = clk_set_rate(lcd->clk, rate * 1000);
+	if (ret) {
+		dev_err(lcd->dev, "set rate failed %d\n", ret);
+		return ret;
+	}
+
+	/* enable the clock */
+	reset_control_deassert(lcd->reset);
+	clk_prepare_enable(lcd->bus);
+	clk_prepare_enable(lcd->clk);
+
+	return ret;
+}
+
+static void de2_tcon_init(struct lcd *lcd)
+{
+	andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
+	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+
+	/* disable/ack interrupts */
+	writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
+}
+
+static void de2_tcon_enable(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;
+
+	orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
+
+	data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
+	writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
+	writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
+	writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
+	writel_relaxed(XY(mode->htotal - 1,
+			 mode->htotal - mode->hsync_start - 1),
+		      lcd->mmio + TCON1_BASIC3_REG);
+	writel_relaxed(XY(mode->vtotal * (3 - interlace),
+			 mode->vtotal - mode->vsync_start - 1),
+		      lcd->mmio + TCON1_BASIC4_REG);
+	writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
+			 mode->vsync_end - mode->vsync_start - 1),
+		      lcd->mmio + TCON1_BASIC5_REG);
+
+	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;
+	writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
+
+	andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
+
+	if (interlace == 2)
+		orl_relaxed(lcd->mmio + TCON1_CTL_REG,
+			    TCON1_CTL_INTERLACE_ENABLE);
+	else
+		andl_relaxed(lcd->mmio + TCON1_CTL_REG,
+			     ~TCON1_CTL_INTERLACE_ENABLE);
+
+	writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
+	writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
+	writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
+	writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
+
+	start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
+	if (start_delay > 31)
+		start_delay = 31;
+	data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
+	data &= ~TCON1_CTL_Start_Delay_MASK;
+	data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
+	writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
+
+	orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
+}
+
+static void de2_tcon_disable(struct lcd *lcd)
+{
+	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+}
+
+static void de2_crtc_enable(struct drm_crtc *crtc)
+{
+	struct lcd *lcd = crtc_to_lcd(crtc);
+	struct drm_display_mode *mode = &crtc->mode;
+
+	if (de2_crtc_set_clock(lcd, mode->clock) < 0)
+		return;
+	lcd->clk_enabled = true;
+
+	/* start the TCON and the DE */
+	de2_tcon_enable(lcd);
+	de2_de_enable(lcd);
+
+	/* turn on blanking interrupt */
+	drm_crtc_vblank_on(crtc);
+}
+
+static void de2_crtc_disable(struct drm_crtc *crtc,
+				struct drm_crtc_state *old_crtc_state)
+{
+	struct lcd *lcd = crtc_to_lcd(crtc);
+
+	if (!lcd->clk_enabled)
+		return;			/* already disabled */
+	lcd->clk_enabled = false;
+
+	de2_de_disable(lcd);
+
+	drm_crtc_vblank_off(crtc);
+
+	de2_tcon_disable(lcd);
+
+	clk_disable_unprepare(lcd->clk);
+	clk_disable_unprepare(lcd->bus);
+	reset_control_assert(lcd->reset);
+}
+
+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,
+	.atomic_disable	= de2_crtc_disable,
+};
+
+/* device init */
+static int de2_lcd_bind(struct device *dev, struct device *master,
+			void *data)
+{
+	struct drm_device *drm = data;
+	struct priv *priv = drm_to_priv(drm);
+	struct lcd *lcd = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &lcd->crtc;
+	int ret, i, crtc_ix;
+
+	lcd->priv = priv;
+
+	/* set the CRTC reference */
+	crtc_ix = drm_crtc_index(crtc);
+	if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
+		dev_err(drm->dev, "Bad crtc index");
+		return -ENOENT;
+	}
+	priv->lcds[crtc_ix] = lcd;
+
+	/* and the mixer index (DT port index in the DE) */
+	for (i = 0; ; i++) {
+		struct device_node *port;
+
+		port = of_parse_phandle(drm->dev->of_node, "ports", i);
+		if (!port)
+			break;
+		if (port == lcd->crtc.port) {
+			lcd->mixer = i;
+			break;
+		}
+	}
+
+	ret = de2_plane_init(drm, lcd);
+	if (ret < 0)
+		return ret;
+
+	drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
+
+	return drm_crtc_init_with_planes(drm, crtc,
+					 &lcd->planes[DE2_PRIMARY_PLANE],
+					 &lcd->planes[DE2_CURSOR_PLANE],
+					 &de2_crtc_funcs, NULL);
+}
+
+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->priv)
+		lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
+}
+
+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;
+
+	lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
+	if (!lcd)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, lcd);
+	lcd->dev = dev;
+	lcd->mixer = 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);
+	}
+
+	/* possible CRTC */
+	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->bus = devm_clk_get(dev, "bus");
+	if (IS_ERR(lcd->bus)) {
+		dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
+		ret = PTR_ERR(lcd->bus);
+		goto err;
+	}
+
+	lcd->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(lcd->clk)) {
+		ret = PTR_ERR(lcd->clk);
+		dev_err(dev, "get video clock err %d\n", ret);
+		goto err;
+	}
+
+	lcd->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(lcd->reset)) {
+		ret = PTR_ERR(lcd->reset);
+		dev_err(dev, "get reset err %d\n", ret);
+		goto err;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "unable to get irq\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	de2_tcon_init(lcd);		/* stop TCON and avoid interrupts */
+
+	ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
+				dev_name(dev), lcd);
+	if (ret < 0) {
+		dev_err(dev, "unable to request irq %d\n", irq);
+		goto err;
+	}
+
+	return component_add(dev, &de2_lcd_ops);
+
+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);
+
+	of_node_put(lcd->crtc.port);
+
+	return 0;
+}
+
+static const struct of_device_id de2_lcd_ids[] = {
+	{ .compatible = "allwinner,sun8i-a83t-tcon", },
+	{ }
+};
+
+struct platform_driver de2_lcd_platform_driver = {
+	.probe = de2_lcd_probe,
+	.remove = de2_lcd_remove,
+	.driver = {
+		.name = "sun8i-de2-tcon",
+		.of_match_table = of_match_ptr(de2_lcd_ids),
+	},
+};
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
new file mode 100644
index 0000000..c0d34a7
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.h
@@ -0,0 +1,50 @@
+#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 <drm/drm_plane_helper.h>
+
+struct clk;
+struct reset_control;
+struct priv;
+
+/* planes */
+#define DE2_PRIMARY_PLANE 0
+#define DE2_CURSOR_PLANE 1
+#define DE2_N_PLANES 5	/* number of planes - see plane_tb[] in de2_plane.c */
+
+struct lcd {
+	void __iomem *mmio;
+
+	struct device *dev;
+	struct drm_crtc crtc;
+
+	struct priv *priv;	/* DRM/DE private data */
+
+	u8 mixer;		/* LCD (mixer) number */
+	u8 delayed;		/* bitmap of planes with delayed update */
+
+	u8 clk_enabled;		/* used for error in crtc_enable */
+
+	struct clk *clk;
+	struct clk *bus;
+	struct reset_control *reset;
+
+	struct drm_plane planes[DE2_N_PLANES];
+};
+
+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
+
+/* in de2_plane.c */
+void de2_de_enable(struct lcd *lcd);
+void de2_de_disable(struct lcd *lcd);
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
+
+#endif /* __DE2_CRTC_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
new file mode 100644
index 0000000..f96babe
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.c
@@ -0,0 +1,317 @@
+/*
+ * 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/of_device.h>
+#include <drm/drm_of.h>
+#include <linux/component.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "de2_drv.h"
+
+#define DRIVER_NAME	"sun8i-de2"
+#define DRIVER_DESC	"Allwinner DRM DE2"
+#define DRIVER_DATE	"20161101"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static const 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_to_priv(drm);
+
+	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_to_priv(drm);
+
+	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,
+};
+
+/*
+ * Platform driver
+ */
+
+static int de2_drm_bind(struct device *dev)
+{
+	struct drm_device *drm;
+	struct priv *priv;
+	struct resource *res;
+	struct lcd *lcd;
+	int i, ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	drm = &priv->drm;
+	dev_set_drvdata(dev, drm);
+
+	/* get the resources */
+	priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
+
+	res = platform_get_resource(to_platform_device(dev),
+				IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "failed to get memory resource\n");
+		ret = -EINVAL;
+		goto out1;
+	}
+
+	priv->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->mmio)) {
+		ret = PTR_ERR(priv->mmio);
+		dev_err(dev, "failed to map registers %d\n", ret);
+		goto out1;
+	}
+
+	priv->gate = devm_clk_get(dev, "bus");
+	if (IS_ERR(priv->gate)) {
+		ret = PTR_ERR(priv->gate);
+		dev_err(dev, "bus gate err %d\n", ret);
+		goto out1;
+	}
+
+	priv->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		dev_err(dev, "clock err %d\n", ret);
+		goto out1;
+	}
+
+	priv->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(priv->reset)) {
+		ret = PTR_ERR(priv->reset);
+		dev_err(dev, "reset err %d\n", ret);
+		goto out1;
+	}
+
+	mutex_init(&priv->mutex);	/* protect DE I/O accesses */
+
+	ret = drm_dev_init(drm, &de2_drm_driver, dev);
+	if (ret != 0) {
+		dev_err(dev, "dev_init failed %d\n", ret);
+		goto out1;
+	}
+
+	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;
+
+	/* start the subdevices */
+	ret = component_bind_all(dev, drm);
+	if (ret < 0)
+		goto out2;
+
+	/* initialize and disable vertical blanking on all CRTCs */
+	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+	if (ret < 0)
+		dev_warn(dev, "vblank_init failed %d\n", ret);
+
+	for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
+		lcd = priv->lcds[i];
+		if (lcd)
+			de2_vblank_reset(lcd);
+	}
+
+	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 out3;
+	}
+
+	drm_kms_helper_poll_init(drm);
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto out4;
+
+	return 0;
+
+out4:
+	drm_fbdev_cma_fini(priv->fbdev);
+out3:
+	component_unbind_all(dev, drm);
+out2:
+	drm_dev_unref(drm);
+out1:
+	kfree(priv);
+	return ret;
+}
+
+static void de2_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+	struct priv *priv = drm_to_priv(drm);
+
+	drm_dev_unregister(drm);
+
+	drm_fbdev_cma_fini(priv->fbdev);
+	drm_kms_helper_poll_fini(drm);
+	drm_vblank_cleanup(drm);
+	drm_mode_config_cleanup(drm);
+
+	component_unbind_all(dev, drm);
+
+	kfree(priv);
+}
+
+static const struct component_master_ops de2_drm_comp_ops = {
+	.bind = de2_drm_bind,
+	.unbind = de2_drm_unbind,
+};
+
+/*
+ * drm_of_component_probe() does:
+ * - bind of the ports (lcd-controller.port)
+ * - bind of the remote nodes (hdmi, tve..)
+ */
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	if (of_node_cmp(np->name, "port") == 0) {
+		np = of_get_parent(np);
+		of_node_put(np);
+	}
+	return dev->of_node == np;
+}
+
+static int de2_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = drm_of_component_probe(&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,
+		.of_match_table = de2_drm_of_match,
+	},
+};
+
+static int __init de2_drm_init(void)
+{
+	int ret;
+
+	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/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
new file mode 100644
index 0000000..c42c30a
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.h
@@ -0,0 +1,48 @@
+#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 <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+struct drm_fbdev_cma;
+struct lcd;
+
+#define N_LCDS 2
+
+struct priv {
+	struct drm_device drm;
+	void __iomem *mmio;
+	struct clk *clk;
+	struct clk *gate;
+	struct reset_control *reset;
+
+	struct mutex mutex;	/* protect DE I/O access */
+	u8 soc_type;
+#define SOC_A83T 0
+#define SOC_H3 1
+	u8 started;		/* bitmap of started mixers */
+	u8 clean;		/* bitmap of clean mixers */
+
+	struct drm_fbdev_cma *fbdev;
+
+	struct lcd *lcds[N_LCDS]; /* CRTCs */
+};
+
+#define drm_to_priv(x) container_of(x, struct priv, drm)
+
+/* in de2_crtc.c */
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_vblank_reset(struct lcd *lcd);
+extern struct platform_driver de2_lcd_platform_driver;
+
+#endif /* __DE2_DRM_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
new file mode 100644
index 0000000..2fd72dc
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_plane.c
@@ -0,0 +1,734 @@
+/*
+ * Allwinner DRM driver - Display Engine 2
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ *	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 <linux/io.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "de2_drv.h"
+#include "de2_crtc.h"
+
+/* DE2 I/O map */
+
+#define DE2_MOD_REG 0x0000		/* 1 bit per LCD */
+#define DE2_GATE_REG 0x0004
+#define DE2_RESET_REG 0x0008
+#define DE2_DIV_REG 0x000c		/* 4 bits per LCD */
+#define DE2_SEL_REG 0x0010
+
+#define DE2_MIXER0_BASE 0x00100000	/* LCD 0 */
+#define DE2_MIXER1_BASE 0x00200000	/* LCD 1 */
+
+/* mixer registers (addr / mixer base) */
+#define MIXER_GLB_REGS	0x00000		/* global control */
+#define MIXER_BLD_REGS	0x01000		/* alpha blending */
+#define MIXER_CHAN_REGS 0x02000		/* VI/UI overlay channels */
+#define		MIXER_CHAN_SZ 0x1000	/* size of a channel */
+#define MIXER_VSU_REGS	0x20000		/* VSU */
+#define MIXER_GSU1_REGS 0x30000		/* GSUs */
+#define MIXER_GSU2_REGS 0x40000
+#define MIXER_GSU3_REGS 0x50000
+#define MIXER_FCE_REGS	0xa0000		/* FCE */
+#define MIXER_BWS_REGS	0xa2000		/* BWS */
+#define MIXER_LTI_REGS	0xa4000		/* LTI */
+#define MIXER_PEAK_REGS 0xa6000		/* PEAK */
+#define MIXER_ASE_REGS	0xa8000		/* ASE */
+#define MIXER_FCC_REGS	0xaa000		/* FCC */
+#define MIXER_DCSC_REGS 0xb0000		/* DCSC/SMBL */
+
+/* global control */
+#define MIXER_GLB_CTL_REG	0x00
+#define		MIXER_GLB_CTL_rt_en BIT(0)
+#define		MIXER_GLB_CTL_finish_irq_en BIT(4)
+#define		MIXER_GLB_CTL_rtwb_port BIT(12)
+#define MIXER_GLB_STATUS_REG	0x04
+#define MIXER_GLB_DBUFF_REG	0x08
+#define MIXER_GLB_SIZE_REG	0x0c
+
+/* alpha blending */
+#define MIXER_BLD_FCOLOR_CTL_REG	0x00
+#define		MIXER_BLD_FCOLOR_CTL_PEN(pipe)	(0x0100 << (pipe))
+#define	MIXER_BLD_ATTR_N 4		/* number of attribute blocks */
+#define	MIXER_BLD_ATTR_SIZE (4 * 4)	/* size of an attribute block */
+#define MIXER_BLD_ATTRx_FCOLOR(x)	(0x04 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_INSIZE(x)	(0x08 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_OFFSET(x)	(0x0c + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ROUTE_REG	0x80
+#define		MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
+#define MIXER_BLD_PREMULTIPLY_REG	0x84
+#define MIXER_BLD_BKCOLOR_REG	0x88
+#define MIXER_BLD_OUTPUT_SIZE_REG	0x8c
+#define MIXER_BLD_MODEx_REG(x)	(0x90 + 4 * (x))	/* x = 0..3 */
+#define		MIXER_BLD_MODE_SRCOVER	0x03010301
+#define MIXER_BLD_OUT_CTL_REG	0xfc
+
+/* VI channel (channel 0) */
+#define VI_CFG_N		4		/* number of layers */
+#define VI_CFG_SIZE		0x30		/* size of a layer */
+#define VI_CFGx_ATTR(l)		(0x00 + VI_CFG_SIZE * (l))
+#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)
+#define VI_CFGx_SIZE(l)		(0x04 + VI_CFG_SIZE * (l))
+#define VI_CFGx_COORD(l)	(0x08 + VI_CFG_SIZE * (l))
+#define VI_N_PLANES 3
+#define VI_CFGx_PITCHy(l, p)	(0x0c + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_FCOLORx(l)		(0xc0 + 4 * (l))
+#define VI_TOP_HADDRx(p)	(0xd0 + 4 * (p))
+#define VI_BOT_HADDRx(p)	(0xdc + 4 * (p))
+#define VI_OVL_SIZEx(n)		(0xe8 + 4 * (n))
+#define VI_HORI_DSx(n)		(0xf0 + 4 * (n))
+#define VI_VERT_DSx(n)		(0xf8 + 4 * (n))
+#define VI_SIZE			0x100
+
+/* UI channel (channels 1..3) */
+#define UI_CFG_N		4		/* number of layers */
+#define UI_CFG_SIZE		(8 * 4)		/* size of a layer */
+#define UI_CFGx_ATTR(l)		(0x00 + UI_CFG_SIZE * (l))
+#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)
+#define UI_CFGx_SIZE(l)		(0x04 + UI_CFG_SIZE * (l))
+#define UI_CFGx_COORD(l)	(0x08 + UI_CFG_SIZE * (l))
+#define UI_CFGx_PITCH(l)	(0x0c + UI_CFG_SIZE * (l))
+#define UI_CFGx_TOP_LADDR(l)	(0x10 + UI_CFG_SIZE * (l))
+#define UI_CFGx_BOT_LADDR(l)	(0x14 + UI_CFG_SIZE * (l))
+#define UI_CFGx_FCOLOR(l)	(0x18 + UI_CFG_SIZE * (l))
+#define UI_TOP_HADDR		0x80
+#define UI_BOT_HADDR		0x84
+#define UI_OVL_SIZE		0x88
+#define UI_SIZE			0x8c
+
+/* 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	/* YVYU */
+#define DE2_FORMAT_YUV422_I_UYVY 2	/* UYVY */
+#define DE2_FORMAT_YUV422_I_YUYV 3	/* YUYV */
+#define DE2_FORMAT_YUV422_P 6		/* YYYY UU VV planar */
+#define DE2_FORMAT_YUV420_P 10		/* YYYY U V planar */
+
+/* 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,
+};
+
+/*
+ * plane table
+ *
+ * The chosen channel/layer assignment of the planes respects
+ * the following constraints:
+ * - the cursor must be in a channel higher than the primary channel
+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
+ */
+static const struct {
+	u8 chan;
+	u8 layer;
+	u8 pipe;
+	u8 type;			/* plane type */
+	const uint32_t *formats;
+	u8 n_formats;
+} plane_tb[] = {
+	[DE2_PRIMARY_PLANE] = {		/* primary plane: channel 0 (VI) */
+		0, 0, 0,
+		DRM_PLANE_TYPE_PRIMARY,
+		ui_formats, ARRAY_SIZE(ui_formats),
+	},
+	[DE2_CURSOR_PLANE] = {		/* cursor: channel 1 (UI) */
+		1, 0, 1,
+		DRM_PLANE_TYPE_CURSOR,
+		ui_formats, ARRAY_SIZE(ui_formats),
+	},
+	{
+		0, 1, 0,		/* 1st overlay: channel 0, layer 1 */
+		DRM_PLANE_TYPE_OVERLAY,
+		vi_formats, ARRAY_SIZE(vi_formats),
+	},
+	{
+		0, 2, 0,		/* 2nd overlay: channel 0, layer 2 */
+		DRM_PLANE_TYPE_OVERLAY,
+		vi_formats, ARRAY_SIZE(vi_formats),
+	},
+	{
+		0, 3, 0,		/* 3rd overlay: channel 0, layer 3 */
+		DRM_PLANE_TYPE_OVERLAY,
+		vi_formats, ARRAY_SIZE(vi_formats),
+	},
+};
+
+static inline void andl_relaxed(void __iomem *addr, u32 val)
+{
+	writel_relaxed(readl_relaxed(addr) & val, addr);
+}
+
+static inline void orl_relaxed(void __iomem *addr, u32 val)
+{
+	writel_relaxed(readl_relaxed(addr) | val, addr);
+}
+
+/* alert the DE processor about changes in a mixer configuration */
+static void de2_mixer_select(struct priv *priv,
+			int mixer,
+			void __iomem *mixer_io)
+{
+	/* select the mixer */
+	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+
+	/* double register switch */
+	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+}
+
+/*
+ * cleanup a mixer
+ *
+ * This is needed only once after power on.
+ */
+static void de2_mixer_cleanup(struct priv *priv, int mixer,
+				u32 size)
+{
+	void __iomem *mixer_io = priv->mmio;
+	void __iomem *chan_io;
+	u32 data;
+	unsigned int i;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+	chan_io = mixer_io + MIXER_CHAN_REGS;
+
+	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+
+	writel_relaxed(MIXER_GLB_CTL_rt_en,
+			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+	/*
+	 * clear the VI/UI channels
+	 *	LCD0: 1 VI and 3 UIs
+	 *	LCD1: 1 VI and 1 UI
+	 */
+	memset_io(chan_io, 0, VI_SIZE);
+	memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
+	if (mixer == 0) {
+		memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
+		memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
+	}
+
+	/* alpha blending */
+	writel_relaxed(0x00000001 |		/* fcolor for primary */
+			MIXER_BLD_FCOLOR_CTL_PEN(0),
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+	for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
+		writel_relaxed(0xff000000,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
+		writel_relaxed(size,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
+		writel_relaxed(0,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
+	}
+	writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+
+	/* prepare the pipe route for the planes */
+	data = 0;
+	for (i = 0; i < DE2_N_PLANES; i++)
+		data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
+	writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
+
+	writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_PREMULTIPLY_REG);
+	writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_BKCOLOR_REG);
+	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_OUTPUT_SIZE_REG);
+	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
+	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
+
+	/* disable the enhancements */
+	writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
+	writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
+	writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
+	writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
+	writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
+	writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
+	writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
+	writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
+	writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
+	writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
+	writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
+}
+
+/* enable a mixer */
+static void de2_mixer_enable(struct lcd *lcd)
+{
+	struct priv *priv = lcd->priv;
+	void __iomem *mixer_io = priv->mmio;
+	struct drm_display_mode *mode = &lcd->crtc.mode;
+	u32 size = WH(mode->hdisplay, mode->vdisplay);
+	u32 data;
+	int mixer = lcd->mixer;
+	int i;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+	/* if not done yet, start the DE processor */
+	if (!priv->started) {
+		reset_control_deassert(priv->reset);
+		clk_prepare_enable(priv->gate);
+		clk_prepare_enable(priv->clk);
+	}
+	priv->started |= 1 << mixer;
+
+	/* set the A83T clock divider (500 / 2) = 250MHz */
+	if (priv->soc_type == SOC_A83T)
+		writel_relaxed(0x00000011, /* div = 2 for both LCDs */
+				priv->mmio + DE2_DIV_REG);
+
+	/* deassert the mixer and enable its clock */
+	orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
+	data = 1 << mixer;			/* 1 bit / lcd */
+	orl_relaxed(priv->mmio + DE2_GATE_REG, data);
+	orl_relaxed(priv->mmio + DE2_MOD_REG, data);
+
+	/* if not done yet, cleanup and enable */
+	if (!(priv->clean & (1 << mixer))) {
+		priv->clean |= 1 << mixer;
+		de2_mixer_cleanup(priv, mixer, size);
+		return;
+	}
+
+	/* enable */
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	writel_relaxed(MIXER_GLB_CTL_rt_en,
+			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+	/* set the size of the frame buffer */
+	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+	for (i = 0; i < MIXER_BLD_ATTR_N; i++)
+		writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+				MIXER_BLD_ATTRx_INSIZE(i));
+	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_OUTPUT_SIZE_REG);
+
+	writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+}
+
+/* enable a LCD (DE mixer) */
+void de2_de_enable(struct lcd *lcd)
+{
+	mutex_lock(&lcd->priv->mutex);
+
+	de2_mixer_enable(lcd);
+
+	mutex_unlock(&lcd->priv->mutex);
+}
+
+/* disable a LCD (DE mixer) */
+void de2_de_disable(struct lcd *lcd)
+{
+	struct priv *priv = lcd->priv;
+	void __iomem *mixer_io = priv->mmio;
+	int mixer = lcd->mixer;
+	u32 data;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+	mutex_lock(&priv->mutex);
+
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+
+	data = ~(1 << mixer);
+	andl_relaxed(priv->mmio + DE2_MOD_REG, data);
+	andl_relaxed(priv->mmio + DE2_GATE_REG, data);
+	andl_relaxed(priv->mmio + DE2_RESET_REG, data);
+
+	mutex_unlock(&priv->mutex);
+
+	/* if all mixers are disabled, stop the DE */
+	priv->started &= ~(1 << mixer);
+	if (!priv->started) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->gate);
+		reset_control_assert(priv->reset);
+	}
+}
+
+static void de2_vi_update(void __iomem *chan_io,
+			  struct drm_gem_cma_object *gem,
+			  int layer,
+			  unsigned int fmt,
+			  u32 ui_sel,
+			  u32 size,
+			  u32 coord,
+			  struct drm_framebuffer *fb,
+			  u32 screen_size)
+{
+	int i;
+
+	writel_relaxed(VI_CFG_ATTR_en |
+			(fmt << VI_CFG_ATTR_fmt_SHIFT) |
+			ui_sel,
+			chan_io + VI_CFGx_ATTR(layer));
+	writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
+	writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+	for (i = 0; i < VI_N_PLANES; i++) {
+		writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
+						fb->pitches[0],
+				chan_io + VI_CFGx_PITCHy(layer, i));
+		writel_relaxed(gem->paddr + fb->offsets[i],
+				chan_io + VI_CFGx_TOP_LADDRy(layer, i));
+	}
+	writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
+	if (layer == 0) {
+		writel_relaxed(screen_size,
+				chan_io + VI_OVL_SIZEx(0));
+	}
+}
+
+static void de2_ui_update(void __iomem *chan_io,
+			  struct drm_gem_cma_object *gem,
+			  int layer,
+			  unsigned int fmt,
+			  u32 alpha_glob,
+			  u32 size,
+			  u32 coord,
+			  struct drm_framebuffer *fb,
+			  u32 screen_size)
+{
+	writel_relaxed(UI_CFG_ATTR_en |
+			(fmt << UI_CFG_ATTR_fmt_SHIFT) |
+			alpha_glob,
+			chan_io + UI_CFGx_ATTR(layer));
+	writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
+	writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+	writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
+	writel_relaxed(gem->paddr + fb->offsets[0],
+			chan_io + UI_CFGx_TOP_LADDR(layer));
+	if (layer == 0)
+		writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
+}
+
+static void de2_plane_update(struct priv *priv, struct lcd *lcd,
+				int plane_num,
+				struct drm_plane_state *state,
+				struct drm_plane_state *old_state)
+{
+	void __iomem *mixer_io = priv->mmio;
+	void __iomem *chan_io;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	u32 size = WH(state->crtc_w, state->crtc_h);
+	u32 coord, screen_size;
+	u32 fcolor;
+	u32 ui_sel, alpha_glob;
+	int mixer = lcd->mixer;
+	int chan, layer, x, y;
+	unsigned int fmt;
+
+	chan = plane_tb[plane_num].chan;
+	layer = plane_tb[plane_num].layer;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_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);
+
+	/* if plane update was delayed, force a full update */
+	if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
+			(1 << plane_num)) {
+		priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
+							~(1 << plane_num);
+
+	/* handle plane move */
+	} else if (fb == old_state->fb) {
+		de2_mixer_select(priv, mixer, mixer_io);
+		if (chan == 0)
+			writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+		else
+			writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+		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("de2_plane_update: format %.4s not yet treated\n",
+			(char *) &fb->pixel_format);
+		return;
+	}
+
+	/* the overlay size is the one of the primary plane */
+	screen_size = plane_num == DE2_PRIMARY_PLANE ?
+		size :
+		readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+	/* prepare pipe enable */
+	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+				MIXER_BLD_FCOLOR_CTL_REG);
+	fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
+
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	if (chan == 0)				/* VI channel */
+		de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
+				fb, screen_size);
+	else					/* UI channel */
+		de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
+				fb, screen_size);
+	writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
+				MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static int vi_nb_layers(void __iomem *chan_io)
+{
+	int layer, n = 0;
+
+	for (layer = 0; layer < 4; layer++) {
+		if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
+			n++;
+	}
+
+	return n;
+}
+
+static int ui_nb_layers(void __iomem *chan_io)
+{
+	int layer, n = 0;
+
+	for (layer = 0; layer < 4; layer++) {
+		if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
+			n++;
+	}
+
+	return n;
+}
+
+static void de2_plane_disable(struct priv *priv,
+				int mixer, int plane_num)
+{
+	void __iomem *mixer_io = priv->mmio;
+	void __iomem *chan_io;
+	u32 fcolor;
+	int chan, layer, n;
+
+	chan = plane_tb[plane_num].chan;
+	layer = plane_tb[plane_num].layer;
+
+	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
+
+	if (chan == 0)
+		n = vi_nb_layers(chan_io);
+	else
+		n = ui_nb_layers(chan_io);
+
+	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+			MIXER_BLD_FCOLOR_CTL_REG);
+
+	de2_mixer_select(priv, mixer, mixer_io);
+
+	if (chan == 0)
+		writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
+	else
+		writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
+
+	/* disable the pipe if no more active layer */
+	if (n <= 1)
+		writel_relaxed(fcolor &
+			~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
+			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static void de2_drm_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 priv *priv = lcd->priv;
+	int plane_num = plane - lcd->planes;
+
+	/* if the crtc is disabled, mark update delayed */
+	if (!(priv->started & (1 << lcd->mixer))) {
+		lcd->delayed |= 1 << plane_num;
+		return;				/* mixer disabled */
+	}
+
+	mutex_lock(&priv->mutex);
+
+	de2_plane_update(priv, lcd, plane_num, state, old_state);
+
+	mutex_unlock(&priv->mutex);
+}
+
+static void de2_drm_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);
+	struct priv *priv = lcd->priv;
+	int plane_num = plane - lcd->planes;
+
+	if (!(priv->started & (1 << lcd->mixer)))
+		return;				/* mixer disabled */
+
+	mutex_lock(&priv->mutex);
+
+	de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
+
+	mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+	.atomic_update = de2_drm_plane_update,
+	.atomic_disable = de2_drm_plane_disable,
+};
+
+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 possible_crtcs,
+				int plane_num)
+{
+	int ret;
+
+	ret = drm_universal_plane_init(drm, plane, possible_crtcs,
+				&plane_funcs,
+				plane_tb[plane_num].formats,
+				plane_tb[plane_num].n_formats,
+				plane_tb[plane_num].type, NULL);
+	if (ret >= 0)
+		drm_plane_helper_add(plane, &plane_helper_funcs);
+
+	return ret;
+}
+
+/* initialize the planes */
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
+{
+	int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
+
+	n = ARRAY_SIZE(plane_tb);
+	if (n != DE2_N_PLANES) {
+		dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
+			__stringify(DE2_N_PLANES) "\n", n);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < n; i++) {
+		ret = de2_one_plane_init(drm, &lcd->planes[i],
+				possible_crtcs, i);
+		if (ret < 0) {
+			dev_err(lcd->dev, "plane init failed %d\n", ret);
+			break;
+		}
+	}
+
+	return ret;
+}
-- 
2.10.2

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

* [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of Allwinner DE2
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-28 18:02     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-28 18:02 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
 .../bindings/display/sunxi/sun8i-de2.txt           | 121 +++++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt

diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
new file mode 100644
index 0000000..edf38b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
@@ -0,0 +1,121 @@
+Allwinner sun8i Display Engine 2 subsystem
+==========================================
+
+The Allwinner DE2 subsystem contains a display controller (DE2),
+one or two LCD controllers (Timing CONtrollers) and their external
+interfaces (encoders/connectors).
+
+          +-----------+
+          | DE2       |
+          |           |
+          | +-------+ |
+  plane --->|       | |   +------+
+          | | mixer |---->| TCON |---> encoder  ---> display
+  plane --->|       | |   +------+     connector     device
+          | +-------+ |
+          |           |
+          | +-------+ |
+  plane --->|       | |   +------+
+          | | mixer |---->| TCON |---> encoder  ---> display
+  plane --->|       | |   +------+     connector     device
+          | +-------+ |
+          +-----------+
+
+The DE2 contains a processor which mixes the input planes and creates
+the images which are sent to the TCONs.
+From the software point of vue, there are 2 independent real-time
+mixers, each one being statically associated to one TCON.
+
+The TCONs adjust the image format to the one of the display device.
+
+Display controller (DE2)
+========================
+
+Required properties:
+
+- compatible: value should be one of the following
+		"allwinner,sun8i-a83t-display-engine"
+		"allwinner,sun8i-h3-display-engine"
+
+- reg: base address and size of the I/O memory
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: must contain
+		"bus": bus gate
+		"clock": clock
+
+- resets: phandle to the reset of the device
+
+- ports: must contain a list of 2 phandles, indexed by mixer number,
+	and pointing to display interface ports of TCONs
+
+LCD controller (TCON)
+=====================
+
+Required properties:
+
+- compatible: should be
+		"allwinner,sun8i-a83t-tcon"
+
+- reg: base address and size of the I/O memory
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: must contain
+		"bus": bus gate
+		"clock": pixel clock
+
+- resets: phandle to the reset of the device
+
+- interrupts: interrupt number for the TCON
+
+- port: port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+
+	de: de-controller@01000000 {
+		compatible = "allwinner,sun8i-h3-display-engine";
+		reg = <0x01000000 0x400000>;
+		clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
+		clock-names = "bus", "clock";
+		resets = <&ccu RST_BUS_DE>;
+		ports = <&tcon0_p>, <&tcon1_p>;
+	};
+
+	tcon0: lcd-controller@01c0c000 {
+		compatible = "allwinner,sun8i-a83t-tcon";
+		reg = <0x01c0c000 0x400>;
+		clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
+		clock-names = "bus", "clock";
+		resets = <&ccu RST_BUS_TCON0>;
+		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		tcon0_p: port {
+			tcon0_hdmi: endpoint {
+				remote-endpoint = <&hdmi_tcon0>;
+			};
+		};
+	};
+
+	/* not used */
+	tcon1: lcd-controller@01c0d000 {
+		compatible = "allwinner,sun8i-h3-tcon";
+		reg = <0x01c0d000 0x400>;
+		clocks = <&ccu CLK_BUS_TCON1>,
+			 <&ccu CLK_TCON0>;	/* no clock */
+		clock-names = "bus", "clock";
+		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		tcon1_p: port {
+			endpoint {
+				/* empty */
+			};
+		};
+	};
-- 
2.10.2

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

* [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of Allwinner DE2
@ 2016-11-28 18:02     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-28 18:02 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 .../bindings/display/sunxi/sun8i-de2.txt           | 121 +++++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt

diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
new file mode 100644
index 0000000..edf38b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
@@ -0,0 +1,121 @@
+Allwinner sun8i Display Engine 2 subsystem
+==========================================
+
+The Allwinner DE2 subsystem contains a display controller (DE2),
+one or two LCD controllers (Timing CONtrollers) and their external
+interfaces (encoders/connectors).
+
+          +-----------+
+          | DE2       |
+          |           |
+          | +-------+ |
+  plane --->|       | |   +------+
+          | | mixer |---->| TCON |---> encoder  ---> display
+  plane --->|       | |   +------+     connector     device
+          | +-------+ |
+          |           |
+          | +-------+ |
+  plane --->|       | |   +------+
+          | | mixer |---->| TCON |---> encoder  ---> display
+  plane --->|       | |   +------+     connector     device
+          | +-------+ |
+          +-----------+
+
+The DE2 contains a processor which mixes the input planes and creates
+the images which are sent to the TCONs.
+From the software point of vue, there are 2 independent real-time
+mixers, each one being statically associated to one TCON.
+
+The TCONs adjust the image format to the one of the display device.
+
+Display controller (DE2)
+========================
+
+Required properties:
+
+- compatible: value should be one of the following
+		"allwinner,sun8i-a83t-display-engine"
+		"allwinner,sun8i-h3-display-engine"
+
+- reg: base address and size of the I/O memory
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: must contain
+		"bus": bus gate
+		"clock": clock
+
+- resets: phandle to the reset of the device
+
+- ports: must contain a list of 2 phandles, indexed by mixer number,
+	and pointing to display interface ports of TCONs
+
+LCD controller (TCON)
+=====================
+
+Required properties:
+
+- compatible: should be
+		"allwinner,sun8i-a83t-tcon"
+
+- reg: base address and size of the I/O memory
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: must contain
+		"bus": bus gate
+		"clock": pixel clock
+
+- resets: phandle to the reset of the device
+
+- interrupts: interrupt number for the TCON
+
+- 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";
+		reg = <0x01000000 0x400000>;
+		clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
+		clock-names = "bus", "clock";
+		resets = <&ccu RST_BUS_DE>;
+		ports = <&tcon0_p>, <&tcon1_p>;
+	};
+
+	tcon0: lcd-controller at 01c0c000 {
+		compatible = "allwinner,sun8i-a83t-tcon";
+		reg = <0x01c0c000 0x400>;
+		clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
+		clock-names = "bus", "clock";
+		resets = <&ccu RST_BUS_TCON0>;
+		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		tcon0_p: port {
+			tcon0_hdmi: endpoint {
+				remote-endpoint = <&hdmi_tcon0>;
+			};
+		};
+	};
+
+	/* not used */
+	tcon1: lcd-controller at 01c0d000 {
+		compatible = "allwinner,sun8i-h3-tcon";
+		reg = <0x01c0d000 0x400>;
+		clocks = <&ccu CLK_BUS_TCON1>,
+			 <&ccu CLK_TCON0>;	/* no clock */
+		clock-names = "bus", "clock";
+		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		tcon1_p: port {
+			endpoint {
+				/* empty */
+			};
+		};
+	};
-- 
2.10.2

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

* [PATCH v7 3/8] drm: sun8i: add HDMI video support to A83T and H3
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29  8:39     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29  8:39 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
 drivers/gpu/drm/sun8i/Kconfig       |   7 +
 drivers/gpu/drm/sun8i/Makefile      |   2 +
 drivers/gpu/drm/sun8i/de2_hdmi.c    | 440 +++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_hdmi.h    |  51 +++
 drivers/gpu/drm/sun8i/de2_hdmi_io.c | 843 ++++++++++++++++++++++++++++++++++++
 5 files changed, 1343 insertions(+)
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c

diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
index 6940895..5c4607b 100644
--- a/drivers/gpu/drm/sun8i/Kconfig
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -17,3 +17,10 @@ config DRM_SUN8I_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
 	  sun8i-de2-drm.
+
+config DRM_SUN8I_DE2_HDMI
+	tristate "Support for DE2 HDMI"
+	depends on DRM_SUN8I_DE2
+	help
+	  Choose this option if you use want HDMI on DE2.
+	  If M is selected the module will be called sun8i-de2-hdmi.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
index f107919..6ba97c2 100644
--- a/drivers/gpu/drm/sun8i/Makefile
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -3,5 +3,7 @@
 #
 
 sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+sun8i-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
 
 obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
+obj-$(CONFIG_DRM_SUN8I_DE2_HDMI) += sun8i-de2-hdmi.o
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.c b/drivers/gpu/drm/sun8i/de2_hdmi.c
new file mode 100644
index 0000000..9ff6132
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.c
@@ -0,0 +1,440 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <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/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.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)
+
+/* --- encoder functions --- */
+
+static int de2_hdmi_set_clock(struct de2_hdmi_priv *priv,
+				int rate)
+{
+	struct clk *parent_clk;
+	u32 parent_rate;
+	int ret;
+
+	/* determine and set the best rate for the parent clock (pll-video) */
+	if ((270000 * 2) % rate == 0)
+		parent_rate = 270000000;
+	else if (297000 % rate == 0)
+		parent_rate = 297000000;
+	else
+		return -EINVAL;			/* unsupported clock */
+
+	parent_clk = clk_get_parent(priv->clk);
+
+	ret = clk_set_rate(parent_clk, parent_rate);
+	if (ret) {
+		dev_err(priv->dev, "set parent rate failed %d\n", ret);
+		return ret;
+	}
+	ret = clk_set_rate(priv->clk, rate * 1000);
+	if (ret)
+		dev_err(priv->dev, "set rate failed %d\n", ret);
+
+	return ret;
+}
+
+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);
+
+	if (de2_hdmi_set_clock(priv, mode->clock) < 0)
+		return;
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_mode_set(priv, mode);
+	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)
+{
+	int cea_mode = drm_match_cea_mode(mode);
+
+	return hdmi_io_mode_valid(cea_mode) < 0 ? MODE_NOMODE : 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;
+}
+
+/* values duplicated from edid_cea_modes[] */
+static const struct drm_display_mode lmodes_tb[] = {
+	/* 2 - 720x480@60Hz */
+	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+	/* 4 - 1280x720@60Hz */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 16 - 1920x1080@60Hz */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+};
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct de2_hdmi_priv *priv = conn_to_priv(connector);
+	struct drm_display_mode *mode;
+	const struct drm_display_mode *lmode;
+	struct edid *edid;
+	int n;
+
+	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;
+
+		if (connector->cmdline_mode.xres == 1920 &&
+		    connector->cmdline_mode.yres == 1080)
+			lmode = &lmodes_tb[2];
+		else if (connector->cmdline_mode.xres == 1280 &&
+			 connector->cmdline_mode.yres == 720)
+			lmode = &lmodes_tb[1];
+		else
+			lmode = &lmodes_tb[0];
+
+		mode = drm_mode_duplicate(connector->dev, lmode);
+		if (!mode)
+			return 0;
+		drm_mode_probed_add(connector, mode);
+
+		return 1;
+	}
+
+	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);
+	reset_control_assert(priv->reset1);
+	reset_control_assert(priv->reset0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct de2_hdmi_priv *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->reset0);
+	if (ret)
+		goto err;
+	ret = reset_control_deassert(priv->reset1);
+	if (ret)
+		goto err;
+
+	ret = clk_prepare_enable(priv->clk_ddc);
+	if (ret)
+		goto err;
+
+	de2_hdmi_set_clock(priv, 147500);	/* set a valid clock rate */
+	ret = clk_prepare_enable(priv->gate);
+	if (ret)
+		goto err;
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		goto err;
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_init(priv);
+	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:
+	dev_err(dev, "err %d\n", ret);
+	return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->connector.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;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	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)) {
+		ret = PTR_ERR(priv->mmio);
+		dev_err(dev, "failed to map registers err %d\n", ret);
+		return ret;
+	}
+
+	priv->gate = devm_clk_get(dev, "bus");
+	if (IS_ERR(priv->gate)) {
+		ret = PTR_ERR(priv->gate);
+		dev_err(dev, "gate clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		dev_err(dev, "hdmi clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+	if (IS_ERR(priv->clk_ddc)) {
+		ret = PTR_ERR(priv->clk_ddc);
+		dev_err(dev, "hdmi-ddc clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->reset0 = devm_reset_control_get(dev, "hdmi0");
+	if (IS_ERR(priv->reset0)) {
+		ret = PTR_ERR(priv->reset0);
+		dev_err(dev, "reset controller err %d\n", ret);
+		return ret;
+	}
+
+	priv->reset1 = devm_reset_control_get(dev, "hdmi1");
+	if (IS_ERR(priv->reset1)) {
+		ret = PTR_ERR(priv->reset1);
+		dev_err(dev, "reset controller err %d\n", ret);
+		return ret;
+	}
+
+	priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+						&pdev->dev)->data;
+
+	de2_hdmi_audio_register(dev);
+
+	return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+	de2_hdmi_audio_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 = "sun8i-de2-hdmi",
+		.of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+	},
+};
+
+/* create the video HDMI driver and the sound card driver */
+static int __init de2_hdmi_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&de2_hdmi_driver);
+
+	return ret;
+}
+
+static void __exit de2_hdmi_fini(void)
+{
+	platform_driver_unregister(&de2_hdmi_driver);
+}
+
+module_init(de2_hdmi_init);
+module_exit(de2_hdmi_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.h b/drivers/gpu/drm/sun8i/de2_hdmi.h
new file mode 100644
index 0000000..6711a76
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.h
@@ -0,0 +1,51 @@
+#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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.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 *reset0;
+	struct reset_control *reset1;
+
+	struct mutex mutex;
+	u8 soc_type;
+	u8 cea_mode;
+};
+
+/* in de2_hdmi_io.c */
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode);
+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);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi_io.c b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
new file mode 100644
index 0000000..b746a52
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
@@ -0,0 +1,843 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ *	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"
+
+static int hdmi_mode = 1;
+MODULE_PARM_DESC(de2_hdmi_mode, "Force HDMI mode.\n"
+"When set, if the display device is connected by HDMI, switch to this mode.\n"
+"When unset, stay in DVI mode (useful when screen overscan).\n");
+module_param_named(de2_hdmi_mode, hdmi_mode, int, 0644);
+
+/* guessed PHY registers */
+#define HDMI_PHY_LOCK_READ_REG	0x10010
+#define HDMI_PHY_CTRL_REG	0x10020
+#define HDMI_PHY_24_REG		0x10024
+#define HDMI_PHY_28_REG		0x10028
+#define HDMI_PHY_PLL_REG	0x1002c
+#define HDMI_PHY_CLK_REG	0x10030
+#define HDMI_PHY_34_REG		0x10034
+#define HDMI_PHY_STATUS_REG	0x10038
+
+/* 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		HDMI_FC_INVIDCONF_HDMI_MODE 0x08
+#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_HSYNCINWIDTH0 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		HDMI_AUD_CONF0_SW_RESET 0x80
+#define		HDMI_AUD_CONF0_I2S_ALL_ENABLE 0x2f
+#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		HDMI_AUD_INPUTCLKFS_64FS 0x04
+#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_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+
+#define VIC_720x480_60		2
+#define VIC_1280x720_60		4
+#define VIC_1920x1080i_60	5
+#define VIC_720x480i_60		6
+#define VIC_1920x1080_60	16
+#define VIC_720x576_50		17
+#define VIC_1280x720_50		19
+#define VIC_1920x1080i_50	20
+#define VIC_720x576i_50		21
+#define VIC_1920x1080_50	31
+#define VIC_1920x1080_24	32
+#define VIC_1920x1080_25	33
+#define VIC_1920x1080_30	34
+
+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 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 void hdmi_orb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+	writeb_relaxed(readb_relaxed(priv->mmio + addr) | data,
+			priv->mmio + addr);
+}
+
+static inline void hdmi_orl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(readl_relaxed(priv->mmio + addr) | data,
+			priv->mmio + addr);
+}
+
+static inline void hdmi_andl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(readl_relaxed(priv->mmio + addr) & data,
+			priv->mmio + addr);
+}
+
+/* read on/off functions */
+static inline void hdmi_read_on(struct de2_hdmi_priv *priv)
+{
+	hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x54524545);
+}
+static inline void hdmi_read_off(struct de2_hdmi_priv *priv)
+{
+	hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x57415452);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+	u8 clkdis = priv->soc_type == SOC_H3 ?
+				~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+	hdmi_read_on(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, HDMI_PHY_CTRL_REG, 0);
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 1 << 0);
+	udelay(5);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 16);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 1);
+	udelay(10);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 2);
+	udelay(5);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 3);
+	usleep_range(40, 50);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 19);
+	usleep_range(100, 120);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 18);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 7 << 4);
+
+	to_cnt = 10;
+	while (1) {
+		if (hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80)
+			break;
+		usleep_range(200, 250);
+		if (--to_cnt == 0) {
+			dev_err(priv->dev, "hdmi phy init timeout\n");
+			break;
+		}
+	}
+
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0xf << 8);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 7);
+
+	hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+	hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+	msleep(20);
+	hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+	msleep(100);
+	tmp = hdmi_readl(priv, HDMI_PHY_STATUS_REG);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, (tmp >> 11) & 0x3f);
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ff0f7f);
+	hdmi_writel(priv, HDMI_PHY_24_REG, 0x80639000);
+	hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+
+	hdmi_inner_init(priv);
+}
+
+static int get_divider(int rate)
+{
+	if (rate <= 27000)
+		return 11;
+	if (rate <= 74250)
+		return 4;
+	if (rate <= 148500)
+		return 2;
+	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 void hdmi_phy_set_a83t(struct de2_hdmi_priv *priv,
+				struct drm_display_mode *mode)
+{
+	switch (get_divider(mode->clock)) {
+	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, 1080 */
+		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 */
+	default:
+		hdmi_i2cm_write(priv, 0x06, 0x01,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 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;
+	}
+	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);
+}
+
+static void hdmi_phy_set_h3(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode)
+{
+	u32 tmp;
+
+	hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~0xf000);
+
+	switch (get_divider(mode->clock)) {
+	case 1:
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x31dc5fc0);
+						/* or 0x30dc5fc0 ? */
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x800863c0);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(200);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		if (tmp < 0x3d)
+			tmp += 2;
+		else
+			tmp = 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		msleep(100);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f8246b5);
+		break;
+	case 2:				/* 1080P @ 60 & 50 */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084381);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063a800);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c485);
+		break;
+	case 4:				/* 720P @ 50 & 60, 1080I, 1080 */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+		break;
+	default:
+/*	case 11:				* 480P/576P */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x8008430a);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+		break;
+	}
+}
+
+/* HDMI functions */
+
+/* hardware init */
+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);
+
+	/* disable hdcp */
+	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);
+}
+
+/* check if the resolution is supported */
+int hdmi_io_mode_valid(int cea_mode)
+{
+	switch (cea_mode) {
+	case VIC_720x480_60:
+	case VIC_1280x720_60:
+	case VIC_1920x1080i_60:
+	case VIC_720x480i_60:
+	case VIC_1920x1080_60:
+	case VIC_720x576_50:
+	case VIC_1280x720_50:
+	case VIC_1920x1080i_50:
+	case VIC_720x576i_50:
+	case VIC_1920x1080_50:
+	case VIC_1920x1080_24:
+	case VIC_1920x1080_25:
+	case VIC_1920x1080_30:
+		return 1;
+	}
+	return -1;
+}
+
+/* output init */
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode)
+{
+	int avi_d2;			/* AVI InfoFrame Data Byte 2 */
+	int h_blank, h_sync_w, h_front_p;
+	int invidconf;
+
+	/* colorimetry and aspect ratio */
+	switch (priv->cea_mode) {
+	case 0:
+		return;			/* bad mode */
+	case VIC_720x480_60:
+	case VIC_720x480i_60:
+	case VIC_720x576_50:
+	case VIC_720x576i_50:
+		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;
+	}
+
+	h_blank = mode->htotal - mode->hdisplay;
+	h_sync_w = mode->hsync_end - mode->hsync_start;
+	h_front_p = mode->hsync_start - mode->hdisplay;
+
+	invidconf = 0;
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		invidconf |= 0x01;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		invidconf |= 0x20;
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		invidconf |= 0x40;
+
+	if (priv->soc_type == SOC_H3) {
+		hdmi_phy_set_h3(priv, mode);
+		hdmi_inner_init(priv);
+	} else {
+		hdmi_phy_init_a83t(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,
+				invidconf |
+				HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+	hdmi_writeb(priv, 0x10001, invidconf < 0x60 ? 0x03 : 0x00);
+	hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, mode->hdisplay >> 8);
+	hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH,
+			mode->vsync_end - mode->vsync_start);
+	hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, mode->vdisplay >> 8);
+	hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, h_blank >> 8);
+	hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY,
+			mode->vsync_start - mode->vdisplay);
+	hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, h_front_p >> 8);
+	hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, h_sync_w >> 8);
+	hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, mode->hdisplay);
+	hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, h_blank);
+	hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, h_front_p);
+	hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH0, h_sync_w);
+	hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, mode->vdisplay);
+	hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK,
+			mode->vtotal - mode->vdisplay);
+	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,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x21 : 0x10);
+	hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 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->connector.eld[0]) {		/* 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);
+
+		/* switch to HDMI mode */
+		if (hdmi_mode) {
+			hdmi_read_on(priv);
+			hdmi_orb(priv, R_1000_HDMI_FC_INVIDCONF,
+					HDMI_FC_INVIDCONF_HDMI_MODE);
+			hdmi_read_off(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)
+		hdmi_phy_set_a83t(priv, mode);
+
+	hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+	if (!priv->cea_mode)
+		return;
+pr_info("*jfm* hdmi video on\n");
+	if (priv->soc_type == SOC_H3)
+		hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0x0f << 12);
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+	if (!priv->cea_mode)
+		return;
+pr_info("*jfm* hdmi video off\n");
+	if (priv->soc_type == SOC_H3)
+		hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~(0x0f << 12));
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+			char pointer, char off,
+			int nbyte, char *pbuf)
+{
+	unsigned int to_cnt;
+	u8 reg;
+	int ret = 0;
+
+	hdmi_read_on(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 */
+			dev_err(priv->dev, "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) {
+				dev_err(priv->dev, "hdmi ddc read error\n");
+				ret = -1;
+				break;
+			}
+			if (--to_cnt == 0) {
+				if (!ret) {
+					dev_err(priv->dev,
+						"hdmi ddc read timeout\n");
+					ret = -1;
+				}
+				break;
+			}
+			usleep_range(500, 800);
+		}
+		if (ret)
+			break;
+		nbyte--;
+		off++;
+	}
+	hdmi_read_off(priv);
+
+	return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+	int ret;
+
+	hdmi_read_on(priv);
+
+	if (priv->soc_type == SOC_H3)
+		ret = hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80000;
+	else
+		ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+	hdmi_read_off(priv);
+
+	return ret != 0;
+}
-- 
2.10.2

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

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

* [PATCH v7 3/8] drm: sun8i: add HDMI video support to A83T and H3
@ 2016-11-29  8:39     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29  8:39 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/gpu/drm/sun8i/Kconfig       |   7 +
 drivers/gpu/drm/sun8i/Makefile      |   2 +
 drivers/gpu/drm/sun8i/de2_hdmi.c    | 440 +++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_hdmi.h    |  51 +++
 drivers/gpu/drm/sun8i/de2_hdmi_io.c | 843 ++++++++++++++++++++++++++++++++++++
 5 files changed, 1343 insertions(+)
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c

diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
index 6940895..5c4607b 100644
--- a/drivers/gpu/drm/sun8i/Kconfig
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -17,3 +17,10 @@ config DRM_SUN8I_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
 	  sun8i-de2-drm.
+
+config DRM_SUN8I_DE2_HDMI
+	tristate "Support for DE2 HDMI"
+	depends on DRM_SUN8I_DE2
+	help
+	  Choose this option if you use want HDMI on DE2.
+	  If M is selected the module will be called sun8i-de2-hdmi.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
index f107919..6ba97c2 100644
--- a/drivers/gpu/drm/sun8i/Makefile
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -3,5 +3,7 @@
 #
 
 sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+sun8i-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
 
 obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
+obj-$(CONFIG_DRM_SUN8I_DE2_HDMI) += sun8i-de2-hdmi.o
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.c b/drivers/gpu/drm/sun8i/de2_hdmi.c
new file mode 100644
index 0000000..9ff6132
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.c
@@ -0,0 +1,440 @@
+/*
+ * 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/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.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)
+
+/* --- encoder functions --- */
+
+static int de2_hdmi_set_clock(struct de2_hdmi_priv *priv,
+				int rate)
+{
+	struct clk *parent_clk;
+	u32 parent_rate;
+	int ret;
+
+	/* determine and set the best rate for the parent clock (pll-video) */
+	if ((270000 * 2) % rate == 0)
+		parent_rate = 270000000;
+	else if (297000 % rate == 0)
+		parent_rate = 297000000;
+	else
+		return -EINVAL;			/* unsupported clock */
+
+	parent_clk = clk_get_parent(priv->clk);
+
+	ret = clk_set_rate(parent_clk, parent_rate);
+	if (ret) {
+		dev_err(priv->dev, "set parent rate failed %d\n", ret);
+		return ret;
+	}
+	ret = clk_set_rate(priv->clk, rate * 1000);
+	if (ret)
+		dev_err(priv->dev, "set rate failed %d\n", ret);
+
+	return ret;
+}
+
+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);
+
+	if (de2_hdmi_set_clock(priv, mode->clock) < 0)
+		return;
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_mode_set(priv, mode);
+	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)
+{
+	int cea_mode = drm_match_cea_mode(mode);
+
+	return hdmi_io_mode_valid(cea_mode) < 0 ? MODE_NOMODE : 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;
+}
+
+/* values duplicated from edid_cea_modes[] */
+static const struct drm_display_mode lmodes_tb[] = {
+	/* 2 - 720x480 at 60Hz */
+	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+	/* 4 - 1280x720 at 60Hz */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 16 - 1920x1080 at 60Hz */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+};
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct de2_hdmi_priv *priv = conn_to_priv(connector);
+	struct drm_display_mode *mode;
+	const struct drm_display_mode *lmode;
+	struct edid *edid;
+	int n;
+
+	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;
+
+		if (connector->cmdline_mode.xres == 1920 &&
+		    connector->cmdline_mode.yres == 1080)
+			lmode = &lmodes_tb[2];
+		else if (connector->cmdline_mode.xres == 1280 &&
+			 connector->cmdline_mode.yres == 720)
+			lmode = &lmodes_tb[1];
+		else
+			lmode = &lmodes_tb[0];
+
+		mode = drm_mode_duplicate(connector->dev, lmode);
+		if (!mode)
+			return 0;
+		drm_mode_probed_add(connector, mode);
+
+		return 1;
+	}
+
+	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);
+	reset_control_assert(priv->reset1);
+	reset_control_assert(priv->reset0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct de2_hdmi_priv *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->reset0);
+	if (ret)
+		goto err;
+	ret = reset_control_deassert(priv->reset1);
+	if (ret)
+		goto err;
+
+	ret = clk_prepare_enable(priv->clk_ddc);
+	if (ret)
+		goto err;
+
+	de2_hdmi_set_clock(priv, 147500);	/* set a valid clock rate */
+	ret = clk_prepare_enable(priv->gate);
+	if (ret)
+		goto err;
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		goto err;
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_init(priv);
+	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:
+	dev_err(dev, "err %d\n", ret);
+	return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->connector.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;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	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)) {
+		ret = PTR_ERR(priv->mmio);
+		dev_err(dev, "failed to map registers err %d\n", ret);
+		return ret;
+	}
+
+	priv->gate = devm_clk_get(dev, "bus");
+	if (IS_ERR(priv->gate)) {
+		ret = PTR_ERR(priv->gate);
+		dev_err(dev, "gate clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		dev_err(dev, "hdmi clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+	if (IS_ERR(priv->clk_ddc)) {
+		ret = PTR_ERR(priv->clk_ddc);
+		dev_err(dev, "hdmi-ddc clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->reset0 = devm_reset_control_get(dev, "hdmi0");
+	if (IS_ERR(priv->reset0)) {
+		ret = PTR_ERR(priv->reset0);
+		dev_err(dev, "reset controller err %d\n", ret);
+		return ret;
+	}
+
+	priv->reset1 = devm_reset_control_get(dev, "hdmi1");
+	if (IS_ERR(priv->reset1)) {
+		ret = PTR_ERR(priv->reset1);
+		dev_err(dev, "reset controller err %d\n", ret);
+		return ret;
+	}
+
+	priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+						&pdev->dev)->data;
+
+	de2_hdmi_audio_register(dev);
+
+	return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+	de2_hdmi_audio_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 = "sun8i-de2-hdmi",
+		.of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+	},
+};
+
+/* create the video HDMI driver and the sound card driver */
+static int __init de2_hdmi_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&de2_hdmi_driver);
+
+	return ret;
+}
+
+static void __exit de2_hdmi_fini(void)
+{
+	platform_driver_unregister(&de2_hdmi_driver);
+}
+
+module_init(de2_hdmi_init);
+module_exit(de2_hdmi_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.h b/drivers/gpu/drm/sun8i/de2_hdmi.h
new file mode 100644
index 0000000..6711a76
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.h
@@ -0,0 +1,51 @@
+#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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.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 *reset0;
+	struct reset_control *reset1;
+
+	struct mutex mutex;
+	u8 soc_type;
+	u8 cea_mode;
+};
+
+/* in de2_hdmi_io.c */
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode);
+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);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi_io.c b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
new file mode 100644
index 0000000..b746a52
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
@@ -0,0 +1,843 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ *	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"
+
+static int hdmi_mode = 1;
+MODULE_PARM_DESC(de2_hdmi_mode, "Force HDMI mode.\n"
+"When set, if the display device is connected by HDMI, switch to this mode.\n"
+"When unset, stay in DVI mode (useful when screen overscan).\n");
+module_param_named(de2_hdmi_mode, hdmi_mode, int, 0644);
+
+/* guessed PHY registers */
+#define HDMI_PHY_LOCK_READ_REG	0x10010
+#define HDMI_PHY_CTRL_REG	0x10020
+#define HDMI_PHY_24_REG		0x10024
+#define HDMI_PHY_28_REG		0x10028
+#define HDMI_PHY_PLL_REG	0x1002c
+#define HDMI_PHY_CLK_REG	0x10030
+#define HDMI_PHY_34_REG		0x10034
+#define HDMI_PHY_STATUS_REG	0x10038
+
+/* 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		HDMI_FC_INVIDCONF_HDMI_MODE 0x08
+#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_HSYNCINWIDTH0 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		HDMI_AUD_CONF0_SW_RESET 0x80
+#define		HDMI_AUD_CONF0_I2S_ALL_ENABLE 0x2f
+#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		HDMI_AUD_INPUTCLKFS_64FS 0x04
+#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_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+
+#define VIC_720x480_60		2
+#define VIC_1280x720_60		4
+#define VIC_1920x1080i_60	5
+#define VIC_720x480i_60		6
+#define VIC_1920x1080_60	16
+#define VIC_720x576_50		17
+#define VIC_1280x720_50		19
+#define VIC_1920x1080i_50	20
+#define VIC_720x576i_50		21
+#define VIC_1920x1080_50	31
+#define VIC_1920x1080_24	32
+#define VIC_1920x1080_25	33
+#define VIC_1920x1080_30	34
+
+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 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 void hdmi_orb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+	writeb_relaxed(readb_relaxed(priv->mmio + addr) | data,
+			priv->mmio + addr);
+}
+
+static inline void hdmi_orl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(readl_relaxed(priv->mmio + addr) | data,
+			priv->mmio + addr);
+}
+
+static inline void hdmi_andl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(readl_relaxed(priv->mmio + addr) & data,
+			priv->mmio + addr);
+}
+
+/* read on/off functions */
+static inline void hdmi_read_on(struct de2_hdmi_priv *priv)
+{
+	hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x54524545);
+}
+static inline void hdmi_read_off(struct de2_hdmi_priv *priv)
+{
+	hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x57415452);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+	u8 clkdis = priv->soc_type == SOC_H3 ?
+				~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+	hdmi_read_on(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, HDMI_PHY_CTRL_REG, 0);
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 1 << 0);
+	udelay(5);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 16);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 1);
+	udelay(10);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 2);
+	udelay(5);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 3);
+	usleep_range(40, 50);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 19);
+	usleep_range(100, 120);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 18);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 7 << 4);
+
+	to_cnt = 10;
+	while (1) {
+		if (hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80)
+			break;
+		usleep_range(200, 250);
+		if (--to_cnt == 0) {
+			dev_err(priv->dev, "hdmi phy init timeout\n");
+			break;
+		}
+	}
+
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0xf << 8);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 7);
+
+	hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+	hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+	msleep(20);
+	hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+	msleep(100);
+	tmp = hdmi_readl(priv, HDMI_PHY_STATUS_REG);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, (tmp >> 11) & 0x3f);
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ff0f7f);
+	hdmi_writel(priv, HDMI_PHY_24_REG, 0x80639000);
+	hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+
+	hdmi_inner_init(priv);
+}
+
+static int get_divider(int rate)
+{
+	if (rate <= 27000)
+		return 11;
+	if (rate <= 74250)
+		return 4;
+	if (rate <= 148500)
+		return 2;
+	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 void hdmi_phy_set_a83t(struct de2_hdmi_priv *priv,
+				struct drm_display_mode *mode)
+{
+	switch (get_divider(mode->clock)) {
+	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, 1080 */
+		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 */
+	default:
+		hdmi_i2cm_write(priv, 0x06, 0x01,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 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;
+	}
+	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);
+}
+
+static void hdmi_phy_set_h3(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode)
+{
+	u32 tmp;
+
+	hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~0xf000);
+
+	switch (get_divider(mode->clock)) {
+	case 1:
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x31dc5fc0);
+						/* or 0x30dc5fc0 ? */
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x800863c0);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(200);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		if (tmp < 0x3d)
+			tmp += 2;
+		else
+			tmp = 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		msleep(100);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f8246b5);
+		break;
+	case 2:				/* 1080P @ 60 & 50 */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084381);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063a800);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c485);
+		break;
+	case 4:				/* 720P @ 50 & 60, 1080I, 1080 */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+		break;
+	default:
+/*	case 11:				* 480P/576P */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x8008430a);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+		break;
+	}
+}
+
+/* HDMI functions */
+
+/* hardware init */
+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);
+
+	/* disable hdcp */
+	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);
+}
+
+/* check if the resolution is supported */
+int hdmi_io_mode_valid(int cea_mode)
+{
+	switch (cea_mode) {
+	case VIC_720x480_60:
+	case VIC_1280x720_60:
+	case VIC_1920x1080i_60:
+	case VIC_720x480i_60:
+	case VIC_1920x1080_60:
+	case VIC_720x576_50:
+	case VIC_1280x720_50:
+	case VIC_1920x1080i_50:
+	case VIC_720x576i_50:
+	case VIC_1920x1080_50:
+	case VIC_1920x1080_24:
+	case VIC_1920x1080_25:
+	case VIC_1920x1080_30:
+		return 1;
+	}
+	return -1;
+}
+
+/* output init */
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode)
+{
+	int avi_d2;			/* AVI InfoFrame Data Byte 2 */
+	int h_blank, h_sync_w, h_front_p;
+	int invidconf;
+
+	/* colorimetry and aspect ratio */
+	switch (priv->cea_mode) {
+	case 0:
+		return;			/* bad mode */
+	case VIC_720x480_60:
+	case VIC_720x480i_60:
+	case VIC_720x576_50:
+	case VIC_720x576i_50:
+		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;
+	}
+
+	h_blank = mode->htotal - mode->hdisplay;
+	h_sync_w = mode->hsync_end - mode->hsync_start;
+	h_front_p = mode->hsync_start - mode->hdisplay;
+
+	invidconf = 0;
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		invidconf |= 0x01;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		invidconf |= 0x20;
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		invidconf |= 0x40;
+
+	if (priv->soc_type == SOC_H3) {
+		hdmi_phy_set_h3(priv, mode);
+		hdmi_inner_init(priv);
+	} else {
+		hdmi_phy_init_a83t(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,
+				invidconf |
+				HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+	hdmi_writeb(priv, 0x10001, invidconf < 0x60 ? 0x03 : 0x00);
+	hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, mode->hdisplay >> 8);
+	hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH,
+			mode->vsync_end - mode->vsync_start);
+	hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, mode->vdisplay >> 8);
+	hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, h_blank >> 8);
+	hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY,
+			mode->vsync_start - mode->vdisplay);
+	hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, h_front_p >> 8);
+	hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, h_sync_w >> 8);
+	hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, mode->hdisplay);
+	hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, h_blank);
+	hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, h_front_p);
+	hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH0, h_sync_w);
+	hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, mode->vdisplay);
+	hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK,
+			mode->vtotal - mode->vdisplay);
+	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,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x21 : 0x10);
+	hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 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->connector.eld[0]) {		/* 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);
+
+		/* switch to HDMI mode */
+		if (hdmi_mode) {
+			hdmi_read_on(priv);
+			hdmi_orb(priv, R_1000_HDMI_FC_INVIDCONF,
+					HDMI_FC_INVIDCONF_HDMI_MODE);
+			hdmi_read_off(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)
+		hdmi_phy_set_a83t(priv, mode);
+
+	hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+	if (!priv->cea_mode)
+		return;
+pr_info("*jfm* hdmi video on\n");
+	if (priv->soc_type == SOC_H3)
+		hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0x0f << 12);
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+	if (!priv->cea_mode)
+		return;
+pr_info("*jfm* hdmi video off\n");
+	if (priv->soc_type == SOC_H3)
+		hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~(0x0f << 12));
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+			char pointer, char off,
+			int nbyte, char *pbuf)
+{
+	unsigned int to_cnt;
+	u8 reg;
+	int ret = 0;
+
+	hdmi_read_on(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 */
+			dev_err(priv->dev, "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) {
+				dev_err(priv->dev, "hdmi ddc read error\n");
+				ret = -1;
+				break;
+			}
+			if (--to_cnt == 0) {
+				if (!ret) {
+					dev_err(priv->dev,
+						"hdmi ddc read timeout\n");
+					ret = -1;
+				}
+				break;
+			}
+			usleep_range(500, 800);
+		}
+		if (ret)
+			break;
+		nbyte--;
+		off++;
+	}
+	hdmi_read_off(priv);
+
+	return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+	int ret;
+
+	hdmi_read_on(priv);
+
+	if (priv->soc_type == SOC_H3)
+		ret = hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80000;
+	else
+		ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+	hdmi_read_off(priv);
+
+	return ret != 0;
+}
-- 
2.10.2

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29  9:08     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29  9:08 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
 .../devicetree/bindings/display/sunxi/hdmi.txt     | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt

diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..1e107cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,56 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - compatible : should be one of
+		"allwinner,sun8i-a83t-hdmi"
+		"allwinner,sun8i-h3-hdmi"
+ - reg: base address and size of the I/O memory
+ - clocks : phandles to the HDMI clocks as described in
+	Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+		"bus" : bus gate
+		"clock" : streaming clock
+		"ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : when 2 phandles, must be
+		"hdmi0" and "hdmi1"
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+	as defined in Documentation/devicetree/bindings/graph.txt.
+	port@0 is video and port@1 is audio.
+
+Example:
+
+	hdmi: hdmi@01ee0000 {
+		compatible = "allwinner,sun8i-a83t-hdmi";
+		reg = <0x01ee0000 0x20000>;
+		clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+			 <&ccu CLK_HDMI_DDC>;
+		clock-names = "bus", "clock", "ddc-clock";
+		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+		reset-names = "hdmi0", "hdmi1";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_pins_a>;
+		status = "disabled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port@0 {			/* video */
+			reg = <0>;
+			hdmi_tcon1: endpoint {
+				remote-endpoint = <&tcon1_hdmi>;
+			};
+		};
+		port@1 {			/* audio */
+			reg = <1>;
+			hdmi_i2s2: endpoint {
+				remote-endpoint = <&i2s2_hdmi>;
+			};
+		};
+	};
-- 
2.10.2

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-29  9:08     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29  9:08 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 .../devicetree/bindings/display/sunxi/hdmi.txt     | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt

diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..1e107cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,56 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - compatible : should be one of
+		"allwinner,sun8i-a83t-hdmi"
+		"allwinner,sun8i-h3-hdmi"
+ - reg: base address and size of the I/O memory
+ - clocks : phandles to the HDMI clocks as described in
+	Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+		"bus" : bus gate
+		"clock" : streaming clock
+		"ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : when 2 phandles, must be
+		"hdmi0" and "hdmi1"
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+	as defined in Documentation/devicetree/bindings/graph.txt.
+	port at 0 is video and port at 1 is audio.
+
+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 = "bus", "clock", "ddc-clock";
+		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+		reset-names = "hdmi0", "hdmi1";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_pins_a>;
+		status = "disabled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port at 0 {			/* video */
+			reg = <0>;
+			hdmi_tcon1: endpoint {
+				remote-endpoint = <&tcon1_hdmi>;
+			};
+		};
+		port at 1 {			/* audio */
+			reg = <1>;
+			hdmi_i2s2: endpoint {
+				remote-endpoint = <&i2s2_hdmi>;
+			};
+		};
+	};
-- 
2.10.2

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

* [PATCH v7 5/8] clk: sunxi-ng: define the PLL DE clock
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29 10:10     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:10 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
 include/dt-bindings/clock/sun8i-h3-ccu.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/sun8i-h3-ccu.h b/include/dt-bindings/clock/sun8i-h3-ccu.h
index efb7ba2..7af57b7 100644
--- a/include/dt-bindings/clock/sun8i-h3-ccu.h
+++ b/include/dt-bindings/clock/sun8i-h3-ccu.h
@@ -44,6 +44,7 @@
 #define _DT_BINDINGS_CLK_SUN8I_H3_H_
 
 #define CLK_CPUX		14
+#define CLK_PLL_DE		13
 
 #define CLK_BUS_CE		20
 #define CLK_BUS_DMA		21
-- 
2.10.2

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

* [PATCH v7 5/8] clk: sunxi-ng: define the PLL DE clock
@ 2016-11-29 10:10     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 include/dt-bindings/clock/sun8i-h3-ccu.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/sun8i-h3-ccu.h b/include/dt-bindings/clock/sun8i-h3-ccu.h
index efb7ba2..7af57b7 100644
--- a/include/dt-bindings/clock/sun8i-h3-ccu.h
+++ b/include/dt-bindings/clock/sun8i-h3-ccu.h
@@ -44,6 +44,7 @@
 #define _DT_BINDINGS_CLK_SUN8I_H3_H_
 
 #define CLK_CPUX		14
+#define CLK_PLL_DE		13
 
 #define CLK_BUS_CE		20
 #define CLK_BUS_DMA		21
-- 
2.10.2

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

* [PATCH v7 6/8] ARM: dts: sun8i-h3: add HDMI video nodes
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29 10:12     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:12 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
Note 1:
 The DE clock is not set in the driver. Instead, it is set at system
 startup time by 'assigned-clocks', but there is a problem in sunxi-ng
 which uses readl_relaxed_poll_timeout(), and, as noticed by
 Ondřej Jirman, this function is not available at startup time.
 The fix of this problem is not part of this patchset series.
Note 2:
 The DE clock is set to a high enough rate (432MHz). It seems that
 this is needed to handle 4K video.
 But, as the proposed DE driver does not treat yet 4K video, the clock
 could be set to a lower rate. For example, the default rate for the A83T
 is 250MHz (no 4K video).
---
 arch/arm/boot/dts/sun8i-h3.dtsi | 65 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index fca66bf..1aa087d 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -140,6 +140,16 @@
 		#size-cells = <1>;
 		ranges;
 
+		de: de-controller@01000000 {
+			compatible = "allwinner,sun8i-h3-display-engine";
+			reg = <0x01000000 0x400000>;
+			clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
+			clock-names = "bus", "clock";
+			resets = <&ccu RST_BUS_DE>;
+			ports = <&tcon0_p>, <&tcon1_p>;
+			status = "disabled";
+		};
+
 		dma: dma-controller@01c02000 {
 			compatible = "allwinner,sun8i-h3-dma";
 			reg = <0x01c02000 0x1000>;
@@ -149,6 +159,37 @@
 			#dma-cells = <1>;
 		};
 
+		tcon0: lcd-controller@01c0c000 {
+			compatible = "allwinner,sun8i-a83t-tcon";
+			reg = <0x01c0c000 0x400>;
+			clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
+			clock-names = "bus", "clock";
+			resets = <&ccu RST_BUS_TCON0>;
+			interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			tcon0_p: port {
+				tcon0_hdmi: endpoint {
+					remote-endpoint = <&hdmi_tcon0>;
+				};
+			};
+		};
+
+		/* not used */
+		tcon1: lcd-controller@01c0d000 {
+			compatible = "allwinner,sun8i-h3-tcon";
+			reg = <0x01c0d000 0x400>;
+			clocks = <&ccu CLK_BUS_TCON1>,
+				 <&ccu CLK_TCON0>;	/* no clock */
+			clock-names = "bus", "clock";
+			interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			tcon1_p: port {
+				endpoint {
+					/* empty */
+				};
+			};
+		};
+
 		mmc0: mmc@01c0f000 {
 			compatible = "allwinner,sun7i-a20-mmc";
 			reg = <0x01c0f000 0x1000>;
@@ -314,6 +355,11 @@
 			clock-names = "hosc", "losc";
 			#clock-cells = <1>;
 			#reset-cells = <1>;
+
+			assigned-clocks = <&ccu CLK_PLL_DE>,
+					  <&ccu CLK_DE>;
+			assigned-clock-rates =  <864000000>,
+						<432000000>;
 		};
 
 		pio: pinctrl@01c20800 {
@@ -567,6 +613,25 @@
 			interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 		};
 
+		hdmi: hdmi@01ee0000 {
+			compatible = "allwinner,sun8i-h3-hdmi";
+			reg = <0x01ee0000 0x20000>;
+			clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+				 <&ccu CLK_HDMI_DDC>;
+			clock-names = "bus", "clock", "ddc-clock";
+			resets = <&ccu RST_BUS_HDMI0>, <&ccu RST_BUS_HDMI1>;
+			reset-names = "hdmi0", "hdmi1";
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port@0 {			/* video */
+				reg = <0>;
+				hdmi_tcon0: endpoint {
+					remote-endpoint = <&tcon0_hdmi>;
+				};
+			};
+		};
+
 		rtc: rtc@01f00000 {
 			compatible = "allwinner,sun6i-a31-rtc";
 			reg = <0x01f00000 0x54>;
-- 
2.10.2

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

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

* [PATCH v7 6/8] ARM: dts: sun8i-h3: add HDMI video nodes
@ 2016-11-29 10:12     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Note 1:
 The DE clock is not set in the driver. Instead, it is set at system
 startup time by 'assigned-clocks', but there is a problem in sunxi-ng
 which uses readl_relaxed_poll_timeout(), and, as noticed by
 Ond??ej Jirman, this function is not available at startup time.
 The fix of this problem is not part of this patchset series.
Note 2:
 The DE clock is set to a high enough rate (432MHz). It seems that
 this is needed to handle 4K video.
 But, as the proposed DE driver does not treat yet 4K video, the clock
 could be set to a lower rate. For example, the default rate for the A83T
 is 250MHz (no 4K video).
---
 arch/arm/boot/dts/sun8i-h3.dtsi | 65 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index fca66bf..1aa087d 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -140,6 +140,16 @@
 		#size-cells = <1>;
 		ranges;
 
+		de: de-controller at 01000000 {
+			compatible = "allwinner,sun8i-h3-display-engine";
+			reg = <0x01000000 0x400000>;
+			clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
+			clock-names = "bus", "clock";
+			resets = <&ccu RST_BUS_DE>;
+			ports = <&tcon0_p>, <&tcon1_p>;
+			status = "disabled";
+		};
+
 		dma: dma-controller at 01c02000 {
 			compatible = "allwinner,sun8i-h3-dma";
 			reg = <0x01c02000 0x1000>;
@@ -149,6 +159,37 @@
 			#dma-cells = <1>;
 		};
 
+		tcon0: lcd-controller at 01c0c000 {
+			compatible = "allwinner,sun8i-a83t-tcon";
+			reg = <0x01c0c000 0x400>;
+			clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
+			clock-names = "bus", "clock";
+			resets = <&ccu RST_BUS_TCON0>;
+			interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			tcon0_p: port {
+				tcon0_hdmi: endpoint {
+					remote-endpoint = <&hdmi_tcon0>;
+				};
+			};
+		};
+
+		/* not used */
+		tcon1: lcd-controller at 01c0d000 {
+			compatible = "allwinner,sun8i-h3-tcon";
+			reg = <0x01c0d000 0x400>;
+			clocks = <&ccu CLK_BUS_TCON1>,
+				 <&ccu CLK_TCON0>;	/* no clock */
+			clock-names = "bus", "clock";
+			interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			tcon1_p: port {
+				endpoint {
+					/* empty */
+				};
+			};
+		};
+
 		mmc0: mmc at 01c0f000 {
 			compatible = "allwinner,sun7i-a20-mmc";
 			reg = <0x01c0f000 0x1000>;
@@ -314,6 +355,11 @@
 			clock-names = "hosc", "losc";
 			#clock-cells = <1>;
 			#reset-cells = <1>;
+
+			assigned-clocks = <&ccu CLK_PLL_DE>,
+					  <&ccu CLK_DE>;
+			assigned-clock-rates =  <864000000>,
+						<432000000>;
 		};
 
 		pio: pinctrl at 01c20800 {
@@ -567,6 +613,25 @@
 			interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 		};
 
+		hdmi: hdmi at 01ee0000 {
+			compatible = "allwinner,sun8i-h3-hdmi";
+			reg = <0x01ee0000 0x20000>;
+			clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+				 <&ccu CLK_HDMI_DDC>;
+			clock-names = "bus", "clock", "ddc-clock";
+			resets = <&ccu RST_BUS_HDMI0>, <&ccu RST_BUS_HDMI1>;
+			reset-names = "hdmi0", "hdmi1";
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port at 0 {			/* video */
+				reg = <0>;
+				hdmi_tcon0: endpoint {
+					remote-endpoint = <&tcon0_hdmi>;
+				};
+			};
+		};
+
 		rtc: rtc at 01f00000 {
 			compatible = "allwinner,sun6i-a31-rtc";
 			reg = <0x01f00000 0x54>;
-- 
2.10.2

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

* [PATCH v7 7/8] ARM: dts: sun8i-h3: Add HDMI video to the Banana Pi M2+
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29 10:14     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:14 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
 arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
index c0c49dd..9f3e2f8 100644
--- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
+++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
@@ -93,6 +93,10 @@
 	};
 };
 
+&de {
+	status = "okay";
+};
+
 &ehci1 {
 	status = "okay";
 };
@@ -101,12 +105,20 @@
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &ir {
 	pinctrl-names = "default";
 	pinctrl-0 = <&ir_pins_a>;
 	status = "okay";
 };
 
+&tcon0 {
+	status = "okay";
+};
+
 &mmc0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>;
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v7 7/8] ARM: dts: sun8i-h3: Add HDMI video to the Banana Pi M2+
@ 2016-11-29 10:14     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:14 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
index c0c49dd..9f3e2f8 100644
--- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
+++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts
@@ -93,6 +93,10 @@
 	};
 };
 
+&de {
+	status = "okay";
+};
+
 &ehci1 {
 	status = "okay";
 };
@@ -101,12 +105,20 @@
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &ir {
 	pinctrl-names = "default";
 	pinctrl-0 = <&ir_pins_a>;
 	status = "okay";
 };
 
+&tcon0 {
+	status = "okay";
+};
+
 &mmc0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>;
-- 
2.10.2

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

* [PATCH v7 8/8] ARM: dts: sun8i-h3: Add HDMI video to the Orange PI 2
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29 10:16     ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:16 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
 arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts
index 047e9e1..7712972 100644
--- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts
+++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts
@@ -105,16 +105,28 @@
 	};
 };
 
+&de {
+	status = "okay";
+};
+
 &ehci1 {
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &ir {
 	pinctrl-names = "default";
 	pinctrl-0 = <&ir_pins_a>;
 	status = "okay";
 };
 
+&tcon0 {
+	status = "okay";
+};
+
 &mmc0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>;
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v7 8/8] ARM: dts: sun8i-h3: Add HDMI video to the Orange PI 2
@ 2016-11-29 10:16     ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:16 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts
index 047e9e1..7712972 100644
--- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts
+++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts
@@ -105,16 +105,28 @@
 	};
 };
 
+&de {
+	status = "okay";
+};
+
 &ehci1 {
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &ir {
 	pinctrl-names = "default";
 	pinctrl-0 = <&ir_pins_a>;
 	status = "okay";
 };
 
+&tcon0 {
+	status = "okay";
+};
+
 &mmc0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>;
-- 
2.10.2

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-29 10:18 ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:18 UTC (permalink / raw)
  To: Dave Airlie, Maxime Ripard, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

This patchset series adds HDMI video support to the Allwinner
sun8i SoCs which include the display engine 2 (DE2).
The driver contains the code for the A83T and H3 SoCs, and
some H3 boards, but it could be used/extended for other SoCs
(A64, H2, H5) and boards (Banana PIs, Orange PIs).

v7:
	- more explanations about the DE2 in the DT documentation
	- separate patches for DT documentation (Rob Herring)
	- show all properties in DT examples (Rob Herring)
	- use drm_of_component_probe()
	- use the index of the DE 'ports' in the DT as
	  the DE mixer number (no alias needed anymore)
	- change some 'lcd' to 'tcon' in the DT
	- add HDMI module parameter for DVI mode when screen overscan
	  problems
	- fall back to some CEA modes in case of EDID read failure
	- fix some settings (interlace) and simplify code
	- fix bug in start of A83T HDMI
	- fix lack of CLK_PLL_DE definition in the DT include
	  (Icenowy Zheng)
v6:
	- remove audio support (other patchset to come)
	- use DRM modeset data for HDMI configuration
		(thanks to Jernej Škrabec)
	- more meaningfull register names
	- use a mutex for DE I/O protection
	- merge DE and plane into one file
	- don't activate the video hardware when video not started
		(Maxime Ripard)
	- remove 'type = "video" in DT graph ports
		(Rob Herring)
	- change the I/O accesses by #define instead of struct
		(Maxime Ripard, André Przywara)
	- remove pm functions (Maxime Ripard)
	- set the pll-de/de clocks in the DT (Maxime Ripard)
	- use platform_get_irq instead of irq_of_parse_and_map
		(Maxime Ripard)
	- rename sunxi to sun8i (Maxime Ripard)
	- fix coding style errors (Maxime Ripard)
	- subclass the drm structure in private data (Daniel Vetter)
	- move drm_dev_register at end of init (Daniel Vetter)
v5:
	- add overlay plane
	- add audio support
	- add support for the A83T
	- add back the HDMI driver
	- many bug fixes
v4: 
	- drivers/clk/sunxi/Makefile was missing (Emil Velikov)
v3:
	- add the hardware cursor
	- simplify and fix the DE2 init sequences
	- generation for all SUNXI SoCs (Andre Przywara)
v2:
	- remove the HDMI driver
	- remarks from Chen-Yu Tsai and Russell King
	- DT documentation added

Jean-Francois Moine (8):
  drm: sun8i: Add a basic DRM driver for Allwinner DE2
  drm/sun8i: Add DT bindings documentation of Allwinner DE2
  drm: sun8i: add HDMI video support to A83T and H3
  drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  clk: sunxi-ng: define the PLL DE clock
  ARM: dts: sun8i-h3: add HDMI video nodes
  ARM: dts: sun8i-h3: Add HDMI video to the Banana Pi M2+
  ARM: dts: sun8i-h3: Add HDMI video to the Orange PI 2

 .../devicetree/bindings/display/sunxi/hdmi.txt     |  56 ++
 .../bindings/display/sunxi/sun8i-de2.txt           | 121 +++
 arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts    |  12 +
 arch/arm/boot/dts/sun8i-h3-orangepi-2.dts          |  12 +
 arch/arm/boot/dts/sun8i-h3.dtsi                    |  65 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/sun8i/Kconfig                      |  26 +
 drivers/gpu/drm/sun8i/Makefile                     |   9 +
 drivers/gpu/drm/sun8i/de2_crtc.c                   | 449 +++++++++++
 drivers/gpu/drm/sun8i/de2_crtc.h                   |  52 ++
 drivers/gpu/drm/sun8i/de2_drv.c                    | 317 ++++++++
 drivers/gpu/drm/sun8i/de2_drv.h                    |  48 ++
 drivers/gpu/drm/sun8i/de2_hdmi.c                   | 440 +++++++++++
 drivers/gpu/drm/sun8i/de2_hdmi.h                   |  51 ++
 drivers/gpu/drm/sun8i/de2_hdmi_io.c                | 842 +++++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_plane.c                  | 734 ++++++++++++++++++
 include/dt-bindings/clock/sun8i-h3-ccu.h           |   1 +
 18 files changed, 3238 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
 create mode 100644 drivers/gpu/drm/sun8i/Kconfig
 create mode 100644 drivers/gpu/drm/sun8i/Makefile
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c

-- 
2.10.2

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

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-29 10:18 ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 10:18 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset series adds HDMI video support to the Allwinner
sun8i SoCs which include the display engine 2 (DE2).
The driver contains the code for the A83T and H3 SoCs, and
some H3 boards, but it could be used/extended for other SoCs
(A64, H2, H5) and boards (Banana PIs, Orange PIs).

v7:
	- more explanations about the DE2 in the DT documentation
	- separate patches for DT documentation (Rob Herring)
	- show all properties in DT examples (Rob Herring)
	- use drm_of_component_probe()
	- use the index of the DE 'ports' in the DT as
	  the DE mixer number (no alias needed anymore)
	- change some 'lcd' to 'tcon' in the DT
	- add HDMI module parameter for DVI mode when screen overscan
	  problems
	- fall back to some CEA modes in case of EDID read failure
	- fix some settings (interlace) and simplify code
	- fix bug in start of A83T HDMI
	- fix lack of CLK_PLL_DE definition in the DT include
	  (Icenowy Zheng)
v6:
	- remove audio support (other patchset to come)
	- use DRM modeset data for HDMI configuration
		(thanks to Jernej ??krabec)
	- more meaningfull register names
	- use a mutex for DE I/O protection
	- merge DE and plane into one file
	- don't activate the video hardware when video not started
		(Maxime Ripard)
	- remove 'type = "video" in DT graph ports
		(Rob Herring)
	- change the I/O accesses by #define instead of struct
		(Maxime Ripard, Andr?? Przywara)
	- remove pm functions (Maxime Ripard)
	- set the pll-de/de clocks in the DT (Maxime Ripard)
	- use platform_get_irq instead of irq_of_parse_and_map
		(Maxime Ripard)
	- rename sunxi to sun8i (Maxime Ripard)
	- fix coding style errors (Maxime Ripard)
	- subclass the drm structure in private data (Daniel Vetter)
	- move drm_dev_register at end of init (Daniel Vetter)
v5:
	- add overlay plane
	- add audio support
	- add support for the A83T
	- add back the HDMI driver
	- many bug fixes
v4: 
	- drivers/clk/sunxi/Makefile was missing (Emil Velikov)
v3:
	- add the hardware cursor
	- simplify and fix the DE2 init sequences
	- generation for all SUNXI SoCs (Andre Przywara)
v2:
	- remove the HDMI driver
	- remarks from Chen-Yu Tsai and Russell King
	- DT documentation added

Jean-Francois Moine (8):
  drm: sun8i: Add a basic DRM driver for Allwinner DE2
  drm/sun8i: Add DT bindings documentation of Allwinner DE2
  drm: sun8i: add HDMI video support to A83T and H3
  drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  clk: sunxi-ng: define the PLL DE clock
  ARM: dts: sun8i-h3: add HDMI video nodes
  ARM: dts: sun8i-h3: Add HDMI video to the Banana Pi M2+
  ARM: dts: sun8i-h3: Add HDMI video to the Orange PI 2

 .../devicetree/bindings/display/sunxi/hdmi.txt     |  56 ++
 .../bindings/display/sunxi/sun8i-de2.txt           | 121 +++
 arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts    |  12 +
 arch/arm/boot/dts/sun8i-h3-orangepi-2.dts          |  12 +
 arch/arm/boot/dts/sun8i-h3.dtsi                    |  65 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/sun8i/Kconfig                      |  26 +
 drivers/gpu/drm/sun8i/Makefile                     |   9 +
 drivers/gpu/drm/sun8i/de2_crtc.c                   | 449 +++++++++++
 drivers/gpu/drm/sun8i/de2_crtc.h                   |  52 ++
 drivers/gpu/drm/sun8i/de2_drv.c                    | 317 ++++++++
 drivers/gpu/drm/sun8i/de2_drv.h                    |  48 ++
 drivers/gpu/drm/sun8i/de2_hdmi.c                   | 440 +++++++++++
 drivers/gpu/drm/sun8i/de2_hdmi.h                   |  51 ++
 drivers/gpu/drm/sun8i/de2_hdmi_io.c                | 842 +++++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_plane.c                  | 734 ++++++++++++++++++
 include/dt-bindings/clock/sun8i-h3-ccu.h           |   1 +
 18 files changed, 3238 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
 create mode 100644 drivers/gpu/drm/sun8i/Kconfig
 create mode 100644 drivers/gpu/drm/sun8i/Makefile
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c

-- 
2.10.2

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

* Re: [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
  2016-11-28 14:23     ` Jean-Francois Moine
@ 2016-11-29 14:30         ` Daniel Vetter
  -1 siblings, 0 replies; 79+ messages in thread
From: Daniel Vetter @ 2016-11-29 14:30 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Dave Airlie, Maxime Ripard, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

On Mon, Nov 28, 2016 at 03:23:54PM +0100, Jean-Francois Moine wrote:
> 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-GANU6spQydw@public.gmane.org>

Scrolled around a bit, seemed all reasonable.

Acked-by: Daniel Vetter <daniel.vetter-/w4YWyX8dFk@public.gmane.org>

Not sure a new driver for each chip is reasonable, experience says that
long-term you want to share quite a pile of code between different hw
platforms from the same vendor. But that's entirely up to you.
-Daniel


> ---
>  drivers/gpu/drm/Kconfig           |   2 +
>  drivers/gpu/drm/Makefile          |   1 +
>  drivers/gpu/drm/sun8i/Kconfig     |  19 +
>  drivers/gpu/drm/sun8i/Makefile    |   7 +
>  drivers/gpu/drm/sun8i/de2_crtc.c  | 449 +++++++++++++++++++++++
>  drivers/gpu/drm/sun8i/de2_crtc.h  |  50 +++
>  drivers/gpu/drm/sun8i/de2_drv.c   | 317 ++++++++++++++++
>  drivers/gpu/drm/sun8i/de2_drv.h   |  48 +++
>  drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
>  9 files changed, 1627 insertions(+)
>  create mode 100644 drivers/gpu/drm/sun8i/Kconfig
>  create mode 100644 drivers/gpu/drm/sun8i/Makefile
>  create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
>  create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
>  create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
>  create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
>  create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 95fc041..bb1bfbc 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
>  
>  source "drivers/gpu/drm/sun4i/Kconfig"
>  
> +source "drivers/gpu/drm/sun8i/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 883f3e7..3e1eaa0 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
>  obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
>  obj-y			+= omapdrm/
>  obj-$(CONFIG_DRM_SUN4I) += sun4i/
> +obj-$(CONFIG_DRM_SUN8I) += sun8i/
>  obj-y			+= tilcdc/
>  obj-$(CONFIG_DRM_QXL) += qxl/
>  obj-$(CONFIG_DRM_BOCHS) += bochs/
> diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
> new file mode 100644
> index 0000000..6940895
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/Kconfig
> @@ -0,0 +1,19 @@
> +#
> +# Allwinner DE2 Video configuration
> +#
> +
> +config DRM_SUN8I
> +	bool
> +
> +config DRM_SUN8I_DE2
> +	tristate "Support for Allwinner Video with DE2 interface"
> +	depends on DRM && OF
> +	depends on ARCH_SUNXI || COMPILE_TEST
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_KMS_HELPER
> +	select DRM_SUN8I
> +	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
> +	  sun8i-de2-drm.
> diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
> new file mode 100644
> index 0000000..f107919
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for Allwinner's sun8i DRM device driver
> +#
> +
> +sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
> +
> +obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
> diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
> new file mode 100644
> index 0000000..4e94ccc
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_crtc.c
> @@ -0,0 +1,449 @@
> +/*
> + * Allwinner DRM driver - DE2 CRTC
> + *
> + * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <linux/component.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <linux/io.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_graph.h>
> +
> +#include "de2_drv.h"
> +#include "de2_crtc.h"
> +
> +/* I/O map */
> +
> +#define TCON_GCTL_REG		0x00
> +#define		TCON_GCTL_TCON_ENABLE BIT(31)
> +#define TCON_GINT0_REG		0x04
> +#define		TCON_GINT0_TCON1_Vb_Int_En BIT(30)
> +#define		TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
> +#define		TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
> +#define TCON0_CTL_REG		0x40
> +#define		TCON0_CTL_TCON_ENABLE BIT(31)
> +#define TCON1_CTL_REG		0x90
> +#define		TCON1_CTL_TCON_ENABLE BIT(31)
> +#define		TCON1_CTL_INTERLACE_ENABLE BIT(20)
> +#define		TCON1_CTL_Start_Delay_SHIFT 4
> +#define		TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
> +#define TCON1_BASIC0_REG	0x94	/* XI/YI */
> +#define TCON1_BASIC1_REG	0x98	/* LS_XO/LS_YO */
> +#define TCON1_BASIC2_REG	0x9c	/* XO/YO */
> +#define TCON1_BASIC3_REG	0xa0	/* HT/HBP */
> +#define TCON1_BASIC4_REG	0xa4	/* VT/VBP */
> +#define TCON1_BASIC5_REG	0xa8	/* HSPW/VSPW */
> +#define TCON1_PS_SYNC_REG	0xb0
> +#define TCON1_IO_POL_REG	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)
> +#define TCON1_IO_TRI_REG	0xf4
> +#define TCON_CEU_CTL_REG	0x100
> +#define		TCON_CEU_CTL_ceu_en BIT(31)
> +#define	TCON1_FILL_CTL_REG	0x300
> +#define TCON1_FILL_START0_REG	0x304
> +#define TCON1_FILL_END0_REG	0x308
> +#define TCON1_FILL_DATA0_REG	0x30c
> +
> +#define XY(x, y) (((x) << 16) | (y))
> +
> +#define andl_relaxed(addr, val) \
> +	writel_relaxed(readl_relaxed(addr) & val, addr)
> +#define orl_relaxed(addr, val) \
> +	writel_relaxed(readl_relaxed(addr) | val, addr)
> +
> +/* 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 = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
> +
> +	drm_crtc_handle_vblank(&lcd->crtc);
> +
> +	writel_relaxed(isr &
> +			~(TCON_GINT0_TCON1_Vb_Int_Flag |
> +			  TCON_GINT0_TCON1_Vb_Line_Int_Flag),
> +			lcd->mmio + TCON_GINT0_REG);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
> +{
> +	struct priv *priv = drm_to_priv(drm);
> +	struct lcd *lcd = priv->lcds[crtc_ix];
> +
> +	orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
> +
> +	return 0;
> +}
> +
> +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
> +{
> +	struct priv *priv = drm_to_priv(drm);
> +	struct lcd *lcd = priv->lcds[crtc_ix];
> +
> +	andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
> +}
> +
> +void de2_vblank_reset(struct lcd *lcd)
> +{
> +	drm_crtc_vblank_reset(&lcd->crtc);
> +}
> +
> +/* frame functions */
> +static int de2_crtc_set_clock(struct lcd *lcd, int rate)
> +{
> +	struct clk *parent_clk;
> +	u32 parent_rate;
> +	int ret;
> +
> +	/* determine and set the best rate for the parent clock (pll-video) */
> +	if ((270000 * 2) % rate == 0)
> +		parent_rate = 270000000;
> +	else if (297000 % rate == 0)
> +		parent_rate = 297000000;
> +	else
> +		return -EINVAL;			/* unsupported clock */
> +
> +	parent_clk = clk_get_parent(lcd->clk);
> +
> +	ret = clk_set_rate(parent_clk, parent_rate);
> +	if (ret) {
> +		dev_err(lcd->dev, "set parent rate failed %d\n", ret);
> +		return ret;
> +	}
> +	ret = clk_set_rate(lcd->clk, rate * 1000);
> +	if (ret) {
> +		dev_err(lcd->dev, "set rate failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* enable the clock */
> +	reset_control_deassert(lcd->reset);
> +	clk_prepare_enable(lcd->bus);
> +	clk_prepare_enable(lcd->clk);
> +
> +	return ret;
> +}
> +
> +static void de2_tcon_init(struct lcd *lcd)
> +{
> +	andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
> +	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
> +	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
> +
> +	/* disable/ack interrupts */
> +	writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
> +}
> +
> +static void de2_tcon_enable(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;
> +
> +	orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
> +
> +	data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
> +	writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
> +	writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
> +	writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
> +	writel_relaxed(XY(mode->htotal - 1,
> +			 mode->htotal - mode->hsync_start - 1),
> +		      lcd->mmio + TCON1_BASIC3_REG);
> +	writel_relaxed(XY(mode->vtotal * (3 - interlace),
> +			 mode->vtotal - mode->vsync_start - 1),
> +		      lcd->mmio + TCON1_BASIC4_REG);
> +	writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
> +			 mode->vsync_end - mode->vsync_start - 1),
> +		      lcd->mmio + TCON1_BASIC5_REG);
> +
> +	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;
> +	writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
> +
> +	andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
> +
> +	if (interlace == 2)
> +		orl_relaxed(lcd->mmio + TCON1_CTL_REG,
> +			    TCON1_CTL_INTERLACE_ENABLE);
> +	else
> +		andl_relaxed(lcd->mmio + TCON1_CTL_REG,
> +			     ~TCON1_CTL_INTERLACE_ENABLE);
> +
> +	writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
> +	writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
> +	writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
> +	writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
> +
> +	start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
> +	if (start_delay > 31)
> +		start_delay = 31;
> +	data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
> +	data &= ~TCON1_CTL_Start_Delay_MASK;
> +	data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
> +	writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
> +
> +	orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
> +}
> +
> +static void de2_tcon_disable(struct lcd *lcd)
> +{
> +	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
> +	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
> +}
> +
> +static void de2_crtc_enable(struct drm_crtc *crtc)
> +{
> +	struct lcd *lcd = crtc_to_lcd(crtc);
> +	struct drm_display_mode *mode = &crtc->mode;
> +
> +	if (de2_crtc_set_clock(lcd, mode->clock) < 0)
> +		return;
> +	lcd->clk_enabled = true;
> +
> +	/* start the TCON and the DE */
> +	de2_tcon_enable(lcd);
> +	de2_de_enable(lcd);
> +
> +	/* turn on blanking interrupt */
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void de2_crtc_disable(struct drm_crtc *crtc,
> +				struct drm_crtc_state *old_crtc_state)
> +{
> +	struct lcd *lcd = crtc_to_lcd(crtc);
> +
> +	if (!lcd->clk_enabled)
> +		return;			/* already disabled */
> +	lcd->clk_enabled = false;
> +
> +	de2_de_disable(lcd);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	de2_tcon_disable(lcd);
> +
> +	clk_disable_unprepare(lcd->clk);
> +	clk_disable_unprepare(lcd->bus);
> +	reset_control_assert(lcd->reset);
> +}
> +
> +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,
> +	.atomic_disable	= de2_crtc_disable,
> +};
> +
> +/* device init */
> +static int de2_lcd_bind(struct device *dev, struct device *master,
> +			void *data)
> +{
> +	struct drm_device *drm = data;
> +	struct priv *priv = drm_to_priv(drm);
> +	struct lcd *lcd = dev_get_drvdata(dev);
> +	struct drm_crtc *crtc = &lcd->crtc;
> +	int ret, i, crtc_ix;
> +
> +	lcd->priv = priv;
> +
> +	/* set the CRTC reference */
> +	crtc_ix = drm_crtc_index(crtc);
> +	if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
> +		dev_err(drm->dev, "Bad crtc index");
> +		return -ENOENT;
> +	}
> +	priv->lcds[crtc_ix] = lcd;
> +
> +	/* and the mixer index (DT port index in the DE) */
> +	for (i = 0; ; i++) {
> +		struct device_node *port;
> +
> +		port = of_parse_phandle(drm->dev->of_node, "ports", i);
> +		if (!port)
> +			break;
> +		if (port == lcd->crtc.port) {
> +			lcd->mixer = i;
> +			break;
> +		}
> +	}
> +
> +	ret = de2_plane_init(drm, lcd);
> +	if (ret < 0)
> +		return ret;
> +
> +	drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
> +
> +	return drm_crtc_init_with_planes(drm, crtc,
> +					 &lcd->planes[DE2_PRIMARY_PLANE],
> +					 &lcd->planes[DE2_CURSOR_PLANE],
> +					 &de2_crtc_funcs, NULL);
> +}
> +
> +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->priv)
> +		lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
> +}
> +
> +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;
> +
> +	lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
> +	if (!lcd)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, lcd);
> +	lcd->dev = dev;
> +	lcd->mixer = 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);
> +	}
> +
> +	/* possible CRTC */
> +	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->bus = devm_clk_get(dev, "bus");
> +	if (IS_ERR(lcd->bus)) {
> +		dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
> +		ret = PTR_ERR(lcd->bus);
> +		goto err;
> +	}
> +
> +	lcd->clk = devm_clk_get(dev, "clock");
> +	if (IS_ERR(lcd->clk)) {
> +		ret = PTR_ERR(lcd->clk);
> +		dev_err(dev, "get video clock err %d\n", ret);
> +		goto err;
> +	}
> +
> +	lcd->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(lcd->reset)) {
> +		ret = PTR_ERR(lcd->reset);
> +		dev_err(dev, "get reset err %d\n", ret);
> +		goto err;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(dev, "unable to get irq\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	de2_tcon_init(lcd);		/* stop TCON and avoid interrupts */
> +
> +	ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
> +				dev_name(dev), lcd);
> +	if (ret < 0) {
> +		dev_err(dev, "unable to request irq %d\n", irq);
> +		goto err;
> +	}
> +
> +	return component_add(dev, &de2_lcd_ops);
> +
> +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);
> +
> +	of_node_put(lcd->crtc.port);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id de2_lcd_ids[] = {
> +	{ .compatible = "allwinner,sun8i-a83t-tcon", },
> +	{ }
> +};
> +
> +struct platform_driver de2_lcd_platform_driver = {
> +	.probe = de2_lcd_probe,
> +	.remove = de2_lcd_remove,
> +	.driver = {
> +		.name = "sun8i-de2-tcon",
> +		.of_match_table = of_match_ptr(de2_lcd_ids),
> +	},
> +};
> diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
> new file mode 100644
> index 0000000..c0d34a7
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_crtc.h
> @@ -0,0 +1,50 @@
> +#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 <drm/drm_plane_helper.h>
> +
> +struct clk;
> +struct reset_control;
> +struct priv;
> +
> +/* planes */
> +#define DE2_PRIMARY_PLANE 0
> +#define DE2_CURSOR_PLANE 1
> +#define DE2_N_PLANES 5	/* number of planes - see plane_tb[] in de2_plane.c */
> +
> +struct lcd {
> +	void __iomem *mmio;
> +
> +	struct device *dev;
> +	struct drm_crtc crtc;
> +
> +	struct priv *priv;	/* DRM/DE private data */
> +
> +	u8 mixer;		/* LCD (mixer) number */
> +	u8 delayed;		/* bitmap of planes with delayed update */
> +
> +	u8 clk_enabled;		/* used for error in crtc_enable */
> +
> +	struct clk *clk;
> +	struct clk *bus;
> +	struct reset_control *reset;
> +
> +	struct drm_plane planes[DE2_N_PLANES];
> +};
> +
> +#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
> +
> +/* in de2_plane.c */
> +void de2_de_enable(struct lcd *lcd);
> +void de2_de_disable(struct lcd *lcd);
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
> +
> +#endif /* __DE2_CRTC_H__ */
> diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
> new file mode 100644
> index 0000000..f96babe
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_drv.c
> @@ -0,0 +1,317 @@
> +/*
> + * Allwinner DRM driver - DE2 DRM driver
> + *
> + * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <drm/drm_of.h>
> +#include <linux/component.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +
> +#include "de2_drv.h"
> +
> +#define DRIVER_NAME	"sun8i-de2"
> +#define DRIVER_DESC	"Allwinner DRM DE2"
> +#define DRIVER_DATE	"20161101"
> +#define DRIVER_MAJOR	1
> +#define DRIVER_MINOR	0
> +
> +static const 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_to_priv(drm);
> +
> +	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_to_priv(drm);
> +
> +	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,
> +};
> +
> +/*
> + * Platform driver
> + */
> +
> +static int de2_drm_bind(struct device *dev)
> +{
> +	struct drm_device *drm;
> +	struct priv *priv;
> +	struct resource *res;
> +	struct lcd *lcd;
> +	int i, ret;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	drm = &priv->drm;
> +	dev_set_drvdata(dev, drm);
> +
> +	/* get the resources */
> +	priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
> +
> +	res = platform_get_resource(to_platform_device(dev),
> +				IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "failed to get memory resource\n");
> +		ret = -EINVAL;
> +		goto out1;
> +	}
> +
> +	priv->mmio = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(priv->mmio)) {
> +		ret = PTR_ERR(priv->mmio);
> +		dev_err(dev, "failed to map registers %d\n", ret);
> +		goto out1;
> +	}
> +
> +	priv->gate = devm_clk_get(dev, "bus");
> +	if (IS_ERR(priv->gate)) {
> +		ret = PTR_ERR(priv->gate);
> +		dev_err(dev, "bus gate err %d\n", ret);
> +		goto out1;
> +	}
> +
> +	priv->clk = devm_clk_get(dev, "clock");
> +	if (IS_ERR(priv->clk)) {
> +		ret = PTR_ERR(priv->clk);
> +		dev_err(dev, "clock err %d\n", ret);
> +		goto out1;
> +	}
> +
> +	priv->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(priv->reset)) {
> +		ret = PTR_ERR(priv->reset);
> +		dev_err(dev, "reset err %d\n", ret);
> +		goto out1;
> +	}
> +
> +	mutex_init(&priv->mutex);	/* protect DE I/O accesses */
> +
> +	ret = drm_dev_init(drm, &de2_drm_driver, dev);
> +	if (ret != 0) {
> +		dev_err(dev, "dev_init failed %d\n", ret);
> +		goto out1;
> +	}
> +
> +	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;
> +
> +	/* start the subdevices */
> +	ret = component_bind_all(dev, drm);
> +	if (ret < 0)
> +		goto out2;
> +
> +	/* initialize and disable vertical blanking on all CRTCs */
> +	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
> +	if (ret < 0)
> +		dev_warn(dev, "vblank_init failed %d\n", ret);
> +
> +	for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
> +		lcd = priv->lcds[i];
> +		if (lcd)
> +			de2_vblank_reset(lcd);
> +	}
> +
> +	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 out3;
> +	}
> +
> +	drm_kms_helper_poll_init(drm);
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret < 0)
> +		goto out4;
> +
> +	return 0;
> +
> +out4:
> +	drm_fbdev_cma_fini(priv->fbdev);
> +out3:
> +	component_unbind_all(dev, drm);
> +out2:
> +	drm_dev_unref(drm);
> +out1:
> +	kfree(priv);
> +	return ret;
> +}
> +
> +static void de2_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm = dev_get_drvdata(dev);
> +	struct priv *priv = drm_to_priv(drm);
> +
> +	drm_dev_unregister(drm);
> +
> +	drm_fbdev_cma_fini(priv->fbdev);
> +	drm_kms_helper_poll_fini(drm);
> +	drm_vblank_cleanup(drm);
> +	drm_mode_config_cleanup(drm);
> +
> +	component_unbind_all(dev, drm);
> +
> +	kfree(priv);
> +}
> +
> +static const struct component_master_ops de2_drm_comp_ops = {
> +	.bind = de2_drm_bind,
> +	.unbind = de2_drm_unbind,
> +};
> +
> +/*
> + * drm_of_component_probe() does:
> + * - bind of the ports (lcd-controller.port)
> + * - bind of the remote nodes (hdmi, tve..)
> + */
> +static int compare_of(struct device *dev, void *data)
> +{
> +	struct device_node *np = data;
> +
> +	if (of_node_cmp(np->name, "port") == 0) {
> +		np = of_get_parent(np);
> +		of_node_put(np);
> +	}
> +	return dev->of_node == np;
> +}
> +
> +static int de2_drm_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = drm_of_component_probe(&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,
> +		.of_match_table = de2_drm_of_match,
> +	},
> +};
> +
> +static int __init de2_drm_init(void)
> +{
> +	int ret;
> +
> +	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-GANU6spQydw@public.gmane.org>");
> +MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
> new file mode 100644
> index 0000000..c42c30a
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_drv.h
> @@ -0,0 +1,48 @@
> +#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 <drm/drmP.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +struct drm_fbdev_cma;
> +struct lcd;
> +
> +#define N_LCDS 2
> +
> +struct priv {
> +	struct drm_device drm;
> +	void __iomem *mmio;
> +	struct clk *clk;
> +	struct clk *gate;
> +	struct reset_control *reset;
> +
> +	struct mutex mutex;	/* protect DE I/O access */
> +	u8 soc_type;
> +#define SOC_A83T 0
> +#define SOC_H3 1
> +	u8 started;		/* bitmap of started mixers */
> +	u8 clean;		/* bitmap of clean mixers */
> +
> +	struct drm_fbdev_cma *fbdev;
> +
> +	struct lcd *lcds[N_LCDS]; /* CRTCs */
> +};
> +
> +#define drm_to_priv(x) container_of(x, struct priv, drm)
> +
> +/* in de2_crtc.c */
> +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
> +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
> +void de2_vblank_reset(struct lcd *lcd);
> +extern struct platform_driver de2_lcd_platform_driver;
> +
> +#endif /* __DE2_DRM_H__ */
> diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
> new file mode 100644
> index 0000000..2fd72dc
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_plane.c
> @@ -0,0 +1,734 @@
> +/*
> + * Allwinner DRM driver - Display Engine 2
> + *
> + * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> + * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
> + *	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 <linux/io.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "de2_drv.h"
> +#include "de2_crtc.h"
> +
> +/* DE2 I/O map */
> +
> +#define DE2_MOD_REG 0x0000		/* 1 bit per LCD */
> +#define DE2_GATE_REG 0x0004
> +#define DE2_RESET_REG 0x0008
> +#define DE2_DIV_REG 0x000c		/* 4 bits per LCD */
> +#define DE2_SEL_REG 0x0010
> +
> +#define DE2_MIXER0_BASE 0x00100000	/* LCD 0 */
> +#define DE2_MIXER1_BASE 0x00200000	/* LCD 1 */
> +
> +/* mixer registers (addr / mixer base) */
> +#define MIXER_GLB_REGS	0x00000		/* global control */
> +#define MIXER_BLD_REGS	0x01000		/* alpha blending */
> +#define MIXER_CHAN_REGS 0x02000		/* VI/UI overlay channels */
> +#define		MIXER_CHAN_SZ 0x1000	/* size of a channel */
> +#define MIXER_VSU_REGS	0x20000		/* VSU */
> +#define MIXER_GSU1_REGS 0x30000		/* GSUs */
> +#define MIXER_GSU2_REGS 0x40000
> +#define MIXER_GSU3_REGS 0x50000
> +#define MIXER_FCE_REGS	0xa0000		/* FCE */
> +#define MIXER_BWS_REGS	0xa2000		/* BWS */
> +#define MIXER_LTI_REGS	0xa4000		/* LTI */
> +#define MIXER_PEAK_REGS 0xa6000		/* PEAK */
> +#define MIXER_ASE_REGS	0xa8000		/* ASE */
> +#define MIXER_FCC_REGS	0xaa000		/* FCC */
> +#define MIXER_DCSC_REGS 0xb0000		/* DCSC/SMBL */
> +
> +/* global control */
> +#define MIXER_GLB_CTL_REG	0x00
> +#define		MIXER_GLB_CTL_rt_en BIT(0)
> +#define		MIXER_GLB_CTL_finish_irq_en BIT(4)
> +#define		MIXER_GLB_CTL_rtwb_port BIT(12)
> +#define MIXER_GLB_STATUS_REG	0x04
> +#define MIXER_GLB_DBUFF_REG	0x08
> +#define MIXER_GLB_SIZE_REG	0x0c
> +
> +/* alpha blending */
> +#define MIXER_BLD_FCOLOR_CTL_REG	0x00
> +#define		MIXER_BLD_FCOLOR_CTL_PEN(pipe)	(0x0100 << (pipe))
> +#define	MIXER_BLD_ATTR_N 4		/* number of attribute blocks */
> +#define	MIXER_BLD_ATTR_SIZE (4 * 4)	/* size of an attribute block */
> +#define MIXER_BLD_ATTRx_FCOLOR(x)	(0x04 + MIXER_BLD_ATTR_SIZE * (x))
> +#define MIXER_BLD_ATTRx_INSIZE(x)	(0x08 + MIXER_BLD_ATTR_SIZE * (x))
> +#define MIXER_BLD_ATTRx_OFFSET(x)	(0x0c + MIXER_BLD_ATTR_SIZE * (x))
> +#define MIXER_BLD_ROUTE_REG	0x80
> +#define		MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
> +#define MIXER_BLD_PREMULTIPLY_REG	0x84
> +#define MIXER_BLD_BKCOLOR_REG	0x88
> +#define MIXER_BLD_OUTPUT_SIZE_REG	0x8c
> +#define MIXER_BLD_MODEx_REG(x)	(0x90 + 4 * (x))	/* x = 0..3 */
> +#define		MIXER_BLD_MODE_SRCOVER	0x03010301
> +#define MIXER_BLD_OUT_CTL_REG	0xfc
> +
> +/* VI channel (channel 0) */
> +#define VI_CFG_N		4		/* number of layers */
> +#define VI_CFG_SIZE		0x30		/* size of a layer */
> +#define VI_CFGx_ATTR(l)		(0x00 + VI_CFG_SIZE * (l))
> +#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)
> +#define VI_CFGx_SIZE(l)		(0x04 + VI_CFG_SIZE * (l))
> +#define VI_CFGx_COORD(l)	(0x08 + VI_CFG_SIZE * (l))
> +#define VI_N_PLANES 3
> +#define VI_CFGx_PITCHy(l, p)	(0x0c + VI_CFG_SIZE * (l) + 4 * (p))
> +#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
> +#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
> +#define VI_FCOLORx(l)		(0xc0 + 4 * (l))
> +#define VI_TOP_HADDRx(p)	(0xd0 + 4 * (p))
> +#define VI_BOT_HADDRx(p)	(0xdc + 4 * (p))
> +#define VI_OVL_SIZEx(n)		(0xe8 + 4 * (n))
> +#define VI_HORI_DSx(n)		(0xf0 + 4 * (n))
> +#define VI_VERT_DSx(n)		(0xf8 + 4 * (n))
> +#define VI_SIZE			0x100
> +
> +/* UI channel (channels 1..3) */
> +#define UI_CFG_N		4		/* number of layers */
> +#define UI_CFG_SIZE		(8 * 4)		/* size of a layer */
> +#define UI_CFGx_ATTR(l)		(0x00 + UI_CFG_SIZE * (l))
> +#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)
> +#define UI_CFGx_SIZE(l)		(0x04 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_COORD(l)	(0x08 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_PITCH(l)	(0x0c + UI_CFG_SIZE * (l))
> +#define UI_CFGx_TOP_LADDR(l)	(0x10 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_BOT_LADDR(l)	(0x14 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_FCOLOR(l)	(0x18 + UI_CFG_SIZE * (l))
> +#define UI_TOP_HADDR		0x80
> +#define UI_BOT_HADDR		0x84
> +#define UI_OVL_SIZE		0x88
> +#define UI_SIZE			0x8c
> +
> +/* 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	/* YVYU */
> +#define DE2_FORMAT_YUV422_I_UYVY 2	/* UYVY */
> +#define DE2_FORMAT_YUV422_I_YUYV 3	/* YUYV */
> +#define DE2_FORMAT_YUV422_P 6		/* YYYY UU VV planar */
> +#define DE2_FORMAT_YUV420_P 10		/* YYYY U V planar */
> +
> +/* 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,
> +};
> +
> +/*
> + * plane table
> + *
> + * The chosen channel/layer assignment of the planes respects
> + * the following constraints:
> + * - the cursor must be in a channel higher than the primary channel
> + * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
> + */
> +static const struct {
> +	u8 chan;
> +	u8 layer;
> +	u8 pipe;
> +	u8 type;			/* plane type */
> +	const uint32_t *formats;
> +	u8 n_formats;
> +} plane_tb[] = {
> +	[DE2_PRIMARY_PLANE] = {		/* primary plane: channel 0 (VI) */
> +		0, 0, 0,
> +		DRM_PLANE_TYPE_PRIMARY,
> +		ui_formats, ARRAY_SIZE(ui_formats),
> +	},
> +	[DE2_CURSOR_PLANE] = {		/* cursor: channel 1 (UI) */
> +		1, 0, 1,
> +		DRM_PLANE_TYPE_CURSOR,
> +		ui_formats, ARRAY_SIZE(ui_formats),
> +	},
> +	{
> +		0, 1, 0,		/* 1st overlay: channel 0, layer 1 */
> +		DRM_PLANE_TYPE_OVERLAY,
> +		vi_formats, ARRAY_SIZE(vi_formats),
> +	},
> +	{
> +		0, 2, 0,		/* 2nd overlay: channel 0, layer 2 */
> +		DRM_PLANE_TYPE_OVERLAY,
> +		vi_formats, ARRAY_SIZE(vi_formats),
> +	},
> +	{
> +		0, 3, 0,		/* 3rd overlay: channel 0, layer 3 */
> +		DRM_PLANE_TYPE_OVERLAY,
> +		vi_formats, ARRAY_SIZE(vi_formats),
> +	},
> +};
> +
> +static inline void andl_relaxed(void __iomem *addr, u32 val)
> +{
> +	writel_relaxed(readl_relaxed(addr) & val, addr);
> +}
> +
> +static inline void orl_relaxed(void __iomem *addr, u32 val)
> +{
> +	writel_relaxed(readl_relaxed(addr) | val, addr);
> +}
> +
> +/* alert the DE processor about changes in a mixer configuration */
> +static void de2_mixer_select(struct priv *priv,
> +			int mixer,
> +			void __iomem *mixer_io)
> +{
> +	/* select the mixer */
> +	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
> +
> +	/* double register switch */
> +	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
> +}
> +
> +/*
> + * cleanup a mixer
> + *
> + * This is needed only once after power on.
> + */
> +static void de2_mixer_cleanup(struct priv *priv, int mixer,
> +				u32 size)
> +{
> +	void __iomem *mixer_io = priv->mmio;
> +	void __iomem *chan_io;
> +	u32 data;
> +	unsigned int i;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +	chan_io = mixer_io + MIXER_CHAN_REGS;
> +
> +	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
> +	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
> +
> +	writel_relaxed(MIXER_GLB_CTL_rt_en,
> +			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
> +	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
> +
> +	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
> +
> +	/*
> +	 * clear the VI/UI channels
> +	 *	LCD0: 1 VI and 3 UIs
> +	 *	LCD1: 1 VI and 1 UI
> +	 */
> +	memset_io(chan_io, 0, VI_SIZE);
> +	memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
> +	if (mixer == 0) {
> +		memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
> +		memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
> +	}
> +
> +	/* alpha blending */
> +	writel_relaxed(0x00000001 |		/* fcolor for primary */
> +			MIXER_BLD_FCOLOR_CTL_PEN(0),
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
> +	for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
> +		writel_relaxed(0xff000000,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
> +		writel_relaxed(size,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
> +		writel_relaxed(0,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
> +	}
> +	writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
> +
> +	/* prepare the pipe route for the planes */
> +	data = 0;
> +	for (i = 0; i < DE2_N_PLANES; i++)
> +		data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
> +	writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
> +
> +	writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_PREMULTIPLY_REG);
> +	writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_BKCOLOR_REG);
> +	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_OUTPUT_SIZE_REG);
> +	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
> +	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
> +
> +	/* disable the enhancements */
> +	writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
> +}
> +
> +/* enable a mixer */
> +static void de2_mixer_enable(struct lcd *lcd)
> +{
> +	struct priv *priv = lcd->priv;
> +	void __iomem *mixer_io = priv->mmio;
> +	struct drm_display_mode *mode = &lcd->crtc.mode;
> +	u32 size = WH(mode->hdisplay, mode->vdisplay);
> +	u32 data;
> +	int mixer = lcd->mixer;
> +	int i;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +
> +	/* if not done yet, start the DE processor */
> +	if (!priv->started) {
> +		reset_control_deassert(priv->reset);
> +		clk_prepare_enable(priv->gate);
> +		clk_prepare_enable(priv->clk);
> +	}
> +	priv->started |= 1 << mixer;
> +
> +	/* set the A83T clock divider (500 / 2) = 250MHz */
> +	if (priv->soc_type == SOC_A83T)
> +		writel_relaxed(0x00000011, /* div = 2 for both LCDs */
> +				priv->mmio + DE2_DIV_REG);
> +
> +	/* deassert the mixer and enable its clock */
> +	orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
> +	data = 1 << mixer;			/* 1 bit / lcd */
> +	orl_relaxed(priv->mmio + DE2_GATE_REG, data);
> +	orl_relaxed(priv->mmio + DE2_MOD_REG, data);
> +
> +	/* if not done yet, cleanup and enable */
> +	if (!(priv->clean & (1 << mixer))) {
> +		priv->clean |= 1 << mixer;
> +		de2_mixer_cleanup(priv, mixer, size);
> +		return;
> +	}
> +
> +	/* enable */
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	writel_relaxed(MIXER_GLB_CTL_rt_en,
> +			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
> +	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
> +
> +	/* set the size of the frame buffer */
> +	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
> +	for (i = 0; i < MIXER_BLD_ATTR_N; i++)
> +		writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
> +				MIXER_BLD_ATTRx_INSIZE(i));
> +	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_OUTPUT_SIZE_REG);
> +
> +	writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
> +}
> +
> +/* enable a LCD (DE mixer) */
> +void de2_de_enable(struct lcd *lcd)
> +{
> +	mutex_lock(&lcd->priv->mutex);
> +
> +	de2_mixer_enable(lcd);
> +
> +	mutex_unlock(&lcd->priv->mutex);
> +}
> +
> +/* disable a LCD (DE mixer) */
> +void de2_de_disable(struct lcd *lcd)
> +{
> +	struct priv *priv = lcd->priv;
> +	void __iomem *mixer_io = priv->mmio;
> +	int mixer = lcd->mixer;
> +	u32 data;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +
> +	mutex_lock(&priv->mutex);
> +
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
> +
> +	data = ~(1 << mixer);
> +	andl_relaxed(priv->mmio + DE2_MOD_REG, data);
> +	andl_relaxed(priv->mmio + DE2_GATE_REG, data);
> +	andl_relaxed(priv->mmio + DE2_RESET_REG, data);
> +
> +	mutex_unlock(&priv->mutex);
> +
> +	/* if all mixers are disabled, stop the DE */
> +	priv->started &= ~(1 << mixer);
> +	if (!priv->started) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->gate);
> +		reset_control_assert(priv->reset);
> +	}
> +}
> +
> +static void de2_vi_update(void __iomem *chan_io,
> +			  struct drm_gem_cma_object *gem,
> +			  int layer,
> +			  unsigned int fmt,
> +			  u32 ui_sel,
> +			  u32 size,
> +			  u32 coord,
> +			  struct drm_framebuffer *fb,
> +			  u32 screen_size)
> +{
> +	int i;
> +
> +	writel_relaxed(VI_CFG_ATTR_en |
> +			(fmt << VI_CFG_ATTR_fmt_SHIFT) |
> +			ui_sel,
> +			chan_io + VI_CFGx_ATTR(layer));
> +	writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
> +	writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
> +	for (i = 0; i < VI_N_PLANES; i++) {
> +		writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
> +						fb->pitches[0],
> +				chan_io + VI_CFGx_PITCHy(layer, i));
> +		writel_relaxed(gem->paddr + fb->offsets[i],
> +				chan_io + VI_CFGx_TOP_LADDRy(layer, i));
> +	}
> +	writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
> +	if (layer == 0) {
> +		writel_relaxed(screen_size,
> +				chan_io + VI_OVL_SIZEx(0));
> +	}
> +}
> +
> +static void de2_ui_update(void __iomem *chan_io,
> +			  struct drm_gem_cma_object *gem,
> +			  int layer,
> +			  unsigned int fmt,
> +			  u32 alpha_glob,
> +			  u32 size,
> +			  u32 coord,
> +			  struct drm_framebuffer *fb,
> +			  u32 screen_size)
> +{
> +	writel_relaxed(UI_CFG_ATTR_en |
> +			(fmt << UI_CFG_ATTR_fmt_SHIFT) |
> +			alpha_glob,
> +			chan_io + UI_CFGx_ATTR(layer));
> +	writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
> +	writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
> +	writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
> +	writel_relaxed(gem->paddr + fb->offsets[0],
> +			chan_io + UI_CFGx_TOP_LADDR(layer));
> +	if (layer == 0)
> +		writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
> +}
> +
> +static void de2_plane_update(struct priv *priv, struct lcd *lcd,
> +				int plane_num,
> +				struct drm_plane_state *state,
> +				struct drm_plane_state *old_state)
> +{
> +	void __iomem *mixer_io = priv->mmio;
> +	void __iomem *chan_io;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	u32 size = WH(state->crtc_w, state->crtc_h);
> +	u32 coord, screen_size;
> +	u32 fcolor;
> +	u32 ui_sel, alpha_glob;
> +	int mixer = lcd->mixer;
> +	int chan, layer, x, y;
> +	unsigned int fmt;
> +
> +	chan = plane_tb[plane_num].chan;
> +	layer = plane_tb[plane_num].layer;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_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);
> +
> +	/* if plane update was delayed, force a full update */
> +	if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
> +			(1 << plane_num)) {
> +		priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
> +							~(1 << plane_num);
> +
> +	/* handle plane move */
> +	} else if (fb == old_state->fb) {
> +		de2_mixer_select(priv, mixer, mixer_io);
> +		if (chan == 0)
> +			writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
> +		else
> +			writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
> +		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("de2_plane_update: format %.4s not yet treated\n",
> +			(char *) &fb->pixel_format);
> +		return;
> +	}
> +
> +	/* the overlay size is the one of the primary plane */
> +	screen_size = plane_num == DE2_PRIMARY_PLANE ?
> +		size :
> +		readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
> +
> +	/* prepare pipe enable */
> +	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
> +				MIXER_BLD_FCOLOR_CTL_REG);
> +	fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
> +
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	if (chan == 0)				/* VI channel */
> +		de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
> +				fb, screen_size);
> +	else					/* UI channel */
> +		de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
> +				fb, screen_size);
> +	writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
> +				MIXER_BLD_FCOLOR_CTL_REG);
> +}
> +
> +static int vi_nb_layers(void __iomem *chan_io)
> +{
> +	int layer, n = 0;
> +
> +	for (layer = 0; layer < 4; layer++) {
> +		if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
> +			n++;
> +	}
> +
> +	return n;
> +}
> +
> +static int ui_nb_layers(void __iomem *chan_io)
> +{
> +	int layer, n = 0;
> +
> +	for (layer = 0; layer < 4; layer++) {
> +		if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
> +			n++;
> +	}
> +
> +	return n;
> +}
> +
> +static void de2_plane_disable(struct priv *priv,
> +				int mixer, int plane_num)
> +{
> +	void __iomem *mixer_io = priv->mmio;
> +	void __iomem *chan_io;
> +	u32 fcolor;
> +	int chan, layer, n;
> +
> +	chan = plane_tb[plane_num].chan;
> +	layer = plane_tb[plane_num].layer;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
> +
> +	if (chan == 0)
> +		n = vi_nb_layers(chan_io);
> +	else
> +		n = ui_nb_layers(chan_io);
> +
> +	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_FCOLOR_CTL_REG);
> +
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	if (chan == 0)
> +		writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
> +	else
> +		writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
> +
> +	/* disable the pipe if no more active layer */
> +	if (n <= 1)
> +		writel_relaxed(fcolor &
> +			~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
> +}
> +
> +static void de2_drm_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 priv *priv = lcd->priv;
> +	int plane_num = plane - lcd->planes;
> +
> +	/* if the crtc is disabled, mark update delayed */
> +	if (!(priv->started & (1 << lcd->mixer))) {
> +		lcd->delayed |= 1 << plane_num;
> +		return;				/* mixer disabled */
> +	}
> +
> +	mutex_lock(&priv->mutex);
> +
> +	de2_plane_update(priv, lcd, plane_num, state, old_state);
> +
> +	mutex_unlock(&priv->mutex);
> +}
> +
> +static void de2_drm_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);
> +	struct priv *priv = lcd->priv;
> +	int plane_num = plane - lcd->planes;
> +
> +	if (!(priv->started & (1 << lcd->mixer)))
> +		return;				/* mixer disabled */
> +
> +	mutex_lock(&priv->mutex);
> +
> +	de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
> +
> +	mutex_unlock(&priv->mutex);
> +}
> +
> +static const struct drm_plane_helper_funcs plane_helper_funcs = {
> +	.atomic_update = de2_drm_plane_update,
> +	.atomic_disable = de2_drm_plane_disable,
> +};
> +
> +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 possible_crtcs,
> +				int plane_num)
> +{
> +	int ret;
> +
> +	ret = drm_universal_plane_init(drm, plane, possible_crtcs,
> +				&plane_funcs,
> +				plane_tb[plane_num].formats,
> +				plane_tb[plane_num].n_formats,
> +				plane_tb[plane_num].type, NULL);
> +	if (ret >= 0)
> +		drm_plane_helper_add(plane, &plane_helper_funcs);
> +
> +	return ret;
> +}
> +
> +/* initialize the planes */
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
> +{
> +	int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
> +
> +	n = ARRAY_SIZE(plane_tb);
> +	if (n != DE2_N_PLANES) {
> +		dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
> +			__stringify(DE2_N_PLANES) "\n", n);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < n; i++) {
> +		ret = de2_one_plane_init(drm, &lcd->planes[i],
> +				possible_crtcs, i);
> +		if (ret < 0) {
> +			dev_err(lcd->dev, "plane init failed %d\n", ret);
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> -- 
> 2.10.2
> 

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


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

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

* [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
@ 2016-11-29 14:30         ` Daniel Vetter
  0 siblings, 0 replies; 79+ messages in thread
From: Daniel Vetter @ 2016-11-29 14:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 28, 2016 at 03:23:54PM +0100, Jean-Francois Moine wrote:
> 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>

Scrolled around a bit, seemed all reasonable.

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>

Not sure a new driver for each chip is reasonable, experience says that
long-term you want to share quite a pile of code between different hw
platforms from the same vendor. But that's entirely up to you.
-Daniel


> ---
>  drivers/gpu/drm/Kconfig           |   2 +
>  drivers/gpu/drm/Makefile          |   1 +
>  drivers/gpu/drm/sun8i/Kconfig     |  19 +
>  drivers/gpu/drm/sun8i/Makefile    |   7 +
>  drivers/gpu/drm/sun8i/de2_crtc.c  | 449 +++++++++++++++++++++++
>  drivers/gpu/drm/sun8i/de2_crtc.h  |  50 +++
>  drivers/gpu/drm/sun8i/de2_drv.c   | 317 ++++++++++++++++
>  drivers/gpu/drm/sun8i/de2_drv.h   |  48 +++
>  drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
>  9 files changed, 1627 insertions(+)
>  create mode 100644 drivers/gpu/drm/sun8i/Kconfig
>  create mode 100644 drivers/gpu/drm/sun8i/Makefile
>  create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
>  create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
>  create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
>  create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
>  create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 95fc041..bb1bfbc 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
>  
>  source "drivers/gpu/drm/sun4i/Kconfig"
>  
> +source "drivers/gpu/drm/sun8i/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 883f3e7..3e1eaa0 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
>  obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
>  obj-y			+= omapdrm/
>  obj-$(CONFIG_DRM_SUN4I) += sun4i/
> +obj-$(CONFIG_DRM_SUN8I) += sun8i/
>  obj-y			+= tilcdc/
>  obj-$(CONFIG_DRM_QXL) += qxl/
>  obj-$(CONFIG_DRM_BOCHS) += bochs/
> diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
> new file mode 100644
> index 0000000..6940895
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/Kconfig
> @@ -0,0 +1,19 @@
> +#
> +# Allwinner DE2 Video configuration
> +#
> +
> +config DRM_SUN8I
> +	bool
> +
> +config DRM_SUN8I_DE2
> +	tristate "Support for Allwinner Video with DE2 interface"
> +	depends on DRM && OF
> +	depends on ARCH_SUNXI || COMPILE_TEST
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_KMS_HELPER
> +	select DRM_SUN8I
> +	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
> +	  sun8i-de2-drm.
> diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
> new file mode 100644
> index 0000000..f107919
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for Allwinner's sun8i DRM device driver
> +#
> +
> +sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
> +
> +obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
> diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
> new file mode 100644
> index 0000000..4e94ccc
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_crtc.c
> @@ -0,0 +1,449 @@
> +/*
> + * 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 <linux/io.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_graph.h>
> +
> +#include "de2_drv.h"
> +#include "de2_crtc.h"
> +
> +/* I/O map */
> +
> +#define TCON_GCTL_REG		0x00
> +#define		TCON_GCTL_TCON_ENABLE BIT(31)
> +#define TCON_GINT0_REG		0x04
> +#define		TCON_GINT0_TCON1_Vb_Int_En BIT(30)
> +#define		TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
> +#define		TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
> +#define TCON0_CTL_REG		0x40
> +#define		TCON0_CTL_TCON_ENABLE BIT(31)
> +#define TCON1_CTL_REG		0x90
> +#define		TCON1_CTL_TCON_ENABLE BIT(31)
> +#define		TCON1_CTL_INTERLACE_ENABLE BIT(20)
> +#define		TCON1_CTL_Start_Delay_SHIFT 4
> +#define		TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
> +#define TCON1_BASIC0_REG	0x94	/* XI/YI */
> +#define TCON1_BASIC1_REG	0x98	/* LS_XO/LS_YO */
> +#define TCON1_BASIC2_REG	0x9c	/* XO/YO */
> +#define TCON1_BASIC3_REG	0xa0	/* HT/HBP */
> +#define TCON1_BASIC4_REG	0xa4	/* VT/VBP */
> +#define TCON1_BASIC5_REG	0xa8	/* HSPW/VSPW */
> +#define TCON1_PS_SYNC_REG	0xb0
> +#define TCON1_IO_POL_REG	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)
> +#define TCON1_IO_TRI_REG	0xf4
> +#define TCON_CEU_CTL_REG	0x100
> +#define		TCON_CEU_CTL_ceu_en BIT(31)
> +#define	TCON1_FILL_CTL_REG	0x300
> +#define TCON1_FILL_START0_REG	0x304
> +#define TCON1_FILL_END0_REG	0x308
> +#define TCON1_FILL_DATA0_REG	0x30c
> +
> +#define XY(x, y) (((x) << 16) | (y))
> +
> +#define andl_relaxed(addr, val) \
> +	writel_relaxed(readl_relaxed(addr) & val, addr)
> +#define orl_relaxed(addr, val) \
> +	writel_relaxed(readl_relaxed(addr) | val, addr)
> +
> +/* 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 = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
> +
> +	drm_crtc_handle_vblank(&lcd->crtc);
> +
> +	writel_relaxed(isr &
> +			~(TCON_GINT0_TCON1_Vb_Int_Flag |
> +			  TCON_GINT0_TCON1_Vb_Line_Int_Flag),
> +			lcd->mmio + TCON_GINT0_REG);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
> +{
> +	struct priv *priv = drm_to_priv(drm);
> +	struct lcd *lcd = priv->lcds[crtc_ix];
> +
> +	orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
> +
> +	return 0;
> +}
> +
> +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
> +{
> +	struct priv *priv = drm_to_priv(drm);
> +	struct lcd *lcd = priv->lcds[crtc_ix];
> +
> +	andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
> +}
> +
> +void de2_vblank_reset(struct lcd *lcd)
> +{
> +	drm_crtc_vblank_reset(&lcd->crtc);
> +}
> +
> +/* frame functions */
> +static int de2_crtc_set_clock(struct lcd *lcd, int rate)
> +{
> +	struct clk *parent_clk;
> +	u32 parent_rate;
> +	int ret;
> +
> +	/* determine and set the best rate for the parent clock (pll-video) */
> +	if ((270000 * 2) % rate == 0)
> +		parent_rate = 270000000;
> +	else if (297000 % rate == 0)
> +		parent_rate = 297000000;
> +	else
> +		return -EINVAL;			/* unsupported clock */
> +
> +	parent_clk = clk_get_parent(lcd->clk);
> +
> +	ret = clk_set_rate(parent_clk, parent_rate);
> +	if (ret) {
> +		dev_err(lcd->dev, "set parent rate failed %d\n", ret);
> +		return ret;
> +	}
> +	ret = clk_set_rate(lcd->clk, rate * 1000);
> +	if (ret) {
> +		dev_err(lcd->dev, "set rate failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* enable the clock */
> +	reset_control_deassert(lcd->reset);
> +	clk_prepare_enable(lcd->bus);
> +	clk_prepare_enable(lcd->clk);
> +
> +	return ret;
> +}
> +
> +static void de2_tcon_init(struct lcd *lcd)
> +{
> +	andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
> +	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
> +	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
> +
> +	/* disable/ack interrupts */
> +	writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
> +}
> +
> +static void de2_tcon_enable(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;
> +
> +	orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
> +
> +	data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
> +	writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
> +	writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
> +	writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
> +	writel_relaxed(XY(mode->htotal - 1,
> +			 mode->htotal - mode->hsync_start - 1),
> +		      lcd->mmio + TCON1_BASIC3_REG);
> +	writel_relaxed(XY(mode->vtotal * (3 - interlace),
> +			 mode->vtotal - mode->vsync_start - 1),
> +		      lcd->mmio + TCON1_BASIC4_REG);
> +	writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
> +			 mode->vsync_end - mode->vsync_start - 1),
> +		      lcd->mmio + TCON1_BASIC5_REG);
> +
> +	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;
> +	writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
> +
> +	andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
> +
> +	if (interlace == 2)
> +		orl_relaxed(lcd->mmio + TCON1_CTL_REG,
> +			    TCON1_CTL_INTERLACE_ENABLE);
> +	else
> +		andl_relaxed(lcd->mmio + TCON1_CTL_REG,
> +			     ~TCON1_CTL_INTERLACE_ENABLE);
> +
> +	writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
> +	writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
> +	writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
> +	writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
> +
> +	start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
> +	if (start_delay > 31)
> +		start_delay = 31;
> +	data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
> +	data &= ~TCON1_CTL_Start_Delay_MASK;
> +	data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
> +	writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
> +
> +	orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
> +}
> +
> +static void de2_tcon_disable(struct lcd *lcd)
> +{
> +	andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
> +	andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
> +}
> +
> +static void de2_crtc_enable(struct drm_crtc *crtc)
> +{
> +	struct lcd *lcd = crtc_to_lcd(crtc);
> +	struct drm_display_mode *mode = &crtc->mode;
> +
> +	if (de2_crtc_set_clock(lcd, mode->clock) < 0)
> +		return;
> +	lcd->clk_enabled = true;
> +
> +	/* start the TCON and the DE */
> +	de2_tcon_enable(lcd);
> +	de2_de_enable(lcd);
> +
> +	/* turn on blanking interrupt */
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void de2_crtc_disable(struct drm_crtc *crtc,
> +				struct drm_crtc_state *old_crtc_state)
> +{
> +	struct lcd *lcd = crtc_to_lcd(crtc);
> +
> +	if (!lcd->clk_enabled)
> +		return;			/* already disabled */
> +	lcd->clk_enabled = false;
> +
> +	de2_de_disable(lcd);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	de2_tcon_disable(lcd);
> +
> +	clk_disable_unprepare(lcd->clk);
> +	clk_disable_unprepare(lcd->bus);
> +	reset_control_assert(lcd->reset);
> +}
> +
> +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,
> +	.atomic_disable	= de2_crtc_disable,
> +};
> +
> +/* device init */
> +static int de2_lcd_bind(struct device *dev, struct device *master,
> +			void *data)
> +{
> +	struct drm_device *drm = data;
> +	struct priv *priv = drm_to_priv(drm);
> +	struct lcd *lcd = dev_get_drvdata(dev);
> +	struct drm_crtc *crtc = &lcd->crtc;
> +	int ret, i, crtc_ix;
> +
> +	lcd->priv = priv;
> +
> +	/* set the CRTC reference */
> +	crtc_ix = drm_crtc_index(crtc);
> +	if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
> +		dev_err(drm->dev, "Bad crtc index");
> +		return -ENOENT;
> +	}
> +	priv->lcds[crtc_ix] = lcd;
> +
> +	/* and the mixer index (DT port index in the DE) */
> +	for (i = 0; ; i++) {
> +		struct device_node *port;
> +
> +		port = of_parse_phandle(drm->dev->of_node, "ports", i);
> +		if (!port)
> +			break;
> +		if (port == lcd->crtc.port) {
> +			lcd->mixer = i;
> +			break;
> +		}
> +	}
> +
> +	ret = de2_plane_init(drm, lcd);
> +	if (ret < 0)
> +		return ret;
> +
> +	drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
> +
> +	return drm_crtc_init_with_planes(drm, crtc,
> +					 &lcd->planes[DE2_PRIMARY_PLANE],
> +					 &lcd->planes[DE2_CURSOR_PLANE],
> +					 &de2_crtc_funcs, NULL);
> +}
> +
> +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->priv)
> +		lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
> +}
> +
> +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;
> +
> +	lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
> +	if (!lcd)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, lcd);
> +	lcd->dev = dev;
> +	lcd->mixer = 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);
> +	}
> +
> +	/* possible CRTC */
> +	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->bus = devm_clk_get(dev, "bus");
> +	if (IS_ERR(lcd->bus)) {
> +		dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
> +		ret = PTR_ERR(lcd->bus);
> +		goto err;
> +	}
> +
> +	lcd->clk = devm_clk_get(dev, "clock");
> +	if (IS_ERR(lcd->clk)) {
> +		ret = PTR_ERR(lcd->clk);
> +		dev_err(dev, "get video clock err %d\n", ret);
> +		goto err;
> +	}
> +
> +	lcd->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(lcd->reset)) {
> +		ret = PTR_ERR(lcd->reset);
> +		dev_err(dev, "get reset err %d\n", ret);
> +		goto err;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(dev, "unable to get irq\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	de2_tcon_init(lcd);		/* stop TCON and avoid interrupts */
> +
> +	ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
> +				dev_name(dev), lcd);
> +	if (ret < 0) {
> +		dev_err(dev, "unable to request irq %d\n", irq);
> +		goto err;
> +	}
> +
> +	return component_add(dev, &de2_lcd_ops);
> +
> +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);
> +
> +	of_node_put(lcd->crtc.port);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id de2_lcd_ids[] = {
> +	{ .compatible = "allwinner,sun8i-a83t-tcon", },
> +	{ }
> +};
> +
> +struct platform_driver de2_lcd_platform_driver = {
> +	.probe = de2_lcd_probe,
> +	.remove = de2_lcd_remove,
> +	.driver = {
> +		.name = "sun8i-de2-tcon",
> +		.of_match_table = of_match_ptr(de2_lcd_ids),
> +	},
> +};
> diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
> new file mode 100644
> index 0000000..c0d34a7
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_crtc.h
> @@ -0,0 +1,50 @@
> +#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 <drm/drm_plane_helper.h>
> +
> +struct clk;
> +struct reset_control;
> +struct priv;
> +
> +/* planes */
> +#define DE2_PRIMARY_PLANE 0
> +#define DE2_CURSOR_PLANE 1
> +#define DE2_N_PLANES 5	/* number of planes - see plane_tb[] in de2_plane.c */
> +
> +struct lcd {
> +	void __iomem *mmio;
> +
> +	struct device *dev;
> +	struct drm_crtc crtc;
> +
> +	struct priv *priv;	/* DRM/DE private data */
> +
> +	u8 mixer;		/* LCD (mixer) number */
> +	u8 delayed;		/* bitmap of planes with delayed update */
> +
> +	u8 clk_enabled;		/* used for error in crtc_enable */
> +
> +	struct clk *clk;
> +	struct clk *bus;
> +	struct reset_control *reset;
> +
> +	struct drm_plane planes[DE2_N_PLANES];
> +};
> +
> +#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
> +
> +/* in de2_plane.c */
> +void de2_de_enable(struct lcd *lcd);
> +void de2_de_disable(struct lcd *lcd);
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
> +
> +#endif /* __DE2_CRTC_H__ */
> diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
> new file mode 100644
> index 0000000..f96babe
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_drv.c
> @@ -0,0 +1,317 @@
> +/*
> + * 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/of_device.h>
> +#include <drm/drm_of.h>
> +#include <linux/component.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +
> +#include "de2_drv.h"
> +
> +#define DRIVER_NAME	"sun8i-de2"
> +#define DRIVER_DESC	"Allwinner DRM DE2"
> +#define DRIVER_DATE	"20161101"
> +#define DRIVER_MAJOR	1
> +#define DRIVER_MINOR	0
> +
> +static const 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_to_priv(drm);
> +
> +	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_to_priv(drm);
> +
> +	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,
> +};
> +
> +/*
> + * Platform driver
> + */
> +
> +static int de2_drm_bind(struct device *dev)
> +{
> +	struct drm_device *drm;
> +	struct priv *priv;
> +	struct resource *res;
> +	struct lcd *lcd;
> +	int i, ret;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	drm = &priv->drm;
> +	dev_set_drvdata(dev, drm);
> +
> +	/* get the resources */
> +	priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
> +
> +	res = platform_get_resource(to_platform_device(dev),
> +				IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "failed to get memory resource\n");
> +		ret = -EINVAL;
> +		goto out1;
> +	}
> +
> +	priv->mmio = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(priv->mmio)) {
> +		ret = PTR_ERR(priv->mmio);
> +		dev_err(dev, "failed to map registers %d\n", ret);
> +		goto out1;
> +	}
> +
> +	priv->gate = devm_clk_get(dev, "bus");
> +	if (IS_ERR(priv->gate)) {
> +		ret = PTR_ERR(priv->gate);
> +		dev_err(dev, "bus gate err %d\n", ret);
> +		goto out1;
> +	}
> +
> +	priv->clk = devm_clk_get(dev, "clock");
> +	if (IS_ERR(priv->clk)) {
> +		ret = PTR_ERR(priv->clk);
> +		dev_err(dev, "clock err %d\n", ret);
> +		goto out1;
> +	}
> +
> +	priv->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(priv->reset)) {
> +		ret = PTR_ERR(priv->reset);
> +		dev_err(dev, "reset err %d\n", ret);
> +		goto out1;
> +	}
> +
> +	mutex_init(&priv->mutex);	/* protect DE I/O accesses */
> +
> +	ret = drm_dev_init(drm, &de2_drm_driver, dev);
> +	if (ret != 0) {
> +		dev_err(dev, "dev_init failed %d\n", ret);
> +		goto out1;
> +	}
> +
> +	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;
> +
> +	/* start the subdevices */
> +	ret = component_bind_all(dev, drm);
> +	if (ret < 0)
> +		goto out2;
> +
> +	/* initialize and disable vertical blanking on all CRTCs */
> +	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
> +	if (ret < 0)
> +		dev_warn(dev, "vblank_init failed %d\n", ret);
> +
> +	for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
> +		lcd = priv->lcds[i];
> +		if (lcd)
> +			de2_vblank_reset(lcd);
> +	}
> +
> +	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 out3;
> +	}
> +
> +	drm_kms_helper_poll_init(drm);
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret < 0)
> +		goto out4;
> +
> +	return 0;
> +
> +out4:
> +	drm_fbdev_cma_fini(priv->fbdev);
> +out3:
> +	component_unbind_all(dev, drm);
> +out2:
> +	drm_dev_unref(drm);
> +out1:
> +	kfree(priv);
> +	return ret;
> +}
> +
> +static void de2_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm = dev_get_drvdata(dev);
> +	struct priv *priv = drm_to_priv(drm);
> +
> +	drm_dev_unregister(drm);
> +
> +	drm_fbdev_cma_fini(priv->fbdev);
> +	drm_kms_helper_poll_fini(drm);
> +	drm_vblank_cleanup(drm);
> +	drm_mode_config_cleanup(drm);
> +
> +	component_unbind_all(dev, drm);
> +
> +	kfree(priv);
> +}
> +
> +static const struct component_master_ops de2_drm_comp_ops = {
> +	.bind = de2_drm_bind,
> +	.unbind = de2_drm_unbind,
> +};
> +
> +/*
> + * drm_of_component_probe() does:
> + * - bind of the ports (lcd-controller.port)
> + * - bind of the remote nodes (hdmi, tve..)
> + */
> +static int compare_of(struct device *dev, void *data)
> +{
> +	struct device_node *np = data;
> +
> +	if (of_node_cmp(np->name, "port") == 0) {
> +		np = of_get_parent(np);
> +		of_node_put(np);
> +	}
> +	return dev->of_node == np;
> +}
> +
> +static int de2_drm_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = drm_of_component_probe(&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,
> +		.of_match_table = de2_drm_of_match,
> +	},
> +};
> +
> +static int __init de2_drm_init(void)
> +{
> +	int ret;
> +
> +	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/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
> new file mode 100644
> index 0000000..c42c30a
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_drv.h
> @@ -0,0 +1,48 @@
> +#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 <drm/drmP.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +struct drm_fbdev_cma;
> +struct lcd;
> +
> +#define N_LCDS 2
> +
> +struct priv {
> +	struct drm_device drm;
> +	void __iomem *mmio;
> +	struct clk *clk;
> +	struct clk *gate;
> +	struct reset_control *reset;
> +
> +	struct mutex mutex;	/* protect DE I/O access */
> +	u8 soc_type;
> +#define SOC_A83T 0
> +#define SOC_H3 1
> +	u8 started;		/* bitmap of started mixers */
> +	u8 clean;		/* bitmap of clean mixers */
> +
> +	struct drm_fbdev_cma *fbdev;
> +
> +	struct lcd *lcds[N_LCDS]; /* CRTCs */
> +};
> +
> +#define drm_to_priv(x) container_of(x, struct priv, drm)
> +
> +/* in de2_crtc.c */
> +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
> +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
> +void de2_vblank_reset(struct lcd *lcd);
> +extern struct platform_driver de2_lcd_platform_driver;
> +
> +#endif /* __DE2_DRM_H__ */
> diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
> new file mode 100644
> index 0000000..2fd72dc
> --- /dev/null
> +++ b/drivers/gpu/drm/sun8i/de2_plane.c
> @@ -0,0 +1,734 @@
> +/*
> + * Allwinner DRM driver - Display Engine 2
> + *
> + * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
> + * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
> + *	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 <linux/io.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "de2_drv.h"
> +#include "de2_crtc.h"
> +
> +/* DE2 I/O map */
> +
> +#define DE2_MOD_REG 0x0000		/* 1 bit per LCD */
> +#define DE2_GATE_REG 0x0004
> +#define DE2_RESET_REG 0x0008
> +#define DE2_DIV_REG 0x000c		/* 4 bits per LCD */
> +#define DE2_SEL_REG 0x0010
> +
> +#define DE2_MIXER0_BASE 0x00100000	/* LCD 0 */
> +#define DE2_MIXER1_BASE 0x00200000	/* LCD 1 */
> +
> +/* mixer registers (addr / mixer base) */
> +#define MIXER_GLB_REGS	0x00000		/* global control */
> +#define MIXER_BLD_REGS	0x01000		/* alpha blending */
> +#define MIXER_CHAN_REGS 0x02000		/* VI/UI overlay channels */
> +#define		MIXER_CHAN_SZ 0x1000	/* size of a channel */
> +#define MIXER_VSU_REGS	0x20000		/* VSU */
> +#define MIXER_GSU1_REGS 0x30000		/* GSUs */
> +#define MIXER_GSU2_REGS 0x40000
> +#define MIXER_GSU3_REGS 0x50000
> +#define MIXER_FCE_REGS	0xa0000		/* FCE */
> +#define MIXER_BWS_REGS	0xa2000		/* BWS */
> +#define MIXER_LTI_REGS	0xa4000		/* LTI */
> +#define MIXER_PEAK_REGS 0xa6000		/* PEAK */
> +#define MIXER_ASE_REGS	0xa8000		/* ASE */
> +#define MIXER_FCC_REGS	0xaa000		/* FCC */
> +#define MIXER_DCSC_REGS 0xb0000		/* DCSC/SMBL */
> +
> +/* global control */
> +#define MIXER_GLB_CTL_REG	0x00
> +#define		MIXER_GLB_CTL_rt_en BIT(0)
> +#define		MIXER_GLB_CTL_finish_irq_en BIT(4)
> +#define		MIXER_GLB_CTL_rtwb_port BIT(12)
> +#define MIXER_GLB_STATUS_REG	0x04
> +#define MIXER_GLB_DBUFF_REG	0x08
> +#define MIXER_GLB_SIZE_REG	0x0c
> +
> +/* alpha blending */
> +#define MIXER_BLD_FCOLOR_CTL_REG	0x00
> +#define		MIXER_BLD_FCOLOR_CTL_PEN(pipe)	(0x0100 << (pipe))
> +#define	MIXER_BLD_ATTR_N 4		/* number of attribute blocks */
> +#define	MIXER_BLD_ATTR_SIZE (4 * 4)	/* size of an attribute block */
> +#define MIXER_BLD_ATTRx_FCOLOR(x)	(0x04 + MIXER_BLD_ATTR_SIZE * (x))
> +#define MIXER_BLD_ATTRx_INSIZE(x)	(0x08 + MIXER_BLD_ATTR_SIZE * (x))
> +#define MIXER_BLD_ATTRx_OFFSET(x)	(0x0c + MIXER_BLD_ATTR_SIZE * (x))
> +#define MIXER_BLD_ROUTE_REG	0x80
> +#define		MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
> +#define MIXER_BLD_PREMULTIPLY_REG	0x84
> +#define MIXER_BLD_BKCOLOR_REG	0x88
> +#define MIXER_BLD_OUTPUT_SIZE_REG	0x8c
> +#define MIXER_BLD_MODEx_REG(x)	(0x90 + 4 * (x))	/* x = 0..3 */
> +#define		MIXER_BLD_MODE_SRCOVER	0x03010301
> +#define MIXER_BLD_OUT_CTL_REG	0xfc
> +
> +/* VI channel (channel 0) */
> +#define VI_CFG_N		4		/* number of layers */
> +#define VI_CFG_SIZE		0x30		/* size of a layer */
> +#define VI_CFGx_ATTR(l)		(0x00 + VI_CFG_SIZE * (l))
> +#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)
> +#define VI_CFGx_SIZE(l)		(0x04 + VI_CFG_SIZE * (l))
> +#define VI_CFGx_COORD(l)	(0x08 + VI_CFG_SIZE * (l))
> +#define VI_N_PLANES 3
> +#define VI_CFGx_PITCHy(l, p)	(0x0c + VI_CFG_SIZE * (l) + 4 * (p))
> +#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
> +#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
> +#define VI_FCOLORx(l)		(0xc0 + 4 * (l))
> +#define VI_TOP_HADDRx(p)	(0xd0 + 4 * (p))
> +#define VI_BOT_HADDRx(p)	(0xdc + 4 * (p))
> +#define VI_OVL_SIZEx(n)		(0xe8 + 4 * (n))
> +#define VI_HORI_DSx(n)		(0xf0 + 4 * (n))
> +#define VI_VERT_DSx(n)		(0xf8 + 4 * (n))
> +#define VI_SIZE			0x100
> +
> +/* UI channel (channels 1..3) */
> +#define UI_CFG_N		4		/* number of layers */
> +#define UI_CFG_SIZE		(8 * 4)		/* size of a layer */
> +#define UI_CFGx_ATTR(l)		(0x00 + UI_CFG_SIZE * (l))
> +#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)
> +#define UI_CFGx_SIZE(l)		(0x04 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_COORD(l)	(0x08 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_PITCH(l)	(0x0c + UI_CFG_SIZE * (l))
> +#define UI_CFGx_TOP_LADDR(l)	(0x10 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_BOT_LADDR(l)	(0x14 + UI_CFG_SIZE * (l))
> +#define UI_CFGx_FCOLOR(l)	(0x18 + UI_CFG_SIZE * (l))
> +#define UI_TOP_HADDR		0x80
> +#define UI_BOT_HADDR		0x84
> +#define UI_OVL_SIZE		0x88
> +#define UI_SIZE			0x8c
> +
> +/* 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	/* YVYU */
> +#define DE2_FORMAT_YUV422_I_UYVY 2	/* UYVY */
> +#define DE2_FORMAT_YUV422_I_YUYV 3	/* YUYV */
> +#define DE2_FORMAT_YUV422_P 6		/* YYYY UU VV planar */
> +#define DE2_FORMAT_YUV420_P 10		/* YYYY U V planar */
> +
> +/* 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,
> +};
> +
> +/*
> + * plane table
> + *
> + * The chosen channel/layer assignment of the planes respects
> + * the following constraints:
> + * - the cursor must be in a channel higher than the primary channel
> + * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
> + */
> +static const struct {
> +	u8 chan;
> +	u8 layer;
> +	u8 pipe;
> +	u8 type;			/* plane type */
> +	const uint32_t *formats;
> +	u8 n_formats;
> +} plane_tb[] = {
> +	[DE2_PRIMARY_PLANE] = {		/* primary plane: channel 0 (VI) */
> +		0, 0, 0,
> +		DRM_PLANE_TYPE_PRIMARY,
> +		ui_formats, ARRAY_SIZE(ui_formats),
> +	},
> +	[DE2_CURSOR_PLANE] = {		/* cursor: channel 1 (UI) */
> +		1, 0, 1,
> +		DRM_PLANE_TYPE_CURSOR,
> +		ui_formats, ARRAY_SIZE(ui_formats),
> +	},
> +	{
> +		0, 1, 0,		/* 1st overlay: channel 0, layer 1 */
> +		DRM_PLANE_TYPE_OVERLAY,
> +		vi_formats, ARRAY_SIZE(vi_formats),
> +	},
> +	{
> +		0, 2, 0,		/* 2nd overlay: channel 0, layer 2 */
> +		DRM_PLANE_TYPE_OVERLAY,
> +		vi_formats, ARRAY_SIZE(vi_formats),
> +	},
> +	{
> +		0, 3, 0,		/* 3rd overlay: channel 0, layer 3 */
> +		DRM_PLANE_TYPE_OVERLAY,
> +		vi_formats, ARRAY_SIZE(vi_formats),
> +	},
> +};
> +
> +static inline void andl_relaxed(void __iomem *addr, u32 val)
> +{
> +	writel_relaxed(readl_relaxed(addr) & val, addr);
> +}
> +
> +static inline void orl_relaxed(void __iomem *addr, u32 val)
> +{
> +	writel_relaxed(readl_relaxed(addr) | val, addr);
> +}
> +
> +/* alert the DE processor about changes in a mixer configuration */
> +static void de2_mixer_select(struct priv *priv,
> +			int mixer,
> +			void __iomem *mixer_io)
> +{
> +	/* select the mixer */
> +	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
> +
> +	/* double register switch */
> +	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
> +}
> +
> +/*
> + * cleanup a mixer
> + *
> + * This is needed only once after power on.
> + */
> +static void de2_mixer_cleanup(struct priv *priv, int mixer,
> +				u32 size)
> +{
> +	void __iomem *mixer_io = priv->mmio;
> +	void __iomem *chan_io;
> +	u32 data;
> +	unsigned int i;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +	chan_io = mixer_io + MIXER_CHAN_REGS;
> +
> +	andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
> +	writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
> +
> +	writel_relaxed(MIXER_GLB_CTL_rt_en,
> +			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
> +	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
> +
> +	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
> +
> +	/*
> +	 * clear the VI/UI channels
> +	 *	LCD0: 1 VI and 3 UIs
> +	 *	LCD1: 1 VI and 1 UI
> +	 */
> +	memset_io(chan_io, 0, VI_SIZE);
> +	memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
> +	if (mixer == 0) {
> +		memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
> +		memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
> +	}
> +
> +	/* alpha blending */
> +	writel_relaxed(0x00000001 |		/* fcolor for primary */
> +			MIXER_BLD_FCOLOR_CTL_PEN(0),
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
> +	for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
> +		writel_relaxed(0xff000000,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
> +		writel_relaxed(size,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
> +		writel_relaxed(0,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
> +	}
> +	writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
> +
> +	/* prepare the pipe route for the planes */
> +	data = 0;
> +	for (i = 0; i < DE2_N_PLANES; i++)
> +		data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
> +	writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
> +
> +	writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_PREMULTIPLY_REG);
> +	writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_BKCOLOR_REG);
> +	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_OUTPUT_SIZE_REG);
> +	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
> +	writel_relaxed(MIXER_BLD_MODE_SRCOVER,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
> +
> +	/* disable the enhancements */
> +	writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
> +	writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
> +}
> +
> +/* enable a mixer */
> +static void de2_mixer_enable(struct lcd *lcd)
> +{
> +	struct priv *priv = lcd->priv;
> +	void __iomem *mixer_io = priv->mmio;
> +	struct drm_display_mode *mode = &lcd->crtc.mode;
> +	u32 size = WH(mode->hdisplay, mode->vdisplay);
> +	u32 data;
> +	int mixer = lcd->mixer;
> +	int i;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +
> +	/* if not done yet, start the DE processor */
> +	if (!priv->started) {
> +		reset_control_deassert(priv->reset);
> +		clk_prepare_enable(priv->gate);
> +		clk_prepare_enable(priv->clk);
> +	}
> +	priv->started |= 1 << mixer;
> +
> +	/* set the A83T clock divider (500 / 2) = 250MHz */
> +	if (priv->soc_type == SOC_A83T)
> +		writel_relaxed(0x00000011, /* div = 2 for both LCDs */
> +				priv->mmio + DE2_DIV_REG);
> +
> +	/* deassert the mixer and enable its clock */
> +	orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
> +	data = 1 << mixer;			/* 1 bit / lcd */
> +	orl_relaxed(priv->mmio + DE2_GATE_REG, data);
> +	orl_relaxed(priv->mmio + DE2_MOD_REG, data);
> +
> +	/* if not done yet, cleanup and enable */
> +	if (!(priv->clean & (1 << mixer))) {
> +		priv->clean |= 1 << mixer;
> +		de2_mixer_cleanup(priv, mixer, size);
> +		return;
> +	}
> +
> +	/* enable */
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	writel_relaxed(MIXER_GLB_CTL_rt_en,
> +			mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
> +	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
> +
> +	/* set the size of the frame buffer */
> +	writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
> +	for (i = 0; i < MIXER_BLD_ATTR_N; i++)
> +		writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
> +				MIXER_BLD_ATTRx_INSIZE(i));
> +	writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_OUTPUT_SIZE_REG);
> +
> +	writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
> +}
> +
> +/* enable a LCD (DE mixer) */
> +void de2_de_enable(struct lcd *lcd)
> +{
> +	mutex_lock(&lcd->priv->mutex);
> +
> +	de2_mixer_enable(lcd);
> +
> +	mutex_unlock(&lcd->priv->mutex);
> +}
> +
> +/* disable a LCD (DE mixer) */
> +void de2_de_disable(struct lcd *lcd)
> +{
> +	struct priv *priv = lcd->priv;
> +	void __iomem *mixer_io = priv->mmio;
> +	int mixer = lcd->mixer;
> +	u32 data;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +
> +	mutex_lock(&priv->mutex);
> +
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
> +
> +	data = ~(1 << mixer);
> +	andl_relaxed(priv->mmio + DE2_MOD_REG, data);
> +	andl_relaxed(priv->mmio + DE2_GATE_REG, data);
> +	andl_relaxed(priv->mmio + DE2_RESET_REG, data);
> +
> +	mutex_unlock(&priv->mutex);
> +
> +	/* if all mixers are disabled, stop the DE */
> +	priv->started &= ~(1 << mixer);
> +	if (!priv->started) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->gate);
> +		reset_control_assert(priv->reset);
> +	}
> +}
> +
> +static void de2_vi_update(void __iomem *chan_io,
> +			  struct drm_gem_cma_object *gem,
> +			  int layer,
> +			  unsigned int fmt,
> +			  u32 ui_sel,
> +			  u32 size,
> +			  u32 coord,
> +			  struct drm_framebuffer *fb,
> +			  u32 screen_size)
> +{
> +	int i;
> +
> +	writel_relaxed(VI_CFG_ATTR_en |
> +			(fmt << VI_CFG_ATTR_fmt_SHIFT) |
> +			ui_sel,
> +			chan_io + VI_CFGx_ATTR(layer));
> +	writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
> +	writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
> +	for (i = 0; i < VI_N_PLANES; i++) {
> +		writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
> +						fb->pitches[0],
> +				chan_io + VI_CFGx_PITCHy(layer, i));
> +		writel_relaxed(gem->paddr + fb->offsets[i],
> +				chan_io + VI_CFGx_TOP_LADDRy(layer, i));
> +	}
> +	writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
> +	if (layer == 0) {
> +		writel_relaxed(screen_size,
> +				chan_io + VI_OVL_SIZEx(0));
> +	}
> +}
> +
> +static void de2_ui_update(void __iomem *chan_io,
> +			  struct drm_gem_cma_object *gem,
> +			  int layer,
> +			  unsigned int fmt,
> +			  u32 alpha_glob,
> +			  u32 size,
> +			  u32 coord,
> +			  struct drm_framebuffer *fb,
> +			  u32 screen_size)
> +{
> +	writel_relaxed(UI_CFG_ATTR_en |
> +			(fmt << UI_CFG_ATTR_fmt_SHIFT) |
> +			alpha_glob,
> +			chan_io + UI_CFGx_ATTR(layer));
> +	writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
> +	writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
> +	writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
> +	writel_relaxed(gem->paddr + fb->offsets[0],
> +			chan_io + UI_CFGx_TOP_LADDR(layer));
> +	if (layer == 0)
> +		writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
> +}
> +
> +static void de2_plane_update(struct priv *priv, struct lcd *lcd,
> +				int plane_num,
> +				struct drm_plane_state *state,
> +				struct drm_plane_state *old_state)
> +{
> +	void __iomem *mixer_io = priv->mmio;
> +	void __iomem *chan_io;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *gem;
> +	u32 size = WH(state->crtc_w, state->crtc_h);
> +	u32 coord, screen_size;
> +	u32 fcolor;
> +	u32 ui_sel, alpha_glob;
> +	int mixer = lcd->mixer;
> +	int chan, layer, x, y;
> +	unsigned int fmt;
> +
> +	chan = plane_tb[plane_num].chan;
> +	layer = plane_tb[plane_num].layer;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_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);
> +
> +	/* if plane update was delayed, force a full update */
> +	if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
> +			(1 << plane_num)) {
> +		priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
> +							~(1 << plane_num);
> +
> +	/* handle plane move */
> +	} else if (fb == old_state->fb) {
> +		de2_mixer_select(priv, mixer, mixer_io);
> +		if (chan == 0)
> +			writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
> +		else
> +			writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
> +		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("de2_plane_update: format %.4s not yet treated\n",
> +			(char *) &fb->pixel_format);
> +		return;
> +	}
> +
> +	/* the overlay size is the one of the primary plane */
> +	screen_size = plane_num == DE2_PRIMARY_PLANE ?
> +		size :
> +		readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
> +
> +	/* prepare pipe enable */
> +	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
> +				MIXER_BLD_FCOLOR_CTL_REG);
> +	fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
> +
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	if (chan == 0)				/* VI channel */
> +		de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
> +				fb, screen_size);
> +	else					/* UI channel */
> +		de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
> +				fb, screen_size);
> +	writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
> +				MIXER_BLD_FCOLOR_CTL_REG);
> +}
> +
> +static int vi_nb_layers(void __iomem *chan_io)
> +{
> +	int layer, n = 0;
> +
> +	for (layer = 0; layer < 4; layer++) {
> +		if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
> +			n++;
> +	}
> +
> +	return n;
> +}
> +
> +static int ui_nb_layers(void __iomem *chan_io)
> +{
> +	int layer, n = 0;
> +
> +	for (layer = 0; layer < 4; layer++) {
> +		if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
> +			n++;
> +	}
> +
> +	return n;
> +}
> +
> +static void de2_plane_disable(struct priv *priv,
> +				int mixer, int plane_num)
> +{
> +	void __iomem *mixer_io = priv->mmio;
> +	void __iomem *chan_io;
> +	u32 fcolor;
> +	int chan, layer, n;
> +
> +	chan = plane_tb[plane_num].chan;
> +	layer = plane_tb[plane_num].layer;
> +
> +	mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
> +	chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
> +
> +	if (chan == 0)
> +		n = vi_nb_layers(chan_io);
> +	else
> +		n = ui_nb_layers(chan_io);
> +
> +	fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
> +			MIXER_BLD_FCOLOR_CTL_REG);
> +
> +	de2_mixer_select(priv, mixer, mixer_io);
> +
> +	if (chan == 0)
> +		writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
> +	else
> +		writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
> +
> +	/* disable the pipe if no more active layer */
> +	if (n <= 1)
> +		writel_relaxed(fcolor &
> +			~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
> +			mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
> +}
> +
> +static void de2_drm_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 priv *priv = lcd->priv;
> +	int plane_num = plane - lcd->planes;
> +
> +	/* if the crtc is disabled, mark update delayed */
> +	if (!(priv->started & (1 << lcd->mixer))) {
> +		lcd->delayed |= 1 << plane_num;
> +		return;				/* mixer disabled */
> +	}
> +
> +	mutex_lock(&priv->mutex);
> +
> +	de2_plane_update(priv, lcd, plane_num, state, old_state);
> +
> +	mutex_unlock(&priv->mutex);
> +}
> +
> +static void de2_drm_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);
> +	struct priv *priv = lcd->priv;
> +	int plane_num = plane - lcd->planes;
> +
> +	if (!(priv->started & (1 << lcd->mixer)))
> +		return;				/* mixer disabled */
> +
> +	mutex_lock(&priv->mutex);
> +
> +	de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
> +
> +	mutex_unlock(&priv->mutex);
> +}
> +
> +static const struct drm_plane_helper_funcs plane_helper_funcs = {
> +	.atomic_update = de2_drm_plane_update,
> +	.atomic_disable = de2_drm_plane_disable,
> +};
> +
> +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 possible_crtcs,
> +				int plane_num)
> +{
> +	int ret;
> +
> +	ret = drm_universal_plane_init(drm, plane, possible_crtcs,
> +				&plane_funcs,
> +				plane_tb[plane_num].formats,
> +				plane_tb[plane_num].n_formats,
> +				plane_tb[plane_num].type, NULL);
> +	if (ret >= 0)
> +		drm_plane_helper_add(plane, &plane_helper_funcs);
> +
> +	return ret;
> +}
> +
> +/* initialize the planes */
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
> +{
> +	int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
> +
> +	n = ARRAY_SIZE(plane_tb);
> +	if (n != DE2_N_PLANES) {
> +		dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
> +			__stringify(DE2_N_PLANES) "\n", n);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < n; i++) {
> +		ret = de2_one_plane_init(drm, &lcd->planes[i],
> +				possible_crtcs, i);
> +		if (ret < 0) {
> +			dev_err(lcd->dev, "plane init failed %d\n", ret);
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> -- 
> 2.10.2
> 

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


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

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

* Re: Re: [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
  2016-11-29 14:30         ` Daniel Vetter
@ 2016-11-29 14:33             ` Icenowy Zheng
  -1 siblings, 0 replies; 79+ messages in thread
From: Icenowy Zheng @ 2016-11-29 14:33 UTC (permalink / raw)
  To: daniel-/w4YWyX8dFk, Jean-Francois Moine
  Cc: Dave Airlie, Maxime Ripard, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW



29.11.2016, 22:30, "Daniel Vetter" <daniel-/w4YWyX8dFk@public.gmane.org>:
> On Mon, Nov 28, 2016 at 03:23:54PM +0100, Jean-Francois Moine wrote:
>>  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-GANU6spQydw@public.gmane.org>
>
> Scrolled around a bit, seemed all reasonable.
>
> Acked-by: Daniel Vetter <daniel.vetter-/w4YWyX8dFk@public.gmane.org>
>
> Not sure a new driver for each chip is reasonable, experience says that
> long-term you want to share quite a pile of code between different hw
> platforms from the same vendor. But that's entirely up to you.
> -Daniel

The Display Engine on A83T and newer SoCs is quite new, different from
the ones on A33 and older.

It's called "DE 2.0" in the user manual of the SoCs.

>
>>  ---
>>   drivers/gpu/drm/Kconfig | 2 +
>>   drivers/gpu/drm/Makefile | 1 +
>>   drivers/gpu/drm/sun8i/Kconfig | 19 +
>>   drivers/gpu/drm/sun8i/Makefile | 7 +
>>   drivers/gpu/drm/sun8i/de2_crtc.c | 449 +++++++++++++++++++++++
>>   drivers/gpu/drm/sun8i/de2_crtc.h | 50 +++
>>   drivers/gpu/drm/sun8i/de2_drv.c | 317 ++++++++++++++++
>>   drivers/gpu/drm/sun8i/de2_drv.h | 48 +++
>>   drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
>>   9 files changed, 1627 insertions(+)
>>   create mode 100644 drivers/gpu/drm/sun8i/Kconfig
>>   create mode 100644 drivers/gpu/drm/sun8i/Makefile
>>   create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
>>   create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
>>   create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
>>   create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
>>   create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
>>
>>  diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>  index 95fc041..bb1bfbc 100644
>>  --- a/drivers/gpu/drm/Kconfig
>>  +++ b/drivers/gpu/drm/Kconfig
>>  @@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
>>
>>   source "drivers/gpu/drm/sun4i/Kconfig"
>>
>>  +source "drivers/gpu/drm/sun8i/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 883f3e7..3e1eaa0 100644
>>  --- a/drivers/gpu/drm/Makefile
>>  +++ b/drivers/gpu/drm/Makefile
>>  @@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
>>   obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
>>   obj-y += omapdrm/
>>   obj-$(CONFIG_DRM_SUN4I) += sun4i/
>>  +obj-$(CONFIG_DRM_SUN8I) += sun8i/
>>   obj-y += tilcdc/
>>   obj-$(CONFIG_DRM_QXL) += qxl/
>>   obj-$(CONFIG_DRM_BOCHS) += bochs/
>>  diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
>>  new file mode 100644
>>  index 0000000..6940895
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/Kconfig
>>  @@ -0,0 +1,19 @@
>>  +#
>>  +# Allwinner DE2 Video configuration
>>  +#
>>  +
>>  +config DRM_SUN8I
>>  + bool
>>  +
>>  +config DRM_SUN8I_DE2
>>  + tristate "Support for Allwinner Video with DE2 interface"
>>  + depends on DRM && OF
>>  + depends on ARCH_SUNXI || COMPILE_TEST
>>  + select DRM_GEM_CMA_HELPER
>>  + select DRM_KMS_CMA_HELPER
>>  + select DRM_KMS_HELPER
>>  + select DRM_SUN8I
>>  + 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
>>  + sun8i-de2-drm.
>>  diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
>>  new file mode 100644
>>  index 0000000..f107919
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/Makefile
>>  @@ -0,0 +1,7 @@
>>  +#
>>  +# Makefile for Allwinner's sun8i DRM device driver
>>  +#
>>  +
>>  +sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
>>  +
>>  +obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
>>  diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
>>  new file mode 100644
>>  index 0000000..4e94ccc
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/de2_crtc.c
>>  @@ -0,0 +1,449 @@
>>  +/*
>>  + * Allwinner DRM driver - DE2 CRTC
>>  + *
>>  + * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
>>  + *
>>  + * This program is free software; you can redistribute it and/or
>>  + * modify it under the terms of the GNU General Public License as
>>  + * published by the Free Software Foundation; either version 2 of
>>  + * the License, or (at your option) any later version.
>>  + */
>>  +
>>  +#include <linux/component.h>
>>  +#include <drm/drm_crtc_helper.h>
>>  +#include <drm/drm_atomic_helper.h>
>>  +#include <linux/io.h>
>>  +#include <linux/of_irq.h>
>>  +#include <linux/of_graph.h>
>>  +
>>  +#include "de2_drv.h"
>>  +#include "de2_crtc.h"
>>  +
>>  +/* I/O map */
>>  +
>>  +#define TCON_GCTL_REG 0x00
>>  +#define TCON_GCTL_TCON_ENABLE BIT(31)
>>  +#define TCON_GINT0_REG 0x04
>>  +#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)
>>  +#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
>>  +#define TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
>>  +#define TCON0_CTL_REG 0x40
>>  +#define TCON0_CTL_TCON_ENABLE BIT(31)
>>  +#define TCON1_CTL_REG 0x90
>>  +#define TCON1_CTL_TCON_ENABLE BIT(31)
>>  +#define TCON1_CTL_INTERLACE_ENABLE BIT(20)
>>  +#define TCON1_CTL_Start_Delay_SHIFT 4
>>  +#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
>>  +#define TCON1_BASIC0_REG 0x94 /* XI/YI */
>>  +#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */
>>  +#define TCON1_BASIC2_REG 0x9c /* XO/YO */
>>  +#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */
>>  +#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */
>>  +#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */
>>  +#define TCON1_PS_SYNC_REG 0xb0
>>  +#define TCON1_IO_POL_REG 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)
>>  +#define TCON1_IO_TRI_REG 0xf4
>>  +#define TCON_CEU_CTL_REG 0x100
>>  +#define TCON_CEU_CTL_ceu_en BIT(31)
>>  +#define TCON1_FILL_CTL_REG 0x300
>>  +#define TCON1_FILL_START0_REG 0x304
>>  +#define TCON1_FILL_END0_REG 0x308
>>  +#define TCON1_FILL_DATA0_REG 0x30c
>>  +
>>  +#define XY(x, y) (((x) << 16) | (y))
>>  +
>>  +#define andl_relaxed(addr, val) \
>>  + writel_relaxed(readl_relaxed(addr) & val, addr)
>>  +#define orl_relaxed(addr, val) \
>>  + writel_relaxed(readl_relaxed(addr) | val, addr)
>>  +
>>  +/* 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 = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
>>  +
>>  + drm_crtc_handle_vblank(&lcd->crtc);
>>  +
>>  + writel_relaxed(isr &
>>  + ~(TCON_GINT0_TCON1_Vb_Int_Flag |
>>  + TCON_GINT0_TCON1_Vb_Line_Int_Flag),
>>  + lcd->mmio + TCON_GINT0_REG);
>>  +
>>  + return IRQ_HANDLED;
>>  +}
>>  +
>>  +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
>>  +{
>>  + struct priv *priv = drm_to_priv(drm);
>>  + struct lcd *lcd = priv->lcds[crtc_ix];
>>  +
>>  + orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
>>  +{
>>  + struct priv *priv = drm_to_priv(drm);
>>  + struct lcd *lcd = priv->lcds[crtc_ix];
>>  +
>>  + andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
>>  +}
>>  +
>>  +void de2_vblank_reset(struct lcd *lcd)
>>  +{
>>  + drm_crtc_vblank_reset(&lcd->crtc);
>>  +}
>>  +
>>  +/* frame functions */
>>  +static int de2_crtc_set_clock(struct lcd *lcd, int rate)
>>  +{
>>  + struct clk *parent_clk;
>>  + u32 parent_rate;
>>  + int ret;
>>  +
>>  + /* determine and set the best rate for the parent clock (pll-video) */
>>  + if ((270000 * 2) % rate == 0)
>>  + parent_rate = 270000000;
>>  + else if (297000 % rate == 0)
>>  + parent_rate = 297000000;
>>  + else
>>  + return -EINVAL; /* unsupported clock */
>>  +
>>  + parent_clk = clk_get_parent(lcd->clk);
>>  +
>>  + ret = clk_set_rate(parent_clk, parent_rate);
>>  + if (ret) {
>>  + dev_err(lcd->dev, "set parent rate failed %d\n", ret);
>>  + return ret;
>>  + }
>>  + ret = clk_set_rate(lcd->clk, rate * 1000);
>>  + if (ret) {
>>  + dev_err(lcd->dev, "set rate failed %d\n", ret);
>>  + return ret;
>>  + }
>>  +
>>  + /* enable the clock */
>>  + reset_control_deassert(lcd->reset);
>>  + clk_prepare_enable(lcd->bus);
>>  + clk_prepare_enable(lcd->clk);
>>  +
>>  + return ret;
>>  +}
>>  +
>>  +static void de2_tcon_init(struct lcd *lcd)
>>  +{
>>  + andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
>>  + andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
>>  + andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
>>  +
>>  + /* disable/ack interrupts */
>>  + writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
>>  +}
>>  +
>>  +static void de2_tcon_enable(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;
>>  +
>>  + orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
>>  +
>>  + data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
>>  + writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
>>  + writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
>>  + writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
>>  + writel_relaxed(XY(mode->htotal - 1,
>>  + mode->htotal - mode->hsync_start - 1),
>>  + lcd->mmio + TCON1_BASIC3_REG);
>>  + writel_relaxed(XY(mode->vtotal * (3 - interlace),
>>  + mode->vtotal - mode->vsync_start - 1),
>>  + lcd->mmio + TCON1_BASIC4_REG);
>>  + writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
>>  + mode->vsync_end - mode->vsync_start - 1),
>>  + lcd->mmio + TCON1_BASIC5_REG);
>>  +
>>  + 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;
>>  + writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
>>  +
>>  + andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
>>  +
>>  + if (interlace == 2)
>>  + orl_relaxed(lcd->mmio + TCON1_CTL_REG,
>>  + TCON1_CTL_INTERLACE_ENABLE);
>>  + else
>>  + andl_relaxed(lcd->mmio + TCON1_CTL_REG,
>>  + ~TCON1_CTL_INTERLACE_ENABLE);
>>  +
>>  + writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
>>  + writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
>>  + writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
>>  + writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
>>  +
>>  + start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
>>  + if (start_delay > 31)
>>  + start_delay = 31;
>>  + data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
>>  + data &= ~TCON1_CTL_Start_Delay_MASK;
>>  + data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
>>  + writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
>>  +
>>  + orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
>>  +}
>>  +
>>  +static void de2_tcon_disable(struct lcd *lcd)
>>  +{
>>  + andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
>>  + andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
>>  +}
>>  +
>>  +static void de2_crtc_enable(struct drm_crtc *crtc)
>>  +{
>>  + struct lcd *lcd = crtc_to_lcd(crtc);
>>  + struct drm_display_mode *mode = &crtc->mode;
>>  +
>>  + if (de2_crtc_set_clock(lcd, mode->clock) < 0)
>>  + return;
>>  + lcd->clk_enabled = true;
>>  +
>>  + /* start the TCON and the DE */
>>  + de2_tcon_enable(lcd);
>>  + de2_de_enable(lcd);
>>  +
>>  + /* turn on blanking interrupt */
>>  + drm_crtc_vblank_on(crtc);
>>  +}
>>  +
>>  +static void de2_crtc_disable(struct drm_crtc *crtc,
>>  + struct drm_crtc_state *old_crtc_state)
>>  +{
>>  + struct lcd *lcd = crtc_to_lcd(crtc);
>>  +
>>  + if (!lcd->clk_enabled)
>>  + return; /* already disabled */
>>  + lcd->clk_enabled = false;
>>  +
>>  + de2_de_disable(lcd);
>>  +
>>  + drm_crtc_vblank_off(crtc);
>>  +
>>  + de2_tcon_disable(lcd);
>>  +
>>  + clk_disable_unprepare(lcd->clk);
>>  + clk_disable_unprepare(lcd->bus);
>>  + reset_control_assert(lcd->reset);
>>  +}
>>  +
>>  +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,
>>  + .atomic_disable = de2_crtc_disable,
>>  +};
>>  +
>>  +/* device init */
>>  +static int de2_lcd_bind(struct device *dev, struct device *master,
>>  + void *data)
>>  +{
>>  + struct drm_device *drm = data;
>>  + struct priv *priv = drm_to_priv(drm);
>>  + struct lcd *lcd = dev_get_drvdata(dev);
>>  + struct drm_crtc *crtc = &lcd->crtc;
>>  + int ret, i, crtc_ix;
>>  +
>>  + lcd->priv = priv;
>>  +
>>  + /* set the CRTC reference */
>>  + crtc_ix = drm_crtc_index(crtc);
>>  + if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
>>  + dev_err(drm->dev, "Bad crtc index");
>>  + return -ENOENT;
>>  + }
>>  + priv->lcds[crtc_ix] = lcd;
>>  +
>>  + /* and the mixer index (DT port index in the DE) */
>>  + for (i = 0; ; i++) {
>>  + struct device_node *port;
>>  +
>>  + port = of_parse_phandle(drm->dev->of_node, "ports", i);
>>  + if (!port)
>>  + break;
>>  + if (port == lcd->crtc.port) {
>>  + lcd->mixer = i;
>>  + break;
>>  + }
>>  + }
>>  +
>>  + ret = de2_plane_init(drm, lcd);
>>  + if (ret < 0)
>>  + return ret;
>>  +
>>  + drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
>>  +
>>  + return drm_crtc_init_with_planes(drm, crtc,
>>  + &lcd->planes[DE2_PRIMARY_PLANE],
>>  + &lcd->planes[DE2_CURSOR_PLANE],
>>  + &de2_crtc_funcs, NULL);
>>  +}
>>  +
>>  +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->priv)
>>  + lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
>>  +}
>>  +
>>  +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;
>>  +
>>  + lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
>>  + if (!lcd)
>>  + return -ENOMEM;
>>  +
>>  + dev_set_drvdata(dev, lcd);
>>  + lcd->dev = dev;
>>  + lcd->mixer = 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);
>>  + }
>>  +
>>  + /* possible CRTC */
>>  + 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->bus = devm_clk_get(dev, "bus");
>>  + if (IS_ERR(lcd->bus)) {
>>  + dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
>>  + ret = PTR_ERR(lcd->bus);
>>  + goto err;
>>  + }
>>  +
>>  + lcd->clk = devm_clk_get(dev, "clock");
>>  + if (IS_ERR(lcd->clk)) {
>>  + ret = PTR_ERR(lcd->clk);
>>  + dev_err(dev, "get video clock err %d\n", ret);
>>  + goto err;
>>  + }
>>  +
>>  + lcd->reset = devm_reset_control_get(dev, NULL);
>>  + if (IS_ERR(lcd->reset)) {
>>  + ret = PTR_ERR(lcd->reset);
>>  + dev_err(dev, "get reset err %d\n", ret);
>>  + goto err;
>>  + }
>>  +
>>  + irq = platform_get_irq(pdev, 0);
>>  + if (irq <= 0) {
>>  + dev_err(dev, "unable to get irq\n");
>>  + ret = -EINVAL;
>>  + goto err;
>>  + }
>>  +
>>  + de2_tcon_init(lcd); /* stop TCON and avoid interrupts */
>>  +
>>  + ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
>>  + dev_name(dev), lcd);
>>  + if (ret < 0) {
>>  + dev_err(dev, "unable to request irq %d\n", irq);
>>  + goto err;
>>  + }
>>  +
>>  + return component_add(dev, &de2_lcd_ops);
>>  +
>>  +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);
>>  +
>>  + of_node_put(lcd->crtc.port);
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +static const struct of_device_id de2_lcd_ids[] = {
>>  + { .compatible = "allwinner,sun8i-a83t-tcon", },
>>  + { }
>>  +};
>>  +
>>  +struct platform_driver de2_lcd_platform_driver = {
>>  + .probe = de2_lcd_probe,
>>  + .remove = de2_lcd_remove,
>>  + .driver = {
>>  + .name = "sun8i-de2-tcon",
>>  + .of_match_table = of_match_ptr(de2_lcd_ids),
>>  + },
>>  +};
>>  diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
>>  new file mode 100644
>>  index 0000000..c0d34a7
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/de2_crtc.h
>>  @@ -0,0 +1,50 @@
>>  +#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 <drm/drm_plane_helper.h>
>>  +
>>  +struct clk;
>>  +struct reset_control;
>>  +struct priv;
>>  +
>>  +/* planes */
>>  +#define DE2_PRIMARY_PLANE 0
>>  +#define DE2_CURSOR_PLANE 1
>>  +#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */
>>  +
>>  +struct lcd {
>>  + void __iomem *mmio;
>>  +
>>  + struct device *dev;
>>  + struct drm_crtc crtc;
>>  +
>>  + struct priv *priv; /* DRM/DE private data */
>>  +
>>  + u8 mixer; /* LCD (mixer) number */
>>  + u8 delayed; /* bitmap of planes with delayed update */
>>  +
>>  + u8 clk_enabled; /* used for error in crtc_enable */
>>  +
>>  + struct clk *clk;
>>  + struct clk *bus;
>>  + struct reset_control *reset;
>>  +
>>  + struct drm_plane planes[DE2_N_PLANES];
>>  +};
>>  +
>>  +#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
>>  +
>>  +/* in de2_plane.c */
>>  +void de2_de_enable(struct lcd *lcd);
>>  +void de2_de_disable(struct lcd *lcd);
>>  +int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
>>  +
>>  +#endif /* __DE2_CRTC_H__ */
>>  diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
>>  new file mode 100644
>>  index 0000000..f96babe
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/de2_drv.c
>>  @@ -0,0 +1,317 @@
>>  +/*
>>  + * Allwinner DRM driver - DE2 DRM driver
>>  + *
>>  + * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
>>  + *
>>  + * This program is free software; you can redistribute it and/or
>>  + * modify it under the terms of the GNU General Public License as
>>  + * published by the Free Software Foundation; either version 2 of
>>  + * the License, or (at your option) any later version.
>>  + */
>>  +
>>  +#include <linux/module.h>
>>  +#include <linux/of_device.h>
>>  +#include <drm/drm_of.h>
>>  +#include <linux/component.h>
>>  +#include <drm/drm_atomic_helper.h>
>>  +#include <drm/drm_crtc_helper.h>
>>  +#include <drm/drm_fb_cma_helper.h>
>>  +#include <drm/drm_gem_cma_helper.h>
>>  +
>>  +#include "de2_drv.h"
>>  +
>>  +#define DRIVER_NAME "sun8i-de2"
>>  +#define DRIVER_DESC "Allwinner DRM DE2"
>>  +#define DRIVER_DATE "20161101"
>>  +#define DRIVER_MAJOR 1
>>  +#define DRIVER_MINOR 0
>>  +
>>  +static const 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_to_priv(drm);
>>  +
>>  + 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_to_priv(drm);
>>  +
>>  + 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,
>>  +};
>>  +
>>  +/*
>>  + * Platform driver
>>  + */
>>  +
>>  +static int de2_drm_bind(struct device *dev)
>>  +{
>>  + struct drm_device *drm;
>>  + struct priv *priv;
>>  + struct resource *res;
>>  + struct lcd *lcd;
>>  + int i, ret;
>>  +
>>  + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
>>  + if (!priv)
>>  + return -ENOMEM;
>>  +
>>  + drm = &priv->drm;
>>  + dev_set_drvdata(dev, drm);
>>  +
>>  + /* get the resources */
>>  + priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
>>  +
>>  + res = platform_get_resource(to_platform_device(dev),
>>  + IORESOURCE_MEM, 0);
>>  + if (!res) {
>>  + dev_err(dev, "failed to get memory resource\n");
>>  + ret = -EINVAL;
>>  + goto out1;
>>  + }
>>  +
>>  + priv->mmio = devm_ioremap_resource(dev, res);
>>  + if (IS_ERR(priv->mmio)) {
>>  + ret = PTR_ERR(priv->mmio);
>>  + dev_err(dev, "failed to map registers %d\n", ret);
>>  + goto out1;
>>  + }
>>  +
>>  + priv->gate = devm_clk_get(dev, "bus");
>>  + if (IS_ERR(priv->gate)) {
>>  + ret = PTR_ERR(priv->gate);
>>  + dev_err(dev, "bus gate err %d\n", ret);
>>  + goto out1;
>>  + }
>>  +
>>  + priv->clk = devm_clk_get(dev, "clock");
>>  + if (IS_ERR(priv->clk)) {
>>  + ret = PTR_ERR(priv->clk);
>>  + dev_err(dev, "clock err %d\n", ret);
>>  + goto out1;
>>  + }
>>  +
>>  + priv->reset = devm_reset_control_get(dev, NULL);
>>  + if (IS_ERR(priv->reset)) {
>>  + ret = PTR_ERR(priv->reset);
>>  + dev_err(dev, "reset err %d\n", ret);
>>  + goto out1;
>>  + }
>>  +
>>  + mutex_init(&priv->mutex); /* protect DE I/O accesses */
>>  +
>>  + ret = drm_dev_init(drm, &de2_drm_driver, dev);
>>  + if (ret != 0) {
>>  + dev_err(dev, "dev_init failed %d\n", ret);
>>  + goto out1;
>>  + }
>>  +
>>  + 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;
>>  +
>>  + /* start the subdevices */
>>  + ret = component_bind_all(dev, drm);
>>  + if (ret < 0)
>>  + goto out2;
>>  +
>>  + /* initialize and disable vertical blanking on all CRTCs */
>>  + ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
>>  + if (ret < 0)
>>  + dev_warn(dev, "vblank_init failed %d\n", ret);
>>  +
>>  + for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
>>  + lcd = priv->lcds[i];
>>  + if (lcd)
>>  + de2_vblank_reset(lcd);
>>  + }
>>  +
>>  + 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 out3;
>>  + }
>>  +
>>  + drm_kms_helper_poll_init(drm);
>>  +
>>  + ret = drm_dev_register(drm, 0);
>>  + if (ret < 0)
>>  + goto out4;
>>  +
>>  + return 0;
>>  +
>>  +out4:
>>  + drm_fbdev_cma_fini(priv->fbdev);
>>  +out3:
>>  + component_unbind_all(dev, drm);
>>  +out2:
>>  + drm_dev_unref(drm);
>>  +out1:
>>  + kfree(priv);
>>  + return ret;
>>  +}
>>  +
>>  +static void de2_drm_unbind(struct device *dev)
>>  +{
>>  + struct drm_device *drm = dev_get_drvdata(dev);
>>  + struct priv *priv = drm_to_priv(drm);
>>  +
>>  + drm_dev_unregister(drm);
>>  +
>>  + drm_fbdev_cma_fini(priv->fbdev);
>>  + drm_kms_helper_poll_fini(drm);
>>  + drm_vblank_cleanup(drm);
>>  + drm_mode_config_cleanup(drm);
>>  +
>>  + component_unbind_all(dev, drm);
>>  +
>>  + kfree(priv);
>>  +}
>>  +
>>  +static const struct component_master_ops de2_drm_comp_ops = {
>>  + .bind = de2_drm_bind,
>>  + .unbind = de2_drm_unbind,
>>  +};
>>  +
>>  +/*
>>  + * drm_of_component_probe() does:
>>  + * - bind of the ports (lcd-controller.port)
>>  + * - bind of the remote nodes (hdmi, tve..)
>>  + */
>>  +static int compare_of(struct device *dev, void *data)
>>  +{
>>  + struct device_node *np = data;
>>  +
>>  + if (of_node_cmp(np->name, "port") == 0) {
>>  + np = of_get_parent(np);
>>  + of_node_put(np);
>>  + }
>>  + return dev->of_node == np;
>>  +}
>>  +
>>  +static int de2_drm_probe(struct platform_device *pdev)
>>  +{
>>  + int ret;
>>  +
>>  + ret = drm_of_component_probe(&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,
>>  + .of_match_table = de2_drm_of_match,
>>  + },
>>  +};
>>  +
>>  +static int __init de2_drm_init(void)
>>  +{
>>  + int ret;
>>  +
>>  + 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-GANU6spQydw@public.gmane.org>");
>>  +MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
>>  +MODULE_LICENSE("GPL v2");
>>  diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
>>  new file mode 100644
>>  index 0000000..c42c30a
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/de2_drv.h
>>  @@ -0,0 +1,48 @@
>>  +#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 <drm/drmP.h>
>>  +#include <linux/clk.h>
>>  +#include <linux/reset.h>
>>  +
>>  +struct drm_fbdev_cma;
>>  +struct lcd;
>>  +
>>  +#define N_LCDS 2
>>  +
>>  +struct priv {
>>  + struct drm_device drm;
>>  + void __iomem *mmio;
>>  + struct clk *clk;
>>  + struct clk *gate;
>>  + struct reset_control *reset;
>>  +
>>  + struct mutex mutex; /* protect DE I/O access */
>>  + u8 soc_type;
>>  +#define SOC_A83T 0
>>  +#define SOC_H3 1
>>  + u8 started; /* bitmap of started mixers */
>>  + u8 clean; /* bitmap of clean mixers */
>>  +
>>  + struct drm_fbdev_cma *fbdev;
>>  +
>>  + struct lcd *lcds[N_LCDS]; /* CRTCs */
>>  +};
>>  +
>>  +#define drm_to_priv(x) container_of(x, struct priv, drm)
>>  +
>>  +/* in de2_crtc.c */
>>  +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
>>  +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
>>  +void de2_vblank_reset(struct lcd *lcd);
>>  +extern struct platform_driver de2_lcd_platform_driver;
>>  +
>>  +#endif /* __DE2_DRM_H__ */
>>  diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
>>  new file mode 100644
>>  index 0000000..2fd72dc
>>  --- /dev/null
>>  +++ b/drivers/gpu/drm/sun8i/de2_plane.c
>>  @@ -0,0 +1,734 @@
>>  +/*
>>  + * Allwinner DRM driver - Display Engine 2
>>  + *
>>  + * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
>>  + * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
>>  + * 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 <linux/io.h>
>>  +#include <drm/drm_atomic_helper.h>
>>  +#include <drm/drm_crtc_helper.h>
>>  +#include <drm/drm_fb_cma_helper.h>
>>  +#include <drm/drm_gem_cma_helper.h>
>>  +#include <drm/drm_plane_helper.h>
>>  +
>>  +#include "de2_drv.h"
>>  +#include "de2_crtc.h"
>>  +
>>  +/* DE2 I/O map */
>>  +
>>  +#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */
>>  +#define DE2_GATE_REG 0x0004
>>  +#define DE2_RESET_REG 0x0008
>>  +#define DE2_DIV_REG 0x000c /* 4 bits per LCD */
>>  +#define DE2_SEL_REG 0x0010
>>  +
>>  +#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */
>>  +#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */
>>  +
>>  +/* mixer registers (addr / mixer base) */
>>  +#define MIXER_GLB_REGS 0x00000 /* global control */
>>  +#define MIXER_BLD_REGS 0x01000 /* alpha blending */
>>  +#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */
>>  +#define MIXER_CHAN_SZ 0x1000 /* size of a channel */
>>  +#define MIXER_VSU_REGS 0x20000 /* VSU */
>>  +#define MIXER_GSU1_REGS 0x30000 /* GSUs */
>>  +#define MIXER_GSU2_REGS 0x40000
>>  +#define MIXER_GSU3_REGS 0x50000
>>  +#define MIXER_FCE_REGS 0xa0000 /* FCE */
>>  +#define MIXER_BWS_REGS 0xa2000 /* BWS */
>>  +#define MIXER_LTI_REGS 0xa4000 /* LTI */
>>  +#define MIXER_PEAK_REGS 0xa6000 /* PEAK */
>>  +#define MIXER_ASE_REGS 0xa8000 /* ASE */
>>  +#define MIXER_FCC_REGS 0xaa000 /* FCC */
>>  +#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */
>>  +
>>  +/* global control */
>>  +#define MIXER_GLB_CTL_REG 0x00
>>  +#define MIXER_GLB_CTL_rt_en BIT(0)
>>  +#define MIXER_GLB_CTL_finish_irq_en BIT(4)
>>  +#define MIXER_GLB_CTL_rtwb_port BIT(12)
>>  +#define MIXER_GLB_STATUS_REG 0x04
>>  +#define MIXER_GLB_DBUFF_REG 0x08
>>  +#define MIXER_GLB_SIZE_REG 0x0c
>>  +
>>  +/* alpha blending */
>>  +#define MIXER_BLD_FCOLOR_CTL_REG 0x00
>>  +#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe))
>>  +#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */
>>  +#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */
>>  +#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x))
>>  +#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x))
>>  +#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x))
>>  +#define MIXER_BLD_ROUTE_REG 0x80
>>  +#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
>>  +#define MIXER_BLD_PREMULTIPLY_REG 0x84
>>  +#define MIXER_BLD_BKCOLOR_REG 0x88
>>  +#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c
>>  +#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */
>>  +#define MIXER_BLD_MODE_SRCOVER 0x03010301
>>  +#define MIXER_BLD_OUT_CTL_REG 0xfc
>>  +
>>  +/* VI channel (channel 0) */
>>  +#define VI_CFG_N 4 /* number of layers */
>>  +#define VI_CFG_SIZE 0x30 /* size of a layer */
>>  +#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l))
>>  +#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)
>>  +#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l))
>>  +#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l))
>>  +#define VI_N_PLANES 3
>>  +#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p))
>>  +#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
>>  +#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
>>  +#define VI_FCOLORx(l) (0xc0 + 4 * (l))
>>  +#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p))
>>  +#define VI_BOT_HADDRx(p) (0xdc + 4 * (p))
>>  +#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n))
>>  +#define VI_HORI_DSx(n) (0xf0 + 4 * (n))
>>  +#define VI_VERT_DSx(n) (0xf8 + 4 * (n))
>>  +#define VI_SIZE 0x100
>>  +
>>  +/* UI channel (channels 1..3) */
>>  +#define UI_CFG_N 4 /* number of layers */
>>  +#define UI_CFG_SIZE (8 * 4) /* size of a layer */
>>  +#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l))
>>  +#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)
>>  +#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l))
>>  +#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l))
>>  +#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l))
>>  +#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l))
>>  +#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l))
>>  +#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l))
>>  +#define UI_TOP_HADDR 0x80
>>  +#define UI_BOT_HADDR 0x84
>>  +#define UI_OVL_SIZE 0x88
>>  +#define UI_SIZE 0x8c
>>  +
>>  +/* 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 /* YVYU */
>>  +#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */
>>  +#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */
>>  +#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
>>  +#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
>>  +
>>  +/* 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,
>>  +};
>>  +
>>  +/*
>>  + * plane table
>>  + *
>>  + * The chosen channel/layer assignment of the planes respects
>>  + * the following constraints:
>>  + * - the cursor must be in a channel higher than the primary channel
>>  + * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
>>  + */
>>  +static const struct {
>>  + u8 chan;
>>  + u8 layer;
>>  + u8 pipe;
>>  + u8 type; /* plane type */
>>  + const uint32_t *formats;
>>  + u8 n_formats;
>>  +} plane_tb[] = {
>>  + [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */
>>  + 0, 0, 0,
>>  + DRM_PLANE_TYPE_PRIMARY,
>>  + ui_formats, ARRAY_SIZE(ui_formats),
>>  + },
>>  + [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */
>>  + 1, 0, 1,
>>  + DRM_PLANE_TYPE_CURSOR,
>>  + ui_formats, ARRAY_SIZE(ui_formats),
>>  + },
>>  + {
>>  + 0, 1, 0, /* 1st overlay: channel 0, layer 1 */
>>  + DRM_PLANE_TYPE_OVERLAY,
>>  + vi_formats, ARRAY_SIZE(vi_formats),
>>  + },
>>  + {
>>  + 0, 2, 0, /* 2nd overlay: channel 0, layer 2 */
>>  + DRM_PLANE_TYPE_OVERLAY,
>>  + vi_formats, ARRAY_SIZE(vi_formats),
>>  + },
>>  + {
>>  + 0, 3, 0, /* 3rd overlay: channel 0, layer 3 */
>>  + DRM_PLANE_TYPE_OVERLAY,
>>  + vi_formats, ARRAY_SIZE(vi_formats),
>>  + },
>>  +};
>>  +
>>  +static inline void andl_relaxed(void __iomem *addr, u32 val)
>>  +{
>>  + writel_relaxed(readl_relaxed(addr) & val, addr);
>>  +}
>>  +
>>  +static inline void orl_relaxed(void __iomem *addr, u32 val)
>>  +{
>>  + writel_relaxed(readl_relaxed(addr) | val, addr);
>>  +}
>>  +
>>  +/* alert the DE processor about changes in a mixer configuration */
>>  +static void de2_mixer_select(struct priv *priv,
>>  + int mixer,
>>  + void __iomem *mixer_io)
>>  +{
>>  + /* select the mixer */
>>  + andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
>>  +
>>  + /* double register switch */
>>  + writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
>>  +}
>>  +
>>  +/*
>>  + * cleanup a mixer
>>  + *
>>  + * This is needed only once after power on.
>>  + */
>>  +static void de2_mixer_cleanup(struct priv *priv, int mixer,
>>  + u32 size)
>>  +{
>>  + void __iomem *mixer_io = priv->mmio;
>>  + void __iomem *chan_io;
>>  + u32 data;
>>  + unsigned int i;
>>  +
>>  + mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>>  + chan_io = mixer_io + MIXER_CHAN_REGS;
>>  +
>>  + andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
>>  + writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
>>  +
>>  + writel_relaxed(MIXER_GLB_CTL_rt_en,
>>  + mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
>>  + writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
>>  +
>>  + writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
>>  +
>>  + /*
>>  + * clear the VI/UI channels
>>  + * LCD0: 1 VI and 3 UIs
>>  + * LCD1: 1 VI and 1 UI
>>  + */
>>  + memset_io(chan_io, 0, VI_SIZE);
>>  + memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
>>  + if (mixer == 0) {
>>  + memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
>>  + memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
>>  + }
>>  +
>>  + /* alpha blending */
>>  + writel_relaxed(0x00000001 | /* fcolor for primary */
>>  + MIXER_BLD_FCOLOR_CTL_PEN(0),
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
>>  + for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
>>  + writel_relaxed(0xff000000,
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
>>  + writel_relaxed(size,
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
>>  + writel_relaxed(0,
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
>>  + }
>>  + writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
>>  +
>>  + /* prepare the pipe route for the planes */
>>  + data = 0;
>>  + for (i = 0; i < DE2_N_PLANES; i++)
>>  + data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
>>  + writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
>>  +
>>  + writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_PREMULTIPLY_REG);
>>  + writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_BKCOLOR_REG);
>>  + writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_OUTPUT_SIZE_REG);
>>  + writel_relaxed(MIXER_BLD_MODE_SRCOVER,
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
>>  + writel_relaxed(MIXER_BLD_MODE_SRCOVER,
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
>>  +
>>  + /* disable the enhancements */
>>  + writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
>>  + writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
>>  +}
>>  +
>>  +/* enable a mixer */
>>  +static void de2_mixer_enable(struct lcd *lcd)
>>  +{
>>  + struct priv *priv = lcd->priv;
>>  + void __iomem *mixer_io = priv->mmio;
>>  + struct drm_display_mode *mode = &lcd->crtc.mode;
>>  + u32 size = WH(mode->hdisplay, mode->vdisplay);
>>  + u32 data;
>>  + int mixer = lcd->mixer;
>>  + int i;
>>  +
>>  + mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>>  +
>>  + /* if not done yet, start the DE processor */
>>  + if (!priv->started) {
>>  + reset_control_deassert(priv->reset);
>>  + clk_prepare_enable(priv->gate);
>>  + clk_prepare_enable(priv->clk);
>>  + }
>>  + priv->started |= 1 << mixer;
>>  +
>>  + /* set the A83T clock divider (500 / 2) = 250MHz */
>>  + if (priv->soc_type == SOC_A83T)
>>  + writel_relaxed(0x00000011, /* div = 2 for both LCDs */
>>  + priv->mmio + DE2_DIV_REG);
>>  +
>>  + /* deassert the mixer and enable its clock */
>>  + orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
>>  + data = 1 << mixer; /* 1 bit / lcd */
>>  + orl_relaxed(priv->mmio + DE2_GATE_REG, data);
>>  + orl_relaxed(priv->mmio + DE2_MOD_REG, data);
>>  +
>>  + /* if not done yet, cleanup and enable */
>>  + if (!(priv->clean & (1 << mixer))) {
>>  + priv->clean |= 1 << mixer;
>>  + de2_mixer_cleanup(priv, mixer, size);
>>  + return;
>>  + }
>>  +
>>  + /* enable */
>>  + de2_mixer_select(priv, mixer, mixer_io);
>>  +
>>  + writel_relaxed(MIXER_GLB_CTL_rt_en,
>>  + mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
>>  + writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
>>  +
>>  + /* set the size of the frame buffer */
>>  + writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
>>  + for (i = 0; i < MIXER_BLD_ATTR_N; i++)
>>  + writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_ATTRx_INSIZE(i));
>>  + writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_OUTPUT_SIZE_REG);
>>  +
>>  + writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
>>  +}
>>  +
>>  +/* enable a LCD (DE mixer) */
>>  +void de2_de_enable(struct lcd *lcd)
>>  +{
>>  + mutex_lock(&lcd->priv->mutex);
>>  +
>>  + de2_mixer_enable(lcd);
>>  +
>>  + mutex_unlock(&lcd->priv->mutex);
>>  +}
>>  +
>>  +/* disable a LCD (DE mixer) */
>>  +void de2_de_disable(struct lcd *lcd)
>>  +{
>>  + struct priv *priv = lcd->priv;
>>  + void __iomem *mixer_io = priv->mmio;
>>  + int mixer = lcd->mixer;
>>  + u32 data;
>>  +
>>  + mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>>  +
>>  + mutex_lock(&priv->mutex);
>>  +
>>  + de2_mixer_select(priv, mixer, mixer_io);
>>  +
>>  + writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
>>  +
>>  + data = ~(1 << mixer);
>>  + andl_relaxed(priv->mmio + DE2_MOD_REG, data);
>>  + andl_relaxed(priv->mmio + DE2_GATE_REG, data);
>>  + andl_relaxed(priv->mmio + DE2_RESET_REG, data);
>>  +
>>  + mutex_unlock(&priv->mutex);
>>  +
>>  + /* if all mixers are disabled, stop the DE */
>>  + priv->started &= ~(1 << mixer);
>>  + if (!priv->started) {
>>  + clk_disable_unprepare(priv->clk);
>>  + clk_disable_unprepare(priv->gate);
>>  + reset_control_assert(priv->reset);
>>  + }
>>  +}
>>  +
>>  +static void de2_vi_update(void __iomem *chan_io,
>>  + struct drm_gem_cma_object *gem,
>>  + int layer,
>>  + unsigned int fmt,
>>  + u32 ui_sel,
>>  + u32 size,
>>  + u32 coord,
>>  + struct drm_framebuffer *fb,
>>  + u32 screen_size)
>>  +{
>>  + int i;
>>  +
>>  + writel_relaxed(VI_CFG_ATTR_en |
>>  + (fmt << VI_CFG_ATTR_fmt_SHIFT) |
>>  + ui_sel,
>>  + chan_io + VI_CFGx_ATTR(layer));
>>  + writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
>>  + writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
>>  + for (i = 0; i < VI_N_PLANES; i++) {
>>  + writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
>>  + fb->pitches[0],
>>  + chan_io + VI_CFGx_PITCHy(layer, i));
>>  + writel_relaxed(gem->paddr + fb->offsets[i],
>>  + chan_io + VI_CFGx_TOP_LADDRy(layer, i));
>>  + }
>>  + writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
>>  + if (layer == 0) {
>>  + writel_relaxed(screen_size,
>>  + chan_io + VI_OVL_SIZEx(0));
>>  + }
>>  +}
>>  +
>>  +static void de2_ui_update(void __iomem *chan_io,
>>  + struct drm_gem_cma_object *gem,
>>  + int layer,
>>  + unsigned int fmt,
>>  + u32 alpha_glob,
>>  + u32 size,
>>  + u32 coord,
>>  + struct drm_framebuffer *fb,
>>  + u32 screen_size)
>>  +{
>>  + writel_relaxed(UI_CFG_ATTR_en |
>>  + (fmt << UI_CFG_ATTR_fmt_SHIFT) |
>>  + alpha_glob,
>>  + chan_io + UI_CFGx_ATTR(layer));
>>  + writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
>>  + writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
>>  + writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
>>  + writel_relaxed(gem->paddr + fb->offsets[0],
>>  + chan_io + UI_CFGx_TOP_LADDR(layer));
>>  + if (layer == 0)
>>  + writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
>>  +}
>>  +
>>  +static void de2_plane_update(struct priv *priv, struct lcd *lcd,
>>  + int plane_num,
>>  + struct drm_plane_state *state,
>>  + struct drm_plane_state *old_state)
>>  +{
>>  + void __iomem *mixer_io = priv->mmio;
>>  + void __iomem *chan_io;
>>  + struct drm_framebuffer *fb = state->fb;
>>  + struct drm_gem_cma_object *gem;
>>  + u32 size = WH(state->crtc_w, state->crtc_h);
>>  + u32 coord, screen_size;
>>  + u32 fcolor;
>>  + u32 ui_sel, alpha_glob;
>>  + int mixer = lcd->mixer;
>>  + int chan, layer, x, y;
>>  + unsigned int fmt;
>>  +
>>  + chan = plane_tb[plane_num].chan;
>>  + layer = plane_tb[plane_num].layer;
>>  +
>>  + mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>>  + chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_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);
>>  +
>>  + /* if plane update was delayed, force a full update */
>>  + if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
>>  + (1 << plane_num)) {
>>  + priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
>>  + ~(1 << plane_num);
>>  +
>>  + /* handle plane move */
>>  + } else if (fb == old_state->fb) {
>>  + de2_mixer_select(priv, mixer, mixer_io);
>>  + if (chan == 0)
>>  + writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
>>  + else
>>  + writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
>>  + 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("de2_plane_update: format %.4s not yet treated\n",
>>  + (char *) &fb->pixel_format);
>>  + return;
>>  + }
>>  +
>>  + /* the overlay size is the one of the primary plane */
>>  + screen_size = plane_num == DE2_PRIMARY_PLANE ?
>>  + size :
>>  + readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
>>  +
>>  + /* prepare pipe enable */
>>  + fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_FCOLOR_CTL_REG);
>>  + fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
>>  +
>>  + de2_mixer_select(priv, mixer, mixer_io);
>>  +
>>  + if (chan == 0) /* VI channel */
>>  + de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
>>  + fb, screen_size);
>>  + else /* UI channel */
>>  + de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
>>  + fb, screen_size);
>>  + writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_FCOLOR_CTL_REG);
>>  +}
>>  +
>>  +static int vi_nb_layers(void __iomem *chan_io)
>>  +{
>>  + int layer, n = 0;
>>  +
>>  + for (layer = 0; layer < 4; layer++) {
>>  + if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
>>  + n++;
>>  + }
>>  +
>>  + return n;
>>  +}
>>  +
>>  +static int ui_nb_layers(void __iomem *chan_io)
>>  +{
>>  + int layer, n = 0;
>>  +
>>  + for (layer = 0; layer < 4; layer++) {
>>  + if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
>>  + n++;
>>  + }
>>  +
>>  + return n;
>>  +}
>>  +
>>  +static void de2_plane_disable(struct priv *priv,
>>  + int mixer, int plane_num)
>>  +{
>>  + void __iomem *mixer_io = priv->mmio;
>>  + void __iomem *chan_io;
>>  + u32 fcolor;
>>  + int chan, layer, n;
>>  +
>>  + chan = plane_tb[plane_num].chan;
>>  + layer = plane_tb[plane_num].layer;
>>  +
>>  + mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>>  + chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
>>  +
>>  + if (chan == 0)
>>  + n = vi_nb_layers(chan_io);
>>  + else
>>  + n = ui_nb_layers(chan_io);
>>  +
>>  + fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
>>  + MIXER_BLD_FCOLOR_CTL_REG);
>>  +
>>  + de2_mixer_select(priv, mixer, mixer_io);
>>  +
>>  + if (chan == 0)
>>  + writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
>>  + else
>>  + writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
>>  +
>>  + /* disable the pipe if no more active layer */
>>  + if (n <= 1)
>>  + writel_relaxed(fcolor &
>>  + ~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
>>  + mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
>>  +}
>>  +
>>  +static void de2_drm_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 priv *priv = lcd->priv;
>>  + int plane_num = plane - lcd->planes;
>>  +
>>  + /* if the crtc is disabled, mark update delayed */
>>  + if (!(priv->started & (1 << lcd->mixer))) {
>>  + lcd->delayed |= 1 << plane_num;
>>  + return; /* mixer disabled */
>>  + }
>>  +
>>  + mutex_lock(&priv->mutex);
>>  +
>>  + de2_plane_update(priv, lcd, plane_num, state, old_state);
>>  +
>>  + mutex_unlock(&priv->mutex);
>>  +}
>>  +
>>  +static void de2_drm_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);
>>  + struct priv *priv = lcd->priv;
>>  + int plane_num = plane - lcd->planes;
>>  +
>>  + if (!(priv->started & (1 << lcd->mixer)))
>>  + return; /* mixer disabled */
>>  +
>>  + mutex_lock(&priv->mutex);
>>  +
>>  + de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
>>  +
>>  + mutex_unlock(&priv->mutex);
>>  +}
>>  +
>>  +static const struct drm_plane_helper_funcs plane_helper_funcs = {
>>  + .atomic_update = de2_drm_plane_update,
>>  + .atomic_disable = de2_drm_plane_disable,
>>  +};
>>  +
>>  +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 possible_crtcs,
>>  + int plane_num)
>>  +{
>>  + int ret;
>>  +
>>  + ret = drm_universal_plane_init(drm, plane, possible_crtcs,
>>  + &plane_funcs,
>>  + plane_tb[plane_num].formats,
>>  + plane_tb[plane_num].n_formats,
>>  + plane_tb[plane_num].type, NULL);
>>  + if (ret >= 0)
>>  + drm_plane_helper_add(plane, &plane_helper_funcs);
>>  +
>>  + return ret;
>>  +}
>>  +
>>  +/* initialize the planes */
>>  +int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
>>  +{
>>  + int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
>>  +
>>  + n = ARRAY_SIZE(plane_tb);
>>  + if (n != DE2_N_PLANES) {
>>  + dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
>>  + __stringify(DE2_N_PLANES) "\n", n);
>>  + return -EINVAL;
>>  + }
>>  +
>>  + for (i = 0; i < n; i++) {
>>  + ret = de2_one_plane_init(drm, &lcd->planes[i],
>>  + possible_crtcs, i);
>>  + if (ret < 0) {
>>  + dev_err(lcd->dev, "plane init failed %d\n", ret);
>>  + break;
>>  + }
>>  + }
>>  +
>>  + return ret;
>>  +}
>>  --
>>  2.10.2
>
>>  _______________________________________________
>>  dri-devel mailing list
>>  dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
>>  https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.

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

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

* [linux-sunxi] Re: [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
@ 2016-11-29 14:33             ` Icenowy Zheng
  0 siblings, 0 replies; 79+ messages in thread
From: Icenowy Zheng @ 2016-11-29 14:33 UTC (permalink / raw)
  To: linux-arm-kernel



29.11.2016, 22:30, "Daniel Vetter" <daniel@ffwll.ch>:
> On Mon, Nov 28, 2016 at 03:23:54PM +0100, Jean-Francois Moine wrote:
>> ?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>
>
> Scrolled around a bit, seemed all reasonable.
>
> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
> Not sure a new driver for each chip is reasonable, experience says that
> long-term you want to share quite a pile of code between different hw
> platforms from the same vendor. But that's entirely up to you.
> -Daniel

The Display Engine on A83T and newer SoCs is quite new, different from
the ones on A33 and older.

It's called "DE 2.0" in the user manual of the SoCs.

>
>> ?---
>> ??drivers/gpu/drm/Kconfig | 2 +
>> ??drivers/gpu/drm/Makefile | 1 +
>> ??drivers/gpu/drm/sun8i/Kconfig | 19 +
>> ??drivers/gpu/drm/sun8i/Makefile | 7 +
>> ??drivers/gpu/drm/sun8i/de2_crtc.c | 449 +++++++++++++++++++++++
>> ??drivers/gpu/drm/sun8i/de2_crtc.h | 50 +++
>> ??drivers/gpu/drm/sun8i/de2_drv.c | 317 ++++++++++++++++
>> ??drivers/gpu/drm/sun8i/de2_drv.h | 48 +++
>> ??drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
>> ??9 files changed, 1627 insertions(+)
>> ??create mode 100644 drivers/gpu/drm/sun8i/Kconfig
>> ??create mode 100644 drivers/gpu/drm/sun8i/Makefile
>> ??create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
>> ??create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
>> ??create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
>> ??create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
>> ??create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
>>
>> ?diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> ?index 95fc041..bb1bfbc 100644
>> ?--- a/drivers/gpu/drm/Kconfig
>> ?+++ b/drivers/gpu/drm/Kconfig
>> ?@@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
>>
>> ??source "drivers/gpu/drm/sun4i/Kconfig"
>>
>> ?+source "drivers/gpu/drm/sun8i/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 883f3e7..3e1eaa0 100644
>> ?--- a/drivers/gpu/drm/Makefile
>> ?+++ b/drivers/gpu/drm/Makefile
>> ?@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
>> ??obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
>> ??obj-y += omapdrm/
>> ??obj-$(CONFIG_DRM_SUN4I) += sun4i/
>> ?+obj-$(CONFIG_DRM_SUN8I) += sun8i/
>> ??obj-y += tilcdc/
>> ??obj-$(CONFIG_DRM_QXL) += qxl/
>> ??obj-$(CONFIG_DRM_BOCHS) += bochs/
>> ?diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
>> ?new file mode 100644
>> ?index 0000000..6940895
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/Kconfig
>> ?@@ -0,0 +1,19 @@
>> ?+#
>> ?+# Allwinner DE2 Video configuration
>> ?+#
>> ?+
>> ?+config DRM_SUN8I
>> ?+ bool
>> ?+
>> ?+config DRM_SUN8I_DE2
>> ?+ tristate "Support for Allwinner Video with DE2 interface"
>> ?+ depends on DRM && OF
>> ?+ depends on ARCH_SUNXI || COMPILE_TEST
>> ?+ select DRM_GEM_CMA_HELPER
>> ?+ select DRM_KMS_CMA_HELPER
>> ?+ select DRM_KMS_HELPER
>> ?+ select DRM_SUN8I
>> ?+ 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
>> ?+ sun8i-de2-drm.
>> ?diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
>> ?new file mode 100644
>> ?index 0000000..f107919
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/Makefile
>> ?@@ -0,0 +1,7 @@
>> ?+#
>> ?+# Makefile for Allwinner's sun8i DRM device driver
>> ?+#
>> ?+
>> ?+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
>> ?+
>> ?+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
>> ?diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
>> ?new file mode 100644
>> ?index 0000000..4e94ccc
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/de2_crtc.c
>> ?@@ -0,0 +1,449 @@
>> ?+/*
>> ?+ * 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 <linux/io.h>
>> ?+#include <linux/of_irq.h>
>> ?+#include <linux/of_graph.h>
>> ?+
>> ?+#include "de2_drv.h"
>> ?+#include "de2_crtc.h"
>> ?+
>> ?+/* I/O map */
>> ?+
>> ?+#define TCON_GCTL_REG 0x00
>> ?+#define TCON_GCTL_TCON_ENABLE BIT(31)
>> ?+#define TCON_GINT0_REG 0x04
>> ?+#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)
>> ?+#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
>> ?+#define TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
>> ?+#define TCON0_CTL_REG 0x40
>> ?+#define TCON0_CTL_TCON_ENABLE BIT(31)
>> ?+#define TCON1_CTL_REG 0x90
>> ?+#define TCON1_CTL_TCON_ENABLE BIT(31)
>> ?+#define TCON1_CTL_INTERLACE_ENABLE BIT(20)
>> ?+#define TCON1_CTL_Start_Delay_SHIFT 4
>> ?+#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
>> ?+#define TCON1_BASIC0_REG 0x94 /* XI/YI */
>> ?+#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */
>> ?+#define TCON1_BASIC2_REG 0x9c /* XO/YO */
>> ?+#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */
>> ?+#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */
>> ?+#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */
>> ?+#define TCON1_PS_SYNC_REG 0xb0
>> ?+#define TCON1_IO_POL_REG 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)
>> ?+#define TCON1_IO_TRI_REG 0xf4
>> ?+#define TCON_CEU_CTL_REG 0x100
>> ?+#define TCON_CEU_CTL_ceu_en BIT(31)
>> ?+#define TCON1_FILL_CTL_REG 0x300
>> ?+#define TCON1_FILL_START0_REG 0x304
>> ?+#define TCON1_FILL_END0_REG 0x308
>> ?+#define TCON1_FILL_DATA0_REG 0x30c
>> ?+
>> ?+#define XY(x, y) (((x) << 16) | (y))
>> ?+
>> ?+#define andl_relaxed(addr, val) \
>> ?+ writel_relaxed(readl_relaxed(addr) & val, addr)
>> ?+#define orl_relaxed(addr, val) \
>> ?+ writel_relaxed(readl_relaxed(addr) | val, addr)
>> ?+
>> ?+/* 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 = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
>> ?+
>> ?+ drm_crtc_handle_vblank(&lcd->crtc);
>> ?+
>> ?+ writel_relaxed(isr &
>> ?+ ~(TCON_GINT0_TCON1_Vb_Int_Flag |
>> ?+ TCON_GINT0_TCON1_Vb_Line_Int_Flag),
>> ?+ lcd->mmio + TCON_GINT0_REG);
>> ?+
>> ?+ return IRQ_HANDLED;
>> ?+}
>> ?+
>> ?+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
>> ?+{
>> ?+ struct priv *priv = drm_to_priv(drm);
>> ?+ struct lcd *lcd = priv->lcds[crtc_ix];
>> ?+
>> ?+ orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
>> ?+{
>> ?+ struct priv *priv = drm_to_priv(drm);
>> ?+ struct lcd *lcd = priv->lcds[crtc_ix];
>> ?+
>> ?+ andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
>> ?+}
>> ?+
>> ?+void de2_vblank_reset(struct lcd *lcd)
>> ?+{
>> ?+ drm_crtc_vblank_reset(&lcd->crtc);
>> ?+}
>> ?+
>> ?+/* frame functions */
>> ?+static int de2_crtc_set_clock(struct lcd *lcd, int rate)
>> ?+{
>> ?+ struct clk *parent_clk;
>> ?+ u32 parent_rate;
>> ?+ int ret;
>> ?+
>> ?+ /* determine and set the best rate for the parent clock (pll-video) */
>> ?+ if ((270000 * 2) % rate == 0)
>> ?+ parent_rate = 270000000;
>> ?+ else if (297000 % rate == 0)
>> ?+ parent_rate = 297000000;
>> ?+ else
>> ?+ return -EINVAL; /* unsupported clock */
>> ?+
>> ?+ parent_clk = clk_get_parent(lcd->clk);
>> ?+
>> ?+ ret = clk_set_rate(parent_clk, parent_rate);
>> ?+ if (ret) {
>> ?+ dev_err(lcd->dev, "set parent rate failed %d\n", ret);
>> ?+ return ret;
>> ?+ }
>> ?+ ret = clk_set_rate(lcd->clk, rate * 1000);
>> ?+ if (ret) {
>> ?+ dev_err(lcd->dev, "set rate failed %d\n", ret);
>> ?+ return ret;
>> ?+ }
>> ?+
>> ?+ /* enable the clock */
>> ?+ reset_control_deassert(lcd->reset);
>> ?+ clk_prepare_enable(lcd->bus);
>> ?+ clk_prepare_enable(lcd->clk);
>> ?+
>> ?+ return ret;
>> ?+}
>> ?+
>> ?+static void de2_tcon_init(struct lcd *lcd)
>> ?+{
>> ?+ andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
>> ?+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
>> ?+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
>> ?+
>> ?+ /* disable/ack interrupts */
>> ?+ writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
>> ?+}
>> ?+
>> ?+static void de2_tcon_enable(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;
>> ?+
>> ?+ orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
>> ?+
>> ?+ data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
>> ?+ writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
>> ?+ writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
>> ?+ writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
>> ?+ writel_relaxed(XY(mode->htotal - 1,
>> ?+ mode->htotal - mode->hsync_start - 1),
>> ?+ lcd->mmio + TCON1_BASIC3_REG);
>> ?+ writel_relaxed(XY(mode->vtotal * (3 - interlace),
>> ?+ mode->vtotal - mode->vsync_start - 1),
>> ?+ lcd->mmio + TCON1_BASIC4_REG);
>> ?+ writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
>> ?+ mode->vsync_end - mode->vsync_start - 1),
>> ?+ lcd->mmio + TCON1_BASIC5_REG);
>> ?+
>> ?+ 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;
>> ?+ writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
>> ?+
>> ?+ andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
>> ?+
>> ?+ if (interlace == 2)
>> ?+ orl_relaxed(lcd->mmio + TCON1_CTL_REG,
>> ?+ TCON1_CTL_INTERLACE_ENABLE);
>> ?+ else
>> ?+ andl_relaxed(lcd->mmio + TCON1_CTL_REG,
>> ?+ ~TCON1_CTL_INTERLACE_ENABLE);
>> ?+
>> ?+ writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
>> ?+ writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
>> ?+ writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
>> ?+ writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
>> ?+
>> ?+ start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
>> ?+ if (start_delay > 31)
>> ?+ start_delay = 31;
>> ?+ data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
>> ?+ data &= ~TCON1_CTL_Start_Delay_MASK;
>> ?+ data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
>> ?+ writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
>> ?+
>> ?+ orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
>> ?+}
>> ?+
>> ?+static void de2_tcon_disable(struct lcd *lcd)
>> ?+{
>> ?+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
>> ?+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
>> ?+}
>> ?+
>> ?+static void de2_crtc_enable(struct drm_crtc *crtc)
>> ?+{
>> ?+ struct lcd *lcd = crtc_to_lcd(crtc);
>> ?+ struct drm_display_mode *mode = &crtc->mode;
>> ?+
>> ?+ if (de2_crtc_set_clock(lcd, mode->clock) < 0)
>> ?+ return;
>> ?+ lcd->clk_enabled = true;
>> ?+
>> ?+ /* start the TCON and the DE */
>> ?+ de2_tcon_enable(lcd);
>> ?+ de2_de_enable(lcd);
>> ?+
>> ?+ /* turn on blanking interrupt */
>> ?+ drm_crtc_vblank_on(crtc);
>> ?+}
>> ?+
>> ?+static void de2_crtc_disable(struct drm_crtc *crtc,
>> ?+ struct drm_crtc_state *old_crtc_state)
>> ?+{
>> ?+ struct lcd *lcd = crtc_to_lcd(crtc);
>> ?+
>> ?+ if (!lcd->clk_enabled)
>> ?+ return; /* already disabled */
>> ?+ lcd->clk_enabled = false;
>> ?+
>> ?+ de2_de_disable(lcd);
>> ?+
>> ?+ drm_crtc_vblank_off(crtc);
>> ?+
>> ?+ de2_tcon_disable(lcd);
>> ?+
>> ?+ clk_disable_unprepare(lcd->clk);
>> ?+ clk_disable_unprepare(lcd->bus);
>> ?+ reset_control_assert(lcd->reset);
>> ?+}
>> ?+
>> ?+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,
>> ?+ .atomic_disable = de2_crtc_disable,
>> ?+};
>> ?+
>> ?+/* device init */
>> ?+static int de2_lcd_bind(struct device *dev, struct device *master,
>> ?+ void *data)
>> ?+{
>> ?+ struct drm_device *drm = data;
>> ?+ struct priv *priv = drm_to_priv(drm);
>> ?+ struct lcd *lcd = dev_get_drvdata(dev);
>> ?+ struct drm_crtc *crtc = &lcd->crtc;
>> ?+ int ret, i, crtc_ix;
>> ?+
>> ?+ lcd->priv = priv;
>> ?+
>> ?+ /* set the CRTC reference */
>> ?+ crtc_ix = drm_crtc_index(crtc);
>> ?+ if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
>> ?+ dev_err(drm->dev, "Bad crtc index");
>> ?+ return -ENOENT;
>> ?+ }
>> ?+ priv->lcds[crtc_ix] = lcd;
>> ?+
>> ?+ /* and the mixer index (DT port index in the DE) */
>> ?+ for (i = 0; ; i++) {
>> ?+ struct device_node *port;
>> ?+
>> ?+ port = of_parse_phandle(drm->dev->of_node, "ports", i);
>> ?+ if (!port)
>> ?+ break;
>> ?+ if (port == lcd->crtc.port) {
>> ?+ lcd->mixer = i;
>> ?+ break;
>> ?+ }
>> ?+ }
>> ?+
>> ?+ ret = de2_plane_init(drm, lcd);
>> ?+ if (ret < 0)
>> ?+ return ret;
>> ?+
>> ?+ drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
>> ?+
>> ?+ return drm_crtc_init_with_planes(drm, crtc,
>> ?+ &lcd->planes[DE2_PRIMARY_PLANE],
>> ?+ &lcd->planes[DE2_CURSOR_PLANE],
>> ?+ &de2_crtc_funcs, NULL);
>> ?+}
>> ?+
>> ?+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->priv)
>> ?+ lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
>> ?+}
>> ?+
>> ?+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;
>> ?+
>> ?+ lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
>> ?+ if (!lcd)
>> ?+ return -ENOMEM;
>> ?+
>> ?+ dev_set_drvdata(dev, lcd);
>> ?+ lcd->dev = dev;
>> ?+ lcd->mixer = 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);
>> ?+ }
>> ?+
>> ?+ /* possible CRTC */
>> ?+ 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->bus = devm_clk_get(dev, "bus");
>> ?+ if (IS_ERR(lcd->bus)) {
>> ?+ dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
>> ?+ ret = PTR_ERR(lcd->bus);
>> ?+ goto err;
>> ?+ }
>> ?+
>> ?+ lcd->clk = devm_clk_get(dev, "clock");
>> ?+ if (IS_ERR(lcd->clk)) {
>> ?+ ret = PTR_ERR(lcd->clk);
>> ?+ dev_err(dev, "get video clock err %d\n", ret);
>> ?+ goto err;
>> ?+ }
>> ?+
>> ?+ lcd->reset = devm_reset_control_get(dev, NULL);
>> ?+ if (IS_ERR(lcd->reset)) {
>> ?+ ret = PTR_ERR(lcd->reset);
>> ?+ dev_err(dev, "get reset err %d\n", ret);
>> ?+ goto err;
>> ?+ }
>> ?+
>> ?+ irq = platform_get_irq(pdev, 0);
>> ?+ if (irq <= 0) {
>> ?+ dev_err(dev, "unable to get irq\n");
>> ?+ ret = -EINVAL;
>> ?+ goto err;
>> ?+ }
>> ?+
>> ?+ de2_tcon_init(lcd); /* stop TCON and avoid interrupts */
>> ?+
>> ?+ ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
>> ?+ dev_name(dev), lcd);
>> ?+ if (ret < 0) {
>> ?+ dev_err(dev, "unable to request irq %d\n", irq);
>> ?+ goto err;
>> ?+ }
>> ?+
>> ?+ return component_add(dev, &de2_lcd_ops);
>> ?+
>> ?+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);
>> ?+
>> ?+ of_node_put(lcd->crtc.port);
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+static const struct of_device_id de2_lcd_ids[] = {
>> ?+ { .compatible = "allwinner,sun8i-a83t-tcon", },
>> ?+ { }
>> ?+};
>> ?+
>> ?+struct platform_driver de2_lcd_platform_driver = {
>> ?+ .probe = de2_lcd_probe,
>> ?+ .remove = de2_lcd_remove,
>> ?+ .driver = {
>> ?+ .name = "sun8i-de2-tcon",
>> ?+ .of_match_table = of_match_ptr(de2_lcd_ids),
>> ?+ },
>> ?+};
>> ?diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
>> ?new file mode 100644
>> ?index 0000000..c0d34a7
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/de2_crtc.h
>> ?@@ -0,0 +1,50 @@
>> ?+#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 <drm/drm_plane_helper.h>
>> ?+
>> ?+struct clk;
>> ?+struct reset_control;
>> ?+struct priv;
>> ?+
>> ?+/* planes */
>> ?+#define DE2_PRIMARY_PLANE 0
>> ?+#define DE2_CURSOR_PLANE 1
>> ?+#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */
>> ?+
>> ?+struct lcd {
>> ?+ void __iomem *mmio;
>> ?+
>> ?+ struct device *dev;
>> ?+ struct drm_crtc crtc;
>> ?+
>> ?+ struct priv *priv; /* DRM/DE private data */
>> ?+
>> ?+ u8 mixer; /* LCD (mixer) number */
>> ?+ u8 delayed; /* bitmap of planes with delayed update */
>> ?+
>> ?+ u8 clk_enabled; /* used for error in crtc_enable */
>> ?+
>> ?+ struct clk *clk;
>> ?+ struct clk *bus;
>> ?+ struct reset_control *reset;
>> ?+
>> ?+ struct drm_plane planes[DE2_N_PLANES];
>> ?+};
>> ?+
>> ?+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
>> ?+
>> ?+/* in de2_plane.c */
>> ?+void de2_de_enable(struct lcd *lcd);
>> ?+void de2_de_disable(struct lcd *lcd);
>> ?+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
>> ?+
>> ?+#endif /* __DE2_CRTC_H__ */
>> ?diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
>> ?new file mode 100644
>> ?index 0000000..f96babe
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/de2_drv.c
>> ?@@ -0,0 +1,317 @@
>> ?+/*
>> ?+ * 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/of_device.h>
>> ?+#include <drm/drm_of.h>
>> ?+#include <linux/component.h>
>> ?+#include <drm/drm_atomic_helper.h>
>> ?+#include <drm/drm_crtc_helper.h>
>> ?+#include <drm/drm_fb_cma_helper.h>
>> ?+#include <drm/drm_gem_cma_helper.h>
>> ?+
>> ?+#include "de2_drv.h"
>> ?+
>> ?+#define DRIVER_NAME "sun8i-de2"
>> ?+#define DRIVER_DESC "Allwinner DRM DE2"
>> ?+#define DRIVER_DATE "20161101"
>> ?+#define DRIVER_MAJOR 1
>> ?+#define DRIVER_MINOR 0
>> ?+
>> ?+static const 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_to_priv(drm);
>> ?+
>> ?+ 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_to_priv(drm);
>> ?+
>> ?+ 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,
>> ?+};
>> ?+
>> ?+/*
>> ?+ * Platform driver
>> ?+ */
>> ?+
>> ?+static int de2_drm_bind(struct device *dev)
>> ?+{
>> ?+ struct drm_device *drm;
>> ?+ struct priv *priv;
>> ?+ struct resource *res;
>> ?+ struct lcd *lcd;
>> ?+ int i, ret;
>> ?+
>> ?+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
>> ?+ if (!priv)
>> ?+ return -ENOMEM;
>> ?+
>> ?+ drm = &priv->drm;
>> ?+ dev_set_drvdata(dev, drm);
>> ?+
>> ?+ /* get the resources */
>> ?+ priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
>> ?+
>> ?+ res = platform_get_resource(to_platform_device(dev),
>> ?+ IORESOURCE_MEM, 0);
>> ?+ if (!res) {
>> ?+ dev_err(dev, "failed to get memory resource\n");
>> ?+ ret = -EINVAL;
>> ?+ goto out1;
>> ?+ }
>> ?+
>> ?+ priv->mmio = devm_ioremap_resource(dev, res);
>> ?+ if (IS_ERR(priv->mmio)) {
>> ?+ ret = PTR_ERR(priv->mmio);
>> ?+ dev_err(dev, "failed to map registers %d\n", ret);
>> ?+ goto out1;
>> ?+ }
>> ?+
>> ?+ priv->gate = devm_clk_get(dev, "bus");
>> ?+ if (IS_ERR(priv->gate)) {
>> ?+ ret = PTR_ERR(priv->gate);
>> ?+ dev_err(dev, "bus gate err %d\n", ret);
>> ?+ goto out1;
>> ?+ }
>> ?+
>> ?+ priv->clk = devm_clk_get(dev, "clock");
>> ?+ if (IS_ERR(priv->clk)) {
>> ?+ ret = PTR_ERR(priv->clk);
>> ?+ dev_err(dev, "clock err %d\n", ret);
>> ?+ goto out1;
>> ?+ }
>> ?+
>> ?+ priv->reset = devm_reset_control_get(dev, NULL);
>> ?+ if (IS_ERR(priv->reset)) {
>> ?+ ret = PTR_ERR(priv->reset);
>> ?+ dev_err(dev, "reset err %d\n", ret);
>> ?+ goto out1;
>> ?+ }
>> ?+
>> ?+ mutex_init(&priv->mutex); /* protect DE I/O accesses */
>> ?+
>> ?+ ret = drm_dev_init(drm, &de2_drm_driver, dev);
>> ?+ if (ret != 0) {
>> ?+ dev_err(dev, "dev_init failed %d\n", ret);
>> ?+ goto out1;
>> ?+ }
>> ?+
>> ?+ 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;
>> ?+
>> ?+ /* start the subdevices */
>> ?+ ret = component_bind_all(dev, drm);
>> ?+ if (ret < 0)
>> ?+ goto out2;
>> ?+
>> ?+ /* initialize and disable vertical blanking on all CRTCs */
>> ?+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
>> ?+ if (ret < 0)
>> ?+ dev_warn(dev, "vblank_init failed %d\n", ret);
>> ?+
>> ?+ for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
>> ?+ lcd = priv->lcds[i];
>> ?+ if (lcd)
>> ?+ de2_vblank_reset(lcd);
>> ?+ }
>> ?+
>> ?+ 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 out3;
>> ?+ }
>> ?+
>> ?+ drm_kms_helper_poll_init(drm);
>> ?+
>> ?+ ret = drm_dev_register(drm, 0);
>> ?+ if (ret < 0)
>> ?+ goto out4;
>> ?+
>> ?+ return 0;
>> ?+
>> ?+out4:
>> ?+ drm_fbdev_cma_fini(priv->fbdev);
>> ?+out3:
>> ?+ component_unbind_all(dev, drm);
>> ?+out2:
>> ?+ drm_dev_unref(drm);
>> ?+out1:
>> ?+ kfree(priv);
>> ?+ return ret;
>> ?+}
>> ?+
>> ?+static void de2_drm_unbind(struct device *dev)
>> ?+{
>> ?+ struct drm_device *drm = dev_get_drvdata(dev);
>> ?+ struct priv *priv = drm_to_priv(drm);
>> ?+
>> ?+ drm_dev_unregister(drm);
>> ?+
>> ?+ drm_fbdev_cma_fini(priv->fbdev);
>> ?+ drm_kms_helper_poll_fini(drm);
>> ?+ drm_vblank_cleanup(drm);
>> ?+ drm_mode_config_cleanup(drm);
>> ?+
>> ?+ component_unbind_all(dev, drm);
>> ?+
>> ?+ kfree(priv);
>> ?+}
>> ?+
>> ?+static const struct component_master_ops de2_drm_comp_ops = {
>> ?+ .bind = de2_drm_bind,
>> ?+ .unbind = de2_drm_unbind,
>> ?+};
>> ?+
>> ?+/*
>> ?+ * drm_of_component_probe() does:
>> ?+ * - bind of the ports (lcd-controller.port)
>> ?+ * - bind of the remote nodes (hdmi, tve..)
>> ?+ */
>> ?+static int compare_of(struct device *dev, void *data)
>> ?+{
>> ?+ struct device_node *np = data;
>> ?+
>> ?+ if (of_node_cmp(np->name, "port") == 0) {
>> ?+ np = of_get_parent(np);
>> ?+ of_node_put(np);
>> ?+ }
>> ?+ return dev->of_node == np;
>> ?+}
>> ?+
>> ?+static int de2_drm_probe(struct platform_device *pdev)
>> ?+{
>> ?+ int ret;
>> ?+
>> ?+ ret = drm_of_component_probe(&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,
>> ?+ .of_match_table = de2_drm_of_match,
>> ?+ },
>> ?+};
>> ?+
>> ?+static int __init de2_drm_init(void)
>> ?+{
>> ?+ int ret;
>> ?+
>> ?+ 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/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
>> ?new file mode 100644
>> ?index 0000000..c42c30a
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/de2_drv.h
>> ?@@ -0,0 +1,48 @@
>> ?+#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 <drm/drmP.h>
>> ?+#include <linux/clk.h>
>> ?+#include <linux/reset.h>
>> ?+
>> ?+struct drm_fbdev_cma;
>> ?+struct lcd;
>> ?+
>> ?+#define N_LCDS 2
>> ?+
>> ?+struct priv {
>> ?+ struct drm_device drm;
>> ?+ void __iomem *mmio;
>> ?+ struct clk *clk;
>> ?+ struct clk *gate;
>> ?+ struct reset_control *reset;
>> ?+
>> ?+ struct mutex mutex; /* protect DE I/O access */
>> ?+ u8 soc_type;
>> ?+#define SOC_A83T 0
>> ?+#define SOC_H3 1
>> ?+ u8 started; /* bitmap of started mixers */
>> ?+ u8 clean; /* bitmap of clean mixers */
>> ?+
>> ?+ struct drm_fbdev_cma *fbdev;
>> ?+
>> ?+ struct lcd *lcds[N_LCDS]; /* CRTCs */
>> ?+};
>> ?+
>> ?+#define drm_to_priv(x) container_of(x, struct priv, drm)
>> ?+
>> ?+/* in de2_crtc.c */
>> ?+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
>> ?+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
>> ?+void de2_vblank_reset(struct lcd *lcd);
>> ?+extern struct platform_driver de2_lcd_platform_driver;
>> ?+
>> ?+#endif /* __DE2_DRM_H__ */
>> ?diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
>> ?new file mode 100644
>> ?index 0000000..2fd72dc
>> ?--- /dev/null
>> ?+++ b/drivers/gpu/drm/sun8i/de2_plane.c
>> ?@@ -0,0 +1,734 @@
>> ?+/*
>> ?+ * Allwinner DRM driver - Display Engine 2
>> ?+ *
>> ?+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
>> ?+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
>> ?+ * 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 <linux/io.h>
>> ?+#include <drm/drm_atomic_helper.h>
>> ?+#include <drm/drm_crtc_helper.h>
>> ?+#include <drm/drm_fb_cma_helper.h>
>> ?+#include <drm/drm_gem_cma_helper.h>
>> ?+#include <drm/drm_plane_helper.h>
>> ?+
>> ?+#include "de2_drv.h"
>> ?+#include "de2_crtc.h"
>> ?+
>> ?+/* DE2 I/O map */
>> ?+
>> ?+#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */
>> ?+#define DE2_GATE_REG 0x0004
>> ?+#define DE2_RESET_REG 0x0008
>> ?+#define DE2_DIV_REG 0x000c /* 4 bits per LCD */
>> ?+#define DE2_SEL_REG 0x0010
>> ?+
>> ?+#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */
>> ?+#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */
>> ?+
>> ?+/* mixer registers (addr / mixer base) */
>> ?+#define MIXER_GLB_REGS 0x00000 /* global control */
>> ?+#define MIXER_BLD_REGS 0x01000 /* alpha blending */
>> ?+#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */
>> ?+#define MIXER_CHAN_SZ 0x1000 /* size of a channel */
>> ?+#define MIXER_VSU_REGS 0x20000 /* VSU */
>> ?+#define MIXER_GSU1_REGS 0x30000 /* GSUs */
>> ?+#define MIXER_GSU2_REGS 0x40000
>> ?+#define MIXER_GSU3_REGS 0x50000
>> ?+#define MIXER_FCE_REGS 0xa0000 /* FCE */
>> ?+#define MIXER_BWS_REGS 0xa2000 /* BWS */
>> ?+#define MIXER_LTI_REGS 0xa4000 /* LTI */
>> ?+#define MIXER_PEAK_REGS 0xa6000 /* PEAK */
>> ?+#define MIXER_ASE_REGS 0xa8000 /* ASE */
>> ?+#define MIXER_FCC_REGS 0xaa000 /* FCC */
>> ?+#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */
>> ?+
>> ?+/* global control */
>> ?+#define MIXER_GLB_CTL_REG 0x00
>> ?+#define MIXER_GLB_CTL_rt_en BIT(0)
>> ?+#define MIXER_GLB_CTL_finish_irq_en BIT(4)
>> ?+#define MIXER_GLB_CTL_rtwb_port BIT(12)
>> ?+#define MIXER_GLB_STATUS_REG 0x04
>> ?+#define MIXER_GLB_DBUFF_REG 0x08
>> ?+#define MIXER_GLB_SIZE_REG 0x0c
>> ?+
>> ?+/* alpha blending */
>> ?+#define MIXER_BLD_FCOLOR_CTL_REG 0x00
>> ?+#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe))
>> ?+#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */
>> ?+#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */
>> ?+#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x))
>> ?+#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x))
>> ?+#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x))
>> ?+#define MIXER_BLD_ROUTE_REG 0x80
>> ?+#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
>> ?+#define MIXER_BLD_PREMULTIPLY_REG 0x84
>> ?+#define MIXER_BLD_BKCOLOR_REG 0x88
>> ?+#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c
>> ?+#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */
>> ?+#define MIXER_BLD_MODE_SRCOVER 0x03010301
>> ?+#define MIXER_BLD_OUT_CTL_REG 0xfc
>> ?+
>> ?+/* VI channel (channel 0) */
>> ?+#define VI_CFG_N 4 /* number of layers */
>> ?+#define VI_CFG_SIZE 0x30 /* size of a layer */
>> ?+#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l))
>> ?+#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)
>> ?+#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l))
>> ?+#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l))
>> ?+#define VI_N_PLANES 3
>> ?+#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p))
>> ?+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
>> ?+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
>> ?+#define VI_FCOLORx(l) (0xc0 + 4 * (l))
>> ?+#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p))
>> ?+#define VI_BOT_HADDRx(p) (0xdc + 4 * (p))
>> ?+#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n))
>> ?+#define VI_HORI_DSx(n) (0xf0 + 4 * (n))
>> ?+#define VI_VERT_DSx(n) (0xf8 + 4 * (n))
>> ?+#define VI_SIZE 0x100
>> ?+
>> ?+/* UI channel (channels 1..3) */
>> ?+#define UI_CFG_N 4 /* number of layers */
>> ?+#define UI_CFG_SIZE (8 * 4) /* size of a layer */
>> ?+#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l))
>> ?+#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)
>> ?+#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l))
>> ?+#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l))
>> ?+#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l))
>> ?+#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l))
>> ?+#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l))
>> ?+#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l))
>> ?+#define UI_TOP_HADDR 0x80
>> ?+#define UI_BOT_HADDR 0x84
>> ?+#define UI_OVL_SIZE 0x88
>> ?+#define UI_SIZE 0x8c
>> ?+
>> ?+/* 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 /* YVYU */
>> ?+#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */
>> ?+#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */
>> ?+#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
>> ?+#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
>> ?+
>> ?+/* 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,
>> ?+};
>> ?+
>> ?+/*
>> ?+ * plane table
>> ?+ *
>> ?+ * The chosen channel/layer assignment of the planes respects
>> ?+ * the following constraints:
>> ?+ * - the cursor must be in a channel higher than the primary channel
>> ?+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
>> ?+ */
>> ?+static const struct {
>> ?+ u8 chan;
>> ?+ u8 layer;
>> ?+ u8 pipe;
>> ?+ u8 type; /* plane type */
>> ?+ const uint32_t *formats;
>> ?+ u8 n_formats;
>> ?+} plane_tb[] = {
>> ?+ [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */
>> ?+ 0, 0, 0,
>> ?+ DRM_PLANE_TYPE_PRIMARY,
>> ?+ ui_formats, ARRAY_SIZE(ui_formats),
>> ?+ },
>> ?+ [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */
>> ?+ 1, 0, 1,
>> ?+ DRM_PLANE_TYPE_CURSOR,
>> ?+ ui_formats, ARRAY_SIZE(ui_formats),
>> ?+ },
>> ?+ {
>> ?+ 0, 1, 0, /* 1st overlay: channel 0, layer 1 */
>> ?+ DRM_PLANE_TYPE_OVERLAY,
>> ?+ vi_formats, ARRAY_SIZE(vi_formats),
>> ?+ },
>> ?+ {
>> ?+ 0, 2, 0, /* 2nd overlay: channel 0, layer 2 */
>> ?+ DRM_PLANE_TYPE_OVERLAY,
>> ?+ vi_formats, ARRAY_SIZE(vi_formats),
>> ?+ },
>> ?+ {
>> ?+ 0, 3, 0, /* 3rd overlay: channel 0, layer 3 */
>> ?+ DRM_PLANE_TYPE_OVERLAY,
>> ?+ vi_formats, ARRAY_SIZE(vi_formats),
>> ?+ },
>> ?+};
>> ?+
>> ?+static inline void andl_relaxed(void __iomem *addr, u32 val)
>> ?+{
>> ?+ writel_relaxed(readl_relaxed(addr) & val, addr);
>> ?+}
>> ?+
>> ?+static inline void orl_relaxed(void __iomem *addr, u32 val)
>> ?+{
>> ?+ writel_relaxed(readl_relaxed(addr) | val, addr);
>> ?+}
>> ?+
>> ?+/* alert the DE processor about changes in a mixer configuration */
>> ?+static void de2_mixer_select(struct priv *priv,
>> ?+ int mixer,
>> ?+ void __iomem *mixer_io)
>> ?+{
>> ?+ /* select the mixer */
>> ?+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
>> ?+
>> ?+ /* double register switch */
>> ?+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
>> ?+}
>> ?+
>> ?+/*
>> ?+ * cleanup a mixer
>> ?+ *
>> ?+ * This is needed only once after power on.
>> ?+ */
>> ?+static void de2_mixer_cleanup(struct priv *priv, int mixer,
>> ?+ u32 size)
>> ?+{
>> ?+ void __iomem *mixer_io = priv->mmio;
>> ?+ void __iomem *chan_io;
>> ?+ u32 data;
>> ?+ unsigned int i;
>> ?+
>> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>> ?+ chan_io = mixer_io + MIXER_CHAN_REGS;
>> ?+
>> ?+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
>> ?+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
>> ?+
>> ?+ writel_relaxed(MIXER_GLB_CTL_rt_en,
>> ?+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
>> ?+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
>> ?+
>> ?+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
>> ?+
>> ?+ /*
>> ?+ * clear the VI/UI channels
>> ?+ * LCD0: 1 VI and 3 UIs
>> ?+ * LCD1: 1 VI and 1 UI
>> ?+ */
>> ?+ memset_io(chan_io, 0, VI_SIZE);
>> ?+ memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
>> ?+ if (mixer == 0) {
>> ?+ memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
>> ?+ memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
>> ?+ }
>> ?+
>> ?+ /* alpha blending */
>> ?+ writel_relaxed(0x00000001 | /* fcolor for primary */
>> ?+ MIXER_BLD_FCOLOR_CTL_PEN(0),
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
>> ?+ for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
>> ?+ writel_relaxed(0xff000000,
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
>> ?+ writel_relaxed(size,
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
>> ?+ writel_relaxed(0,
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
>> ?+ }
>> ?+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
>> ?+
>> ?+ /* prepare the pipe route for the planes */
>> ?+ data = 0;
>> ?+ for (i = 0; i < DE2_N_PLANES; i++)
>> ?+ data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
>> ?+ writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
>> ?+
>> ?+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_PREMULTIPLY_REG);
>> ?+ writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_BKCOLOR_REG);
>> ?+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_OUTPUT_SIZE_REG);
>> ?+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
>> ?+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
>> ?+
>> ?+ /* disable the enhancements */
>> ?+ writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
>> ?+ writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
>> ?+}
>> ?+
>> ?+/* enable a mixer */
>> ?+static void de2_mixer_enable(struct lcd *lcd)
>> ?+{
>> ?+ struct priv *priv = lcd->priv;
>> ?+ void __iomem *mixer_io = priv->mmio;
>> ?+ struct drm_display_mode *mode = &lcd->crtc.mode;
>> ?+ u32 size = WH(mode->hdisplay, mode->vdisplay);
>> ?+ u32 data;
>> ?+ int mixer = lcd->mixer;
>> ?+ int i;
>> ?+
>> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>> ?+
>> ?+ /* if not done yet, start the DE processor */
>> ?+ if (!priv->started) {
>> ?+ reset_control_deassert(priv->reset);
>> ?+ clk_prepare_enable(priv->gate);
>> ?+ clk_prepare_enable(priv->clk);
>> ?+ }
>> ?+ priv->started |= 1 << mixer;
>> ?+
>> ?+ /* set the A83T clock divider (500 / 2) = 250MHz */
>> ?+ if (priv->soc_type == SOC_A83T)
>> ?+ writel_relaxed(0x00000011, /* div = 2 for both LCDs */
>> ?+ priv->mmio + DE2_DIV_REG);
>> ?+
>> ?+ /* deassert the mixer and enable its clock */
>> ?+ orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
>> ?+ data = 1 << mixer; /* 1 bit / lcd */
>> ?+ orl_relaxed(priv->mmio + DE2_GATE_REG, data);
>> ?+ orl_relaxed(priv->mmio + DE2_MOD_REG, data);
>> ?+
>> ?+ /* if not done yet, cleanup and enable */
>> ?+ if (!(priv->clean & (1 << mixer))) {
>> ?+ priv->clean |= 1 << mixer;
>> ?+ de2_mixer_cleanup(priv, mixer, size);
>> ?+ return;
>> ?+ }
>> ?+
>> ?+ /* enable */
>> ?+ de2_mixer_select(priv, mixer, mixer_io);
>> ?+
>> ?+ writel_relaxed(MIXER_GLB_CTL_rt_en,
>> ?+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
>> ?+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
>> ?+
>> ?+ /* set the size of the frame buffer */
>> ?+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
>> ?+ for (i = 0; i < MIXER_BLD_ATTR_N; i++)
>> ?+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_ATTRx_INSIZE(i));
>> ?+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_OUTPUT_SIZE_REG);
>> ?+
>> ?+ writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
>> ?+}
>> ?+
>> ?+/* enable a LCD (DE mixer) */
>> ?+void de2_de_enable(struct lcd *lcd)
>> ?+{
>> ?+ mutex_lock(&lcd->priv->mutex);
>> ?+
>> ?+ de2_mixer_enable(lcd);
>> ?+
>> ?+ mutex_unlock(&lcd->priv->mutex);
>> ?+}
>> ?+
>> ?+/* disable a LCD (DE mixer) */
>> ?+void de2_de_disable(struct lcd *lcd)
>> ?+{
>> ?+ struct priv *priv = lcd->priv;
>> ?+ void __iomem *mixer_io = priv->mmio;
>> ?+ int mixer = lcd->mixer;
>> ?+ u32 data;
>> ?+
>> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>> ?+
>> ?+ mutex_lock(&priv->mutex);
>> ?+
>> ?+ de2_mixer_select(priv, mixer, mixer_io);
>> ?+
>> ?+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
>> ?+
>> ?+ data = ~(1 << mixer);
>> ?+ andl_relaxed(priv->mmio + DE2_MOD_REG, data);
>> ?+ andl_relaxed(priv->mmio + DE2_GATE_REG, data);
>> ?+ andl_relaxed(priv->mmio + DE2_RESET_REG, data);
>> ?+
>> ?+ mutex_unlock(&priv->mutex);
>> ?+
>> ?+ /* if all mixers are disabled, stop the DE */
>> ?+ priv->started &= ~(1 << mixer);
>> ?+ if (!priv->started) {
>> ?+ clk_disable_unprepare(priv->clk);
>> ?+ clk_disable_unprepare(priv->gate);
>> ?+ reset_control_assert(priv->reset);
>> ?+ }
>> ?+}
>> ?+
>> ?+static void de2_vi_update(void __iomem *chan_io,
>> ?+ struct drm_gem_cma_object *gem,
>> ?+ int layer,
>> ?+ unsigned int fmt,
>> ?+ u32 ui_sel,
>> ?+ u32 size,
>> ?+ u32 coord,
>> ?+ struct drm_framebuffer *fb,
>> ?+ u32 screen_size)
>> ?+{
>> ?+ int i;
>> ?+
>> ?+ writel_relaxed(VI_CFG_ATTR_en |
>> ?+ (fmt << VI_CFG_ATTR_fmt_SHIFT) |
>> ?+ ui_sel,
>> ?+ chan_io + VI_CFGx_ATTR(layer));
>> ?+ writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
>> ?+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
>> ?+ for (i = 0; i < VI_N_PLANES; i++) {
>> ?+ writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
>> ?+ fb->pitches[0],
>> ?+ chan_io + VI_CFGx_PITCHy(layer, i));
>> ?+ writel_relaxed(gem->paddr + fb->offsets[i],
>> ?+ chan_io + VI_CFGx_TOP_LADDRy(layer, i));
>> ?+ }
>> ?+ writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
>> ?+ if (layer == 0) {
>> ?+ writel_relaxed(screen_size,
>> ?+ chan_io + VI_OVL_SIZEx(0));
>> ?+ }
>> ?+}
>> ?+
>> ?+static void de2_ui_update(void __iomem *chan_io,
>> ?+ struct drm_gem_cma_object *gem,
>> ?+ int layer,
>> ?+ unsigned int fmt,
>> ?+ u32 alpha_glob,
>> ?+ u32 size,
>> ?+ u32 coord,
>> ?+ struct drm_framebuffer *fb,
>> ?+ u32 screen_size)
>> ?+{
>> ?+ writel_relaxed(UI_CFG_ATTR_en |
>> ?+ (fmt << UI_CFG_ATTR_fmt_SHIFT) |
>> ?+ alpha_glob,
>> ?+ chan_io + UI_CFGx_ATTR(layer));
>> ?+ writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
>> ?+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
>> ?+ writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
>> ?+ writel_relaxed(gem->paddr + fb->offsets[0],
>> ?+ chan_io + UI_CFGx_TOP_LADDR(layer));
>> ?+ if (layer == 0)
>> ?+ writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
>> ?+}
>> ?+
>> ?+static void de2_plane_update(struct priv *priv, struct lcd *lcd,
>> ?+ int plane_num,
>> ?+ struct drm_plane_state *state,
>> ?+ struct drm_plane_state *old_state)
>> ?+{
>> ?+ void __iomem *mixer_io = priv->mmio;
>> ?+ void __iomem *chan_io;
>> ?+ struct drm_framebuffer *fb = state->fb;
>> ?+ struct drm_gem_cma_object *gem;
>> ?+ u32 size = WH(state->crtc_w, state->crtc_h);
>> ?+ u32 coord, screen_size;
>> ?+ u32 fcolor;
>> ?+ u32 ui_sel, alpha_glob;
>> ?+ int mixer = lcd->mixer;
>> ?+ int chan, layer, x, y;
>> ?+ unsigned int fmt;
>> ?+
>> ?+ chan = plane_tb[plane_num].chan;
>> ?+ layer = plane_tb[plane_num].layer;
>> ?+
>> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>> ?+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_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);
>> ?+
>> ?+ /* if plane update was delayed, force a full update */
>> ?+ if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
>> ?+ (1 << plane_num)) {
>> ?+ priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
>> ?+ ~(1 << plane_num);
>> ?+
>> ?+ /* handle plane move */
>> ?+ } else if (fb == old_state->fb) {
>> ?+ de2_mixer_select(priv, mixer, mixer_io);
>> ?+ if (chan == 0)
>> ?+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
>> ?+ else
>> ?+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
>> ?+ 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("de2_plane_update: format %.4s not yet treated\n",
>> ?+ (char *) &fb->pixel_format);
>> ?+ return;
>> ?+ }
>> ?+
>> ?+ /* the overlay size is the one of the primary plane */
>> ?+ screen_size = plane_num == DE2_PRIMARY_PLANE ?
>> ?+ size :
>> ?+ readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
>> ?+
>> ?+ /* prepare pipe enable */
>> ?+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_FCOLOR_CTL_REG);
>> ?+ fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
>> ?+
>> ?+ de2_mixer_select(priv, mixer, mixer_io);
>> ?+
>> ?+ if (chan == 0) /* VI channel */
>> ?+ de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
>> ?+ fb, screen_size);
>> ?+ else /* UI channel */
>> ?+ de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
>> ?+ fb, screen_size);
>> ?+ writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_FCOLOR_CTL_REG);
>> ?+}
>> ?+
>> ?+static int vi_nb_layers(void __iomem *chan_io)
>> ?+{
>> ?+ int layer, n = 0;
>> ?+
>> ?+ for (layer = 0; layer < 4; layer++) {
>> ?+ if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
>> ?+ n++;
>> ?+ }
>> ?+
>> ?+ return n;
>> ?+}
>> ?+
>> ?+static int ui_nb_layers(void __iomem *chan_io)
>> ?+{
>> ?+ int layer, n = 0;
>> ?+
>> ?+ for (layer = 0; layer < 4; layer++) {
>> ?+ if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
>> ?+ n++;
>> ?+ }
>> ?+
>> ?+ return n;
>> ?+}
>> ?+
>> ?+static void de2_plane_disable(struct priv *priv,
>> ?+ int mixer, int plane_num)
>> ?+{
>> ?+ void __iomem *mixer_io = priv->mmio;
>> ?+ void __iomem *chan_io;
>> ?+ u32 fcolor;
>> ?+ int chan, layer, n;
>> ?+
>> ?+ chan = plane_tb[plane_num].chan;
>> ?+ layer = plane_tb[plane_num].layer;
>> ?+
>> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
>> ?+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
>> ?+
>> ?+ if (chan == 0)
>> ?+ n = vi_nb_layers(chan_io);
>> ?+ else
>> ?+ n = ui_nb_layers(chan_io);
>> ?+
>> ?+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
>> ?+ MIXER_BLD_FCOLOR_CTL_REG);
>> ?+
>> ?+ de2_mixer_select(priv, mixer, mixer_io);
>> ?+
>> ?+ if (chan == 0)
>> ?+ writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
>> ?+ else
>> ?+ writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
>> ?+
>> ?+ /* disable the pipe if no more active layer */
>> ?+ if (n <= 1)
>> ?+ writel_relaxed(fcolor &
>> ?+ ~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
>> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
>> ?+}
>> ?+
>> ?+static void de2_drm_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 priv *priv = lcd->priv;
>> ?+ int plane_num = plane - lcd->planes;
>> ?+
>> ?+ /* if the crtc is disabled, mark update delayed */
>> ?+ if (!(priv->started & (1 << lcd->mixer))) {
>> ?+ lcd->delayed |= 1 << plane_num;
>> ?+ return; /* mixer disabled */
>> ?+ }
>> ?+
>> ?+ mutex_lock(&priv->mutex);
>> ?+
>> ?+ de2_plane_update(priv, lcd, plane_num, state, old_state);
>> ?+
>> ?+ mutex_unlock(&priv->mutex);
>> ?+}
>> ?+
>> ?+static void de2_drm_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);
>> ?+ struct priv *priv = lcd->priv;
>> ?+ int plane_num = plane - lcd->planes;
>> ?+
>> ?+ if (!(priv->started & (1 << lcd->mixer)))
>> ?+ return; /* mixer disabled */
>> ?+
>> ?+ mutex_lock(&priv->mutex);
>> ?+
>> ?+ de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
>> ?+
>> ?+ mutex_unlock(&priv->mutex);
>> ?+}
>> ?+
>> ?+static const struct drm_plane_helper_funcs plane_helper_funcs = {
>> ?+ .atomic_update = de2_drm_plane_update,
>> ?+ .atomic_disable = de2_drm_plane_disable,
>> ?+};
>> ?+
>> ?+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 possible_crtcs,
>> ?+ int plane_num)
>> ?+{
>> ?+ int ret;
>> ?+
>> ?+ ret = drm_universal_plane_init(drm, plane, possible_crtcs,
>> ?+ &plane_funcs,
>> ?+ plane_tb[plane_num].formats,
>> ?+ plane_tb[plane_num].n_formats,
>> ?+ plane_tb[plane_num].type, NULL);
>> ?+ if (ret >= 0)
>> ?+ drm_plane_helper_add(plane, &plane_helper_funcs);
>> ?+
>> ?+ return ret;
>> ?+}
>> ?+
>> ?+/* initialize the planes */
>> ?+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
>> ?+{
>> ?+ int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
>> ?+
>> ?+ n = ARRAY_SIZE(plane_tb);
>> ?+ if (n != DE2_N_PLANES) {
>> ?+ dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
>> ?+ __stringify(DE2_N_PLANES) "\n", n);
>> ?+ return -EINVAL;
>> ?+ }
>> ?+
>> ?+ for (i = 0; i < n; i++) {
>> ?+ ret = de2_one_plane_init(drm, &lcd->planes[i],
>> ?+ possible_crtcs, i);
>> ?+ if (ret < 0) {
>> ?+ dev_err(lcd->dev, "plane init failed %d\n", ret);
>> ?+ break;
>> ?+ }
>> ?+ }
>> ?+
>> ?+ return ret;
>> ?+}
>> ?--
>> ?2.10.2
>
>> ?_______________________________________________
>> ?dri-devel mailing list
>> ?dri-devel at lists.freedesktop.org
>> ?https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

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

* Re: [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of Allwinner DE2
  2016-11-28 18:02     ` Jean-Francois Moine
@ 2016-11-29 18:41         ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 18:41 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Jean-Francois Moine, Dave Airlie, Maxime Ripard, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

Thank you for the patch.

On Monday 28 Nov 2016 19:02:39 Jean-Francois Moine wrote:
> Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> ---
>  .../bindings/display/sunxi/sun8i-de2.txt           | 121 ++++++++++++++++++
>  1 file changed, 121 insertions(+)
>  create mode 100644
> Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt new file
> mode 100644
> index 0000000..edf38b8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> @@ -0,0 +1,121 @@
> +Allwinner sun8i Display Engine 2 subsystem
> +==========================================
> +
> +The Allwinner DE2 subsystem contains a display controller (DE2),
> +one or two LCD controllers (Timing CONtrollers) and their external
> +interfaces (encoders/connectors).
> +
> +          +-----------+
> +          | DE2       |
> +          |           |
> +          | +-------+ |
> +  plane --->|       | |   +------+
> +          | | mixer |---->| TCON |---> encoder  ---> display
> +  plane --->|       | |   +------+     connector     device
> +          | +-------+ |
> +          |           |
> +          | +-------+ |
> +  plane --->|       | |   +------+
> +          | | mixer |---->| TCON |---> encoder  ---> display
> +  plane --->|       | |   +------+     connector     device
> +          | +-------+ |
> +          +-----------+
> +
> +The DE2 contains a processor which mixes the input planes and creates
> +the images which are sent to the TCONs.
> +From the software point of vue, there are 2 independent real-time
> +mixers, each one being statically associated to one TCON.
> +
> +The TCONs adjust the image format to the one of the display device.
> +
> +Display controller (DE2)
> +========================
> +
> +Required properties:
> +
> +- compatible: value should be one of the following
> +		"allwinner,sun8i-a83t-display-engine"
> +		"allwinner,sun8i-h3-display-engine"
> +
> +- reg: base address and size of the I/O memory
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> +		clock-names property.
> +
> +- clock-names: must contain
> +		"bus": bus gate
> +		"clock": clock
> +
> +- resets: phandle to the reset of the device
> +
> +- ports: must contain a list of 2 phandles, indexed by mixer number,
> +	and pointing to display interface ports of TCONs
> +
> +LCD controller (TCON)
> +=====================
> +
> +Required properties:
> +
> +- compatible: should be
> +		"allwinner,sun8i-a83t-tcon"
> +
> +- reg: base address and size of the I/O memory
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> +		clock-names property.
> +
> +- clock-names: must contain
> +		"bus": bus gate
> +		"clock": pixel clock
> +
> +- resets: phandle to the reset of the device
> +
> +- interrupts: interrupt number for the TCON
> +
> +- port: port node with endpoint definitions as defined in
> +	Documentation/devicetree/bindings/media/video-interfaces.txt
> +
> +Example:
> +
> +	de: de-controller@01000000 {
> +		compatible = "allwinner,sun8i-h3-display-engine";
> +		reg = <0x01000000 0x400000>;
> +		clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> +		clock-names = "bus", "clock";
> +		resets = <&ccu RST_BUS_DE>;
> +		ports = <&tcon0_p>, <&tcon1_p>;

This isn't how the OF graph DT bindings are used. You should instead have

	ports {
		#address-cells = <1>;
		#size-cells = <0>;
		port@0 {
			de_out_0: endpoint {
				remote_endpoint = <&tcon0_hdmi>;
			};
		};
		port@1 {
			/* No endpoint as the port is not connected */
		};
	};

> +	};
> +
> +	tcon0: lcd-controller@01c0c000 {
> +		compatible = "allwinner,sun8i-a83t-tcon";
> +		reg = <0x01c0c000 0x400>;
> +		clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> +		clock-names = "bus", "clock";
> +		resets = <&ccu RST_BUS_TCON0>;
> +		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		tcon0_p: port {
> +			tcon0_hdmi: endpoint {
> +				remote-endpoint = <&hdmi_tcon0>;
> +			};
> +		};

and here

	port {
		tcon0_hdmi: endpoint {
			remote-endpoint = <&de_out_0>;
		};
	};

> +	};
> +
> +	/* not used */
> +	tcon1: lcd-controller@01c0d000 {
> +		compatible = "allwinner,sun8i-h3-tcon";
> +		reg = <0x01c0d000 0x400>;
> +		clocks = <&ccu CLK_BUS_TCON1>,
> +			 <&ccu CLK_TCON0>;	/* no clock */
> +		clock-names = "bus", "clock";
> +		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
> +		status = "disabled";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		tcon1_p: port {
> +			endpoint {
> +				/* empty */
> +			};
> +		};

and here

	port {
		/* No endpoint as the port is not connected */
	};

(although I'm not sure why you don't connect it)

> +	};

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of Allwinner DE2
@ 2016-11-29 18:41         ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 18:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

Thank you for the patch.

On Monday 28 Nov 2016 19:02:39 Jean-Francois Moine wrote:
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
>  .../bindings/display/sunxi/sun8i-de2.txt           | 121 ++++++++++++++++++
>  1 file changed, 121 insertions(+)
>  create mode 100644
> Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt new file
> mode 100644
> index 0000000..edf38b8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> @@ -0,0 +1,121 @@
> +Allwinner sun8i Display Engine 2 subsystem
> +==========================================
> +
> +The Allwinner DE2 subsystem contains a display controller (DE2),
> +one or two LCD controllers (Timing CONtrollers) and their external
> +interfaces (encoders/connectors).
> +
> +          +-----------+
> +          | DE2       |
> +          |           |
> +          | +-------+ |
> +  plane --->|       | |   +------+
> +          | | mixer |---->| TCON |---> encoder  ---> display
> +  plane --->|       | |   +------+     connector     device
> +          | +-------+ |
> +          |           |
> +          | +-------+ |
> +  plane --->|       | |   +------+
> +          | | mixer |---->| TCON |---> encoder  ---> display
> +  plane --->|       | |   +------+     connector     device
> +          | +-------+ |
> +          +-----------+
> +
> +The DE2 contains a processor which mixes the input planes and creates
> +the images which are sent to the TCONs.
> +From the software point of vue, there are 2 independent real-time
> +mixers, each one being statically associated to one TCON.
> +
> +The TCONs adjust the image format to the one of the display device.
> +
> +Display controller (DE2)
> +========================
> +
> +Required properties:
> +
> +- compatible: value should be one of the following
> +		"allwinner,sun8i-a83t-display-engine"
> +		"allwinner,sun8i-h3-display-engine"
> +
> +- reg: base address and size of the I/O memory
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> +		clock-names property.
> +
> +- clock-names: must contain
> +		"bus": bus gate
> +		"clock": clock
> +
> +- resets: phandle to the reset of the device
> +
> +- ports: must contain a list of 2 phandles, indexed by mixer number,
> +	and pointing to display interface ports of TCONs
> +
> +LCD controller (TCON)
> +=====================
> +
> +Required properties:
> +
> +- compatible: should be
> +		"allwinner,sun8i-a83t-tcon"
> +
> +- reg: base address and size of the I/O memory
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> +		clock-names property.
> +
> +- clock-names: must contain
> +		"bus": bus gate
> +		"clock": pixel clock
> +
> +- resets: phandle to the reset of the device
> +
> +- interrupts: interrupt number for the TCON
> +
> +- 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";
> +		reg = <0x01000000 0x400000>;
> +		clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> +		clock-names = "bus", "clock";
> +		resets = <&ccu RST_BUS_DE>;
> +		ports = <&tcon0_p>, <&tcon1_p>;

This isn't how the OF graph DT bindings are used. You should instead have

	ports {
		#address-cells = <1>;
		#size-cells = <0>;
		port at 0 {
			de_out_0: endpoint {
				remote_endpoint = <&tcon0_hdmi>;
			};
		};
		port at 1 {
			/* No endpoint as the port is not connected */
		};
	};

> +	};
> +
> +	tcon0: lcd-controller at 01c0c000 {
> +		compatible = "allwinner,sun8i-a83t-tcon";
> +		reg = <0x01c0c000 0x400>;
> +		clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> +		clock-names = "bus", "clock";
> +		resets = <&ccu RST_BUS_TCON0>;
> +		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		tcon0_p: port {
> +			tcon0_hdmi: endpoint {
> +				remote-endpoint = <&hdmi_tcon0>;
> +			};
> +		};

and here

	port {
		tcon0_hdmi: endpoint {
			remote-endpoint = <&de_out_0>;
		};
	};

> +	};
> +
> +	/* not used */
> +	tcon1: lcd-controller at 01c0d000 {
> +		compatible = "allwinner,sun8i-h3-tcon";
> +		reg = <0x01c0d000 0x400>;
> +		clocks = <&ccu CLK_BUS_TCON1>,
> +			 <&ccu CLK_TCON0>;	/* no clock */
> +		clock-names = "bus", "clock";
> +		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
> +		status = "disabled";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		tcon1_p: port {
> +			endpoint {
> +				/* empty */
> +			};
> +		};

and here

	port {
		/* No endpoint as the port is not connected */
	};

(although I'm not sure why you don't connect it)

> +	};

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of Allwinner DE2
  2016-11-29 18:41         ` Laurent Pinchart
@ 2016-11-29 18:45           ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 18:45 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring, Maxime Ripard,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

A brief update.

On Tuesday 29 Nov 2016 20:41:30 Laurent Pinchart wrote:
> On Monday 28 Nov 2016 19:02:39 Jean-Francois Moine wrote:
> > Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> > ---
> > 
> >  .../bindings/display/sunxi/sun8i-de2.txt           | 121 ++++++++++++++++
> >  1 file changed, 121 insertions(+)
> >  create mode 100644
> > 
> > Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> > b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt new file
> > mode 100644
> > index 0000000..edf38b8
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> > @@ -0,0 +1,121 @@
> > +Allwinner sun8i Display Engine 2 subsystem
> > +==========================================
> > +
> > +The Allwinner DE2 subsystem contains a display controller (DE2),
> > +one or two LCD controllers (Timing CONtrollers) and their external
> > +interfaces (encoders/connectors).
> > +
> > +          +-----------+
> > +          | DE2       |
> > +          |           |
> > +          | +-------+ |
> > +  plane --->|       | |   +------+
> > +          | | mixer |---->| TCON |---> encoder  ---> display
> > +  plane --->|       | |   +------+     connector     device
> > +          | +-------+ |
> > +          |           |
> > +          | +-------+ |
> > +  plane --->|       | |   +------+
> > +          | | mixer |---->| TCON |---> encoder  ---> display
> > +  plane --->|       | |   +------+     connector     device
> > +          | +-------+ |
> > +          +-----------+
> > +
> > +The DE2 contains a processor which mixes the input planes and creates
> > +the images which are sent to the TCONs.
> > +From the software point of vue, there are 2 independent real-time
> > +mixers, each one being statically associated to one TCON.
> > +
> > +The TCONs adjust the image format to the one of the display device.
> > +
> > +Display controller (DE2)
> > +========================
> > +
> > +Required properties:
> > +
> > +- compatible: value should be one of the following
> > +		"allwinner,sun8i-a83t-display-engine"
> > +		"allwinner,sun8i-h3-display-engine"
> > +
> > +- reg: base address and size of the I/O memory
> > +
> > +- clocks: must include clock specifiers corresponding to entries in the
> > +		clock-names property.
> > +
> > +- clock-names: must contain
> > +		"bus": bus gate
> > +		"clock": clock
> > +
> > +- resets: phandle to the reset of the device
> > +
> > +- ports: must contain a list of 2 phandles, indexed by mixer number,
> > +	and pointing to display interface ports of TCONs
> > +
> > +LCD controller (TCON)
> > +=====================
> > +
> > +Required properties:
> > +
> > +- compatible: should be
> > +		"allwinner,sun8i-a83t-tcon"
> > +
> > +- reg: base address and size of the I/O memory
> > +
> > +- clocks: must include clock specifiers corresponding to entries in the
> > +		clock-names property.
> > +
> > +- clock-names: must contain
> > +		"bus": bus gate
> > +		"clock": pixel clock
> > +
> > +- resets: phandle to the reset of the device
> > +
> > +- interrupts: interrupt number for the TCON
> > +
> > +- port: port node with endpoint definitions as defined in
> > +	Documentation/devicetree/bindings/media/video-interfaces.txt
> > +
> > +Example:
> > +
> > +	de: de-controller@01000000 {
> > +		compatible = "allwinner,sun8i-h3-display-engine";
> > +		reg = <0x01000000 0x400000>;
> > +		clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> > +		clock-names = "bus", "clock";
> > +		resets = <&ccu RST_BUS_DE>;
> > +		ports = <&tcon0_p>, <&tcon1_p>;
> 
> This isn't how the OF graph DT bindings are used. You should instead have
> 
> 	ports {
> 		#address-cells = <1>;
> 		#size-cells = <0>;
> 		port@0 {

I forgot to add reg = <0>; (and similarly for port 1).

> 			de_out_0: endpoint {
> 				remote_endpoint = <&tcon0_hdmi>;
> 			};
> 		};
> 		port@1 {
> 			/* No endpoint as the port is not connected */
> 		};
> 	};
> 
> > +	};
> > +
> > +	tcon0: lcd-controller@01c0c000 {
> > +		compatible = "allwinner,sun8i-a83t-tcon";
> > +		reg = <0x01c0c000 0x400>;
> > +		clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> > +		clock-names = "bus", "clock";
> > +		resets = <&ccu RST_BUS_TCON0>;
> > +		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		tcon0_p: port {
> > +			tcon0_hdmi: endpoint {
> > +				remote-endpoint = <&hdmi_tcon0>;
> > +			};
> > +		};
> 
> and here
> 
> 	port {
> 		tcon0_hdmi: endpoint {
> 			remote-endpoint = <&de_out_0>;
> 		};
> 	};

The TCON has an output, so this should instead be

	port@0 {
		reg = <0>;
 		tcon0_in: endpoint {
 			remote-endpoint = <&de_out_0>;
 		};
	};
	port@1 {
		reg = <1>;
 		tcon1_out: endpoint {
 			remote-endpoint = <&hdmi_in>;
 		};
	};

(the second port requires adding the HDMI encoder to the example)

> > +	};
> > +
> > +	/* not used */
> > +	tcon1: lcd-controller@01c0d000 {
> > +		compatible = "allwinner,sun8i-h3-tcon";
> > +		reg = <0x01c0d000 0x400>;
> > +		clocks = <&ccu CLK_BUS_TCON1>,
> > +			 <&ccu CLK_TCON0>;	/* no clock */
> > +		clock-names = "bus", "clock";
> > +		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
> > +		status = "disabled";
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		tcon1_p: port {
> > +			endpoint {
> > +				/* empty */
> > +			};
> > +		};
> 
> and here
> 
> 	port {
> 		/* No endpoint as the port is not connected */
> 	};
> 
> (although I'm not sure why you don't connect it)
> 
> > +	};

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of Allwinner DE2
@ 2016-11-29 18:45           ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 18:45 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

A brief update.

On Tuesday 29 Nov 2016 20:41:30 Laurent Pinchart wrote:
> On Monday 28 Nov 2016 19:02:39 Jean-Francois Moine wrote:
> > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > ---
> > 
> >  .../bindings/display/sunxi/sun8i-de2.txt           | 121 ++++++++++++++++
> >  1 file changed, 121 insertions(+)
> >  create mode 100644
> > 
> > Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> > b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt new file
> > mode 100644
> > index 0000000..edf38b8
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
> > @@ -0,0 +1,121 @@
> > +Allwinner sun8i Display Engine 2 subsystem
> > +==========================================
> > +
> > +The Allwinner DE2 subsystem contains a display controller (DE2),
> > +one or two LCD controllers (Timing CONtrollers) and their external
> > +interfaces (encoders/connectors).
> > +
> > +          +-----------+
> > +          | DE2       |
> > +          |           |
> > +          | +-------+ |
> > +  plane --->|       | |   +------+
> > +          | | mixer |---->| TCON |---> encoder  ---> display
> > +  plane --->|       | |   +------+     connector     device
> > +          | +-------+ |
> > +          |           |
> > +          | +-------+ |
> > +  plane --->|       | |   +------+
> > +          | | mixer |---->| TCON |---> encoder  ---> display
> > +  plane --->|       | |   +------+     connector     device
> > +          | +-------+ |
> > +          +-----------+
> > +
> > +The DE2 contains a processor which mixes the input planes and creates
> > +the images which are sent to the TCONs.
> > +From the software point of vue, there are 2 independent real-time
> > +mixers, each one being statically associated to one TCON.
> > +
> > +The TCONs adjust the image format to the one of the display device.
> > +
> > +Display controller (DE2)
> > +========================
> > +
> > +Required properties:
> > +
> > +- compatible: value should be one of the following
> > +		"allwinner,sun8i-a83t-display-engine"
> > +		"allwinner,sun8i-h3-display-engine"
> > +
> > +- reg: base address and size of the I/O memory
> > +
> > +- clocks: must include clock specifiers corresponding to entries in the
> > +		clock-names property.
> > +
> > +- clock-names: must contain
> > +		"bus": bus gate
> > +		"clock": clock
> > +
> > +- resets: phandle to the reset of the device
> > +
> > +- ports: must contain a list of 2 phandles, indexed by mixer number,
> > +	and pointing to display interface ports of TCONs
> > +
> > +LCD controller (TCON)
> > +=====================
> > +
> > +Required properties:
> > +
> > +- compatible: should be
> > +		"allwinner,sun8i-a83t-tcon"
> > +
> > +- reg: base address and size of the I/O memory
> > +
> > +- clocks: must include clock specifiers corresponding to entries in the
> > +		clock-names property.
> > +
> > +- clock-names: must contain
> > +		"bus": bus gate
> > +		"clock": pixel clock
> > +
> > +- resets: phandle to the reset of the device
> > +
> > +- interrupts: interrupt number for the TCON
> > +
> > +- 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";
> > +		reg = <0x01000000 0x400000>;
> > +		clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> > +		clock-names = "bus", "clock";
> > +		resets = <&ccu RST_BUS_DE>;
> > +		ports = <&tcon0_p>, <&tcon1_p>;
> 
> This isn't how the OF graph DT bindings are used. You should instead have
> 
> 	ports {
> 		#address-cells = <1>;
> 		#size-cells = <0>;
> 		port at 0 {

I forgot to add reg = <0>; (and similarly for port 1).

> 			de_out_0: endpoint {
> 				remote_endpoint = <&tcon0_hdmi>;
> 			};
> 		};
> 		port at 1 {
> 			/* No endpoint as the port is not connected */
> 		};
> 	};
> 
> > +	};
> > +
> > +	tcon0: lcd-controller at 01c0c000 {
> > +		compatible = "allwinner,sun8i-a83t-tcon";
> > +		reg = <0x01c0c000 0x400>;
> > +		clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> > +		clock-names = "bus", "clock";
> > +		resets = <&ccu RST_BUS_TCON0>;
> > +		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		tcon0_p: port {
> > +			tcon0_hdmi: endpoint {
> > +				remote-endpoint = <&hdmi_tcon0>;
> > +			};
> > +		};
> 
> and here
> 
> 	port {
> 		tcon0_hdmi: endpoint {
> 			remote-endpoint = <&de_out_0>;
> 		};
> 	};

The TCON has an output, so this should instead be

	port at 0 {
		reg = <0>;
 		tcon0_in: endpoint {
 			remote-endpoint = <&de_out_0>;
 		};
	};
	port at 1 {
		reg = <1>;
 		tcon1_out: endpoint {
 			remote-endpoint = <&hdmi_in>;
 		};
	};

(the second port requires adding the HDMI encoder to the example)

> > +	};
> > +
> > +	/* not used */
> > +	tcon1: lcd-controller at 01c0d000 {
> > +		compatible = "allwinner,sun8i-h3-tcon";
> > +		reg = <0x01c0d000 0x400>;
> > +		clocks = <&ccu CLK_BUS_TCON1>,
> > +			 <&ccu CLK_TCON0>;	/* no clock */
> > +		clock-names = "bus", "clock";
> > +		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
> > +		status = "disabled";
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		tcon1_p: port {
> > +			endpoint {
> > +				/* empty */
> > +			};
> > +		};
> 
> and here
> 
> 	port {
> 		/* No endpoint as the port is not connected */
> 	};
> 
> (although I'm not sure why you don't connect it)
> 
> > +	};

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29  9:08     ` Jean-Francois Moine
@ 2016-11-29 18:46         ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 18:46 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Jean-Francois Moine, Dave Airlie, Maxime Ripard, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

Thank you for the patch.

On Tuesday 29 Nov 2016 10:08:25 Jean-Francois Moine wrote:
> Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> ---
>  .../devicetree/bindings/display/sunxi/hdmi.txt     | 56 +++++++++++++++++++
>  1 file changed, 56 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt new file mode
> 100644
> index 0000000..1e107cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> @@ -0,0 +1,56 @@
> +Allwinner HDMI Transmitter
> +==========================
> +
> +The Allwinner HDMI transmitters are included in the SoCs.
> +They support audio and video.
> +
> +Required properties:
> + - compatible : should be one of
> +		"allwinner,sun8i-a83t-hdmi"
> +		"allwinner,sun8i-h3-hdmi"
> + - reg: base address and size of the I/O memory
> + - clocks : phandles to the HDMI clocks as described in
> +	Documentation/devicetree/bindings/clock/clock-bindings.txt
> + - clock-names : must be
> +		"bus" : bus gate
> +		"clock" : streaming clock
> +		"ddc-clock" : DDC clock
> + - resets : One or two phandles to the HDMI resets
> + - reset-names : when 2 phandles, must be
> +		"hdmi0" and "hdmi1"
> + - #address-cells : should be <1>
> + - #size-cells : should be <0>
> +
> +Required nodes:
> + - port: Audio and video input port nodes with endpoint definitions
> +	as defined in Documentation/devicetree/bindings/graph.txt.
> +	port@0 is video and port@1 is audio.
> +
> +Example:
> +
> +	hdmi: hdmi@01ee0000 {
> +		compatible = "allwinner,sun8i-a83t-hdmi";
> +		reg = <0x01ee0000 0x20000>;
> +		clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
> +			 <&ccu CLK_HDMI_DDC>;
> +		clock-names = "bus", "clock", "ddc-clock";
> +		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
> +		reset-names = "hdmi0", "hdmi1";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&hdmi_pins_a>;
> +		status = "disabled";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		port@0 {			/* video */
> +			reg = <0>;
> +			hdmi_tcon1: endpoint {
> +				remote-endpoint = <&tcon1_hdmi>;
> +			};
> +		};
> +		port@1 {			/* audio */
> +			reg = <1>;
> +			hdmi_i2s2: endpoint {
> +				remote-endpoint = <&i2s2_hdmi>;
> +			};
> +		};

You need a third port for the HDMI encoder output, connected to an HDMI 
connector DT node.

> +	};

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-29 18:46         ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 18:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

Thank you for the patch.

On Tuesday 29 Nov 2016 10:08:25 Jean-Francois Moine wrote:
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
>  .../devicetree/bindings/display/sunxi/hdmi.txt     | 56 +++++++++++++++++++
>  1 file changed, 56 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt new file mode
> 100644
> index 0000000..1e107cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> @@ -0,0 +1,56 @@
> +Allwinner HDMI Transmitter
> +==========================
> +
> +The Allwinner HDMI transmitters are included in the SoCs.
> +They support audio and video.
> +
> +Required properties:
> + - compatible : should be one of
> +		"allwinner,sun8i-a83t-hdmi"
> +		"allwinner,sun8i-h3-hdmi"
> + - reg: base address and size of the I/O memory
> + - clocks : phandles to the HDMI clocks as described in
> +	Documentation/devicetree/bindings/clock/clock-bindings.txt
> + - clock-names : must be
> +		"bus" : bus gate
> +		"clock" : streaming clock
> +		"ddc-clock" : DDC clock
> + - resets : One or two phandles to the HDMI resets
> + - reset-names : when 2 phandles, must be
> +		"hdmi0" and "hdmi1"
> + - #address-cells : should be <1>
> + - #size-cells : should be <0>
> +
> +Required nodes:
> + - port: Audio and video input port nodes with endpoint definitions
> +	as defined in Documentation/devicetree/bindings/graph.txt.
> +	port at 0 is video and port at 1 is audio.
> +
> +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 = "bus", "clock", "ddc-clock";
> +		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
> +		reset-names = "hdmi0", "hdmi1";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&hdmi_pins_a>;
> +		status = "disabled";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		port at 0 {			/* video */
> +			reg = <0>;
> +			hdmi_tcon1: endpoint {
> +				remote-endpoint = <&tcon1_hdmi>;
> +			};
> +		};
> +		port at 1 {			/* audio */
> +			reg = <1>;
> +			hdmi_i2s2: endpoint {
> +				remote-endpoint = <&i2s2_hdmi>;
> +			};
> +		};

You need a third port for the HDMI encoder output, connected to an HDMI 
connector DT node.

> +	};

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29 18:46         ` Laurent Pinchart
@ 2016-11-29 19:27           ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 19:27 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 29 Nov 2016 20:46:22 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:
	[snip]
> > +Example:
> > +
> > +	hdmi: hdmi@01ee0000 {
> > +		compatible = "allwinner,sun8i-a83t-hdmi";
> > +		reg = <0x01ee0000 0x20000>;
> > +		clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
> > +			 <&ccu CLK_HDMI_DDC>;
> > +		clock-names = "bus", "clock", "ddc-clock";
> > +		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
> > +		reset-names = "hdmi0", "hdmi1";
> > +		pinctrl-names = "default";
> > +		pinctrl-0 = <&hdmi_pins_a>;
> > +		status = "disabled";
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		port@0 {			/* video */
> > +			reg = <0>;
> > +			hdmi_tcon1: endpoint {
> > +				remote-endpoint = <&tcon1_hdmi>;
> > +			};
> > +		};
> > +		port@1 {			/* audio */
> > +			reg = <1>;
> > +			hdmi_i2s2: endpoint {
> > +				remote-endpoint = <&i2s2_hdmi>;
> > +			};
> > +		};
> 
> You need a third port for the HDMI encoder output, connected to an HDMI 
> connector DT node.

I don't see what you mean. The HDMI device is both the encoder
and connector (as the TDA998x):

plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
----- plane ------    - CRTC -   - encoder  \
                                   connector -- (HDMI cable)
         audio-controller -   - audio-codec /

> > +	};

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-29 19:27           ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 19:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 29 Nov 2016 20:46:22 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
	[snip]
> > +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 = "bus", "clock", "ddc-clock";
> > +		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
> > +		reset-names = "hdmi0", "hdmi1";
> > +		pinctrl-names = "default";
> > +		pinctrl-0 = <&hdmi_pins_a>;
> > +		status = "disabled";
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		port at 0 {			/* video */
> > +			reg = <0>;
> > +			hdmi_tcon1: endpoint {
> > +				remote-endpoint = <&tcon1_hdmi>;
> > +			};
> > +		};
> > +		port at 1 {			/* audio */
> > +			reg = <1>;
> > +			hdmi_i2s2: endpoint {
> > +				remote-endpoint = <&i2s2_hdmi>;
> > +			};
> > +		};
> 
> You need a third port for the HDMI encoder output, connected to an HDMI 
> connector DT node.

I don't see what you mean. The HDMI device is both the encoder
and connector (as the TDA998x):

plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
----- plane ------    - CRTC -   - encoder  \
                                   connector -- (HDMI cable)
         audio-controller -   - audio-codec /

> > +	};

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29 19:27           ` Jean-Francois Moine
@ 2016-11-29 19:33               ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 19:33 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

On Tuesday 29 Nov 2016 20:27:51 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 20:46:22 +0200 Laurent Pinchart wrote:
> [snip]
> 
> >> +Example:
> >> +
> >> +	hdmi: hdmi@01ee0000 {
> >> +		compatible = "allwinner,sun8i-a83t-hdmi";
> >> +		reg = <0x01ee0000 0x20000>;
> >> +		clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
> >> +			 <&ccu CLK_HDMI_DDC>;
> >> +		clock-names = "bus", "clock", "ddc-clock";
> >> +		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
> >> +		reset-names = "hdmi0", "hdmi1";
> >> +		pinctrl-names = "default";
> >> +		pinctrl-0 = <&hdmi_pins_a>;
> >> +		status = "disabled";
> >> +		#address-cells = <1>;
> >> +		#size-cells = <0>;
> >> +		port@0 {			/* video */
> >> +			reg = <0>;
> >> +			hdmi_tcon1: endpoint {
> >> +				remote-endpoint = <&tcon1_hdmi>;
> >> +			};
> >> +		};
> >> +		port@1 {			/* audio */
> >> +			reg = <1>;
> >> +			hdmi_i2s2: endpoint {
> >> +				remote-endpoint = <&i2s2_hdmi>;
> >> +			};
> >> +		};
> > 
> > You need a third port for the HDMI encoder output, connected to an HDMI
> > connector DT node.
> 
> I don't see what you mean. The HDMI device is both the encoder
> and connector (as the TDA998x):

The driver might create both an encoder and a connector, but I very much doubt 
that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector, unless the 
SoC package has an HDMI connector coming out of it :-)

> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> ----- plane ------    - CRTC -   - encoder  \
>                                    connector -- (HDMI cable)
>          audio-controller -   - audio-codec /
> 
> > > +	};

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-29 19:33               ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 19:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

On Tuesday 29 Nov 2016 20:27:51 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 20:46:22 +0200 Laurent Pinchart wrote:
> [snip]
> 
> >> +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 = "bus", "clock", "ddc-clock";
> >> +		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
> >> +		reset-names = "hdmi0", "hdmi1";
> >> +		pinctrl-names = "default";
> >> +		pinctrl-0 = <&hdmi_pins_a>;
> >> +		status = "disabled";
> >> +		#address-cells = <1>;
> >> +		#size-cells = <0>;
> >> +		port at 0 {			/* video */
> >> +			reg = <0>;
> >> +			hdmi_tcon1: endpoint {
> >> +				remote-endpoint = <&tcon1_hdmi>;
> >> +			};
> >> +		};
> >> +		port at 1 {			/* audio */
> >> +			reg = <1>;
> >> +			hdmi_i2s2: endpoint {
> >> +				remote-endpoint = <&i2s2_hdmi>;
> >> +			};
> >> +		};
> > 
> > You need a third port for the HDMI encoder output, connected to an HDMI
> > connector DT node.
> 
> I don't see what you mean. The HDMI device is both the encoder
> and connector (as the TDA998x):

The driver might create both an encoder and a connector, but I very much doubt 
that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector, unless the 
SoC package has an HDMI connector coming out of it :-)

> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> ----- plane ------    - CRTC -   - encoder  \
>                                    connector -- (HDMI cable)
>          audio-controller -   - audio-codec /
> 
> > > +	};

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29 19:33               ` Laurent Pinchart
@ 2016-11-29 20:04                 ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 20:04 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 29 Nov 2016 21:33 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:

> > > You need a third port for the HDMI encoder output, connected to an HDMI
> > > connector DT node.
> > 
> > I don't see what you mean. The HDMI device is both the encoder
> > and connector (as the TDA998x):
> 
> The driver might create both an encoder and a connector, but I very much doubt 
> that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector, unless the 
> SoC package has an HDMI connector coming out of it :-)
> 
> > plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> > ----- plane ------    - CRTC -   - encoder  \
> >                                    connector -- (HDMI cable)
> >          audio-controller -   - audio-codec /

The schema is the same as the Dove Cubox: the TDA998x is just a chip
with some wires going out and the physical connector is supposed to be
at the end of the wires.

Here, the HDMI pins of the SoC go to a pure hardware chip and then to
the physical connector. Which software entity do you want to add?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-29 20:04                 ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-29 20:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 29 Nov 2016 21:33 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> > > You need a third port for the HDMI encoder output, connected to an HDMI
> > > connector DT node.
> > 
> > I don't see what you mean. The HDMI device is both the encoder
> > and connector (as the TDA998x):
> 
> The driver might create both an encoder and a connector, but I very much doubt 
> that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector, unless the 
> SoC package has an HDMI connector coming out of it :-)
> 
> > plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> > ----- plane ------    - CRTC -   - encoder  \
> >                                    connector -- (HDMI cable)
> >          audio-controller -   - audio-codec /

The schema is the same as the Dove Cubox: the TDA998x is just a chip
with some wires going out and the physical connector is supposed to be
at the end of the wires.

Here, the HDMI pins of the SoC go to a pure hardware chip and then to
the physical connector. Which software entity do you want to add?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29 20:04                 ` Jean-Francois Moine
@ 2016-11-29 20:10                     ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 20:10 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

On Tuesday 29 Nov 2016 21:04:55 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 21:33 +0200 Laurent Pinchart wrote:
> >>> You need a third port for the HDMI encoder output, connected to an
> >>> HDMI connector DT node.
> >> 
> >> I don't see what you mean. The HDMI device is both the encoder
> >> and connector (as the TDA998x):
> >
> > The driver might create both an encoder and a connector, but I very much
> > doubt that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector,
> > unless the SoC package has an HDMI connector coming out of it :-)
> > 
> >> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> >> ----- plane ------    - CRTC -   - encoder  \
> >>                                    connector -- (HDMI cable)
> >>          audio-controller -   - audio-codec /
> 
> The schema is the same as the Dove Cubox: the TDA998x is just a chip
> with some wires going out and the physical connector is supposed to be
> at the end of the wires.

I've missed the Dove Cubox DT bindings when they were submitted. Fortunately 
(or unfortunately for you, depending on how you look at it ;-)) I've paid more 
attention this time.

> Here, the HDMI pins of the SoC go to a pure hardware chip and then to
> the physical connector. Which software entity do you want to add?

I don't want to add a software entity, I just want to model the connector in 
DT as it's present in the system. Even though that's more common for other bus 
types than HDMI (LVDS for instance) it wouldn't be inconceivable to connect 
the HDMI signals to an on-board chim instead of an HDMI connector, so the HDMI 
encoder output should be modelled by a port and connected to a connector DT 
node in this case.

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-29 20:10                     ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 20:10 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

On Tuesday 29 Nov 2016 21:04:55 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 21:33 +0200 Laurent Pinchart wrote:
> >>> You need a third port for the HDMI encoder output, connected to an
> >>> HDMI connector DT node.
> >> 
> >> I don't see what you mean. The HDMI device is both the encoder
> >> and connector (as the TDA998x):
> >
> > The driver might create both an encoder and a connector, but I very much
> > doubt that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector,
> > unless the SoC package has an HDMI connector coming out of it :-)
> > 
> >> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> >> ----- plane ------    - CRTC -   - encoder  \
> >>                                    connector -- (HDMI cable)
> >>          audio-controller -   - audio-codec /
> 
> The schema is the same as the Dove Cubox: the TDA998x is just a chip
> with some wires going out and the physical connector is supposed to be
> at the end of the wires.

I've missed the Dove Cubox DT bindings when they were submitted. Fortunately 
(or unfortunately for you, depending on how you look at it ;-)) I've paid more 
attention this time.

> Here, the HDMI pins of the SoC go to a pure hardware chip and then to
> the physical connector. Which software entity do you want to add?

I don't want to add a software entity, I just want to model the connector in 
DT as it's present in the system. Even though that's more common for other bus 
types than HDMI (LVDS for instance) it wouldn't be inconceivable to connect 
the HDMI signals to an on-board chim instead of an HDMI connector, so the HDMI 
encoder output should be modelled by a port and connected to a connector DT 
node in this case.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-29 10:18 ` Jean-Francois Moine
@ 2016-11-29 21:36     ` Maxime Ripard
  -1 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-11-29 21:36 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Dave Airlie, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Laurent Pinchart

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

On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> This patchset series adds HDMI video support to the Allwinner
> sun8i SoCs which include the display engine 2 (DE2).
> The driver contains the code for the A83T and H3 SoCs, and
> some H3 boards, but it could be used/extended for other SoCs
> (A64, H2, H5) and boards (Banana PIs, Orange PIs).

Honestly, I'm getting a bit worried by the fact that you ignore
reviews.

On the important reviews that you got that are to be seen as major
issues that block the inclusion, we have:
  - The fact that the HDMI driver is actually just a designware IP,
    and while you should use the driver that already exists, you just
    duplicated all that code.

  - The fact that you ignored Rob (v6) and I (v5) comment on using OF
    graph to model the connection between the display engine and the
    TCON. Something that Laurent also pointed out in this version.

  - The fact that you ignored that you needed an HDMI connector node
    as a child of the HDMI controller. This has been reported by Rob
    (v6) and yet again in this version by Laurent.

  - And finally the fact that we can't have several display engine in
    parallel, if needs be. This has happened in the past already on
    Allwinner SoCs, so it's definitely something we should consider in
    the DT bindings, since we can't break them.

Until those are fixed, I cannot see how this driver can be merged,
unfortunately.

Maxime

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

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-29 21:36     ` Maxime Ripard
  0 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-11-29 21:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> This patchset series adds HDMI video support to the Allwinner
> sun8i SoCs which include the display engine 2 (DE2).
> The driver contains the code for the A83T and H3 SoCs, and
> some H3 boards, but it could be used/extended for other SoCs
> (A64, H2, H5) and boards (Banana PIs, Orange PIs).

Honestly, I'm getting a bit worried by the fact that you ignore
reviews.

On the important reviews that you got that are to be seen as major
issues that block the inclusion, we have:
  - The fact that the HDMI driver is actually just a designware IP,
    and while you should use the driver that already exists, you just
    duplicated all that code.

  - The fact that you ignored Rob (v6) and I (v5) comment on using OF
    graph to model the connection between the display engine and the
    TCON. Something that Laurent also pointed out in this version.

  - The fact that you ignored that you needed an HDMI connector node
    as a child of the HDMI controller. This has been reported by Rob
    (v6) and yet again in this version by Laurent.

  - And finally the fact that we can't have several display engine in
    parallel, if needs be. This has happened in the past already on
    Allwinner SoCs, so it's definitely something we should consider in
    the DT bindings, since we can't break them.

Until those are fixed, I cannot see how this driver can be merged,
unfortunately.

Maxime

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

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-29 21:36     ` Maxime Ripard
  (?)
@ 2016-11-29 22:47     ` Jernej Skrabec
       [not found]       ` <8398357e-5c5e-4d76-9022-1c668aff5076-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
  -1 siblings, 1 reply; 79+ messages in thread
From: Jernej Skrabec @ 2016-11-29 22:47 UTC (permalink / raw)
  To: linux-sunxi
  Cc: moinejf-GANU6spQydw, airlied-cv59FeDIM0c,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8


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

Hi Maxime,

Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard napisala:
>
> On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote: 
> > This patchset series adds HDMI video support to the Allwinner 
> > sun8i SoCs which include the display engine 2 (DE2). 
> > The driver contains the code for the A83T and H3 SoCs, and 
> > some H3 boards, but it could be used/extended for other SoCs 
> > (A64, H2, H5) and boards (Banana PIs, Orange PIs). 
>
> Honestly, I'm getting a bit worried by the fact that you ignore 
> reviews. 
>
> On the important reviews that you got that are to be seen as major 
> issues that block the inclusion, we have: 
>   - The fact that the HDMI driver is actually just a designware IP, 
>     and while you should use the driver that already exists, you just 
>     duplicated all that code. 
>
>  
That might be hard thing to do. A83T fits perfectly, but H3 and newer SoCs 
do
not. They are using completely different HDMI phy. Decoupling controller and
phy code means rewritting a good portion of the code, unless some tricks are
applied, like calling phy function pointers, if they are defined.

Register addresses also differ, but that can be easily solved by using
undocumented magic value to restore them.
 

>   - The fact that you ignored Rob (v6) and I (v5) comment on using OF 
>     graph to model the connection between the display engine and the 
>     TCON. Something that Laurent also pointed out in this version. 
>
>   - The fact that you ignored that you needed an HDMI connector node 
>     as a child of the HDMI controller. This has been reported by Rob 
>     (v6) and yet again in this version by Laurent. 
>
>   - And finally the fact that we can't have several display engine in 
>     parallel, if needs be. This has happened in the past already on 
>     Allwinner SoCs, so it's definitely something we should consider in 
>     the DT bindings, since we can't break them. 
>
> Until those are fixed, I cannot see how this driver can be merged, 
> unfortunately. 
>
> Maxime 
>
> -- 
> Maxime Ripard, Free Electrons 
> Embedded Linux and Kernel engineering 
> http://free-electrons.com


Best regards,
Jernej Škrabec 

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

[-- Attachment #1.2: Type: text/html, Size: 3602 bytes --]

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-29 22:47     ` Jernej Skrabec
@ 2016-11-29 22:56           ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 22:56 UTC (permalink / raw)
  To: Jernej Skrabec
  Cc: linux-sunxi, moinejf-GANU6spQydw, airlied-cv59FeDIM0c,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw

Hi Jernej,

(CC'ing Kieran Bingham)

On Tuesday 29 Nov 2016 14:47:20 Jernej Skrabec wrote:
> Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard napisala:
> > On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> >> This patchset series adds HDMI video support to the Allwinner
> >> sun8i SoCs which include the display engine 2 (DE2).
> >> The driver contains the code for the A83T and H3 SoCs, and
> >> some H3 boards, but it could be used/extended for other SoCs
> >> (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> > 
> > Honestly, I'm getting a bit worried by the fact that you ignore
> > reviews.
> > 
> > On the important reviews that you got that are to be seen as major
> > 
> > issues that block the inclusion, we have:
> >   - The fact that the HDMI driver is actually just a designware IP,
> >     and while you should use the driver that already exists, you just
> >     duplicated all that code.
> 
> That might be hard thing to do. A83T fits perfectly, but H3 and newer SoCs
> do not. They are using completely different HDMI phy. Decoupling controller
> and phy code means rewritting a good portion of the code, unless some tricks
> are applied, like calling phy function pointers, if they are defined.

Same HDMI TX but different HDMI TX PHY ? Kieran is working on decoupling the 
PHY configuration code for a Renesas SoC, that might be of interest to you.

> Register addresses also differ, but that can be easily solved by using
> undocumented magic value to restore them.

I love that :-)

> >   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
> >     graph to model the connection between the display engine and the
> >     TCON. Something that Laurent also pointed out in this version.
> >   
> >   - The fact that you ignored that you needed an HDMI connector node
> >     as a child of the HDMI controller. This has been reported by Rob
> >     (v6) and yet again in this version by Laurent.
> >   
> >   - And finally the fact that we can't have several display engine in
> >     parallel, if needs be. This has happened in the past already on
> >     Allwinner SoCs, so it's definitely something we should consider in
> >     the DT bindings, since we can't break them.
> > 
> > Until those are fixed, I cannot see how this driver can be merged,
> > unfortunately.

-- 
Regards,

Laurent Pinchart

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-29 22:56           ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-29 22:56 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jernej,

(CC'ing Kieran Bingham)

On Tuesday 29 Nov 2016 14:47:20 Jernej Skrabec wrote:
> Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard napisala:
> > On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> >> This patchset series adds HDMI video support to the Allwinner
> >> sun8i SoCs which include the display engine 2 (DE2).
> >> The driver contains the code for the A83T and H3 SoCs, and
> >> some H3 boards, but it could be used/extended for other SoCs
> >> (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> > 
> > Honestly, I'm getting a bit worried by the fact that you ignore
> > reviews.
> > 
> > On the important reviews that you got that are to be seen as major
> > 
> > issues that block the inclusion, we have:
> >   - The fact that the HDMI driver is actually just a designware IP,
> >     and while you should use the driver that already exists, you just
> >     duplicated all that code.
> 
> That might be hard thing to do. A83T fits perfectly, but H3 and newer SoCs
> do not. They are using completely different HDMI phy. Decoupling controller
> and phy code means rewritting a good portion of the code, unless some tricks
> are applied, like calling phy function pointers, if they are defined.

Same HDMI TX but different HDMI TX PHY ? Kieran is working on decoupling the 
PHY configuration code for a Renesas SoC, that might be of interest to you.

> Register addresses also differ, but that can be easily solved by using
> undocumented magic value to restore them.

I love that :-)

> >   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
> >     graph to model the connection between the display engine and the
> >     TCON. Something that Laurent also pointed out in this version.
> >   
> >   - The fact that you ignored that you needed an HDMI connector node
> >     as a child of the HDMI controller. This has been reported by Rob
> >     (v6) and yet again in this version by Laurent.
> >   
> >   - And finally the fact that we can't have several display engine in
> >     parallel, if needs be. This has happened in the past already on
> >     Allwinner SoCs, so it's definitely something we should consider in
> >     the DT bindings, since we can't break them.
> > 
> > Until those are fixed, I cannot see how this driver can be merged,
> > unfortunately.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-29 22:56           ` Laurent Pinchart
  (?)
@ 2016-11-29 23:24           ` Jernej Skrabec
       [not found]             ` <239b60f3-3ea7-4aa7-8e8d-353e1a1d4ee5-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
  -1 siblings, 1 reply; 79+ messages in thread
From: Jernej Skrabec @ 2016-11-29 23:24 UTC (permalink / raw)
  To: linux-sunxi
  Cc: jernej.skrabec-Re5JQEeQqe8AvxtiuMwx3w, moinejf-GANU6spQydw,
	airlied-cv59FeDIM0c, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw


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

Hi Laurent,

Dne torek, 29. november 2016 23.56.31 UTC+1 je oseba Laurent Pinchart 
napisala:
>
> Hi Jernej, 
>
> (CC'ing Kieran Bingham) 
>
> On Tuesday 29 Nov 2016 14:47:20 Jernej Skrabec wrote: 
> > Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard 
> napisala: 
> > > On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote: 
> > >> This patchset series adds HDMI video support to the Allwinner 
> > >> sun8i SoCs which include the display engine 2 (DE2). 
> > >> The driver contains the code for the A83T and H3 SoCs, and 
> > >> some H3 boards, but it could be used/extended for other SoCs 
> > >> (A64, H2, H5) and boards (Banana PIs, Orange PIs). 
> > > 
> > > Honestly, I'm getting a bit worried by the fact that you ignore 
> > > reviews. 
> > > 
> > > On the important reviews that you got that are to be seen as major 
> > > 
> > > issues that block the inclusion, we have: 
> > >   - The fact that the HDMI driver is actually just a designware IP, 
> > >     and while you should use the driver that already exists, you just 
> > >     duplicated all that code. 
> > 
> > That might be hard thing to do. A83T fits perfectly, but H3 and newer 
> SoCs 
> > do not. They are using completely different HDMI phy. Decoupling 
> controller 
> > and phy code means rewritting a good portion of the code, unless some 
> tricks 
> > are applied, like calling phy function pointers, if they are defined. 
>
> Same HDMI TX but different HDMI TX PHY ? Kieran is working on decoupling 
> the 
> PHY configuration code for a Renesas SoC, that might be of interest to 
> you. 
>

Exactly. I'm developing only U-Boot driver, but Jean-Francois will probably
have more interest in this.
 

>
> > Register addresses also differ, but that can be easily solved by using 
> > undocumented magic value to restore them. 
>
> I love that :-) 
>
>
Is it allowed to use magic number which was found in binary blob? I'm new in
all this.
 

> > >   - The fact that you ignored Rob (v6) and I (v5) comment on using OF 
> > >     graph to model the connection between the display engine and the 
> > >     TCON. Something that Laurent also pointed out in this version. 
> > >   
> > >   - The fact that you ignored that you needed an HDMI connector node 
> > >     as a child of the HDMI controller. This has been reported by Rob 
> > >     (v6) and yet again in this version by Laurent. 
> > >   
> > >   - And finally the fact that we can't have several display engine in 
> > >     parallel, if needs be. This has happened in the past already on 
> > >     Allwinner SoCs, so it's definitely something we should consider in 
> > >     the DT bindings, since we can't break them. 
> > > 
> > > Until those are fixed, I cannot see how this driver can be merged, 
> > > unfortunately. 
>
> -- 
> Regards, 
>
> Laurent Pinchart 
>
>
Best regards,
Jernej Škrabec 

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

[-- Attachment #1.2: Type: text/html, Size: 4232 bytes --]

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-29 23:24           ` Jernej Skrabec
@ 2016-11-30  8:08                 ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30  8:08 UTC (permalink / raw)
  To: Jernej Skrabec
  Cc: linux-sunxi, moinejf-GANU6spQydw, airlied-cv59FeDIM0c,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw

Hi Jernej,

On Tuesday 29 Nov 2016 15:24:25 Jernej Skrabec wrote:
> Dne torek, 29. november 2016 23.56.31 UTC+1 je oseba Laurent Pinchart
> napisala:
> > On Tuesday 29 Nov 2016 14:47:20 Jernej Skrabec wrote:
> >> Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard
> > napisala:
> >>> On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> >>>> This patchset series adds HDMI video support to the Allwinner
> >>>> sun8i SoCs which include the display engine 2 (DE2).
> >>>> The driver contains the code for the A83T and H3 SoCs, and
> >>>> some H3 boards, but it could be used/extended for other SoCs
> >>>> (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> >>> 
> >>> Honestly, I'm getting a bit worried by the fact that you ignore
> >>> reviews.
> >>> 
> >>> On the important reviews that you got that are to be seen as major
> >>> issues that block the inclusion, we have:
> >>>   - The fact that the HDMI driver is actually just a designware IP,
> >>>     and while you should use the driver that already exists, you just
> >>>     duplicated all that code.
> >> 
> >> That might be hard thing to do. A83T fits perfectly, but H3 and newer
> >> SoCs do not. They are using completely different HDMI phy. Decoupling
> >> controller and phy code means rewritting a good portion of the code,
> >> unless some tricks are applied, like calling phy function pointers, if
> >> they are defined.
> > 
> > Same HDMI TX but different HDMI TX PHY ? Kieran is working on decoupling
> > the PHY configuration code for a Renesas SoC, that might be of interest to
> > you.
> 
> Exactly. I'm developing only U-Boot driver, but Jean-Francois will probably
> have more interest in this.

We'll post patches as soon as they're ready.

By the way, do you know if the H3 and newer SoCs use a different PHY from 
Synopsys, or a custom PHY developed by Allwinner ?

> >> Register addresses also differ, but that can be easily solved by using
> >> undocumented magic value to restore them.
> > 
> > I love that :-)
> 
> Is it allowed to use magic number which was found in binary blob? I'm new in
> all this.

I don't really see a problem with that, we have many drivers in the kernel 
that have been developed through reverse-engineering. You should not include 
large pieces of code that have been obtained through decompilation of a 
proprietary binary blob as those could be protected by copyright, but writing 
to undocumented registers based on information found through usage of a binary 
driver isn't a problem. (Please remember that I'm not a lawyer though)

> >>>   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
> >>>     graph to model the connection between the display engine and the
> >>>     TCON. Something that Laurent also pointed out in this version.
> >>>   
> >>>   - The fact that you ignored that you needed an HDMI connector node
> >>>     as a child of the HDMI controller. This has been reported by Rob
> >>>     (v6) and yet again in this version by Laurent.
> >>>   
> >>>   - And finally the fact that we can't have several display engine in
> >>>     parallel, if needs be. This has happened in the past already on
> >>>     Allwinner SoCs, so it's definitely something we should consider in
> >>>     the DT bindings, since we can't break them.
> >>> 
> >>> Until those are fixed, I cannot see how this driver can be merged,
> >>> unfortunately.

-- 
Regards,

Laurent Pinchart

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-30  8:08                 ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jernej,

On Tuesday 29 Nov 2016 15:24:25 Jernej Skrabec wrote:
> Dne torek, 29. november 2016 23.56.31 UTC+1 je oseba Laurent Pinchart
> napisala:
> > On Tuesday 29 Nov 2016 14:47:20 Jernej Skrabec wrote:
> >> Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard
> > napisala:
> >>> On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> >>>> This patchset series adds HDMI video support to the Allwinner
> >>>> sun8i SoCs which include the display engine 2 (DE2).
> >>>> The driver contains the code for the A83T and H3 SoCs, and
> >>>> some H3 boards, but it could be used/extended for other SoCs
> >>>> (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> >>> 
> >>> Honestly, I'm getting a bit worried by the fact that you ignore
> >>> reviews.
> >>> 
> >>> On the important reviews that you got that are to be seen as major
> >>> issues that block the inclusion, we have:
> >>>   - The fact that the HDMI driver is actually just a designware IP,
> >>>     and while you should use the driver that already exists, you just
> >>>     duplicated all that code.
> >> 
> >> That might be hard thing to do. A83T fits perfectly, but H3 and newer
> >> SoCs do not. They are using completely different HDMI phy. Decoupling
> >> controller and phy code means rewritting a good portion of the code,
> >> unless some tricks are applied, like calling phy function pointers, if
> >> they are defined.
> > 
> > Same HDMI TX but different HDMI TX PHY ? Kieran is working on decoupling
> > the PHY configuration code for a Renesas SoC, that might be of interest to
> > you.
> 
> Exactly. I'm developing only U-Boot driver, but Jean-Francois will probably
> have more interest in this.

We'll post patches as soon as they're ready.

By the way, do you know if the H3 and newer SoCs use a different PHY from 
Synopsys, or a custom PHY developed by Allwinner ?

> >> Register addresses also differ, but that can be easily solved by using
> >> undocumented magic value to restore them.
> > 
> > I love that :-)
> 
> Is it allowed to use magic number which was found in binary blob? I'm new in
> all this.

I don't really see a problem with that, we have many drivers in the kernel 
that have been developed through reverse-engineering. You should not include 
large pieces of code that have been obtained through decompilation of a 
proprietary binary blob as those could be protected by copyright, but writing 
to undocumented registers based on information found through usage of a binary 
driver isn't a problem. (Please remember that I'm not a lawyer though)

> >>>   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
> >>>     graph to model the connection between the display engine and the
> >>>     TCON. Something that Laurent also pointed out in this version.
> >>>   
> >>>   - The fact that you ignored that you needed an HDMI connector node
> >>>     as a child of the HDMI controller. This has been reported by Rob
> >>>     (v6) and yet again in this version by Laurent.
> >>>   
> >>>   - And finally the fact that we can't have several display engine in
> >>>     parallel, if needs be. This has happened in the past already on
> >>>     Allwinner SoCs, so it's definitely something we should consider in
> >>>     the DT bindings, since we can't break them.
> >>> 
> >>> Until those are fixed, I cannot see how this driver can be merged,
> >>> unfortunately.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-29 20:10                     ` Laurent Pinchart
@ 2016-11-30  8:12                       ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30  8:12 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 29 Nov 2016 22:10:01 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:

> Hi Jean-François,
> 
> On Tuesday 29 Nov 2016 21:04:55 Jean-Francois Moine wrote:
> > On Tue, 29 Nov 2016 21:33 +0200 Laurent Pinchart wrote:
> > >>> You need a third port for the HDMI encoder output, connected to an
> > >>> HDMI connector DT node.
> > >> 
> > >> I don't see what you mean. The HDMI device is both the encoder
> > >> and connector (as the TDA998x):
> > >
> > > The driver might create both an encoder and a connector, but I very much
> > > doubt that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector,
> > > unless the SoC package has an HDMI connector coming out of it :-)
> > > 
> > >> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> > >> ----- plane ------    - CRTC -   - encoder  \
> > >>                                    connector -- (HDMI cable)
> > >>          audio-controller -   - audio-codec /
> > 
> > The schema is the same as the Dove Cubox: the TDA998x is just a chip
> > with some wires going out and the physical connector is supposed to be
> > at the end of the wires.
> 
> I've missed the Dove Cubox DT bindings when they were submitted. Fortunately 
> (or unfortunately for you, depending on how you look at it ;-)) I've paid more 
> attention this time.
> 
> > Here, the HDMI pins of the SoC go to a pure hardware chip and then to
> > the physical connector. Which software entity do you want to add?
> 
> I don't want to add a software entity, I just want to model the connector in 
> DT as it's present in the system. Even though that's more common for other bus 
> types than HDMI (LVDS for instance) it wouldn't be inconceivable to connect 
> the HDMI signals to an on-board chim instead of an HDMI connector, so the HDMI 
> encoder output should be modelled by a port and connected to a connector DT 
> node in this case.

Well, I don't see what this connector can be.
May you give me a DT example?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30  8:12                       ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30  8:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 29 Nov 2016 22:10:01 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Jean-Fran?ois,
> 
> On Tuesday 29 Nov 2016 21:04:55 Jean-Francois Moine wrote:
> > On Tue, 29 Nov 2016 21:33 +0200 Laurent Pinchart wrote:
> > >>> You need a third port for the HDMI encoder output, connected to an
> > >>> HDMI connector DT node.
> > >> 
> > >> I don't see what you mean. The HDMI device is both the encoder
> > >> and connector (as the TDA998x):
> > >
> > > The driver might create both an encoder and a connector, but I very much
> > > doubt that the "allwinner,sun8i-a83t-hdmi" hardware contains a connector,
> > > unless the SoC package has an HDMI connector coming out of it :-)
> > > 
> > >> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> > >> ----- plane ------    - CRTC -   - encoder  \
> > >>                                    connector -- (HDMI cable)
> > >>          audio-controller -   - audio-codec /
> > 
> > The schema is the same as the Dove Cubox: the TDA998x is just a chip
> > with some wires going out and the physical connector is supposed to be
> > at the end of the wires.
> 
> I've missed the Dove Cubox DT bindings when they were submitted. Fortunately 
> (or unfortunately for you, depending on how you look at it ;-)) I've paid more 
> attention this time.
> 
> > Here, the HDMI pins of the SoC go to a pure hardware chip and then to
> > the physical connector. Which software entity do you want to add?
> 
> I don't want to add a software entity, I just want to model the connector in 
> DT as it's present in the system. Even though that's more common for other bus 
> types than HDMI (LVDS for instance) it wouldn't be inconceivable to connect 
> the HDMI signals to an on-board chim instead of an HDMI connector, so the HDMI 
> encoder output should be modelled by a port and connected to a connector DT 
> node in this case.

Well, I don't see what this connector can be.
May you give me a DT example?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30  8:12                       ` Jean-Francois Moine
@ 2016-11-30  8:20                           ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30  8:20 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

On Wednesday 30 Nov 2016 09:12:08 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 22:10:01 +0200 Laurent Pinchart wrote:
> > On Tuesday 29 Nov 2016 21:04:55 Jean-Francois Moine wrote:
> >> On Tue, 29 Nov 2016 21:33 +0200 Laurent Pinchart wrote:
> >>>>> You need a third port for the HDMI encoder output, connected to an
> >>>>> HDMI connector DT node.
> >>>> 
> >>>> I don't see what you mean. The HDMI device is both the encoder
> >>> 
> >>>> and connector (as the TDA998x):
> >>> The driver might create both an encoder and a connector, but I very
> >>> much doubt that the "allwinner,sun8i-a83t-hdmi" hardware contains a
> >>> connector, unless the SoC package has an HDMI connector coming out of
> >>> it :-)
> >>> 
> >>>> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> >>>> ----- plane ------    - CRTC -   - encoder  \
> >>>>                                    connector -- (HDMI cable)
> >>>>          audio-controller -   - audio-codec /
> >> 
> >> The schema is the same as the Dove Cubox: the TDA998x is just a chip
> >> with some wires going out and the physical connector is supposed to be
> >> at the end of the wires.
> > 
> > I've missed the Dove Cubox DT bindings when they were submitted.
> > Fortunately (or unfortunately for you, depending on how you look at it
> > ;-)) I've paid more attention this time.
> > 
> >> Here, the HDMI pins of the SoC go to a pure hardware chip and then to
> >> the physical connector. Which software entity do you want to add?
> > 
> > I don't want to add a software entity, I just want to model the connector
> > in DT as it's present in the system. Even though that's more common for
> > other bus types than HDMI (LVDS for instance) it wouldn't be
> > inconceivable to connect the HDMI signals to an on-board chim instead of
> > an HDMI connector, so the HDMI encoder output should be modelled by a
> > port and connected to a connector DT node in this case.
> 
> Well, I don't see what this connector can be.
> May you give me a DT example?

Sure.

arch/arm/boot/dts/r8a7791-koelsch.dts

        /* HDMI encoder */

        hdmi@39 {
                compatible = "adi,adv7511w";
                reg = <0x39>;
                interrupt-parent = <&gpio3>;
                interrupts = <29 IRQ_TYPE_LEVEL_LOW>;

                adi,input-depth = <8>;
                adi,input-colorspace = "rgb";
                adi,input-clock = "1x";
                adi,input-style = <1>;
                adi,input-justification = "evenly";

                ports {
                        #address-cells = <1>;
                        #size-cells = <0>;

                        port@0 {
                                reg = <0>;
                                adv7511_in: endpoint {
                                        remote-endpoint = <&du_out_rgb>;
                                };
                        };

                        port@1 {
                                reg = <1>;
                                adv7511_out: endpoint {
                                        remote-endpoint = <&hdmi_con>;
                                };
                        };
                };
        };

        /* HDMI connector */

        hdmi-out {
                compatible = "hdmi-connector";
                type = "a";

                port {
                        hdmi_con: endpoint {
                                remote-endpoint = <&adv7511_out>;
                        };
                };
        };

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30  8:20                           ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30  8:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

On Wednesday 30 Nov 2016 09:12:08 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 22:10:01 +0200 Laurent Pinchart wrote:
> > On Tuesday 29 Nov 2016 21:04:55 Jean-Francois Moine wrote:
> >> On Tue, 29 Nov 2016 21:33 +0200 Laurent Pinchart wrote:
> >>>>> You need a third port for the HDMI encoder output, connected to an
> >>>>> HDMI connector DT node.
> >>>> 
> >>>> I don't see what you mean. The HDMI device is both the encoder
> >>> 
> >>>> and connector (as the TDA998x):
> >>> The driver might create both an encoder and a connector, but I very
> >>> much doubt that the "allwinner,sun8i-a83t-hdmi" hardware contains a
> >>> connector, unless the SoC package has an HDMI connector coming out of
> >>> it :-)
> >>> 
> >>>> plane -> DE2 mixer ---> TCON -----> HDMI -----> display device
> >>>> ----- plane ------    - CRTC -   - encoder  \
> >>>>                                    connector -- (HDMI cable)
> >>>>          audio-controller -   - audio-codec /
> >> 
> >> The schema is the same as the Dove Cubox: the TDA998x is just a chip
> >> with some wires going out and the physical connector is supposed to be
> >> at the end of the wires.
> > 
> > I've missed the Dove Cubox DT bindings when they were submitted.
> > Fortunately (or unfortunately for you, depending on how you look at it
> > ;-)) I've paid more attention this time.
> > 
> >> Here, the HDMI pins of the SoC go to a pure hardware chip and then to
> >> the physical connector. Which software entity do you want to add?
> > 
> > I don't want to add a software entity, I just want to model the connector
> > in DT as it's present in the system. Even though that's more common for
> > other bus types than HDMI (LVDS for instance) it wouldn't be
> > inconceivable to connect the HDMI signals to an on-board chim instead of
> > an HDMI connector, so the HDMI encoder output should be modelled by a
> > port and connected to a connector DT node in this case.
> 
> Well, I don't see what this connector can be.
> May you give me a DT example?

Sure.

arch/arm/boot/dts/r8a7791-koelsch.dts

        /* HDMI encoder */

        hdmi at 39 {
                compatible = "adi,adv7511w";
                reg = <0x39>;
                interrupt-parent = <&gpio3>;
                interrupts = <29 IRQ_TYPE_LEVEL_LOW>;

                adi,input-depth = <8>;
                adi,input-colorspace = "rgb";
                adi,input-clock = "1x";
                adi,input-style = <1>;
                adi,input-justification = "evenly";

                ports {
                        #address-cells = <1>;
                        #size-cells = <0>;

                        port at 0 {
                                reg = <0>;
                                adv7511_in: endpoint {
                                        remote-endpoint = <&du_out_rgb>;
                                };
                        };

                        port at 1 {
                                reg = <1>;
                                adv7511_out: endpoint {
                                        remote-endpoint = <&hdmi_con>;
                                };
                        };
                };
        };

        /* HDMI connector */

        hdmi-out {
                compatible = "hdmi-connector";
                type = "a";

                port {
                        hdmi_con: endpoint {
                                remote-endpoint = <&adv7511_out>;
                        };
                };
        };

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-29 21:36     ` Maxime Ripard
@ 2016-11-30  9:05       ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30  9:05 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Dave Airlie, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Laurent Pinchart

On Tue, 29 Nov 2016 22:36:50 +0100
Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:

> On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> > This patchset series adds HDMI video support to the Allwinner
> > sun8i SoCs which include the display engine 2 (DE2).
> > The driver contains the code for the A83T and H3 SoCs, and
> > some H3 boards, but it could be used/extended for other SoCs
> > (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> 
> Honestly, I'm getting a bit worried by the fact that you ignore
> reviews.
> 
> On the important reviews that you got that are to be seen as major
> issues that block the inclusion, we have:
>   - The fact that the HDMI driver is actually just a designware IP,
>     and while you should use the driver that already exists, you just
>     duplicated all that code.

The DW registers in the A83T and H3 are obfuscated, so, the code in
bridge/DW cannot be used as it is. There should be either a translation
table or a function to compute the register addresses.

More, it is not sure that the bridge/DW code would work with
Allwinner's SoCs. It seems that they got some schematics from
DesignWare, but, is it really the same hardware? Also, if some changes
had to be done in the bridge code, I could not check if this would
break or not the other SoCs.

Eventually, I went the same way as omap/hdmi5: different driver.

>   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
>     graph to model the connection between the display engine and the
>     TCON. Something that Laurent also pointed out in this version.

I simply use the drm function drm_of_component_probe().
If this one is in the DRM core, why should I not use it?
If it must not be used, it would be nice to mark it as deprecated and
to update the code of the drivers which are using it.

>   - The fact that you ignored that you needed an HDMI connector node
>     as a child of the HDMI controller. This has been reported by Rob
>     (v6) and yet again in this version by Laurent.

As I don't know what is a DT 'connector', I cannot go further.
I hope Laurent will give me clearer explanations and a real example.

>   - And finally the fact that we can't have several display engine in
>     parallel, if needs be. This has happened in the past already on
>     Allwinner SoCs, so it's definitely something we should consider in
>     the DT bindings, since we can't break them.

IIRC, I proposed my driver before yours, and the DE2 is completely
different from the other display engines.
What you are telling is "add more code to already complex code and have
a big driver for all SoCs in each kernels".
I think it should be better to have small modules, each one treating
specific hardware, and to let only the needed code in the kernel memory
at startup time.

> Until those are fixed, I cannot see how this driver can be merged,
> unfortunately.

No problem. I just wanted to help people by giving the job I did on the
boards I have. My boards are working for almost one year, fine enough
for I use them as daily desktop computers. I don't want to spend one
more year for having my code in the Linux kernel: there are so much
other exciting things to do...

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-30  9:05       ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30  9:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 29 Nov 2016 22:36:50 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:

> On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> > This patchset series adds HDMI video support to the Allwinner
> > sun8i SoCs which include the display engine 2 (DE2).
> > The driver contains the code for the A83T and H3 SoCs, and
> > some H3 boards, but it could be used/extended for other SoCs
> > (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> 
> Honestly, I'm getting a bit worried by the fact that you ignore
> reviews.
> 
> On the important reviews that you got that are to be seen as major
> issues that block the inclusion, we have:
>   - The fact that the HDMI driver is actually just a designware IP,
>     and while you should use the driver that already exists, you just
>     duplicated all that code.

The DW registers in the A83T and H3 are obfuscated, so, the code in
bridge/DW cannot be used as it is. There should be either a translation
table or a function to compute the register addresses.

More, it is not sure that the bridge/DW code would work with
Allwinner's SoCs. It seems that they got some schematics from
DesignWare, but, is it really the same hardware? Also, if some changes
had to be done in the bridge code, I could not check if this would
break or not the other SoCs.

Eventually, I went the same way as omap/hdmi5: different driver.

>   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
>     graph to model the connection between the display engine and the
>     TCON. Something that Laurent also pointed out in this version.

I simply use the drm function drm_of_component_probe().
If this one is in the DRM core, why should I not use it?
If it must not be used, it would be nice to mark it as deprecated and
to update the code of the drivers which are using it.

>   - The fact that you ignored that you needed an HDMI connector node
>     as a child of the HDMI controller. This has been reported by Rob
>     (v6) and yet again in this version by Laurent.

As I don't know what is a DT 'connector', I cannot go further.
I hope Laurent will give me clearer explanations and a real example.

>   - And finally the fact that we can't have several display engine in
>     parallel, if needs be. This has happened in the past already on
>     Allwinner SoCs, so it's definitely something we should consider in
>     the DT bindings, since we can't break them.

IIRC, I proposed my driver before yours, and the DE2 is completely
different from the other display engines.
What you are telling is "add more code to already complex code and have
a big driver for all SoCs in each kernels".
I think it should be better to have small modules, each one treating
specific hardware, and to let only the needed code in the kernel memory
at startup time.

> Until those are fixed, I cannot see how this driver can be merged,
> unfortunately.

No problem. I just wanted to help people by giving the job I did on the
boards I have. My boards are working for almost one year, fine enough
for I use them as daily desktop computers. I don't want to spend one
more year for having my code in the Linux kernel: there are so much
other exciting things to do...

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30  8:20                           ` Laurent Pinchart
@ 2016-11-30  9:27                             ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30  9:27 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Wed, 30 Nov 2016 10:20:21 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:

> > Well, I don't see what this connector can be.
> > May you give me a DT example?
> 
> Sure.
> 
> arch/arm/boot/dts/r8a7791-koelsch.dts
> 
>         /* HDMI encoder */
> 
>         hdmi@39 {
>                 compatible = "adi,adv7511w";
>                 reg = <0x39>;
>                 interrupt-parent = <&gpio3>;
>                 interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
> 
>                 adi,input-depth = <8>;
>                 adi,input-colorspace = "rgb";
>                 adi,input-clock = "1x";
>                 adi,input-style = <1>;
>                 adi,input-justification = "evenly";
> 
>                 ports {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
> 
>                         port@0 {
>                                 reg = <0>;
>                                 adv7511_in: endpoint {
>                                         remote-endpoint = <&du_out_rgb>;
>                                 };
>                         };
> 
>                         port@1 {
>                                 reg = <1>;
>                                 adv7511_out: endpoint {
>                                         remote-endpoint = <&hdmi_con>;
>                                 };
>                         };
>                 };
>         };
> 
>         /* HDMI connector */
> 
>         hdmi-out {
>                 compatible = "hdmi-connector";
>                 type = "a";
> 
>                 port {
>                         hdmi_con: endpoint {
>                                 remote-endpoint = <&adv7511_out>;
>                         };
>                 };
>         };

Hi Laurent,

Sorry for I don't see the interest:
- it is obvious that the HDMI connector is a 'hdmi-connector'!
- the physical connector type may be changed on any board by a soldering
  iron or a connector to connector cable.
- what does the software do with the connector type?
- why not to put the connector information in the HDMI device?

And, if I follow you, the graph of ports could also be used to describe
the way the various parts of the SoCs are powered, to describe the pin
connections, to describe the USB connectors, to describe the board
internal hubs and bridges...

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30  9:27                             ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30  9:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 30 Nov 2016 10:20:21 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> > Well, I don't see what this connector can be.
> > May you give me a DT example?
> 
> Sure.
> 
> arch/arm/boot/dts/r8a7791-koelsch.dts
> 
>         /* HDMI encoder */
> 
>         hdmi at 39 {
>                 compatible = "adi,adv7511w";
>                 reg = <0x39>;
>                 interrupt-parent = <&gpio3>;
>                 interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
> 
>                 adi,input-depth = <8>;
>                 adi,input-colorspace = "rgb";
>                 adi,input-clock = "1x";
>                 adi,input-style = <1>;
>                 adi,input-justification = "evenly";
> 
>                 ports {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
> 
>                         port at 0 {
>                                 reg = <0>;
>                                 adv7511_in: endpoint {
>                                         remote-endpoint = <&du_out_rgb>;
>                                 };
>                         };
> 
>                         port at 1 {
>                                 reg = <1>;
>                                 adv7511_out: endpoint {
>                                         remote-endpoint = <&hdmi_con>;
>                                 };
>                         };
>                 };
>         };
> 
>         /* HDMI connector */
> 
>         hdmi-out {
>                 compatible = "hdmi-connector";
>                 type = "a";
> 
>                 port {
>                         hdmi_con: endpoint {
>                                 remote-endpoint = <&adv7511_out>;
>                         };
>                 };
>         };

Hi Laurent,

Sorry for I don't see the interest:
- it is obvious that the HDMI connector is a 'hdmi-connector'!
- the physical connector type may be changed on any board by a soldering
  iron or a connector to connector cable.
- what does the software do with the connector type?
- why not to put the connector information in the HDMI device?

And, if I follow you, the graph of ports could also be used to describe
the way the various parts of the SoCs are powered, to describe the pin
connections, to describe the USB connectors, to describe the board
internal hubs and bridges...

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30  9:27                             ` Jean-Francois Moine
@ 2016-11-30  9:52                                 ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30  9:52 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

On Wednesday 30 Nov 2016 10:27:57 Jean-Francois Moine wrote:
> On Wed, 30 Nov 2016 10:20:21 +0200 Laurent Pinchart wrote:
> >> Well, I don't see what this connector can be.
> >> May you give me a DT example?
> > 
> > Sure.
> > 
> > arch/arm/boot/dts/r8a7791-koelsch.dts
> > 
> >         /* HDMI encoder */
> >         
> >         hdmi@39 {
> >                 compatible = "adi,adv7511w";
> >                 reg = <0x39>;
> >                 interrupt-parent = <&gpio3>;
> >                 interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
> >                 
> >                 adi,input-depth = <8>;
> >                 adi,input-colorspace = "rgb";
> >                 adi,input-clock = "1x";
> >                 adi,input-style = <1>;
> >                 adi,input-justification = "evenly";
> >                 
> >                 ports {
> >                         #address-cells = <1>;
> >                         #size-cells = <0>;
> >                         
> >                         port@0 {
> >                                 reg = <0>;
> >                                 adv7511_in: endpoint {
> >                                         remote-endpoint = <&du_out_rgb>;
> >                                 };
> >                         };
> >                         
> >                         port@1 {
> >                                 reg = <1>;
> >                                 adv7511_out: endpoint {
> >                                         remote-endpoint = <&hdmi_con>;
> >                                 };
> >                         };
> >                 };
> >         
> >         };
> >         
> >         /* HDMI connector */
> >         
> >         hdmi-out {
> >                 compatible = "hdmi-connector";
> >                 type = "a";
> >                 
> >                 port {
> >                         hdmi_con: endpoint {
> >                                 remote-endpoint = <&adv7511_out>;
> >                         };
> >                 };
> >         };
> 
> Hi Laurent,
> 
> Sorry for I don't see the interest:
> - it is obvious that the HDMI connector is a 'hdmi-connector'!

It still has to be told to the drivers, they don't know how to identify a 
connector by looking at it :-)

> - the physical connector type may be changed on any board by a soldering
>   iron or a connector to connector cable.

Which is also true for any other component on the board. DT (and for that 
matter any firmware description of the platform) isn't soldering-proof.

> - what does the software do with the connector type?

That's up to the software to decide, the DT bindings should describe the 
hardware in the most accurate and usable way for the OS as possible. One of my 
longer term goals is to add connector drivers to handle DDC and HPD when 
they're not handled by the encoder (they are in the above example).

If the DDC was connected to a general-purpose I2C bus of the SoC, and the HPD 
to a GPIO, we would have

	hdmi-out {
		compatible = "hdmi-connector";
		type = "a";
		/* I2C bus and GPIO references are made up for the example */
		ddc-i2c-bus = <&i2c4>;
		hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>

		port {
			hdmi_con: endpoint {
				remote-endpoint = <&adv7511_out>;
			};
		};
	};

and both HPD and EDID reading should be handled by the connector driver.

> - why not to put the connector information in the HDMI device?

Because the connector is separate from the encoder. It's not uncommon 
(depending on the encoder type) to have the encoder output connected to a non-
connector entity such as another chained encoder.

For example most LVDS encoders are connected to a panel, but I have a board 
with the following encoders chain.

CRTC -- parallel RGB --> on-SoC LVDS encoder -- LVDS --> on-board LVDS decoder 
-- parallel RGB --> HDMI encoder -- HDMI --> HDMI connector

I can't support that if the LVDS encoder driver hardcodes the assumption that 
the encoder output is connected to a panel. This kind of usage might be less 
common for HDMI but is certainly not inconceivable.

> And, if I follow you, the graph of ports could also be used to describe
> the way the various parts of the SoCs are powered, to describe the pin
> connections, to describe the USB connectors, to describe the board
> internal hubs and bridges...

It should be used where applicable, it's not meant as the only possible 
hardware description for all pieces of the system.

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30  9:52                                 ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30  9:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

On Wednesday 30 Nov 2016 10:27:57 Jean-Francois Moine wrote:
> On Wed, 30 Nov 2016 10:20:21 +0200 Laurent Pinchart wrote:
> >> Well, I don't see what this connector can be.
> >> May you give me a DT example?
> > 
> > Sure.
> > 
> > arch/arm/boot/dts/r8a7791-koelsch.dts
> > 
> >         /* HDMI encoder */
> >         
> >         hdmi at 39 {
> >                 compatible = "adi,adv7511w";
> >                 reg = <0x39>;
> >                 interrupt-parent = <&gpio3>;
> >                 interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
> >                 
> >                 adi,input-depth = <8>;
> >                 adi,input-colorspace = "rgb";
> >                 adi,input-clock = "1x";
> >                 adi,input-style = <1>;
> >                 adi,input-justification = "evenly";
> >                 
> >                 ports {
> >                         #address-cells = <1>;
> >                         #size-cells = <0>;
> >                         
> >                         port at 0 {
> >                                 reg = <0>;
> >                                 adv7511_in: endpoint {
> >                                         remote-endpoint = <&du_out_rgb>;
> >                                 };
> >                         };
> >                         
> >                         port at 1 {
> >                                 reg = <1>;
> >                                 adv7511_out: endpoint {
> >                                         remote-endpoint = <&hdmi_con>;
> >                                 };
> >                         };
> >                 };
> >         
> >         };
> >         
> >         /* HDMI connector */
> >         
> >         hdmi-out {
> >                 compatible = "hdmi-connector";
> >                 type = "a";
> >                 
> >                 port {
> >                         hdmi_con: endpoint {
> >                                 remote-endpoint = <&adv7511_out>;
> >                         };
> >                 };
> >         };
> 
> Hi Laurent,
> 
> Sorry for I don't see the interest:
> - it is obvious that the HDMI connector is a 'hdmi-connector'!

It still has to be told to the drivers, they don't know how to identify a 
connector by looking at it :-)

> - the physical connector type may be changed on any board by a soldering
>   iron or a connector to connector cable.

Which is also true for any other component on the board. DT (and for that 
matter any firmware description of the platform) isn't soldering-proof.

> - what does the software do with the connector type?

That's up to the software to decide, the DT bindings should describe the 
hardware in the most accurate and usable way for the OS as possible. One of my 
longer term goals is to add connector drivers to handle DDC and HPD when 
they're not handled by the encoder (they are in the above example).

If the DDC was connected to a general-purpose I2C bus of the SoC, and the HPD 
to a GPIO, we would have

	hdmi-out {
		compatible = "hdmi-connector";
		type = "a";
		/* I2C bus and GPIO references are made up for the example */
		ddc-i2c-bus = <&i2c4>;
		hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>

		port {
			hdmi_con: endpoint {
				remote-endpoint = <&adv7511_out>;
			};
		};
	};

and both HPD and EDID reading should be handled by the connector driver.

> - why not to put the connector information in the HDMI device?

Because the connector is separate from the encoder. It's not uncommon 
(depending on the encoder type) to have the encoder output connected to a non-
connector entity such as another chained encoder.

For example most LVDS encoders are connected to a panel, but I have a board 
with the following encoders chain.

CRTC -- parallel RGB --> on-SoC LVDS encoder -- LVDS --> on-board LVDS decoder 
-- parallel RGB --> HDMI encoder -- HDMI --> HDMI connector

I can't support that if the LVDS encoder driver hardcodes the assumption that 
the encoder output is connected to a panel. This kind of usage might be less 
common for HDMI but is certainly not inconceivable.

> And, if I follow you, the graph of ports could also be used to describe
> the way the various parts of the SoCs are powered, to describe the pin
> connections, to describe the USB connectors, to describe the board
> internal hubs and bridges...

It should be used where applicable, it's not meant as the only possible 
hardware description for all pieces of the system.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-30  9:05       ` Jean-Francois Moine
@ 2016-11-30 10:12           ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30 10:12 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Jean-Francois Moine, Maxime Ripard,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Jean-François,

On Wednesday 30 Nov 2016 10:05:45 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 22:36:50 +0100 Maxime Ripard wrote:
> > On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> > > This patchset series adds HDMI video support to the Allwinner
> > > sun8i SoCs which include the display engine 2 (DE2).
> > > The driver contains the code for the A83T and H3 SoCs, and
> > > some H3 boards, but it could be used/extended for other SoCs
> > > (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> > 
> > Honestly, I'm getting a bit worried by the fact that you ignore
> > reviews.
> > 
> > On the important reviews that you got that are to be seen as major
> > 
> > issues that block the inclusion, we have:
> >   - The fact that the HDMI driver is actually just a designware IP,
> >     and while you should use the driver that already exists, you just
> >     duplicated all that code.
> 
> The DW registers in the A83T and H3 are obfuscated, so, the code in
> bridge/DW cannot be used as it is. There should be either a translation
> table or a function to compute the register addresses.

Jernej mentioned there could be a way to restore the original Synopsys memory 
map. If that works then using the dw-hdmi could be an option.

> More, it is not sure that the bridge/DW code would work with Allwinner's
> SoCs.

If it doesn't work and can't be made to work in a non-invasive way they it 
should certainly not be used :-)

> It seems that they got some schematics from DesignWare, but, is it really
> the same hardware?

That's not really relevant, as long as the hardware is compatible, it doesn't 
matter if it's a Synopsys IP or a custom implementation of the HDMI TX with a 
compatible interface.

> Also, if some changes had to be done in the bridge code, I could not check
> if this would break or not the other SoCs.

Nothing new here, all drivers that support multiple SoCs have the same 
problem. That's why we have maintainers, testers and board farms to try and 
catch issues as early as possible.

> Eventually, I went the same way as omap/hdmi5: different driver.

I might try to fix that for OMAP5 at some point, we'll see.

> >   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
> >     graph to model the connection between the display engine and the
> >     TCON. Something that Laurent also pointed out in this version.
> 
> I simply use the drm function drm_of_component_probe().
> If this one is in the DRM core, why should I not use it?
> If it must not be used, it would be nice to mark it as deprecated and
> to update the code of the drivers which are using it.

I haven't used that function so I can't comment here, except for the fact that 
DT bindings are not designed based on a given OS implementation. It should be 
the other way around, the bindings should be designed to clearly describe the 
hardware in a clean and consistent way that can be used by any host software, 
and the Linux helper functions should then be developed based on those 
bindings.

> >   - The fact that you ignored that you needed an HDMI connector node
> >     as a child of the HDMI controller. This has been reported by Rob
> >     (v6) and yet again in this version by Laurent.
> 
> As I don't know what is a DT 'connector', I cannot go further.
> I hope Laurent will give me clearer explanations and a real example.

Done, we can discuss this in that part of the mail thread.

> >   - And finally the fact that we can't have several display engine in
> >     parallel, if needs be. This has happened in the past already on
> >     Allwinner SoCs, so it's definitely something we should consider in
> >     the DT bindings, since we can't break them.
> 
> IIRC, I proposed my driver before yours, and the DE2 is completely
> different from the other display engines.
> What you are telling is "add more code to already complex code and have
> a big driver for all SoCs in each kernels".
> I think it should be better to have small modules, each one treating
> specific hardware, and to let only the needed code in the kernel memory
> at startup time.
> 
> > Until those are fixed, I cannot see how this driver can be merged,
> > unfortunately.
> 
> No problem. I just wanted to help people by giving the job I did on the
> boards I have. My boards are working for almost one year, fine enough
> for I use them as daily desktop computers. I don't want to spend one
> more year for having my code in the Linux kernel: there are so much
> other exciting things to do...

And you're certainly welcome to contribute drivers to the kernel, that's 
always appreciated. Of course, to ensure a reasonable level of quality and 
consistency between drivers, the review process often requires changes to be 
made to the code being submitted. When it comes to drivers I mostly pay 
attention to DT bindings, userspace APIs and modification to common code. 
Driver code itself, as long as it's reasonably clean and doesn't impede 
development of other drivers or impact system security in an adverse way, is 
still important but maybe slightly less so. I'll defer to Maxime to come to an 
agreement on the multiple display engines in parallel problem as I'm not 
familiar with it for the Allwinner platforms.

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-11-30 10:12           ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-11-30 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jean-Fran?ois,

On Wednesday 30 Nov 2016 10:05:45 Jean-Francois Moine wrote:
> On Tue, 29 Nov 2016 22:36:50 +0100 Maxime Ripard wrote:
> > On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote:
> > > This patchset series adds HDMI video support to the Allwinner
> > > sun8i SoCs which include the display engine 2 (DE2).
> > > The driver contains the code for the A83T and H3 SoCs, and
> > > some H3 boards, but it could be used/extended for other SoCs
> > > (A64, H2, H5) and boards (Banana PIs, Orange PIs).
> > 
> > Honestly, I'm getting a bit worried by the fact that you ignore
> > reviews.
> > 
> > On the important reviews that you got that are to be seen as major
> > 
> > issues that block the inclusion, we have:
> >   - The fact that the HDMI driver is actually just a designware IP,
> >     and while you should use the driver that already exists, you just
> >     duplicated all that code.
> 
> The DW registers in the A83T and H3 are obfuscated, so, the code in
> bridge/DW cannot be used as it is. There should be either a translation
> table or a function to compute the register addresses.

Jernej mentioned there could be a way to restore the original Synopsys memory 
map. If that works then using the dw-hdmi could be an option.

> More, it is not sure that the bridge/DW code would work with Allwinner's
> SoCs.

If it doesn't work and can't be made to work in a non-invasive way they it 
should certainly not be used :-)

> It seems that they got some schematics from DesignWare, but, is it really
> the same hardware?

That's not really relevant, as long as the hardware is compatible, it doesn't 
matter if it's a Synopsys IP or a custom implementation of the HDMI TX with a 
compatible interface.

> Also, if some changes had to be done in the bridge code, I could not check
> if this would break or not the other SoCs.

Nothing new here, all drivers that support multiple SoCs have the same 
problem. That's why we have maintainers, testers and board farms to try and 
catch issues as early as possible.

> Eventually, I went the same way as omap/hdmi5: different driver.

I might try to fix that for OMAP5 at some point, we'll see.

> >   - The fact that you ignored Rob (v6) and I (v5) comment on using OF
> >     graph to model the connection between the display engine and the
> >     TCON. Something that Laurent also pointed out in this version.
> 
> I simply use the drm function drm_of_component_probe().
> If this one is in the DRM core, why should I not use it?
> If it must not be used, it would be nice to mark it as deprecated and
> to update the code of the drivers which are using it.

I haven't used that function so I can't comment here, except for the fact that 
DT bindings are not designed based on a given OS implementation. It should be 
the other way around, the bindings should be designed to clearly describe the 
hardware in a clean and consistent way that can be used by any host software, 
and the Linux helper functions should then be developed based on those 
bindings.

> >   - The fact that you ignored that you needed an HDMI connector node
> >     as a child of the HDMI controller. This has been reported by Rob
> >     (v6) and yet again in this version by Laurent.
> 
> As I don't know what is a DT 'connector', I cannot go further.
> I hope Laurent will give me clearer explanations and a real example.

Done, we can discuss this in that part of the mail thread.

> >   - And finally the fact that we can't have several display engine in
> >     parallel, if needs be. This has happened in the past already on
> >     Allwinner SoCs, so it's definitely something we should consider in
> >     the DT bindings, since we can't break them.
> 
> IIRC, I proposed my driver before yours, and the DE2 is completely
> different from the other display engines.
> What you are telling is "add more code to already complex code and have
> a big driver for all SoCs in each kernels".
> I think it should be better to have small modules, each one treating
> specific hardware, and to let only the needed code in the kernel memory
> at startup time.
> 
> > Until those are fixed, I cannot see how this driver can be merged,
> > unfortunately.
> 
> No problem. I just wanted to help people by giving the job I did on the
> boards I have. My boards are working for almost one year, fine enough
> for I use them as daily desktop computers. I don't want to spend one
> more year for having my code in the Linux kernel: there are so much
> other exciting things to do...

And you're certainly welcome to contribute drivers to the kernel, that's 
always appreciated. Of course, to ensure a reasonable level of quality and 
consistency between drivers, the review process often requires changes to be 
made to the code being submitted. When it comes to drivers I mostly pay 
attention to DT bindings, userspace APIs and modification to common code. 
Driver code itself, as long as it's reasonably clean and doesn't impede 
development of other drivers or impact system security in an adverse way, is 
still important but maybe slightly less so. I'll defer to Maxime to come to an 
agreement on the multiple display engines in parallel problem as I'm not 
familiar with it for the Allwinner platforms.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30  9:52                                 ` Laurent Pinchart
@ 2016-11-30 10:44                                   ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30 10:44 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Wed, 30 Nov 2016 11:52:25 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:

> Hi Jean-François,
> 
> On Wednesday 30 Nov 2016 10:27:57 Jean-Francois Moine wrote:
> > On Wed, 30 Nov 2016 10:20:21 +0200 Laurent Pinchart wrote:
> > >> Well, I don't see what this connector can be.
> > >> May you give me a DT example?
> > > 
> > > Sure.
> > > 
> > > arch/arm/boot/dts/r8a7791-koelsch.dts
> > > 
> > >         /* HDMI encoder */
	[snip]
> > >         /* HDMI connector */
> > >         
> > >         hdmi-out {
> > >                 compatible = "hdmi-connector";
> > >                 type = "a";
> > >                 
> > >                 port {
> > >                         hdmi_con: endpoint {
> > >                                 remote-endpoint = <&adv7511_out>;
> > >                         };
> > >                 };
> > >         };
	[snip]
> > - what does the software do with the connector type?
> 
> That's up to the software to decide, the DT bindings should describe the 
> hardware in the most accurate and usable way for the OS as possible. One of my 
> longer term goals is to add connector drivers to handle DDC and HPD when 
> they're not handled by the encoder (they are in the above example).
> 
> If the DDC was connected to a general-purpose I2C bus of the SoC, and the HPD 
> to a GPIO, we would have
> 
> 	hdmi-out {
> 		compatible = "hdmi-connector";
> 		type = "a";
> 		/* I2C bus and GPIO references are made up for the example */
> 		ddc-i2c-bus = <&i2c4>;
> 		hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
> 
> 		port {
> 			hdmi_con: endpoint {
> 				remote-endpoint = <&adv7511_out>;
> 			};
> 		};
> 	};
> 
> and both HPD and EDID reading should be handled by the connector driver.
	[snip]

Hi Laurent,

OK. I understand. This connector complexity should be added in all DTs,
and the same code would be used.

Actually, for component binding, I use drm_of_component_probe():

- from the DRM master, loop on the "ports" phandle array and bind the
  CRTCs,

- for each CRTC, loop on the first remote port level and bind the
  encoders/connectors

Now, this should be:

- from the DRM master, loop on the first remote ports level and bind
  the CRTCs,

- for each CRTC, loop on the second remote port level and bind the
  encoders (and bridges?),

- for each encoder, loop on the third remote port level and bind the
  connectors.

Then, it would be nice to have a generic function for doing this job.

Otherwise, from your description:

> 	hdmi-out {
> 		compatible = "hdmi-connector";
> 		type = "a";
> 		/* I2C bus and GPIO references are made up for the example */
> 		ddc-i2c-bus = <&i2c4>;
> 		hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>

the "hdmi-connector" is a big piece of software. It must handle a lot
of more and more exotic connectors.
So, I hope that you have written a "simple-hdmi-connector" which does
nothing but setting the connector type.
Where is it?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30 10:44                                   ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-11-30 10:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 30 Nov 2016 11:52:25 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Jean-Fran?ois,
> 
> On Wednesday 30 Nov 2016 10:27:57 Jean-Francois Moine wrote:
> > On Wed, 30 Nov 2016 10:20:21 +0200 Laurent Pinchart wrote:
> > >> Well, I don't see what this connector can be.
> > >> May you give me a DT example?
> > > 
> > > Sure.
> > > 
> > > arch/arm/boot/dts/r8a7791-koelsch.dts
> > > 
> > >         /* HDMI encoder */
	[snip]
> > >         /* HDMI connector */
> > >         
> > >         hdmi-out {
> > >                 compatible = "hdmi-connector";
> > >                 type = "a";
> > >                 
> > >                 port {
> > >                         hdmi_con: endpoint {
> > >                                 remote-endpoint = <&adv7511_out>;
> > >                         };
> > >                 };
> > >         };
	[snip]
> > - what does the software do with the connector type?
> 
> That's up to the software to decide, the DT bindings should describe the 
> hardware in the most accurate and usable way for the OS as possible. One of my 
> longer term goals is to add connector drivers to handle DDC and HPD when 
> they're not handled by the encoder (they are in the above example).
> 
> If the DDC was connected to a general-purpose I2C bus of the SoC, and the HPD 
> to a GPIO, we would have
> 
> 	hdmi-out {
> 		compatible = "hdmi-connector";
> 		type = "a";
> 		/* I2C bus and GPIO references are made up for the example */
> 		ddc-i2c-bus = <&i2c4>;
> 		hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
> 
> 		port {
> 			hdmi_con: endpoint {
> 				remote-endpoint = <&adv7511_out>;
> 			};
> 		};
> 	};
> 
> and both HPD and EDID reading should be handled by the connector driver.
	[snip]

Hi Laurent,

OK. I understand. This connector complexity should be added in all DTs,
and the same code would be used.

Actually, for component binding, I use drm_of_component_probe():

- from the DRM master, loop on the "ports" phandle array and bind the
  CRTCs,

- for each CRTC, loop on the first remote port level and bind the
  encoders/connectors

Now, this should be:

- from the DRM master, loop on the first remote ports level and bind
  the CRTCs,

- for each CRTC, loop on the second remote port level and bind the
  encoders (and bridges?),

- for each encoder, loop on the third remote port level and bind the
  connectors.

Then, it would be nice to have a generic function for doing this job.

Otherwise, from your description:

> 	hdmi-out {
> 		compatible = "hdmi-connector";
> 		type = "a";
> 		/* I2C bus and GPIO references are made up for the example */
> 		ddc-i2c-bus = <&i2c4>;
> 		hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>

the "hdmi-connector" is a big piece of software. It must handle a lot
of more and more exotic connectors.
So, I hope that you have written a "simple-hdmi-connector" which does
nothing but setting the connector type.
Where is it?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30  9:27                             ` Jean-Francois Moine
@ 2016-11-30 17:24                                 ` Icenowy Zheng
  -1 siblings, 0 replies; 79+ messages in thread
From: Icenowy Zheng @ 2016-11-30 17:24 UTC (permalink / raw)
  To: moinejf-Re5JQEeQqe8AvxtiuMwx3w, Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r



30.11.2016, 17:28, "Jean-Francois Moine" <moinejf@free.fr>:
> On Wed, 30 Nov 2016 10:20:21 +0200
> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
>
>>  > Well, I don't see what this connector can be.
>>  > May you give me a DT example?
>>
>>  Sure.
>>
>>  arch/arm/boot/dts/r8a7791-koelsch.dts
>>
>>          /* HDMI encoder */
>>
>>          hdmi@39 {
>>                  compatible = "adi,adv7511w";
>>                  reg = <0x39>;
>>                  interrupt-parent = <&gpio3>;
>>                  interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
>>
>>                  adi,input-depth = <8>;
>>                  adi,input-colorspace = "rgb";
>>                  adi,input-clock = "1x";
>>                  adi,input-style = <1>;
>>                  adi,input-justification = "evenly";
>>
>>                  ports {
>>                          #address-cells = <1>;
>>                          #size-cells = <0>;
>>
>>                          port@0 {
>>                                  reg = <0>;
>>                                  adv7511_in: endpoint {
>>                                          remote-endpoint = <&du_out_rgb>;
>>                                  };
>>                          };
>>
>>                          port@1 {
>>                                  reg = <1>;
>>                                  adv7511_out: endpoint {
>>                                          remote-endpoint = <&hdmi_con>;
>>                                  };
>>                          };
>>                  };
>>          };
>>
>>          /* HDMI connector */
>>
>>          hdmi-out {
>>                  compatible = "hdmi-connector";
>>                  type = "a";
>>
>>                  port {
>>                          hdmi_con: endpoint {
>>                                  remote-endpoint = <&adv7511_out>;
>>                          };
>>                  };
>>          };
>
> Hi Laurent,
>
> Sorry for I don't see the interest:
> - it is obvious that the HDMI connector is a 'hdmi-connector'!

Yes, it means the wire between the HDMI pins on the SoC and the connector ;-)

> - the physical connector type may be changed on any board by a soldering
>   iron or a connector to connector cable.

I can always alter the devices on a board ;-)

But I should also alter the dt after altering the board.

> - what does the software do with the connector type?
> - why not to put the connector information in the HDMI device?
>
> And, if I follow you, the graph of ports could also be used to describe
> the way the various parts of the SoCs are powered, to describe the pin
> connections, to describe the USB connectors, to describe the board
> internal hubs and bridges...
>
> --
> Ken ar c'hentañ | ** Breizh ha Linux atav! **
> Jef | http://moinejf.free.fr/
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

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

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

* [linux-sunxi] Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30 17:24                                 ` Icenowy Zheng
  0 siblings, 0 replies; 79+ messages in thread
From: Icenowy Zheng @ 2016-11-30 17:24 UTC (permalink / raw)
  To: linux-arm-kernel



30.11.2016, 17:28, "Jean-Francois Moine" <moinejf@free.fr>:
> On Wed, 30 Nov 2016 10:20:21 +0200
> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
>
>> ?> Well, I don't see what this connector can be.
>> ?> May you give me a DT example?
>>
>> ?Sure.
>>
>> ?arch/arm/boot/dts/r8a7791-koelsch.dts
>>
>> ?????????/* HDMI encoder */
>>
>> ?????????hdmi at 39 {
>> ?????????????????compatible = "adi,adv7511w";
>> ?????????????????reg = <0x39>;
>> ?????????????????interrupt-parent = <&gpio3>;
>> ?????????????????interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
>>
>> ?????????????????adi,input-depth = <8>;
>> ?????????????????adi,input-colorspace = "rgb";
>> ?????????????????adi,input-clock = "1x";
>> ?????????????????adi,input-style = <1>;
>> ?????????????????adi,input-justification = "evenly";
>>
>> ?????????????????ports {
>> ?????????????????????????#address-cells = <1>;
>> ?????????????????????????#size-cells = <0>;
>>
>> ?????????????????????????port at 0 {
>> ?????????????????????????????????reg = <0>;
>> ?????????????????????????????????adv7511_in: endpoint {
>> ?????????????????????????????????????????remote-endpoint = <&du_out_rgb>;
>> ?????????????????????????????????};
>> ?????????????????????????};
>>
>> ?????????????????????????port at 1 {
>> ?????????????????????????????????reg = <1>;
>> ?????????????????????????????????adv7511_out: endpoint {
>> ?????????????????????????????????????????remote-endpoint = <&hdmi_con>;
>> ?????????????????????????????????};
>> ?????????????????????????};
>> ?????????????????};
>> ?????????};
>>
>> ?????????/* HDMI connector */
>>
>> ?????????hdmi-out {
>> ?????????????????compatible = "hdmi-connector";
>> ?????????????????type = "a";
>>
>> ?????????????????port {
>> ?????????????????????????hdmi_con: endpoint {
>> ?????????????????????????????????remote-endpoint = <&adv7511_out>;
>> ?????????????????????????};
>> ?????????????????};
>> ?????????};
>
> Hi Laurent,
>
> Sorry for I don't see the interest:
> - it is obvious that the HDMI connector is a 'hdmi-connector'!

Yes, it means the wire between the HDMI pins on the SoC and the connector ;-)

> - the physical connector type may be changed on any board by a soldering
> ??iron or a connector to connector cable.

I can always alter the devices on a board ;-)

But I should also alter the dt after altering the board.

> - what does the software do with the connector type?
> - why not to put the connector information in the HDMI device?
>
> And, if I follow you, the graph of ports could also be used to describe
> the way the various parts of the SoCs are powered, to describe the pin
> connections, to describe the USB connectors, to describe the board
> internal hubs and bridges...
>
> --
> Ken ar c'henta? | ** Breizh ha Linux atav! **
> Jef | http://moinejf.free.fr/
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

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

* Re: Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30 10:44                                   ` Jean-Francois Moine
@ 2016-11-30 17:33                                       ` Icenowy Zheng
  -1 siblings, 0 replies; 79+ messages in thread
From: Icenowy Zheng @ 2016-11-30 17:33 UTC (permalink / raw)
  To: moinejf-Re5JQEeQqe8AvxtiuMwx3w, Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Maxime Ripard, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r



30.11.2016, 18:44, "Jean-Francois Moine" <moinejf-GANU6spQydw@public.gmane.org>:
> On Wed, 30 Nov 2016 11:52:25 +0200
> Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:
>
>>  Hi Jean-François,
>>
>>  On Wednesday 30 Nov 2016 10:27:57 Jean-Francois Moine wrote:
>>  > On Wed, 30 Nov 2016 10:20:21 +0200 Laurent Pinchart wrote:
>>  > >> Well, I don't see what this connector can be.
>>  > >> May you give me a DT example?
>>  > >
>>  > > Sure.
>>  > >
>>  > > arch/arm/boot/dts/r8a7791-koelsch.dts
>>  > >
>>  > > /* HDMI encoder */
>
>         [snip]
>>  > > /* HDMI connector */
>>  > >
>>  > > hdmi-out {
>>  > > compatible = "hdmi-connector";
>>  > > type = "a";
>>  > >
>>  > > port {
>>  > > hdmi_con: endpoint {
>>  > > remote-endpoint = <&adv7511_out>;
>>  > > };
>>  > > };
>>  > > };
>
>         [snip]
>>  > - what does the software do with the connector type?
>>
>>  That's up to the software to decide, the DT bindings should describe the
>>  hardware in the most accurate and usable way for the OS as possible. One of my
>>  longer term goals is to add connector drivers to handle DDC and HPD when
>>  they're not handled by the encoder (they are in the above example).
>>
>>  If the DDC was connected to a general-purpose I2C bus of the SoC, and the HPD
>>  to a GPIO, we would have
>>
>>          hdmi-out {
>>                  compatible = "hdmi-connector";
>>                  type = "a";
>>                  /* I2C bus and GPIO references are made up for the example */
>>                  ddc-i2c-bus = <&i2c4>;
>>                  hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
>>
>>                  port {
>>                          hdmi_con: endpoint {
>>                                  remote-endpoint = <&adv7511_out>;
>>                          };
>>                  };
>>          };
>>
>>  and both HPD and EDID reading should be handled by the connector driver.
>
>         [snip]
>
> Hi Laurent,
>
> OK. I understand. This connector complexity should be added in all DTs,
> and the same code would be used.
>
> Actually, for component binding, I use drm_of_component_probe():
>
> - from the DRM master, loop on the "ports" phandle array and bind the
>   CRTCs,
>
> - for each CRTC, loop on the first remote port level and bind the
>   encoders/connectors
>
> Now, this should be:
>
> - from the DRM master, loop on the first remote ports level and bind
>   the CRTCs,
>
> - for each CRTC, loop on the second remote port level and bind the
>   encoders (and bridges?),
>
> - for each encoder, loop on the third remote port level and bind the
>   connectors.
>
> Then, it would be nice to have a generic function for doing this job.
>
> Otherwise, from your description:
>
>>          hdmi-out {
>>                  compatible = "hdmi-connector";
>>                  type = "a";
>>                  /* I2C bus and GPIO references are made up for the example */
>>                  ddc-i2c-bus = <&i2c4>;
>>                  hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
>
> the "hdmi-connector" is a big piece of software. It must handle a lot
> of more and more exotic connectors.
> So, I hope that you have written a "simple-hdmi-connector" which does
> nothing but setting the connector type.
> Where is it?

I suddenly thought about something...

If a DVI connector instead of a HDMI connector is soldered, how should such a
device tree be written?

How about solder a HDMI-to-VGA bridge on the board? (Maybe there should be
"dumb-hdmi-dvi-bridge" and "dumb-hdmi-vga-bridge" drivers?)

>
> --
> Ken ar c'hentañ | ** Breizh ha Linux atav! **
> Jef | http://moinejf.free.fr/
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.

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

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

* [linux-sunxi] Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-11-30 17:33                                       ` Icenowy Zheng
  0 siblings, 0 replies; 79+ messages in thread
From: Icenowy Zheng @ 2016-11-30 17:33 UTC (permalink / raw)
  To: linux-arm-kernel



30.11.2016, 18:44, "Jean-Francois Moine" <moinejf@free.fr>:
> On Wed, 30 Nov 2016 11:52:25 +0200
> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
>
>> ?Hi Jean-Fran?ois,
>>
>> ?On Wednesday 30 Nov 2016 10:27:57 Jean-Francois Moine wrote:
>> ?> On Wed, 30 Nov 2016 10:20:21 +0200 Laurent Pinchart wrote:
>> ?> >> Well, I don't see what this connector can be.
>> ?> >> May you give me a DT example?
>> ?> >
>> ?> > Sure.
>> ?> >
>> ?> > arch/arm/boot/dts/r8a7791-koelsch.dts
>> ?> >
>> ?> > /* HDMI encoder */
>
> ????????[snip]
>> ?> > /* HDMI connector */
>> ?> >
>> ?> > hdmi-out {
>> ?> > compatible = "hdmi-connector";
>> ?> > type = "a";
>> ?> >
>> ?> > port {
>> ?> > hdmi_con: endpoint {
>> ?> > remote-endpoint = <&adv7511_out>;
>> ?> > };
>> ?> > };
>> ?> > };
>
> ????????[snip]
>> ?> - what does the software do with the connector type?
>>
>> ?That's up to the software to decide, the DT bindings should describe the
>> ?hardware in the most accurate and usable way for the OS as possible. One of my
>> ?longer term goals is to add connector drivers to handle DDC and HPD when
>> ?they're not handled by the encoder (they are in the above example).
>>
>> ?If the DDC was connected to a general-purpose I2C bus of the SoC, and the HPD
>> ?to a GPIO, we would have
>>
>> ?????????hdmi-out {
>> ?????????????????compatible = "hdmi-connector";
>> ?????????????????type = "a";
>> ?????????????????/* I2C bus and GPIO references are made up for the example */
>> ?????????????????ddc-i2c-bus = <&i2c4>;
>> ?????????????????hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
>>
>> ?????????????????port {
>> ?????????????????????????hdmi_con: endpoint {
>> ?????????????????????????????????remote-endpoint = <&adv7511_out>;
>> ?????????????????????????};
>> ?????????????????};
>> ?????????};
>>
>> ?and both HPD and EDID reading should be handled by the connector driver.
>
> ????????[snip]
>
> Hi Laurent,
>
> OK. I understand. This connector complexity should be added in all DTs,
> and the same code would be used.
>
> Actually, for component binding, I use drm_of_component_probe():
>
> - from the DRM master, loop on the "ports" phandle array and bind the
> ??CRTCs,
>
> - for each CRTC, loop on the first remote port level and bind the
> ??encoders/connectors
>
> Now, this should be:
>
> - from the DRM master, loop on the first remote ports level and bind
> ??the CRTCs,
>
> - for each CRTC, loop on the second remote port level and bind the
> ??encoders (and bridges?),
>
> - for each encoder, loop on the third remote port level and bind the
> ??connectors.
>
> Then, it would be nice to have a generic function for doing this job.
>
> Otherwise, from your description:
>
>> ?????????hdmi-out {
>> ?????????????????compatible = "hdmi-connector";
>> ?????????????????type = "a";
>> ?????????????????/* I2C bus and GPIO references are made up for the example */
>> ?????????????????ddc-i2c-bus = <&i2c4>;
>> ?????????????????hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
>
> the "hdmi-connector" is a big piece of software. It must handle a lot
> of more and more exotic connectors.
> So, I hope that you have written a "simple-hdmi-connector" which does
> nothing but setting the connector type.
> Where is it?

I suddenly thought about something...

If a DVI connector instead of a HDMI connector is soldered, how should such a
device tree be written?

How about solder a HDMI-to-VGA bridge on the board? (Maybe there should be
"dumb-hdmi-dvi-bridge" and "dumb-hdmi-vga-bridge" drivers?)

>
> --
> Ken ar c'henta? | ** Breizh ha Linux atav! **
> Jef | http://moinejf.free.fr/
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-30  8:08                 ` Laurent Pinchart
  (?)
@ 2016-11-30 18:04                 ` Jernej Skrabec
  -1 siblings, 0 replies; 79+ messages in thread
From: Jernej Skrabec @ 2016-11-30 18:04 UTC (permalink / raw)
  To: linux-sunxi
  Cc: jernej.skrabec-Re5JQEeQqe8AvxtiuMwx3w, moinejf-GANU6spQydw,
	airlied-cv59FeDIM0c, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw


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

Hi Laurent,

Dne sreda, 30. november 2016 09.08.22 UTC+1 je oseba Laurent Pinchart 
napisala:
>
> Hi Jernej, 
>
> On Tuesday 29 Nov 2016 15:24:25 Jernej Skrabec wrote: 
> > Dne torek, 29. november 2016 23.56.31 UTC+1 je oseba Laurent Pinchart 
> > napisala: 
> > > On Tuesday 29 Nov 2016 14:47:20 Jernej Skrabec wrote: 
> > >> Dne torek, 29. november 2016 22.37.03 UTC+1 je oseba Maxime Ripard 
> > > napisala: 
> > >>> On Tue, Nov 29, 2016 at 11:18:35AM +0100, Jean-Francois Moine wrote: 
> > >>>> This patchset series adds HDMI video support to the Allwinner 
> > >>>> sun8i SoCs which include the display engine 2 (DE2). 
> > >>>> The driver contains the code for the A83T and H3 SoCs, and 
> > >>>> some H3 boards, but it could be used/extended for other SoCs 
> > >>>> (A64, H2, H5) and boards (Banana PIs, Orange PIs). 
> > >>> 
> > >>> Honestly, I'm getting a bit worried by the fact that you ignore 
> > >>> reviews. 
> > >>> 
> > >>> On the important reviews that you got that are to be seen as major 
> > >>> issues that block the inclusion, we have: 
> > >>>   - The fact that the HDMI driver is actually just a designware IP, 
> > >>>     and while you should use the driver that already exists, you 
> just 
> > >>>     duplicated all that code. 
> > >> 
> > >> That might be hard thing to do. A83T fits perfectly, but H3 and newer 
> > >> SoCs do not. They are using completely different HDMI phy. Decoupling 
> > >> controller and phy code means rewritting a good portion of the code, 
> > >> unless some tricks are applied, like calling phy function pointers, 
> if 
> > >> they are defined. 
> > > 
> > > Same HDMI TX but different HDMI TX PHY ? Kieran is working on 
> decoupling 
> > > the PHY configuration code for a Renesas SoC, that might be of 
> interest to 
> > > you. 
> > 
> > Exactly. I'm developing only U-Boot driver, but Jean-Francois will 
> probably 
> > have more interest in this. 
>
> We'll post patches as soon as they're ready. 
>

Great. Is datasheet public? I'm curious if HDMI PHY is by any chance 
similar.
 

>
> By the way, do you know if the H3 and newer SoCs use a different PHY from 
> Synopsys, or a custom PHY developed by Allwinner ? 
>
>
Unfortunatelly, noone managed to identify PHY and Alliwinner never released 
a
bit of information about HDMI. Does config2_id code 0xFE (PHY type) tell you
anything?
 

> > >> Register addresses also differ, but that can be easily solved by 
> using 
> > >> undocumented magic value to restore them. 
> > > 
> > > I love that :-) 
> > 
> > Is it allowed to use magic number which was found in binary blob? I'm 
> new in 
> > all this. 
>
> I don't really see a problem with that, we have many drivers in the kernel 
> that have been developed through reverse-engineering. You should not 
> include 
> large pieces of code that have been obtained through decompilation of a 
> proprietary binary blob as those could be protected by copyright, but 
> writing 
> to undocumented registers based on information found through usage of a 
> binary 
> driver isn't a problem. (Please remember that I'm not a lawyer though) 
>
> > >>>   - The fact that you ignored Rob (v6) and I (v5) comment on using 
> OF 
> > >>>     graph to model the connection between the display engine and the 
> > >>>     TCON. Something that Laurent also pointed out in this version. 
> > >>>   
> > >>>   - The fact that you ignored that you needed an HDMI connector node 
> > >>>     as a child of the HDMI controller. This has been reported by Rob 
> > >>>     (v6) and yet again in this version by Laurent. 
> > >>>   
> > >>>   - And finally the fact that we can't have several display engine 
> in 
> > >>>     parallel, if needs be. This has happened in the past already on 
> > >>>     Allwinner SoCs, so it's definitely something we should consider 
> in 
> > >>>     the DT bindings, since we can't break them. 
> > >>> 
> > >>> Until those are fixed, I cannot see how this driver can be merged, 
> > >>> unfortunately. 
>

Best regards,
Jernej Škrabec

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

[-- Attachment #1.2: Type: text/html, Size: 5701 bytes --]

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

* Re: Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-11-30 17:33                                       ` [linux-sunxi] " Icenowy Zheng
@ 2016-12-01  8:55                                           ` Maxime Ripard
  -1 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-12-01  8:55 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: moinejf-Re5JQEeQqe8AvxtiuMwx3w, Laurent Pinchart,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

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

On Thu, Dec 01, 2016 at 01:33:30AM +0800, Icenowy Zheng wrote:
> >>          hdmi-out {
> >>                  compatible = "hdmi-connector";
> >>                  type = "a";
> >>                  /* I2C bus and GPIO references are made up for the example */
> >>                  ddc-i2c-bus = <&i2c4>;
> >>                  hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
> >
> > the "hdmi-connector" is a big piece of software. It must handle a lot
> > of more and more exotic connectors.
> > So, I hope that you have written a "simple-hdmi-connector" which does
> > nothing but setting the connector type.
> > Where is it?
> 
> I suddenly thought about something...
> 
> If a DVI connector instead of a HDMI connector is soldered, how
> should such a device tree be written?

Use a dvi-connector instead :)

> How about solder a HDMI-to-VGA bridge on the board? (Maybe there
> should be "dumb-hdmi-dvi-bridge" and "dumb-hdmi-vga-bridge"
> drivers?)

It probably wouldn't be dumb, but yeah, it would definitely be a
bridge instead of the connector.

Maxime

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

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

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

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

* [linux-sunxi] Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-12-01  8:55                                           ` Maxime Ripard
  0 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-12-01  8:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 01, 2016 at 01:33:30AM +0800, Icenowy Zheng wrote:
> >> ?????????hdmi-out {
> >> ?????????????????compatible = "hdmi-connector";
> >> ?????????????????type = "a";
> >> ?????????????????/* I2C bus and GPIO references are made up for the example */
> >> ?????????????????ddc-i2c-bus = <&i2c4>;
> >> ?????????????????hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
> >
> > the "hdmi-connector" is a big piece of software. It must handle a lot
> > of more and more exotic connectors.
> > So, I hope that you have written a "simple-hdmi-connector" which does
> > nothing but setting the connector type.
> > Where is it?
> 
> I suddenly thought about something...
> 
> If a DVI connector instead of a HDMI connector is soldered, how
> should such a device tree be written?

Use a dvi-connector instead :)

> How about solder a HDMI-to-VGA bridge on the board? (Maybe there
> should be "dumb-hdmi-dvi-bridge" and "dumb-hdmi-vga-bridge"
> drivers?)

It probably wouldn't be dumb, but yeah, it would definitely be a
bridge instead of the connector.

Maxime

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

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-11-30 10:12           ` Laurent Pinchart
@ 2016-12-01  9:13             ` Maxime Ripard
  -1 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-12-01  9:13 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Jean-Francois Moine,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

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

Hi Laurent,

On Wed, Nov 30, 2016 at 12:12:55PM +0200, Laurent Pinchart wrote:
> > More, it is not sure that the bridge/DW code would work with Allwinner's
> > SoCs.
> 
> If it doesn't work and can't be made to work in a non-invasive way they it 
> should certainly not be used :-)

Even if the register layout is completely scrambled, as long as the
bits themselves aren't (and by comparing the two drivers it looks like
they haven't changed), you can easily deal with that using the
regmap_fields, with the two implementations (the original one and the
scrambled one) providing their register map that way, and the driver
code using whatever has been provided.

> > Eventually, I went the same way as omap/hdmi5: different driver.
> 
> I might try to fix that for OMAP5 at some point, we'll see.

For complex drivers that have already a driver written and a lot of
testing that already happened, I don't think duplication is a smart
move.

> > >   - And finally the fact that we can't have several display engine in
> > >     parallel, if needs be. This has happened in the past already on
> > >     Allwinner SoCs, so it's definitely something we should consider in
> > >     the DT bindings, since we can't break them.
> > 
> > IIRC, I proposed my driver before yours, and the DE2 is completely
> > different from the other display engines.
> > What you are telling is "add more code to already complex code and have
> > a big driver for all SoCs in each kernels".
> > I think it should be better to have small modules, each one treating
> > specific hardware, and to let only the needed code in the kernel memory
> > at startup time.
> > 
> > > Until those are fixed, I cannot see how this driver can be merged,
> > > unfortunately.
> > 
> > No problem. I just wanted to help people by giving the job I did on the
> > boards I have. My boards are working for almost one year, fine enough
> > for I use them as daily desktop computers. I don't want to spend one
> > more year for having my code in the Linux kernel: there are so much
> > other exciting things to do...
> 
> And you're certainly welcome to contribute drivers to the kernel, that's 
> always appreciated. Of course, to ensure a reasonable level of quality and 
> consistency between drivers, the review process often requires changes to be 
> made to the code being submitted. When it comes to drivers I mostly pay 
> attention to DT bindings, userspace APIs and modification to common code. 
> Driver code itself, as long as it's reasonably clean and doesn't impede 
> development of other drivers or impact system security in an adverse way, is 
> still important but maybe slightly less so. I'll defer to Maxime to come to an 
> agreement on the multiple display engines in parallel problem as I'm not 
> familiar with it for the Allwinner platforms.

The earlier Allwinner SoCs (with the old display engine), we had some
SoCs with multiple instances of the display engine and TCON (the
display engine roughly implementing the planes, the TCON the
CRTC. Roughly.). However, those were sharing some encoders (HDMI,
DSI) after that.

So we need to have a single DRM device taking care of the multiple
display engines, which essentialy means that we have to decouple the
DRM device from the display engine. This was done in the earlier
designs using an additional node with a list of phandles to the
display engines in the system, and obviously, I'd prefer to have some
consistency and reuse the same thing.

But the current approach doesn't work and will require some DT
modifications if that case happens again, which we can't do because of
the DT ABI.

Maxime

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

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-12-01  9:13             ` Maxime Ripard
  0 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-12-01  9:13 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laurent,

On Wed, Nov 30, 2016 at 12:12:55PM +0200, Laurent Pinchart wrote:
> > More, it is not sure that the bridge/DW code would work with Allwinner's
> > SoCs.
> 
> If it doesn't work and can't be made to work in a non-invasive way they it 
> should certainly not be used :-)

Even if the register layout is completely scrambled, as long as the
bits themselves aren't (and by comparing the two drivers it looks like
they haven't changed), you can easily deal with that using the
regmap_fields, with the two implementations (the original one and the
scrambled one) providing their register map that way, and the driver
code using whatever has been provided.

> > Eventually, I went the same way as omap/hdmi5: different driver.
> 
> I might try to fix that for OMAP5 at some point, we'll see.

For complex drivers that have already a driver written and a lot of
testing that already happened, I don't think duplication is a smart
move.

> > >   - And finally the fact that we can't have several display engine in
> > >     parallel, if needs be. This has happened in the past already on
> > >     Allwinner SoCs, so it's definitely something we should consider in
> > >     the DT bindings, since we can't break them.
> > 
> > IIRC, I proposed my driver before yours, and the DE2 is completely
> > different from the other display engines.
> > What you are telling is "add more code to already complex code and have
> > a big driver for all SoCs in each kernels".
> > I think it should be better to have small modules, each one treating
> > specific hardware, and to let only the needed code in the kernel memory
> > at startup time.
> > 
> > > Until those are fixed, I cannot see how this driver can be merged,
> > > unfortunately.
> > 
> > No problem. I just wanted to help people by giving the job I did on the
> > boards I have. My boards are working for almost one year, fine enough
> > for I use them as daily desktop computers. I don't want to spend one
> > more year for having my code in the Linux kernel: there are so much
> > other exciting things to do...
> 
> And you're certainly welcome to contribute drivers to the kernel, that's 
> always appreciated. Of course, to ensure a reasonable level of quality and 
> consistency between drivers, the review process often requires changes to be 
> made to the code being submitted. When it comes to drivers I mostly pay 
> attention to DT bindings, userspace APIs and modification to common code. 
> Driver code itself, as long as it's reasonably clean and doesn't impede 
> development of other drivers or impact system security in an adverse way, is 
> still important but maybe slightly less so. I'll defer to Maxime to come to an 
> agreement on the multiple display engines in parallel problem as I'm not 
> familiar with it for the Allwinner platforms.

The earlier Allwinner SoCs (with the old display engine), we had some
SoCs with multiple instances of the display engine and TCON (the
display engine roughly implementing the planes, the TCON the
CRTC. Roughly.). However, those were sharing some encoders (HDMI,
DSI) after that.

So we need to have a single DRM device taking care of the multiple
display engines, which essentialy means that we have to decouple the
DRM device from the display engine. This was done in the earlier
designs using an additional node with a list of phandles to the
display engines in the system, and obviously, I'd prefer to have some
consistency and reuse the same thing.

But the current approach doesn't work and will require some DT
modifications if that case happens again, which we can't do because of
the DT ABI.

Maxime

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

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-12-01  9:13             ` Maxime Ripard
@ 2016-12-01  9:19               ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01  9:19 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Jean-Francois Moine,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Maxime,

On Thursday 01 Dec 2016 10:13:13 Maxime Ripard wrote:
> On Wed, Nov 30, 2016 at 12:12:55PM +0200, Laurent Pinchart wrote:
> >> More, it is not sure that the bridge/DW code would work with Allwinner's
> >> SoCs.
> > 
> > If it doesn't work and can't be made to work in a non-invasive way they it
> > should certainly not be used :-)
> 
> Even if the register layout is completely scrambled, as long as the
> bits themselves aren't (and by comparing the two drivers it looks like
> they haven't changed), you can easily deal with that using the
> regmap_fields, with the two implementations (the original one and the
> scrambled one) providing their register map that way, and the driver
> code using whatever has been provided.

Looking at https://linux-sunxi.org/DWC_HDMI_Controller#DWC_HDMI_Controller it 
seems that an address remapping function could be used.

> >> Eventually, I went the same way as omap/hdmi5: different driver.
> > 
> > I might try to fix that for OMAP5 at some point, we'll see.
> 
> For complex drivers that have already a driver written and a lot of
> testing that already happened, I don't think duplication is a smart
> move.
> 
> >>>   - And finally the fact that we can't have several display engine in
> >>>     parallel, if needs be. This has happened in the past already on
> >>>     Allwinner SoCs, so it's definitely something we should consider in
> >>>     the DT bindings, since we can't break them.
> >> 
> >> IIRC, I proposed my driver before yours, and the DE2 is completely
> >> different from the other display engines.
> >> What you are telling is "add more code to already complex code and have
> >> a big driver for all SoCs in each kernels".
> >> I think it should be better to have small modules, each one treating
> >> specific hardware, and to let only the needed code in the kernel memory
> >> at startup time.
> >> 
> >>> Until those are fixed, I cannot see how this driver can be merged,
> >>> unfortunately.
> >> 
> >> No problem. I just wanted to help people by giving the job I did on the
> >> boards I have. My boards are working for almost one year, fine enough
> >> for I use them as daily desktop computers. I don't want to spend one
> >> more year for having my code in the Linux kernel: there are so much
> >> other exciting things to do...
> > 
> > And you're certainly welcome to contribute drivers to the kernel, that's
> > always appreciated. Of course, to ensure a reasonable level of quality and
> > consistency between drivers, the review process often requires changes to
> > be made to the code being submitted. When it comes to drivers I mostly
> > pay attention to DT bindings, userspace APIs and modification to common
> > code. Driver code itself, as long as it's reasonably clean and doesn't
> > impede development of other drivers or impact system security in an
> > adverse way, is still important but maybe slightly less so. I'll defer to
> > Maxime to come to an agreement on the multiple display engines in
> > parallel problem as I'm not familiar with it for the Allwinner platforms.
> 
> The earlier Allwinner SoCs (with the old display engine), we had some
> SoCs with multiple instances of the display engine and TCON (the
> display engine roughly implementing the planes, the TCON the
> CRTC. Roughly.). However, those were sharing some encoders (HDMI,
> DSI) after that.
> 
> So we need to have a single DRM device taking care of the multiple
> display engines, which essentialy means that we have to decouple the
> DRM device from the display engine. This was done in the earlier
> designs using an additional node with a list of phandles to the
> display engines in the system, and obviously, I'd prefer to have some
> consistency and reuse the same thing.
> 
> But the current approach doesn't work and will require some DT
> modifications if that case happens again, which we can't do because of
> the DT ABI.

-- 
Regards,

Laurent Pinchart

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-12-01  9:19               ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01  9:19 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

On Thursday 01 Dec 2016 10:13:13 Maxime Ripard wrote:
> On Wed, Nov 30, 2016 at 12:12:55PM +0200, Laurent Pinchart wrote:
> >> More, it is not sure that the bridge/DW code would work with Allwinner's
> >> SoCs.
> > 
> > If it doesn't work and can't be made to work in a non-invasive way they it
> > should certainly not be used :-)
> 
> Even if the register layout is completely scrambled, as long as the
> bits themselves aren't (and by comparing the two drivers it looks like
> they haven't changed), you can easily deal with that using the
> regmap_fields, with the two implementations (the original one and the
> scrambled one) providing their register map that way, and the driver
> code using whatever has been provided.

Looking at https://linux-sunxi.org/DWC_HDMI_Controller#DWC_HDMI_Controller it 
seems that an address remapping function could be used.

> >> Eventually, I went the same way as omap/hdmi5: different driver.
> > 
> > I might try to fix that for OMAP5 at some point, we'll see.
> 
> For complex drivers that have already a driver written and a lot of
> testing that already happened, I don't think duplication is a smart
> move.
> 
> >>>   - And finally the fact that we can't have several display engine in
> >>>     parallel, if needs be. This has happened in the past already on
> >>>     Allwinner SoCs, so it's definitely something we should consider in
> >>>     the DT bindings, since we can't break them.
> >> 
> >> IIRC, I proposed my driver before yours, and the DE2 is completely
> >> different from the other display engines.
> >> What you are telling is "add more code to already complex code and have
> >> a big driver for all SoCs in each kernels".
> >> I think it should be better to have small modules, each one treating
> >> specific hardware, and to let only the needed code in the kernel memory
> >> at startup time.
> >> 
> >>> Until those are fixed, I cannot see how this driver can be merged,
> >>> unfortunately.
> >> 
> >> No problem. I just wanted to help people by giving the job I did on the
> >> boards I have. My boards are working for almost one year, fine enough
> >> for I use them as daily desktop computers. I don't want to spend one
> >> more year for having my code in the Linux kernel: there are so much
> >> other exciting things to do...
> > 
> > And you're certainly welcome to contribute drivers to the kernel, that's
> > always appreciated. Of course, to ensure a reasonable level of quality and
> > consistency between drivers, the review process often requires changes to
> > be made to the code being submitted. When it comes to drivers I mostly
> > pay attention to DT bindings, userspace APIs and modification to common
> > code. Driver code itself, as long as it's reasonably clean and doesn't
> > impede development of other drivers or impact system security in an
> > adverse way, is still important but maybe slightly less so. I'll defer to
> > Maxime to come to an agreement on the multiple display engines in
> > parallel problem as I'm not familiar with it for the Allwinner platforms.
> 
> The earlier Allwinner SoCs (with the old display engine), we had some
> SoCs with multiple instances of the display engine and TCON (the
> display engine roughly implementing the planes, the TCON the
> CRTC. Roughly.). However, those were sharing some encoders (HDMI,
> DSI) after that.
> 
> So we need to have a single DRM device taking care of the multiple
> display engines, which essentialy means that we have to decouple the
> DRM device from the display engine. This was done in the earlier
> designs using an additional node with a list of phandles to the
> display engines in the system, and obviously, I'd prefer to have some
> consistency and reuse the same thing.
> 
> But the current approach doesn't work and will require some DT
> modifications if that case happens again, which we can't do because of
> the DT ABI.

-- 
Regards,

Laurent Pinchart

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

* How should we group related devices in DT ? (was Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support)
  2016-12-01  9:13             ` Maxime Ripard
@ 2016-12-01  9:28               ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01  9:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Jean-Francois Moine,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hello,

On Thursday 01 Dec 2016 10:13:13 Maxime Ripard wrote:

[snip]

> The earlier Allwinner SoCs (with the old display engine), we had some
> SoCs with multiple instances of the display engine and TCON (the
> display engine roughly implementing the planes, the TCON the
> CRTC. Roughly.). However, those were sharing some encoders (HDMI,
> DSI) after that.
> 
> So we need to have a single DRM device taking care of the multiple
> display engines, which essentialy means that we have to decouple the
> DRM device from the display engine. This was done in the earlier
> designs using an additional node with a list of phandles to the
> display engines in the system, and obviously, I'd prefer to have some
> consistency and reuse the same thing.

I believe this problem isn't limited to sunxi and should be addressed in a 
more generic way. How should we describe in the device tree that multiple 
instances of a device unrelated from a control point of view are related at 
the hardware level ? There are multiple reasons why we need this, and here are 
a few.

- As described above, unrelated display controller instances that share 
encoders at their output need to be exposed to userspace as a single DRM 
device. This is also the case on Renesas platforms (where the display engines 
are independent except for the "small" detail that output routing is 
controlled through the first display engine).

- On Renesas platforms again a radio-related SPI receiver has multiple 
independent channels that each have their own registers, clocks and 
interrupts, but share the same physical clock and sync pins. They are used to 
receive multiple channels of the same data stream and must be exposed as a 
single V4L2 device to userspace. A generic DT binding RFC is available at 
http://www.spinics.net/lists/devicetree/msg152414.html.

> But the current approach doesn't work and will require some DT
> modifications if that case happens again, which we can't do because of
> the DT ABI.

-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* How should we group related devices in DT ? (was Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support)
@ 2016-12-01  9:28               ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01  9:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Thursday 01 Dec 2016 10:13:13 Maxime Ripard wrote:

[snip]

> The earlier Allwinner SoCs (with the old display engine), we had some
> SoCs with multiple instances of the display engine and TCON (the
> display engine roughly implementing the planes, the TCON the
> CRTC. Roughly.). However, those were sharing some encoders (HDMI,
> DSI) after that.
> 
> So we need to have a single DRM device taking care of the multiple
> display engines, which essentialy means that we have to decouple the
> DRM device from the display engine. This was done in the earlier
> designs using an additional node with a list of phandles to the
> display engines in the system, and obviously, I'd prefer to have some
> consistency and reuse the same thing.

I believe this problem isn't limited to sunxi and should be addressed in a 
more generic way. How should we describe in the device tree that multiple 
instances of a device unrelated from a control point of view are related at 
the hardware level ? There are multiple reasons why we need this, and here are 
a few.

- As described above, unrelated display controller instances that share 
encoders at their output need to be exposed to userspace as a single DRM 
device. This is also the case on Renesas platforms (where the display engines 
are independent except for the "small" detail that output routing is 
controlled through the first display engine).

- On Renesas platforms again a radio-related SPI receiver has multiple 
independent channels that each have their own registers, clocks and 
interrupts, but share the same physical clock and sync pins. They are used to 
receive multiple channels of the same data stream and must be exposed as a 
single V4L2 device to userspace. A generic DT binding RFC is available at 
http://www.spinics.net/lists/devicetree/msg152414.html.

> But the current approach doesn't work and will require some DT
> modifications if that case happens again, which we can't do because of
> the DT ABI.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
  2016-12-01  9:19               ` Laurent Pinchart
@ 2016-12-01  9:42                 ` Maxime Ripard
  -1 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-12-01  9:42 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Jean-Francois Moine,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

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

On Thu, Dec 01, 2016 at 11:19:56AM +0200, Laurent Pinchart wrote:
> Hi Maxime,
> 
> On Thursday 01 Dec 2016 10:13:13 Maxime Ripard wrote:
> > On Wed, Nov 30, 2016 at 12:12:55PM +0200, Laurent Pinchart wrote:
> > >> More, it is not sure that the bridge/DW code would work with Allwinner's
> > >> SoCs.
> > > 
> > > If it doesn't work and can't be made to work in a non-invasive way they it
> > > should certainly not be used :-)
> > 
> > Even if the register layout is completely scrambled, as long as the
> > bits themselves aren't (and by comparing the two drivers it looks like
> > they haven't changed), you can easily deal with that using the
> > regmap_fields, with the two implementations (the original one and the
> > scrambled one) providing their register map that way, and the driver
> > code using whatever has been provided.
> 
> Looking at https://linux-sunxi.org/DWC_HDMI_Controller#DWC_HDMI_Controller it 
> seems that an address remapping function could be used.

Even better.

Maxime

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

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

* [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support
@ 2016-12-01  9:42                 ` Maxime Ripard
  0 siblings, 0 replies; 79+ messages in thread
From: Maxime Ripard @ 2016-12-01  9:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 01, 2016 at 11:19:56AM +0200, Laurent Pinchart wrote:
> Hi Maxime,
> 
> On Thursday 01 Dec 2016 10:13:13 Maxime Ripard wrote:
> > On Wed, Nov 30, 2016 at 12:12:55PM +0200, Laurent Pinchart wrote:
> > >> More, it is not sure that the bridge/DW code would work with Allwinner's
> > >> SoCs.
> > > 
> > > If it doesn't work and can't be made to work in a non-invasive way they it
> > > should certainly not be used :-)
> > 
> > Even if the register layout is completely scrambled, as long as the
> > bits themselves aren't (and by comparing the two drivers it looks like
> > they haven't changed), you can easily deal with that using the
> > regmap_fields, with the two implementations (the original one and the
> > scrambled one) providing their register map that way, and the driver
> > code using whatever has been provided.
> 
> Looking at https://linux-sunxi.org/DWC_HDMI_Controller#DWC_HDMI_Controller it 
> seems that an address remapping function could be used.

Even better.

Maxime

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

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

* Re: Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-12-01  8:55                                           ` [linux-sunxi] " Maxime Ripard
@ 2016-12-01 10:41                                             ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01 10:41 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Icenowy Zheng, moinejf-Re5JQEeQqe8AvxtiuMwx3w,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Dave Airlie,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Thursday 01 Dec 2016 09:55:20 Maxime Ripard wrote:
> On Thu, Dec 01, 2016 at 01:33:30AM +0800, Icenowy Zheng wrote:
> >>>          hdmi-out {
> >>>                  compatible = "hdmi-connector";
> >>>                  type = "a";
> >>>                  /* I2C bus and GPIO references are made up for the
> >>> example */ ddc-i2c-bus = <&i2c4>;
> >>>                  hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
> >> 
> >> the "hdmi-connector" is a big piece of software. It must handle a lot
> >> of more and more exotic connectors.
> >> So, I hope that you have written a "simple-hdmi-connector" which does
> >> nothing but setting the connector type.
> >> Where is it?
> > 
> > I suddenly thought about something...
> > 
> > If a DVI connector instead of a HDMI connector is soldered, how
> > should such a device tree be written?
> 
> Use a dvi-connector instead :)

The HDMI encoder DT node doesn't (and certainly shouldn't) report what type of 
connector is mounted on the board. Having a connector node in DT makes the 
connector type available to the system, allowing the DRM driver to expose the 
right connector type to userspace (it would be confusing for the user to 
report DRM_MODE_CONNECTOR_HDMIA for a DVI connector).

> > How about solder a HDMI-to-VGA bridge on the board? (Maybe there
> > should be "dumb-hdmi-dvi-bridge" and "dumb-hdmi-vga-bridge"
> > drivers?)
> 
> It probably wouldn't be dumb, but yeah, it would definitely be a
> bridge instead of the connector.

-- 
Regards,

Laurent Pinchart

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

* [linux-sunxi] Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-12-01 10:41                                             ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01 10:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 01 Dec 2016 09:55:20 Maxime Ripard wrote:
> On Thu, Dec 01, 2016 at 01:33:30AM +0800, Icenowy Zheng wrote:
> >>>          hdmi-out {
> >>>                  compatible = "hdmi-connector";
> >>>                  type = "a";
> >>>                  /* I2C bus and GPIO references are made up for the
> >>> example */ ddc-i2c-bus = <&i2c4>;
> >>>                  hpd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>
> >> 
> >> the "hdmi-connector" is a big piece of software. It must handle a lot
> >> of more and more exotic connectors.
> >> So, I hope that you have written a "simple-hdmi-connector" which does
> >> nothing but setting the connector type.
> >> Where is it?
> > 
> > I suddenly thought about something...
> > 
> > If a DVI connector instead of a HDMI connector is soldered, how
> > should such a device tree be written?
> 
> Use a dvi-connector instead :)

The HDMI encoder DT node doesn't (and certainly shouldn't) report what type of 
connector is mounted on the board. Having a connector node in DT makes the 
connector type available to the system, allowing the DRM driver to expose the 
right connector type to userspace (it would be confusing for the user to 
report DRM_MODE_CONNECTOR_HDMIA for a DVI connector).

> > How about solder a HDMI-to-VGA bridge on the board? (Maybe there
> > should be "dumb-hdmi-dvi-bridge" and "dumb-hdmi-vga-bridge"
> > drivers?)
> 
> It probably wouldn't be dumb, but yeah, it would definitely be a
> bridge instead of the connector.

-- 
Regards,

Laurent Pinchart

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

* Re: Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-12-01 10:41                                             ` [linux-sunxi] " Laurent Pinchart
@ 2016-12-01 11:30                                               ` Jean-Francois Moine
  -1 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-12-01 11:30 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Maxime Ripard, devicetree-u79uwXL29TY76Z2rM5mHXA,
	moinejf-Re5JQEeQqe8AvxtiuMwx3w,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring, Icenowy Zheng,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Thu, 01 Dec 2016 12:41:20 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:

> > > If a DVI connector instead of a HDMI connector is soldered, how
> > > should such a device tree be written?
> > 
> > Use a dvi-connector instead :)
> 
> The HDMI encoder DT node doesn't (and certainly shouldn't) report what type of 
> connector is mounted on the board. Having a connector node in DT makes the 
> connector type available to the system, allowing the DRM driver to expose the 
> right connector type to userspace (it would be confusing for the user to 
> report DRM_MODE_CONNECTOR_HDMIA for a DVI connector).

The connector type, HDMI or DVI, is known by the EDID.
The user is not interested by the software indication of the connector
type: (s)he knows it because (s)he connected him/herself the display
device.
And the DRM driver does not do anything from the knowledge of the
connector type in the DT. Only the EDID may tell if the display device
may do audio streaming (direct HDMI with audio capability) or not
(direct HDMI without audio, HDMI to DVI cable, DVI physical connector).

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

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

* [linux-sunxi] Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-12-01 11:30                                               ` Jean-Francois Moine
  0 siblings, 0 replies; 79+ messages in thread
From: Jean-Francois Moine @ 2016-12-01 11:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 01 Dec 2016 12:41:20 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> > > If a DVI connector instead of a HDMI connector is soldered, how
> > > should such a device tree be written?
> > 
> > Use a dvi-connector instead :)
> 
> The HDMI encoder DT node doesn't (and certainly shouldn't) report what type of 
> connector is mounted on the board. Having a connector node in DT makes the 
> connector type available to the system, allowing the DRM driver to expose the 
> right connector type to userspace (it would be confusing for the user to 
> report DRM_MODE_CONNECTOR_HDMIA for a DVI connector).

The connector type, HDMI or DVI, is known by the EDID.
The user is not interested by the software indication of the connector
type: (s)he knows it because (s)he connected him/herself the display
device.
And the DRM driver does not do anything from the knowledge of the
connector type in the DT. Only the EDID may tell if the display device
may do audio streaming (direct HDMI with audio capability) or not
(direct HDMI without audio, HDMI to DVI cable, DVI physical connector).

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

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

* Re: Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
  2016-12-01 11:30                                               ` [linux-sunxi] " Jean-Francois Moine
@ 2016-12-01 11:44                                                   ` Laurent Pinchart
  -1 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01 11:44 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Maxime Ripard, devicetree-u79uwXL29TY76Z2rM5mHXA,
	moinejf-Re5JQEeQqe8AvxtiuMwx3w,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Rob Herring, Icenowy Zheng,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Thursday 01 Dec 2016 12:30:09 Jean-Francois Moine wrote:
> On Thu, 01 Dec 2016 12:41:20 +0200 Laurent Pinchart wrote:
> >>> If a DVI connector instead of a HDMI connector is soldered, how
> >>> should such a device tree be written?
> >> 
> >> Use a dvi-connector instead :)
> > 
> > The HDMI encoder DT node doesn't (and certainly shouldn't) report what
> > type of connector is mounted on the board. Having a connector node in DT
> > makes the connector type available to the system, allowing the DRM driver
> > to expose the right connector type to userspace (it would be confusing
> > for the user to report DRM_MODE_CONNECTOR_HDMIA for a DVI connector).
> 
> The connector type, HDMI or DVI, is known by the EDID.
> The user is not interested by the software indication of the connector
> type: (s)he knows it because (s)he connected him/herself the display
> device.

That's not correct. The connector type reported by DRM to userspace can be 
used as a hint to information the user about connectors. Displaying a "Please 
connect a monitor to the HDMI connector" is confusing when the system has a 
DVI connector.

> And the DRM driver does not do anything from the knowledge of the
> connector type in the DT. Only the EDID may tell if the display device
> may do audio streaming (direct HDMI with audio capability) or not
> (direct HDMI without audio, HDMI to DVI cable, DVI physical connector).

-- 
Regards,

Laurent Pinchart

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

* [linux-sunxi] Re: [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
@ 2016-12-01 11:44                                                   ` Laurent Pinchart
  0 siblings, 0 replies; 79+ messages in thread
From: Laurent Pinchart @ 2016-12-01 11:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 01 Dec 2016 12:30:09 Jean-Francois Moine wrote:
> On Thu, 01 Dec 2016 12:41:20 +0200 Laurent Pinchart wrote:
> >>> If a DVI connector instead of a HDMI connector is soldered, how
> >>> should such a device tree be written?
> >> 
> >> Use a dvi-connector instead :)
> > 
> > The HDMI encoder DT node doesn't (and certainly shouldn't) report what
> > type of connector is mounted on the board. Having a connector node in DT
> > makes the connector type available to the system, allowing the DRM driver
> > to expose the right connector type to userspace (it would be confusing
> > for the user to report DRM_MODE_CONNECTOR_HDMIA for a DVI connector).
> 
> The connector type, HDMI or DVI, is known by the EDID.
> The user is not interested by the software indication of the connector
> type: (s)he knows it because (s)he connected him/herself the display
> device.

That's not correct. The connector type reported by DRM to userspace can be 
used as a hint to information the user about connectors. Displaying a "Please 
connect a monitor to the HDMI connector" is confusing when the system has a 
DVI connector.

> And the DRM driver does not do anything from the knowledge of the
> connector type in the DT. Only the EDID may tell if the display device
> may do audio streaming (direct HDMI with audio capability) or not
> (direct HDMI without audio, HDMI to DVI cable, DVI physical connector).

-- 
Regards,

Laurent Pinchart

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

end of thread, other threads:[~2016-12-01 11:44 UTC | newest]

Thread overview: 79+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-29 10:18 [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support Jean-Francois Moine
2016-11-29 10:18 ` Jean-Francois Moine
     [not found] ` <cover.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
2016-11-28 14:23   ` [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2 Jean-Francois Moine
2016-11-28 14:23     ` Jean-Francois Moine
     [not found]     ` <592a49a2d9505fc0f09b555847892fb7a4047530.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
2016-11-29 14:30       ` Daniel Vetter
2016-11-29 14:30         ` Daniel Vetter
     [not found]         ` <20161129143051.vn6exubsirwcauag-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2016-11-29 14:33           ` Icenowy Zheng
2016-11-29 14:33             ` [linux-sunxi] " Icenowy Zheng
2016-11-28 18:02   ` [PATCH v7 2/8] drm/sun8i: Add DT bindings documentation of " Jean-Francois Moine
2016-11-28 18:02     ` Jean-Francois Moine
     [not found]     ` <92fc53084274d5ab04fe28f2dc0b16cf5d94481e.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
2016-11-29 18:41       ` Laurent Pinchart
2016-11-29 18:41         ` Laurent Pinchart
2016-11-29 18:45         ` Laurent Pinchart
2016-11-29 18:45           ` Laurent Pinchart
2016-11-29  8:39   ` [PATCH v7 3/8] drm: sun8i: add HDMI video support to A83T and H3 Jean-Francois Moine
2016-11-29  8:39     ` Jean-Francois Moine
2016-11-29  9:08   ` [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI Jean-Francois Moine
2016-11-29  9:08     ` Jean-Francois Moine
     [not found]     ` <cd1caff75b6069af98680f78d672546748cb43c5.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
2016-11-29 18:46       ` Laurent Pinchart
2016-11-29 18:46         ` Laurent Pinchart
2016-11-29 19:27         ` Jean-Francois Moine
2016-11-29 19:27           ` Jean-Francois Moine
     [not found]           ` <20161129202751.e94f72a1b9e2c2fb064e9933-GANU6spQydw@public.gmane.org>
2016-11-29 19:33             ` Laurent Pinchart
2016-11-29 19:33               ` Laurent Pinchart
2016-11-29 20:04               ` Jean-Francois Moine
2016-11-29 20:04                 ` Jean-Francois Moine
     [not found]                 ` <20161129210455.d0c8450d3ceb83c1abe521b0-GANU6spQydw@public.gmane.org>
2016-11-29 20:10                   ` Laurent Pinchart
2016-11-29 20:10                     ` Laurent Pinchart
2016-11-30  8:12                     ` Jean-Francois Moine
2016-11-30  8:12                       ` Jean-Francois Moine
     [not found]                       ` <20161130091208.f38cd79f1cc5d27b27f3b130-GANU6spQydw@public.gmane.org>
2016-11-30  8:20                         ` Laurent Pinchart
2016-11-30  8:20                           ` Laurent Pinchart
2016-11-30  9:27                           ` Jean-Francois Moine
2016-11-30  9:27                             ` Jean-Francois Moine
     [not found]                             ` <20161130102757.9eec1f7f3377d0f4787e3829-GANU6spQydw@public.gmane.org>
2016-11-30  9:52                               ` Laurent Pinchart
2016-11-30  9:52                                 ` Laurent Pinchart
2016-11-30 10:44                                 ` Jean-Francois Moine
2016-11-30 10:44                                   ` Jean-Francois Moine
     [not found]                                   ` <20161130114415.2280151e2965280733a629e5-GANU6spQydw@public.gmane.org>
2016-11-30 17:33                                     ` Icenowy Zheng
2016-11-30 17:33                                       ` [linux-sunxi] " Icenowy Zheng
     [not found]                                       ` <1971681480527210-RAIFsGP/6J5uio3avFS2gg@public.gmane.org>
2016-12-01  8:55                                         ` Maxime Ripard
2016-12-01  8:55                                           ` [linux-sunxi] " Maxime Ripard
2016-12-01 10:41                                           ` Laurent Pinchart
2016-12-01 10:41                                             ` [linux-sunxi] " Laurent Pinchart
2016-12-01 11:30                                             ` Jean-Francois Moine
2016-12-01 11:30                                               ` [linux-sunxi] " Jean-Francois Moine
     [not found]                                               ` <20161201123009.d2b4d9d389070200c54138ee-GANU6spQydw@public.gmane.org>
2016-12-01 11:44                                                 ` Laurent Pinchart
2016-12-01 11:44                                                   ` [linux-sunxi] " Laurent Pinchart
2016-11-30 17:24                               ` Icenowy Zheng
2016-11-30 17:24                                 ` [linux-sunxi] " Icenowy Zheng
2016-11-29 10:10   ` [PATCH v7 5/8] clk: sunxi-ng: define the PLL DE clock Jean-Francois Moine
2016-11-29 10:10     ` Jean-Francois Moine
2016-11-29 10:12   ` [PATCH v7 6/8] ARM: dts: sun8i-h3: add HDMI video nodes Jean-Francois Moine
2016-11-29 10:12     ` Jean-Francois Moine
2016-11-29 10:14   ` [PATCH v7 7/8] ARM: dts: sun8i-h3: Add HDMI video to the Banana Pi M2+ Jean-Francois Moine
2016-11-29 10:14     ` Jean-Francois Moine
2016-11-29 10:16   ` [PATCH v7 8/8] ARM: dts: sun8i-h3: Add HDMI video to the Orange PI 2 Jean-Francois Moine
2016-11-29 10:16     ` Jean-Francois Moine
2016-11-29 21:36   ` [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support Maxime Ripard
2016-11-29 21:36     ` Maxime Ripard
2016-11-29 22:47     ` Jernej Skrabec
     [not found]       ` <8398357e-5c5e-4d76-9022-1c668aff5076-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
2016-11-29 22:56         ` Laurent Pinchart
2016-11-29 22:56           ` Laurent Pinchart
2016-11-29 23:24           ` Jernej Skrabec
     [not found]             ` <239b60f3-3ea7-4aa7-8e8d-353e1a1d4ee5-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
2016-11-30  8:08               ` Laurent Pinchart
2016-11-30  8:08                 ` Laurent Pinchart
2016-11-30 18:04                 ` Jernej Skrabec
2016-11-30  9:05     ` Jean-Francois Moine
2016-11-30  9:05       ` Jean-Francois Moine
     [not found]       ` <20161130100545.44546ed28896cdb9768f68cd-GANU6spQydw@public.gmane.org>
2016-11-30 10:12         ` Laurent Pinchart
2016-11-30 10:12           ` Laurent Pinchart
2016-12-01  9:13           ` Maxime Ripard
2016-12-01  9:13             ` Maxime Ripard
2016-12-01  9:19             ` Laurent Pinchart
2016-12-01  9:19               ` Laurent Pinchart
2016-12-01  9:42               ` Maxime Ripard
2016-12-01  9:42                 ` Maxime Ripard
2016-12-01  9:28             ` How should we group related devices in DT ? (was Re: [PATCH v7 0/8] drm: sun8i: Add DE2 HDMI video support) Laurent Pinchart
2016-12-01  9:28               ` Laurent Pinchart

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