[PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2

Daniel Vetter daniel at ffwll.ch
Tue Nov 29 14:30:51 UTC 2016


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 at free.fr>

Scrolled around a bit, seemed all reasonable.

Acked-by: Daniel Vetter <daniel.vetter at 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 at 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 at 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 at 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 at 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


More information about the dri-devel mailing list