diff for duplicates of <825691480430000@web35j.yandex.ru> diff --git a/a/1.txt b/N1/1.txt index 145530a..656effd 100644 --- a/a/1.txt +++ b/N1/1.txt @@ -1,16 +1,16 @@ -29.11.2016, 22:30, "Daniel Vetter" <daniel@ffwll.ch>: +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. +>> 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> +>> Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org> > > Scrolled around a bit, seemed all reasonable. > -> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> +> 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 @@ -23,1723 +23,1723 @@ 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 +>> --- +>> 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" +>> 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/sun4i/Kconfig" >> ->> ?+source "drivers/gpu/drm/sun8i/Kconfig" ->> ?+ ->> ??source "drivers/gpu/drm/omapdrm/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 +>> 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 at lists.freedesktop.org ->> ?https://lists.freedesktop.org/mailman/listinfo/dri-devel +>> _______________________________________________ +>> dri-devel mailing list +>> dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org +>> https://lists.freedesktop.org/mailman/listinfo/dri-devel > > -- > Daniel Vetter @@ -1748,5 +1748,10 @@ It's called "DE 2.0" in the user manual of the SoCs. > > -- > 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. +> 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. diff --git a/a/content_digest b/N1/content_digest index fdb7c0b..e8f9c05 100644 --- a/a/content_digest +++ b/N1/content_digest @@ -1,25 +1,34 @@ "ref\0cover.1480414715.git.moinejf@free.fr\0" "ref\0592a49a2d9505fc0f09b555847892fb7a4047530.1480414715.git.moinejf@free.fr\0" "ref\020161129143051.vn6exubsirwcauag@phenom.ffwll.local\0" - "From\0icenowy@aosc.xyz (Icenowy Zheng)\0" - "Subject\0[linux-sunxi] Re: [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2\0" + "ref\020161129143051.vn6exubsirwcauag-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org\0" + "From\0Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>\0" + "Subject\0Re: Re: [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2\0" "Date\0Tue, 29 Nov 2016 22:33:20 +0800\0" - "To\0linux-arm-kernel@lists.infradead.org\0" + "To\0daniel-/w4YWyX8dFk@public.gmane.org <daniel-/w4YWyX8dFk@public.gmane.org>" + " Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>\0" + "Cc\0Dave Airlie <airlied-cv59FeDIM0c@public.gmane.org>" + Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> + Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> + devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org <devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org> + linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org <linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org> + linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org <linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org> + " dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org <dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org>\0" "\00:1\0" "b\0" "\n" "\n" - "29.11.2016, 22:30, \"Daniel Vetter\" <daniel@ffwll.ch>:\n" + "29.11.2016, 22:30, \"Daniel Vetter\" <daniel-/w4YWyX8dFk@public.gmane.org>:\n" "> On Mon, Nov 28, 2016 at 03:23:54PM +0100, Jean-Francois Moine wrote:\n" - ">> ?Allwinner's recent SoCs, as A64, A83T and H3, contain a new display\n" - ">> ?engine, DE2.\n" - ">> ?This patch adds a DRM video driver for this device.\n" + ">> \302\240Allwinner's recent SoCs, as A64, A83T and H3, contain a new display\n" + ">> \302\240engine, DE2.\n" + ">> \302\240This patch adds a DRM video driver for this device.\n" ">>\n" - ">> ?Signed-off-by: Jean-Francois Moine <moinejf@free.fr>\n" + ">> \302\240Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>\n" ">\n" "> Scrolled around a bit, seemed all reasonable.\n" ">\n" - "> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>\n" + "> Acked-by: Daniel Vetter <daniel.vetter-/w4YWyX8dFk@public.gmane.org>\n" ">\n" "> Not sure a new driver for each chip is reasonable, experience says that\n" "> long-term you want to share quite a pile of code between different hw\n" @@ -32,1723 +41,1723 @@ "It's called \"DE 2.0\" in the user manual of the SoCs.\n" "\n" ">\n" - ">> ?---\n" - ">> ??drivers/gpu/drm/Kconfig | 2 +\n" - ">> ??drivers/gpu/drm/Makefile | 1 +\n" - ">> ??drivers/gpu/drm/sun8i/Kconfig | 19 +\n" - ">> ??drivers/gpu/drm/sun8i/Makefile | 7 +\n" - ">> ??drivers/gpu/drm/sun8i/de2_crtc.c | 449 +++++++++++++++++++++++\n" - ">> ??drivers/gpu/drm/sun8i/de2_crtc.h | 50 +++\n" - ">> ??drivers/gpu/drm/sun8i/de2_drv.c | 317 ++++++++++++++++\n" - ">> ??drivers/gpu/drm/sun8i/de2_drv.h | 48 +++\n" - ">> ??drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++\n" - ">> ??9 files changed, 1627 insertions(+)\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/Kconfig\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/Makefile\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h\n" - ">> ??create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c\n" + ">> \302\240---\n" + ">> \302\240\302\240drivers/gpu/drm/Kconfig | 2 +\n" + ">> \302\240\302\240drivers/gpu/drm/Makefile | 1 +\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/Kconfig | 19 +\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/Makefile | 7 +\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/de2_crtc.c | 449 +++++++++++++++++++++++\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/de2_crtc.h | 50 +++\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/de2_drv.c | 317 ++++++++++++++++\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/de2_drv.h | 48 +++\n" + ">> \302\240\302\240drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++\n" + ">> \302\240\302\2409 files changed, 1627 insertions(+)\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/Kconfig\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/Makefile\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h\n" + ">> \302\240\302\240create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c\n" ">>\n" - ">> ?diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig\n" - ">> ?index 95fc041..bb1bfbc 100644\n" - ">> ?--- a/drivers/gpu/drm/Kconfig\n" - ">> ?+++ b/drivers/gpu/drm/Kconfig\n" - ">> ?@@ -202,6 +202,8 @@ source \"drivers/gpu/drm/shmobile/Kconfig\"\n" + ">> \302\240diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig\n" + ">> \302\240index 95fc041..bb1bfbc 100644\n" + ">> \302\240--- a/drivers/gpu/drm/Kconfig\n" + ">> \302\240+++ b/drivers/gpu/drm/Kconfig\n" + ">> \302\240@@ -202,6 +202,8 @@ source \"drivers/gpu/drm/shmobile/Kconfig\"\n" ">>\n" - ">> ??source \"drivers/gpu/drm/sun4i/Kconfig\"\n" + ">> \302\240\302\240source \"drivers/gpu/drm/sun4i/Kconfig\"\n" ">>\n" - ">> ?+source \"drivers/gpu/drm/sun8i/Kconfig\"\n" - ">> ?+\n" - ">> ??source \"drivers/gpu/drm/omapdrm/Kconfig\"\n" + ">> \302\240+source \"drivers/gpu/drm/sun8i/Kconfig\"\n" + ">> \302\240+\n" + ">> \302\240\302\240source \"drivers/gpu/drm/omapdrm/Kconfig\"\n" ">>\n" - ">> ??source \"drivers/gpu/drm/tilcdc/Kconfig\"\n" - ">> ?diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile\n" - ">> ?index 883f3e7..3e1eaa0 100644\n" - ">> ?--- a/drivers/gpu/drm/Makefile\n" - ">> ?+++ b/drivers/gpu/drm/Makefile\n" - ">> ?@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/\n" - ">> ??obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/\n" - ">> ??obj-y += omapdrm/\n" - ">> ??obj-$(CONFIG_DRM_SUN4I) += sun4i/\n" - ">> ?+obj-$(CONFIG_DRM_SUN8I) += sun8i/\n" - ">> ??obj-y += tilcdc/\n" - ">> ??obj-$(CONFIG_DRM_QXL) += qxl/\n" - ">> ??obj-$(CONFIG_DRM_BOCHS) += bochs/\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..6940895\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/Kconfig\n" - ">> ?@@ -0,0 +1,19 @@\n" - ">> ?+#\n" - ">> ?+# Allwinner DE2 Video configuration\n" - ">> ?+#\n" - ">> ?+\n" - ">> ?+config DRM_SUN8I\n" - ">> ?+ bool\n" - ">> ?+\n" - ">> ?+config DRM_SUN8I_DE2\n" - ">> ?+ tristate \"Support for Allwinner Video with DE2 interface\"\n" - ">> ?+ depends on DRM && OF\n" - ">> ?+ depends on ARCH_SUNXI || COMPILE_TEST\n" - ">> ?+ select DRM_GEM_CMA_HELPER\n" - ">> ?+ select DRM_KMS_CMA_HELPER\n" - ">> ?+ select DRM_KMS_HELPER\n" - ">> ?+ select DRM_SUN8I\n" - ">> ?+ help\n" - ">> ?+ Choose this option if your Allwinner chipset has the DE2 interface\n" - ">> ?+ as the A64, A83T and H3. If M is selected the module will be called\n" - ">> ?+ sun8i-de2-drm.\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..f107919\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/Makefile\n" - ">> ?@@ -0,0 +1,7 @@\n" - ">> ?+#\n" - ">> ?+# Makefile for Allwinner's sun8i DRM device driver\n" - ">> ?+#\n" - ">> ?+\n" - ">> ?+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o\n" - ">> ?+\n" - ">> ?+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..4e94ccc\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/de2_crtc.c\n" - ">> ?@@ -0,0 +1,449 @@\n" - ">> ?+/*\n" - ">> ?+ * Allwinner DRM driver - DE2 CRTC\n" - ">> ?+ *\n" - ">> ?+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>\n" - ">> ?+ *\n" - ">> ?+ * This program is free software; you can redistribute it and/or\n" - ">> ?+ * modify it under the terms of the GNU General Public License as\n" - ">> ?+ * published by the Free Software Foundation; either version 2 of\n" - ">> ?+ * the License, or (at your option) any later version.\n" - ">> ?+ */\n" - ">> ?+\n" - ">> ?+#include <linux/component.h>\n" - ">> ?+#include <drm/drm_crtc_helper.h>\n" - ">> ?+#include <drm/drm_atomic_helper.h>\n" - ">> ?+#include <linux/io.h>\n" - ">> ?+#include <linux/of_irq.h>\n" - ">> ?+#include <linux/of_graph.h>\n" - ">> ?+\n" - ">> ?+#include \"de2_drv.h\"\n" - ">> ?+#include \"de2_crtc.h\"\n" - ">> ?+\n" - ">> ?+/* I/O map */\n" - ">> ?+\n" - ">> ?+#define TCON_GCTL_REG 0x00\n" - ">> ?+#define TCON_GCTL_TCON_ENABLE BIT(31)\n" - ">> ?+#define TCON_GINT0_REG 0x04\n" - ">> ?+#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)\n" - ">> ?+#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)\n" - ">> ?+#define TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)\n" - ">> ?+#define TCON0_CTL_REG 0x40\n" - ">> ?+#define TCON0_CTL_TCON_ENABLE BIT(31)\n" - ">> ?+#define TCON1_CTL_REG 0x90\n" - ">> ?+#define TCON1_CTL_TCON_ENABLE BIT(31)\n" - ">> ?+#define TCON1_CTL_INTERLACE_ENABLE BIT(20)\n" - ">> ?+#define TCON1_CTL_Start_Delay_SHIFT 4\n" - ">> ?+#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)\n" - ">> ?+#define TCON1_BASIC0_REG 0x94 /* XI/YI */\n" - ">> ?+#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */\n" - ">> ?+#define TCON1_BASIC2_REG 0x9c /* XO/YO */\n" - ">> ?+#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */\n" - ">> ?+#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */\n" - ">> ?+#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */\n" - ">> ?+#define TCON1_PS_SYNC_REG 0xb0\n" - ">> ?+#define TCON1_IO_POL_REG 0xf0\n" - ">> ?+#define TCON1_IO_POL_IO0_inv BIT(24)\n" - ">> ?+#define TCON1_IO_POL_IO1_inv BIT(25)\n" - ">> ?+#define TCON1_IO_POL_IO2_inv BIT(26)\n" - ">> ?+#define TCON1_IO_TRI_REG 0xf4\n" - ">> ?+#define TCON_CEU_CTL_REG 0x100\n" - ">> ?+#define TCON_CEU_CTL_ceu_en BIT(31)\n" - ">> ?+#define TCON1_FILL_CTL_REG 0x300\n" - ">> ?+#define TCON1_FILL_START0_REG 0x304\n" - ">> ?+#define TCON1_FILL_END0_REG 0x308\n" - ">> ?+#define TCON1_FILL_DATA0_REG 0x30c\n" - ">> ?+\n" - ">> ?+#define XY(x, y) (((x) << 16) | (y))\n" - ">> ?+\n" - ">> ?+#define andl_relaxed(addr, val) \\\n" - ">> ?+ writel_relaxed(readl_relaxed(addr) & val, addr)\n" - ">> ?+#define orl_relaxed(addr, val) \\\n" - ">> ?+ writel_relaxed(readl_relaxed(addr) | val, addr)\n" - ">> ?+\n" - ">> ?+/* vertical blank functions */\n" - ">> ?+\n" - ">> ?+static void de2_atomic_flush(struct drm_crtc *crtc,\n" - ">> ?+ struct drm_crtc_state *old_state)\n" - ">> ?+{\n" - ">> ?+ struct drm_pending_vblank_event *event = crtc->state->event;\n" - ">> ?+\n" - ">> ?+ if (event) {\n" - ">> ?+ crtc->state->event = NULL;\n" - ">> ?+ spin_lock_irq(&crtc->dev->event_lock);\n" - ">> ?+ if (drm_crtc_vblank_get(crtc) == 0)\n" - ">> ?+ drm_crtc_arm_vblank_event(crtc, event);\n" - ">> ?+ else\n" - ">> ?+ drm_crtc_send_vblank_event(crtc, event);\n" - ">> ?+ spin_unlock_irq(&crtc->dev->event_lock);\n" - ">> ?+ }\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static irqreturn_t de2_lcd_irq(int irq, void *dev_id)\n" - ">> ?+{\n" - ">> ?+ struct lcd *lcd = (struct lcd *) dev_id;\n" - ">> ?+ u32 isr;\n" - ">> ?+\n" - ">> ?+ isr = readl_relaxed(lcd->mmio + TCON_GINT0_REG);\n" - ">> ?+\n" - ">> ?+ drm_crtc_handle_vblank(&lcd->crtc);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(isr &\n" - ">> ?+ ~(TCON_GINT0_TCON1_Vb_Int_Flag |\n" - ">> ?+ TCON_GINT0_TCON1_Vb_Line_Int_Flag),\n" - ">> ?+ lcd->mmio + TCON_GINT0_REG);\n" - ">> ?+\n" - ">> ?+ return IRQ_HANDLED;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)\n" - ">> ?+{\n" - ">> ?+ struct priv *priv = drm_to_priv(drm);\n" - ">> ?+ struct lcd *lcd = priv->lcds[crtc_ix];\n" - ">> ?+\n" - ">> ?+ orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);\n" - ">> ?+\n" - ">> ?+ return 0;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)\n" - ">> ?+{\n" - ">> ?+ struct priv *priv = drm_to_priv(drm);\n" - ">> ?+ struct lcd *lcd = priv->lcds[crtc_ix];\n" - ">> ?+\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+void de2_vblank_reset(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ drm_crtc_vblank_reset(&lcd->crtc);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/* frame functions */\n" - ">> ?+static int de2_crtc_set_clock(struct lcd *lcd, int rate)\n" - ">> ?+{\n" - ">> ?+ struct clk *parent_clk;\n" - ">> ?+ u32 parent_rate;\n" - ">> ?+ int ret;\n" - ">> ?+\n" - ">> ?+ /* determine and set the best rate for the parent clock (pll-video) */\n" - ">> ?+ if ((270000 * 2) % rate == 0)\n" - ">> ?+ parent_rate = 270000000;\n" - ">> ?+ else if (297000 % rate == 0)\n" - ">> ?+ parent_rate = 297000000;\n" - ">> ?+ else\n" - ">> ?+ return -EINVAL; /* unsupported clock */\n" - ">> ?+\n" - ">> ?+ parent_clk = clk_get_parent(lcd->clk);\n" - ">> ?+\n" - ">> ?+ ret = clk_set_rate(parent_clk, parent_rate);\n" - ">> ?+ if (ret) {\n" - ">> ?+ dev_err(lcd->dev, \"set parent rate failed %d\\n\", ret);\n" - ">> ?+ return ret;\n" - ">> ?+ }\n" - ">> ?+ ret = clk_set_rate(lcd->clk, rate * 1000);\n" - ">> ?+ if (ret) {\n" - ">> ?+ dev_err(lcd->dev, \"set rate failed %d\\n\", ret);\n" - ">> ?+ return ret;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ /* enable the clock */\n" - ">> ?+ reset_control_deassert(lcd->reset);\n" - ">> ?+ clk_prepare_enable(lcd->bus);\n" - ">> ?+ clk_prepare_enable(lcd->clk);\n" - ">> ?+\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_tcon_init(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);\n" - ">> ?+\n" - ">> ?+ /* disable/ack interrupts */\n" - ">> ?+ writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_tcon_enable(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ struct drm_crtc *crtc = &lcd->crtc;\n" - ">> ?+ const struct drm_display_mode *mode = &crtc->mode;\n" - ">> ?+ int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;\n" - ">> ?+ int start_delay;\n" - ">> ?+ u32 data;\n" - ">> ?+\n" - ">> ?+ orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);\n" - ">> ?+\n" - ">> ?+ data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);\n" - ">> ?+ writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);\n" - ">> ?+ writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);\n" - ">> ?+ writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);\n" - ">> ?+ writel_relaxed(XY(mode->htotal - 1,\n" - ">> ?+ mode->htotal - mode->hsync_start - 1),\n" - ">> ?+ lcd->mmio + TCON1_BASIC3_REG);\n" - ">> ?+ writel_relaxed(XY(mode->vtotal * (3 - interlace),\n" - ">> ?+ mode->vtotal - mode->vsync_start - 1),\n" - ">> ?+ lcd->mmio + TCON1_BASIC4_REG);\n" - ">> ?+ writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,\n" - ">> ?+ mode->vsync_end - mode->vsync_start - 1),\n" - ">> ?+ lcd->mmio + TCON1_BASIC5_REG);\n" - ">> ?+\n" - ">> ?+ data = TCON1_IO_POL_IO2_inv;\n" - ">> ?+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)\n" - ">> ?+ data |= TCON1_IO_POL_IO0_inv;\n" - ">> ?+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)\n" - ">> ?+ data |= TCON1_IO_POL_IO1_inv;\n" - ">> ?+ writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);\n" - ">> ?+\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);\n" - ">> ?+\n" - ">> ?+ if (interlace == 2)\n" - ">> ?+ orl_relaxed(lcd->mmio + TCON1_CTL_REG,\n" - ">> ?+ TCON1_CTL_INTERLACE_ENABLE);\n" - ">> ?+ else\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON1_CTL_REG,\n" - ">> ?+ ~TCON1_CTL_INTERLACE_ENABLE);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);\n" - ">> ?+ writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);\n" - ">> ?+ writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);\n" - ">> ?+ writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);\n" - ">> ?+\n" - ">> ?+ start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;\n" - ">> ?+ if (start_delay > 31)\n" - ">> ?+ start_delay = 31;\n" - ">> ?+ data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);\n" - ">> ?+ data &= ~TCON1_CTL_Start_Delay_MASK;\n" - ">> ?+ data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;\n" - ">> ?+ writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);\n" - ">> ?+\n" - ">> ?+ orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_tcon_disable(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);\n" - ">> ?+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_crtc_enable(struct drm_crtc *crtc)\n" - ">> ?+{\n" - ">> ?+ struct lcd *lcd = crtc_to_lcd(crtc);\n" - ">> ?+ struct drm_display_mode *mode = &crtc->mode;\n" - ">> ?+\n" - ">> ?+ if (de2_crtc_set_clock(lcd, mode->clock) < 0)\n" - ">> ?+ return;\n" - ">> ?+ lcd->clk_enabled = true;\n" - ">> ?+\n" - ">> ?+ /* start the TCON and the DE */\n" - ">> ?+ de2_tcon_enable(lcd);\n" - ">> ?+ de2_de_enable(lcd);\n" - ">> ?+\n" - ">> ?+ /* turn on blanking interrupt */\n" - ">> ?+ drm_crtc_vblank_on(crtc);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_crtc_disable(struct drm_crtc *crtc,\n" - ">> ?+ struct drm_crtc_state *old_crtc_state)\n" - ">> ?+{\n" - ">> ?+ struct lcd *lcd = crtc_to_lcd(crtc);\n" - ">> ?+\n" - ">> ?+ if (!lcd->clk_enabled)\n" - ">> ?+ return; /* already disabled */\n" - ">> ?+ lcd->clk_enabled = false;\n" - ">> ?+\n" - ">> ?+ de2_de_disable(lcd);\n" - ">> ?+\n" - ">> ?+ drm_crtc_vblank_off(crtc);\n" - ">> ?+\n" - ">> ?+ de2_tcon_disable(lcd);\n" - ">> ?+\n" - ">> ?+ clk_disable_unprepare(lcd->clk);\n" - ">> ?+ clk_disable_unprepare(lcd->bus);\n" - ">> ?+ reset_control_assert(lcd->reset);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct drm_crtc_funcs de2_crtc_funcs = {\n" - ">> ?+ .destroy = drm_crtc_cleanup,\n" - ">> ?+ .set_config = drm_atomic_helper_set_config,\n" - ">> ?+ .page_flip = drm_atomic_helper_page_flip,\n" - ">> ?+ .reset = drm_atomic_helper_crtc_reset,\n" - ">> ?+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,\n" - ">> ?+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {\n" - ">> ?+ .atomic_flush = de2_atomic_flush,\n" - ">> ?+ .enable = de2_crtc_enable,\n" - ">> ?+ .atomic_disable = de2_crtc_disable,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+/* device init */\n" - ">> ?+static int de2_lcd_bind(struct device *dev, struct device *master,\n" - ">> ?+ void *data)\n" - ">> ?+{\n" - ">> ?+ struct drm_device *drm = data;\n" - ">> ?+ struct priv *priv = drm_to_priv(drm);\n" - ">> ?+ struct lcd *lcd = dev_get_drvdata(dev);\n" - ">> ?+ struct drm_crtc *crtc = &lcd->crtc;\n" - ">> ?+ int ret, i, crtc_ix;\n" - ">> ?+\n" - ">> ?+ lcd->priv = priv;\n" - ">> ?+\n" - ">> ?+ /* set the CRTC reference */\n" - ">> ?+ crtc_ix = drm_crtc_index(crtc);\n" - ">> ?+ if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {\n" - ">> ?+ dev_err(drm->dev, \"Bad crtc index\");\n" - ">> ?+ return -ENOENT;\n" - ">> ?+ }\n" - ">> ?+ priv->lcds[crtc_ix] = lcd;\n" - ">> ?+\n" - ">> ?+ /* and the mixer index (DT port index in the DE) */\n" - ">> ?+ for (i = 0; ; i++) {\n" - ">> ?+ struct device_node *port;\n" - ">> ?+\n" - ">> ?+ port = of_parse_phandle(drm->dev->of_node, \"ports\", i);\n" - ">> ?+ if (!port)\n" - ">> ?+ break;\n" - ">> ?+ if (port == lcd->crtc.port) {\n" - ">> ?+ lcd->mixer = i;\n" - ">> ?+ break;\n" - ">> ?+ }\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ ret = de2_plane_init(drm, lcd);\n" - ">> ?+ if (ret < 0)\n" - ">> ?+ return ret;\n" - ">> ?+\n" - ">> ?+ drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);\n" - ">> ?+\n" - ">> ?+ return drm_crtc_init_with_planes(drm, crtc,\n" - ">> ?+ &lcd->planes[DE2_PRIMARY_PLANE],\n" - ">> ?+ &lcd->planes[DE2_CURSOR_PLANE],\n" - ">> ?+ &de2_crtc_funcs, NULL);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_lcd_unbind(struct device *dev, struct device *master,\n" - ">> ?+ void *data)\n" - ">> ?+{\n" - ">> ?+ struct platform_device *pdev = to_platform_device(dev);\n" - ">> ?+ struct lcd *lcd = platform_get_drvdata(pdev);\n" - ">> ?+\n" - ">> ?+ if (lcd->priv)\n" - ">> ?+ lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct component_ops de2_lcd_ops = {\n" - ">> ?+ .bind = de2_lcd_bind,\n" - ">> ?+ .unbind = de2_lcd_unbind,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static int de2_lcd_probe(struct platform_device *pdev)\n" - ">> ?+{\n" - ">> ?+ struct device *dev = &pdev->dev;\n" - ">> ?+ struct device_node *np = dev->of_node, *tmp, *parent, *port;\n" - ">> ?+ struct lcd *lcd;\n" - ">> ?+ struct resource *res;\n" - ">> ?+ int id, irq, ret;\n" - ">> ?+\n" - ">> ?+ lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);\n" - ">> ?+ if (!lcd)\n" - ">> ?+ return -ENOMEM;\n" - ">> ?+\n" - ">> ?+ dev_set_drvdata(dev, lcd);\n" - ">> ?+ lcd->dev = dev;\n" - ">> ?+ lcd->mixer = id;\n" - ">> ?+\n" - ">> ?+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\n" - ">> ?+ if (!res) {\n" - ">> ?+ dev_err(dev, \"failed to get memory resource\\n\");\n" - ">> ?+ return -EINVAL;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ lcd->mmio = devm_ioremap_resource(dev, res);\n" - ">> ?+ if (IS_ERR(lcd->mmio)) {\n" - ">> ?+ dev_err(dev, \"failed to map registers\\n\");\n" - ">> ?+ return PTR_ERR(lcd->mmio);\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ /* possible CRTC */\n" - ">> ?+ parent = np;\n" - ">> ?+ tmp = of_get_child_by_name(np, \"ports\");\n" - ">> ?+ if (tmp)\n" - ">> ?+ parent = tmp;\n" - ">> ?+ port = of_get_child_by_name(parent, \"port\");\n" - ">> ?+ of_node_put(tmp);\n" - ">> ?+ if (!port) {\n" - ">> ?+ dev_err(dev, \"no port node\\n\");\n" - ">> ?+ return -ENXIO;\n" - ">> ?+ }\n" - ">> ?+ lcd->crtc.port = port;\n" - ">> ?+\n" - ">> ?+ lcd->bus = devm_clk_get(dev, \"bus\");\n" - ">> ?+ if (IS_ERR(lcd->bus)) {\n" - ">> ?+ dev_err(dev, \"get bus clock err %d\\n\", (int) PTR_ERR(lcd->bus));\n" - ">> ?+ ret = PTR_ERR(lcd->bus);\n" - ">> ?+ goto err;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ lcd->clk = devm_clk_get(dev, \"clock\");\n" - ">> ?+ if (IS_ERR(lcd->clk)) {\n" - ">> ?+ ret = PTR_ERR(lcd->clk);\n" - ">> ?+ dev_err(dev, \"get video clock err %d\\n\", ret);\n" - ">> ?+ goto err;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ lcd->reset = devm_reset_control_get(dev, NULL);\n" - ">> ?+ if (IS_ERR(lcd->reset)) {\n" - ">> ?+ ret = PTR_ERR(lcd->reset);\n" - ">> ?+ dev_err(dev, \"get reset err %d\\n\", ret);\n" - ">> ?+ goto err;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ irq = platform_get_irq(pdev, 0);\n" - ">> ?+ if (irq <= 0) {\n" - ">> ?+ dev_err(dev, \"unable to get irq\\n\");\n" - ">> ?+ ret = -EINVAL;\n" - ">> ?+ goto err;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ de2_tcon_init(lcd); /* stop TCON and avoid interrupts */\n" - ">> ?+\n" - ">> ?+ ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,\n" - ">> ?+ dev_name(dev), lcd);\n" - ">> ?+ if (ret < 0) {\n" - ">> ?+ dev_err(dev, \"unable to request irq %d\\n\", irq);\n" - ">> ?+ goto err;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ return component_add(dev, &de2_lcd_ops);\n" - ">> ?+\n" - ">> ?+err:\n" - ">> ?+ of_node_put(lcd->crtc.port);\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static int de2_lcd_remove(struct platform_device *pdev)\n" - ">> ?+{\n" - ">> ?+ struct lcd *lcd = platform_get_drvdata(pdev);\n" - ">> ?+\n" - ">> ?+ component_del(&pdev->dev, &de2_lcd_ops);\n" - ">> ?+\n" - ">> ?+ of_node_put(lcd->crtc.port);\n" - ">> ?+\n" - ">> ?+ return 0;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct of_device_id de2_lcd_ids[] = {\n" - ">> ?+ { .compatible = \"allwinner,sun8i-a83t-tcon\", },\n" - ">> ?+ { }\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+struct platform_driver de2_lcd_platform_driver = {\n" - ">> ?+ .probe = de2_lcd_probe,\n" - ">> ?+ .remove = de2_lcd_remove,\n" - ">> ?+ .driver = {\n" - ">> ?+ .name = \"sun8i-de2-tcon\",\n" - ">> ?+ .of_match_table = of_match_ptr(de2_lcd_ids),\n" - ">> ?+ },\n" - ">> ?+};\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..c0d34a7\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/de2_crtc.h\n" - ">> ?@@ -0,0 +1,50 @@\n" - ">> ?+#ifndef __DE2_CRTC_H__\n" - ">> ?+#define __DE2_CRTC_H__\n" - ">> ?+/*\n" - ">> ?+ * Copyright (C) 2016 Jean-Fran??ois Moine\n" - ">> ?+ *\n" - ">> ?+ * This program is free software; you can redistribute it and/or\n" - ">> ?+ * modify it under the terms of the GNU General Public License as\n" - ">> ?+ * published by the Free Software Foundation; either version 2 of\n" - ">> ?+ * the License, or (at your option) any later version.\n" - ">> ?+ */\n" - ">> ?+\n" - ">> ?+#include <drm/drm_plane_helper.h>\n" - ">> ?+\n" - ">> ?+struct clk;\n" - ">> ?+struct reset_control;\n" - ">> ?+struct priv;\n" - ">> ?+\n" - ">> ?+/* planes */\n" - ">> ?+#define DE2_PRIMARY_PLANE 0\n" - ">> ?+#define DE2_CURSOR_PLANE 1\n" - ">> ?+#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */\n" - ">> ?+\n" - ">> ?+struct lcd {\n" - ">> ?+ void __iomem *mmio;\n" - ">> ?+\n" - ">> ?+ struct device *dev;\n" - ">> ?+ struct drm_crtc crtc;\n" - ">> ?+\n" - ">> ?+ struct priv *priv; /* DRM/DE private data */\n" - ">> ?+\n" - ">> ?+ u8 mixer; /* LCD (mixer) number */\n" - ">> ?+ u8 delayed; /* bitmap of planes with delayed update */\n" - ">> ?+\n" - ">> ?+ u8 clk_enabled; /* used for error in crtc_enable */\n" - ">> ?+\n" - ">> ?+ struct clk *clk;\n" - ">> ?+ struct clk *bus;\n" - ">> ?+ struct reset_control *reset;\n" - ">> ?+\n" - ">> ?+ struct drm_plane planes[DE2_N_PLANES];\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)\n" - ">> ?+\n" - ">> ?+/* in de2_plane.c */\n" - ">> ?+void de2_de_enable(struct lcd *lcd);\n" - ">> ?+void de2_de_disable(struct lcd *lcd);\n" - ">> ?+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);\n" - ">> ?+\n" - ">> ?+#endif /* __DE2_CRTC_H__ */\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..f96babe\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/de2_drv.c\n" - ">> ?@@ -0,0 +1,317 @@\n" - ">> ?+/*\n" - ">> ?+ * Allwinner DRM driver - DE2 DRM driver\n" - ">> ?+ *\n" - ">> ?+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>\n" - ">> ?+ *\n" - ">> ?+ * This program is free software; you can redistribute it and/or\n" - ">> ?+ * modify it under the terms of the GNU General Public License as\n" - ">> ?+ * published by the Free Software Foundation; either version 2 of\n" - ">> ?+ * the License, or (at your option) any later version.\n" - ">> ?+ */\n" - ">> ?+\n" - ">> ?+#include <linux/module.h>\n" - ">> ?+#include <linux/of_device.h>\n" - ">> ?+#include <drm/drm_of.h>\n" - ">> ?+#include <linux/component.h>\n" - ">> ?+#include <drm/drm_atomic_helper.h>\n" - ">> ?+#include <drm/drm_crtc_helper.h>\n" - ">> ?+#include <drm/drm_fb_cma_helper.h>\n" - ">> ?+#include <drm/drm_gem_cma_helper.h>\n" - ">> ?+\n" - ">> ?+#include \"de2_drv.h\"\n" - ">> ?+\n" - ">> ?+#define DRIVER_NAME \"sun8i-de2\"\n" - ">> ?+#define DRIVER_DESC \"Allwinner DRM DE2\"\n" - ">> ?+#define DRIVER_DATE \"20161101\"\n" - ">> ?+#define DRIVER_MAJOR 1\n" - ">> ?+#define DRIVER_MINOR 0\n" - ">> ?+\n" - ">> ?+static const struct of_device_id de2_drm_of_match[] = {\n" - ">> ?+ { .compatible = \"allwinner,sun8i-a83t-display-engine\",\n" - ">> ?+ .data = (void *) SOC_A83T },\n" - ">> ?+ { .compatible = \"allwinner,sun8i-h3-display-engine\",\n" - ">> ?+ .data = (void *) SOC_H3 },\n" - ">> ?+ { },\n" - ">> ?+};\n" - ">> ?+MODULE_DEVICE_TABLE(of, de2_drm_of_match);\n" - ">> ?+\n" - ">> ?+static void de2_fb_output_poll_changed(struct drm_device *drm)\n" - ">> ?+{\n" - ">> ?+ struct priv *priv = drm_to_priv(drm);\n" - ">> ?+\n" - ">> ?+ if (priv->fbdev)\n" - ">> ?+ drm_fbdev_cma_hotplug_event(priv->fbdev);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct drm_mode_config_funcs de2_mode_config_funcs = {\n" - ">> ?+ .fb_create = drm_fb_cma_create,\n" - ">> ?+ .output_poll_changed = de2_fb_output_poll_changed,\n" - ">> ?+ .atomic_check = drm_atomic_helper_check,\n" - ">> ?+ .atomic_commit = drm_atomic_helper_commit,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+/* -- DRM operations -- */\n" - ">> ?+\n" - ">> ?+static void de2_lastclose(struct drm_device *drm)\n" - ">> ?+{\n" - ">> ?+ struct priv *priv = drm_to_priv(drm);\n" - ">> ?+\n" - ">> ?+ if (priv->fbdev)\n" - ">> ?+ drm_fbdev_cma_restore_mode(priv->fbdev);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct file_operations de2_fops = {\n" - ">> ?+ .owner = THIS_MODULE,\n" - ">> ?+ .open = drm_open,\n" - ">> ?+ .release = drm_release,\n" - ">> ?+ .unlocked_ioctl = drm_ioctl,\n" - ">> ?+ .poll = drm_poll,\n" - ">> ?+ .read = drm_read,\n" - ">> ?+ .llseek = no_llseek,\n" - ">> ?+ .mmap = drm_gem_cma_mmap,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static struct drm_driver de2_drm_driver = {\n" - ">> ?+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |\n" - ">> ?+ DRIVER_ATOMIC,\n" - ">> ?+ .lastclose = de2_lastclose,\n" - ">> ?+ .get_vblank_counter = drm_vblank_no_hw_counter,\n" - ">> ?+ .enable_vblank = de2_enable_vblank,\n" - ">> ?+ .disable_vblank = de2_disable_vblank,\n" - ">> ?+ .gem_free_object = drm_gem_cma_free_object,\n" - ">> ?+ .gem_vm_ops = &drm_gem_cma_vm_ops,\n" - ">> ?+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,\n" - ">> ?+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,\n" - ">> ?+ .gem_prime_import = drm_gem_prime_import,\n" - ">> ?+ .gem_prime_export = drm_gem_prime_export,\n" - ">> ?+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,\n" - ">> ?+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,\n" - ">> ?+ .gem_prime_vmap = drm_gem_cma_prime_vmap,\n" - ">> ?+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,\n" - ">> ?+ .gem_prime_mmap = drm_gem_cma_prime_mmap,\n" - ">> ?+ .dumb_create = drm_gem_cma_dumb_create,\n" - ">> ?+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,\n" - ">> ?+ .dumb_destroy = drm_gem_dumb_destroy,\n" - ">> ?+ .fops = &de2_fops,\n" - ">> ?+ .name = DRIVER_NAME,\n" - ">> ?+ .desc = DRIVER_DESC,\n" - ">> ?+ .date = DRIVER_DATE,\n" - ">> ?+ .major = DRIVER_MAJOR,\n" - ">> ?+ .minor = DRIVER_MINOR,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+/*\n" - ">> ?+ * Platform driver\n" - ">> ?+ */\n" - ">> ?+\n" - ">> ?+static int de2_drm_bind(struct device *dev)\n" - ">> ?+{\n" - ">> ?+ struct drm_device *drm;\n" - ">> ?+ struct priv *priv;\n" - ">> ?+ struct resource *res;\n" - ">> ?+ struct lcd *lcd;\n" - ">> ?+ int i, ret;\n" - ">> ?+\n" - ">> ?+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);\n" - ">> ?+ if (!priv)\n" - ">> ?+ return -ENOMEM;\n" - ">> ?+\n" - ">> ?+ drm = &priv->drm;\n" - ">> ?+ dev_set_drvdata(dev, drm);\n" - ">> ?+\n" - ">> ?+ /* get the resources */\n" - ">> ?+ priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;\n" - ">> ?+\n" - ">> ?+ res = platform_get_resource(to_platform_device(dev),\n" - ">> ?+ IORESOURCE_MEM, 0);\n" - ">> ?+ if (!res) {\n" - ">> ?+ dev_err(dev, \"failed to get memory resource\\n\");\n" - ">> ?+ ret = -EINVAL;\n" - ">> ?+ goto out1;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ priv->mmio = devm_ioremap_resource(dev, res);\n" - ">> ?+ if (IS_ERR(priv->mmio)) {\n" - ">> ?+ ret = PTR_ERR(priv->mmio);\n" - ">> ?+ dev_err(dev, \"failed to map registers %d\\n\", ret);\n" - ">> ?+ goto out1;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ priv->gate = devm_clk_get(dev, \"bus\");\n" - ">> ?+ if (IS_ERR(priv->gate)) {\n" - ">> ?+ ret = PTR_ERR(priv->gate);\n" - ">> ?+ dev_err(dev, \"bus gate err %d\\n\", ret);\n" - ">> ?+ goto out1;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ priv->clk = devm_clk_get(dev, \"clock\");\n" - ">> ?+ if (IS_ERR(priv->clk)) {\n" - ">> ?+ ret = PTR_ERR(priv->clk);\n" - ">> ?+ dev_err(dev, \"clock err %d\\n\", ret);\n" - ">> ?+ goto out1;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ priv->reset = devm_reset_control_get(dev, NULL);\n" - ">> ?+ if (IS_ERR(priv->reset)) {\n" - ">> ?+ ret = PTR_ERR(priv->reset);\n" - ">> ?+ dev_err(dev, \"reset err %d\\n\", ret);\n" - ">> ?+ goto out1;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ mutex_init(&priv->mutex); /* protect DE I/O accesses */\n" - ">> ?+\n" - ">> ?+ ret = drm_dev_init(drm, &de2_drm_driver, dev);\n" - ">> ?+ if (ret != 0) {\n" - ">> ?+ dev_err(dev, \"dev_init failed %d\\n\", ret);\n" - ">> ?+ goto out1;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ drm_mode_config_init(drm);\n" - ">> ?+ drm->mode_config.min_width = 32; /* needed for cursor */\n" - ">> ?+ drm->mode_config.min_height = 32;\n" - ">> ?+ drm->mode_config.max_width = 1920;\n" - ">> ?+ drm->mode_config.max_height = 1080;\n" - ">> ?+ drm->mode_config.funcs = &de2_mode_config_funcs;\n" - ">> ?+\n" - ">> ?+ drm->irq_enabled = true;\n" - ">> ?+\n" - ">> ?+ /* start the subdevices */\n" - ">> ?+ ret = component_bind_all(dev, drm);\n" - ">> ?+ if (ret < 0)\n" - ">> ?+ goto out2;\n" - ">> ?+\n" - ">> ?+ /* initialize and disable vertical blanking on all CRTCs */\n" - ">> ?+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);\n" - ">> ?+ if (ret < 0)\n" - ">> ?+ dev_warn(dev, \"vblank_init failed %d\\n\", ret);\n" - ">> ?+\n" - ">> ?+ for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {\n" - ">> ?+ lcd = priv->lcds[i];\n" - ">> ?+ if (lcd)\n" - ">> ?+ de2_vblank_reset(lcd);\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ drm_mode_config_reset(drm);\n" - ">> ?+\n" - ">> ?+ priv->fbdev = drm_fbdev_cma_init(drm,\n" - ">> ?+ 32, /* bpp */\n" - ">> ?+ drm->mode_config.num_crtc,\n" - ">> ?+ drm->mode_config.num_connector);\n" - ">> ?+ if (IS_ERR(priv->fbdev)) {\n" - ">> ?+ ret = PTR_ERR(priv->fbdev);\n" - ">> ?+ priv->fbdev = NULL;\n" - ">> ?+ goto out3;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ drm_kms_helper_poll_init(drm);\n" - ">> ?+\n" - ">> ?+ ret = drm_dev_register(drm, 0);\n" - ">> ?+ if (ret < 0)\n" - ">> ?+ goto out4;\n" - ">> ?+\n" - ">> ?+ return 0;\n" - ">> ?+\n" - ">> ?+out4:\n" - ">> ?+ drm_fbdev_cma_fini(priv->fbdev);\n" - ">> ?+out3:\n" - ">> ?+ component_unbind_all(dev, drm);\n" - ">> ?+out2:\n" - ">> ?+ drm_dev_unref(drm);\n" - ">> ?+out1:\n" - ">> ?+ kfree(priv);\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_drm_unbind(struct device *dev)\n" - ">> ?+{\n" - ">> ?+ struct drm_device *drm = dev_get_drvdata(dev);\n" - ">> ?+ struct priv *priv = drm_to_priv(drm);\n" - ">> ?+\n" - ">> ?+ drm_dev_unregister(drm);\n" - ">> ?+\n" - ">> ?+ drm_fbdev_cma_fini(priv->fbdev);\n" - ">> ?+ drm_kms_helper_poll_fini(drm);\n" - ">> ?+ drm_vblank_cleanup(drm);\n" - ">> ?+ drm_mode_config_cleanup(drm);\n" - ">> ?+\n" - ">> ?+ component_unbind_all(dev, drm);\n" - ">> ?+\n" - ">> ?+ kfree(priv);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct component_master_ops de2_drm_comp_ops = {\n" - ">> ?+ .bind = de2_drm_bind,\n" - ">> ?+ .unbind = de2_drm_unbind,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+/*\n" - ">> ?+ * drm_of_component_probe() does:\n" - ">> ?+ * - bind of the ports (lcd-controller.port)\n" - ">> ?+ * - bind of the remote nodes (hdmi, tve..)\n" - ">> ?+ */\n" - ">> ?+static int compare_of(struct device *dev, void *data)\n" - ">> ?+{\n" - ">> ?+ struct device_node *np = data;\n" - ">> ?+\n" - ">> ?+ if (of_node_cmp(np->name, \"port\") == 0) {\n" - ">> ?+ np = of_get_parent(np);\n" - ">> ?+ of_node_put(np);\n" - ">> ?+ }\n" - ">> ?+ return dev->of_node == np;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static int de2_drm_probe(struct platform_device *pdev)\n" - ">> ?+{\n" - ">> ?+ int ret;\n" - ">> ?+\n" - ">> ?+ ret = drm_of_component_probe(&pdev->dev,\n" - ">> ?+ compare_of,\n" - ">> ?+ &de2_drm_comp_ops);\n" - ">> ?+ if (ret == -EINVAL)\n" - ">> ?+ ret = -ENXIO;\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static int de2_drm_remove(struct platform_device *pdev)\n" - ">> ?+{\n" - ">> ?+ component_master_del(&pdev->dev, &de2_drm_comp_ops);\n" - ">> ?+\n" - ">> ?+ return 0;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static struct platform_driver de2_drm_platform_driver = {\n" - ">> ?+ .probe = de2_drm_probe,\n" - ">> ?+ .remove = de2_drm_remove,\n" - ">> ?+ .driver = {\n" - ">> ?+ .name = DRIVER_NAME,\n" - ">> ?+ .of_match_table = de2_drm_of_match,\n" - ">> ?+ },\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static int __init de2_drm_init(void)\n" - ">> ?+{\n" - ">> ?+ int ret;\n" - ">> ?+\n" - ">> ?+ ret = platform_driver_register(&de2_lcd_platform_driver);\n" - ">> ?+ if (ret < 0)\n" - ">> ?+ return ret;\n" - ">> ?+\n" - ">> ?+ ret = platform_driver_register(&de2_drm_platform_driver);\n" - ">> ?+ if (ret < 0)\n" - ">> ?+ platform_driver_unregister(&de2_lcd_platform_driver);\n" - ">> ?+\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void __exit de2_drm_fini(void)\n" - ">> ?+{\n" - ">> ?+ platform_driver_unregister(&de2_lcd_platform_driver);\n" - ">> ?+ platform_driver_unregister(&de2_drm_platform_driver);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+module_init(de2_drm_init);\n" - ">> ?+module_exit(de2_drm_fini);\n" - ">> ?+\n" - ">> ?+MODULE_AUTHOR(\"Jean-Francois Moine <moinejf@free.fr>\");\n" - ">> ?+MODULE_DESCRIPTION(\"Allwinner DE2 DRM Driver\");\n" - ">> ?+MODULE_LICENSE(\"GPL v2\");\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..c42c30a\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/de2_drv.h\n" - ">> ?@@ -0,0 +1,48 @@\n" - ">> ?+#ifndef __DE2_DRM_H__\n" - ">> ?+#define __DE2_DRM_H__\n" - ">> ?+/*\n" - ">> ?+ * Copyright (C) 2016 Jean-Fran??ois Moine\n" - ">> ?+ *\n" - ">> ?+ * This program is free software; you can redistribute it and/or\n" - ">> ?+ * modify it under the terms of the GNU General Public License as\n" - ">> ?+ * published by the Free Software Foundation; either version 2 of\n" - ">> ?+ * the License, or (at your option) any later version.\n" - ">> ?+ */\n" - ">> ?+\n" - ">> ?+#include <drm/drmP.h>\n" - ">> ?+#include <linux/clk.h>\n" - ">> ?+#include <linux/reset.h>\n" - ">> ?+\n" - ">> ?+struct drm_fbdev_cma;\n" - ">> ?+struct lcd;\n" - ">> ?+\n" - ">> ?+#define N_LCDS 2\n" - ">> ?+\n" - ">> ?+struct priv {\n" - ">> ?+ struct drm_device drm;\n" - ">> ?+ void __iomem *mmio;\n" - ">> ?+ struct clk *clk;\n" - ">> ?+ struct clk *gate;\n" - ">> ?+ struct reset_control *reset;\n" - ">> ?+\n" - ">> ?+ struct mutex mutex; /* protect DE I/O access */\n" - ">> ?+ u8 soc_type;\n" - ">> ?+#define SOC_A83T 0\n" - ">> ?+#define SOC_H3 1\n" - ">> ?+ u8 started; /* bitmap of started mixers */\n" - ">> ?+ u8 clean; /* bitmap of clean mixers */\n" - ">> ?+\n" - ">> ?+ struct drm_fbdev_cma *fbdev;\n" - ">> ?+\n" - ">> ?+ struct lcd *lcds[N_LCDS]; /* CRTCs */\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+#define drm_to_priv(x) container_of(x, struct priv, drm)\n" - ">> ?+\n" - ">> ?+/* in de2_crtc.c */\n" - ">> ?+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);\n" - ">> ?+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);\n" - ">> ?+void de2_vblank_reset(struct lcd *lcd);\n" - ">> ?+extern struct platform_driver de2_lcd_platform_driver;\n" - ">> ?+\n" - ">> ?+#endif /* __DE2_DRM_H__ */\n" - ">> ?diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c\n" - ">> ?new file mode 100644\n" - ">> ?index 0000000..2fd72dc\n" - ">> ?--- /dev/null\n" - ">> ?+++ b/drivers/gpu/drm/sun8i/de2_plane.c\n" - ">> ?@@ -0,0 +1,734 @@\n" - ">> ?+/*\n" - ">> ?+ * Allwinner DRM driver - Display Engine 2\n" - ">> ?+ *\n" - ">> ?+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>\n" - ">> ?+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers\n" - ">> ?+ * Copyright (c) 2016 Allwinnertech Co., Ltd.\n" - ">> ?+ *\n" - ">> ?+ * This program is free software; you can redistribute it and/or\n" - ">> ?+ * modify it under the terms of the GNU General Public License as\n" - ">> ?+ * published by the Free Software Foundation; either version 2 of\n" - ">> ?+ * the License, or (at your option) any later version.\n" - ">> ?+ */\n" - ">> ?+\n" - ">> ?+#include <linux/io.h>\n" - ">> ?+#include <drm/drm_atomic_helper.h>\n" - ">> ?+#include <drm/drm_crtc_helper.h>\n" - ">> ?+#include <drm/drm_fb_cma_helper.h>\n" - ">> ?+#include <drm/drm_gem_cma_helper.h>\n" - ">> ?+#include <drm/drm_plane_helper.h>\n" - ">> ?+\n" - ">> ?+#include \"de2_drv.h\"\n" - ">> ?+#include \"de2_crtc.h\"\n" - ">> ?+\n" - ">> ?+/* DE2 I/O map */\n" - ">> ?+\n" - ">> ?+#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */\n" - ">> ?+#define DE2_GATE_REG 0x0004\n" - ">> ?+#define DE2_RESET_REG 0x0008\n" - ">> ?+#define DE2_DIV_REG 0x000c /* 4 bits per LCD */\n" - ">> ?+#define DE2_SEL_REG 0x0010\n" - ">> ?+\n" - ">> ?+#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */\n" - ">> ?+#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */\n" - ">> ?+\n" - ">> ?+/* mixer registers (addr / mixer base) */\n" - ">> ?+#define MIXER_GLB_REGS 0x00000 /* global control */\n" - ">> ?+#define MIXER_BLD_REGS 0x01000 /* alpha blending */\n" - ">> ?+#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */\n" - ">> ?+#define MIXER_CHAN_SZ 0x1000 /* size of a channel */\n" - ">> ?+#define MIXER_VSU_REGS 0x20000 /* VSU */\n" - ">> ?+#define MIXER_GSU1_REGS 0x30000 /* GSUs */\n" - ">> ?+#define MIXER_GSU2_REGS 0x40000\n" - ">> ?+#define MIXER_GSU3_REGS 0x50000\n" - ">> ?+#define MIXER_FCE_REGS 0xa0000 /* FCE */\n" - ">> ?+#define MIXER_BWS_REGS 0xa2000 /* BWS */\n" - ">> ?+#define MIXER_LTI_REGS 0xa4000 /* LTI */\n" - ">> ?+#define MIXER_PEAK_REGS 0xa6000 /* PEAK */\n" - ">> ?+#define MIXER_ASE_REGS 0xa8000 /* ASE */\n" - ">> ?+#define MIXER_FCC_REGS 0xaa000 /* FCC */\n" - ">> ?+#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */\n" - ">> ?+\n" - ">> ?+/* global control */\n" - ">> ?+#define MIXER_GLB_CTL_REG 0x00\n" - ">> ?+#define MIXER_GLB_CTL_rt_en BIT(0)\n" - ">> ?+#define MIXER_GLB_CTL_finish_irq_en BIT(4)\n" - ">> ?+#define MIXER_GLB_CTL_rtwb_port BIT(12)\n" - ">> ?+#define MIXER_GLB_STATUS_REG 0x04\n" - ">> ?+#define MIXER_GLB_DBUFF_REG 0x08\n" - ">> ?+#define MIXER_GLB_SIZE_REG 0x0c\n" - ">> ?+\n" - ">> ?+/* alpha blending */\n" - ">> ?+#define MIXER_BLD_FCOLOR_CTL_REG 0x00\n" - ">> ?+#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe))\n" - ">> ?+#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */\n" - ">> ?+#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */\n" - ">> ?+#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x))\n" - ">> ?+#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x))\n" - ">> ?+#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x))\n" - ">> ?+#define MIXER_BLD_ROUTE_REG 0x80\n" - ">> ?+#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))\n" - ">> ?+#define MIXER_BLD_PREMULTIPLY_REG 0x84\n" - ">> ?+#define MIXER_BLD_BKCOLOR_REG 0x88\n" - ">> ?+#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c\n" - ">> ?+#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */\n" - ">> ?+#define MIXER_BLD_MODE_SRCOVER 0x03010301\n" - ">> ?+#define MIXER_BLD_OUT_CTL_REG 0xfc\n" - ">> ?+\n" - ">> ?+/* VI channel (channel 0) */\n" - ">> ?+#define VI_CFG_N 4 /* number of layers */\n" - ">> ?+#define VI_CFG_SIZE 0x30 /* size of a layer */\n" - ">> ?+#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l))\n" - ">> ?+#define VI_CFG_ATTR_en BIT(0)\n" - ">> ?+#define VI_CFG_ATTR_fcolor_en BIT(4)\n" - ">> ?+#define VI_CFG_ATTR_fmt_SHIFT 8\n" - ">> ?+#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)\n" - ">> ?+#define VI_CFG_ATTR_ui_sel BIT(15)\n" - ">> ?+#define VI_CFG_ATTR_top_down BIT(23)\n" - ">> ?+#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l))\n" - ">> ?+#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l))\n" - ">> ?+#define VI_N_PLANES 3\n" - ">> ?+#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p))\n" - ">> ?+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))\n" - ">> ?+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))\n" - ">> ?+#define VI_FCOLORx(l) (0xc0 + 4 * (l))\n" - ">> ?+#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p))\n" - ">> ?+#define VI_BOT_HADDRx(p) (0xdc + 4 * (p))\n" - ">> ?+#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n))\n" - ">> ?+#define VI_HORI_DSx(n) (0xf0 + 4 * (n))\n" - ">> ?+#define VI_VERT_DSx(n) (0xf8 + 4 * (n))\n" - ">> ?+#define VI_SIZE 0x100\n" - ">> ?+\n" - ">> ?+/* UI channel (channels 1..3) */\n" - ">> ?+#define UI_CFG_N 4 /* number of layers */\n" - ">> ?+#define UI_CFG_SIZE (8 * 4) /* size of a layer */\n" - ">> ?+#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_CFG_ATTR_en BIT(0)\n" - ">> ?+#define UI_CFG_ATTR_alpmod_SHIFT 1\n" - ">> ?+#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)\n" - ">> ?+#define UI_CFG_ATTR_fcolor_en BIT(4)\n" - ">> ?+#define UI_CFG_ATTR_fmt_SHIFT 8\n" - ">> ?+#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)\n" - ">> ?+#define UI_CFG_ATTR_top_down BIT(23)\n" - ">> ?+#define UI_CFG_ATTR_alpha_SHIFT 24\n" - ">> ?+#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)\n" - ">> ?+#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l))\n" - ">> ?+#define UI_TOP_HADDR 0x80\n" - ">> ?+#define UI_BOT_HADDR 0x84\n" - ">> ?+#define UI_OVL_SIZE 0x88\n" - ">> ?+#define UI_SIZE 0x8c\n" - ">> ?+\n" - ">> ?+/* coordinates and sizes */\n" - ">> ?+#define XY(x, y) (((y) << 16) | (x))\n" - ">> ?+#define WH(w, h) ((((h) - 1) << 16) | ((w) - 1))\n" - ">> ?+\n" - ">> ?+/* UI video formats */\n" - ">> ?+#define DE2_FORMAT_ARGB_8888 0\n" - ">> ?+#define DE2_FORMAT_BGRA_8888 3\n" - ">> ?+#define DE2_FORMAT_XRGB_8888 4\n" - ">> ?+#define DE2_FORMAT_RGB_888 8\n" - ">> ?+#define DE2_FORMAT_BGR_888 9\n" - ">> ?+\n" - ">> ?+/* VI video formats */\n" - ">> ?+#define DE2_FORMAT_YUV422_I_YVYU 1 /* YVYU */\n" - ">> ?+#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */\n" - ">> ?+#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */\n" - ">> ?+#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */\n" - ">> ?+#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */\n" - ">> ?+\n" - ">> ?+/* plane formats */\n" - ">> ?+static const uint32_t ui_formats[] = {\n" - ">> ?+ DRM_FORMAT_ARGB8888,\n" - ">> ?+ DRM_FORMAT_BGRA8888,\n" - ">> ?+ DRM_FORMAT_XRGB8888,\n" - ">> ?+ DRM_FORMAT_RGB888,\n" - ">> ?+ DRM_FORMAT_BGR888,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static const uint32_t vi_formats[] = {\n" - ">> ?+ DRM_FORMAT_XRGB8888,\n" - ">> ?+ DRM_FORMAT_YUYV,\n" - ">> ?+ DRM_FORMAT_YVYU,\n" - ">> ?+ DRM_FORMAT_YUV422,\n" - ">> ?+ DRM_FORMAT_YUV420,\n" - ">> ?+ DRM_FORMAT_UYVY,\n" - ">> ?+ DRM_FORMAT_BGRA8888,\n" - ">> ?+ DRM_FORMAT_RGB888,\n" - ">> ?+ DRM_FORMAT_BGR888,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+/*\n" - ">> ?+ * plane table\n" - ">> ?+ *\n" - ">> ?+ * The chosen channel/layer assignment of the planes respects\n" - ">> ?+ * the following constraints:\n" - ">> ?+ * - the cursor must be in a channel higher than the primary channel\n" - ">> ?+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1\n" - ">> ?+ */\n" - ">> ?+static const struct {\n" - ">> ?+ u8 chan;\n" - ">> ?+ u8 layer;\n" - ">> ?+ u8 pipe;\n" - ">> ?+ u8 type; /* plane type */\n" - ">> ?+ const uint32_t *formats;\n" - ">> ?+ u8 n_formats;\n" - ">> ?+} plane_tb[] = {\n" - ">> ?+ [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */\n" - ">> ?+ 0, 0, 0,\n" - ">> ?+ DRM_PLANE_TYPE_PRIMARY,\n" - ">> ?+ ui_formats, ARRAY_SIZE(ui_formats),\n" - ">> ?+ },\n" - ">> ?+ [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */\n" - ">> ?+ 1, 0, 1,\n" - ">> ?+ DRM_PLANE_TYPE_CURSOR,\n" - ">> ?+ ui_formats, ARRAY_SIZE(ui_formats),\n" - ">> ?+ },\n" - ">> ?+ {\n" - ">> ?+ 0, 1, 0, /* 1st overlay: channel 0, layer 1 */\n" - ">> ?+ DRM_PLANE_TYPE_OVERLAY,\n" - ">> ?+ vi_formats, ARRAY_SIZE(vi_formats),\n" - ">> ?+ },\n" - ">> ?+ {\n" - ">> ?+ 0, 2, 0, /* 2nd overlay: channel 0, layer 2 */\n" - ">> ?+ DRM_PLANE_TYPE_OVERLAY,\n" - ">> ?+ vi_formats, ARRAY_SIZE(vi_formats),\n" - ">> ?+ },\n" - ">> ?+ {\n" - ">> ?+ 0, 3, 0, /* 3rd overlay: channel 0, layer 3 */\n" - ">> ?+ DRM_PLANE_TYPE_OVERLAY,\n" - ">> ?+ vi_formats, ARRAY_SIZE(vi_formats),\n" - ">> ?+ },\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static inline void andl_relaxed(void __iomem *addr, u32 val)\n" - ">> ?+{\n" - ">> ?+ writel_relaxed(readl_relaxed(addr) & val, addr);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static inline void orl_relaxed(void __iomem *addr, u32 val)\n" - ">> ?+{\n" - ">> ?+ writel_relaxed(readl_relaxed(addr) | val, addr);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/* alert the DE processor about changes in a mixer configuration */\n" - ">> ?+static void de2_mixer_select(struct priv *priv,\n" - ">> ?+ int mixer,\n" - ">> ?+ void __iomem *mixer_io)\n" - ">> ?+{\n" - ">> ?+ /* select the mixer */\n" - ">> ?+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);\n" - ">> ?+\n" - ">> ?+ /* double register switch */\n" - ">> ?+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/*\n" - ">> ?+ * cleanup a mixer\n" - ">> ?+ *\n" - ">> ?+ * This is needed only once after power on.\n" - ">> ?+ */\n" - ">> ?+static void de2_mixer_cleanup(struct priv *priv, int mixer,\n" - ">> ?+ u32 size)\n" - ">> ?+{\n" - ">> ?+ void __iomem *mixer_io = priv->mmio;\n" - ">> ?+ void __iomem *chan_io;\n" - ">> ?+ u32 data;\n" - ">> ?+ unsigned int i;\n" - ">> ?+\n" - ">> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" - ">> ?+ chan_io = mixer_io + MIXER_CHAN_REGS;\n" - ">> ?+\n" - ">> ?+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);\n" - ">> ?+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(MIXER_GLB_CTL_rt_en,\n" - ">> ?+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);\n" - ">> ?+\n" - ">> ?+ /*\n" - ">> ?+ * clear the VI/UI channels\n" - ">> ?+ * LCD0: 1 VI and 3 UIs\n" - ">> ?+ * LCD1: 1 VI and 1 UI\n" - ">> ?+ */\n" - ">> ?+ memset_io(chan_io, 0, VI_SIZE);\n" - ">> ?+ memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);\n" - ">> ?+ if (mixer == 0) {\n" - ">> ?+ memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);\n" - ">> ?+ memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ /* alpha blending */\n" - ">> ?+ writel_relaxed(0x00000001 | /* fcolor for primary */\n" - ">> ?+ MIXER_BLD_FCOLOR_CTL_PEN(0),\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);\n" - ">> ?+ for (i = 0; i < MIXER_BLD_ATTR_N; i++) {\n" - ">> ?+ writel_relaxed(0xff000000,\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));\n" - ">> ?+ writel_relaxed(size,\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));\n" - ">> ?+ writel_relaxed(0,\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));\n" - ">> ?+ }\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);\n" - ">> ?+\n" - ">> ?+ /* prepare the pipe route for the planes */\n" - ">> ?+ data = 0;\n" - ">> ?+ for (i = 0; i < DE2_N_PLANES; i++)\n" - ">> ?+ data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);\n" - ">> ?+ writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_PREMULTIPLY_REG);\n" - ">> ?+ writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_BKCOLOR_REG);\n" - ">> ?+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_OUTPUT_SIZE_REG);\n" - ">> ?+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));\n" - ">> ?+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));\n" - ">> ?+\n" - ">> ?+ /* disable the enhancements */\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_VSU_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_FCE_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_BWS_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_LTI_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_ASE_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_FCC_REGS);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/* enable a mixer */\n" - ">> ?+static void de2_mixer_enable(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ struct priv *priv = lcd->priv;\n" - ">> ?+ void __iomem *mixer_io = priv->mmio;\n" - ">> ?+ struct drm_display_mode *mode = &lcd->crtc.mode;\n" - ">> ?+ u32 size = WH(mode->hdisplay, mode->vdisplay);\n" - ">> ?+ u32 data;\n" - ">> ?+ int mixer = lcd->mixer;\n" - ">> ?+ int i;\n" - ">> ?+\n" - ">> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" - ">> ?+\n" - ">> ?+ /* if not done yet, start the DE processor */\n" - ">> ?+ if (!priv->started) {\n" - ">> ?+ reset_control_deassert(priv->reset);\n" - ">> ?+ clk_prepare_enable(priv->gate);\n" - ">> ?+ clk_prepare_enable(priv->clk);\n" - ">> ?+ }\n" - ">> ?+ priv->started |= 1 << mixer;\n" - ">> ?+\n" - ">> ?+ /* set the A83T clock divider (500 / 2) = 250MHz */\n" - ">> ?+ if (priv->soc_type == SOC_A83T)\n" - ">> ?+ writel_relaxed(0x00000011, /* div = 2 for both LCDs */\n" - ">> ?+ priv->mmio + DE2_DIV_REG);\n" - ">> ?+\n" - ">> ?+ /* deassert the mixer and enable its clock */\n" - ">> ?+ orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);\n" - ">> ?+ data = 1 << mixer; /* 1 bit / lcd */\n" - ">> ?+ orl_relaxed(priv->mmio + DE2_GATE_REG, data);\n" - ">> ?+ orl_relaxed(priv->mmio + DE2_MOD_REG, data);\n" - ">> ?+\n" - ">> ?+ /* if not done yet, cleanup and enable */\n" - ">> ?+ if (!(priv->clean & (1 << mixer))) {\n" - ">> ?+ priv->clean |= 1 << mixer;\n" - ">> ?+ de2_mixer_cleanup(priv, mixer, size);\n" - ">> ?+ return;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ /* enable */\n" - ">> ?+ de2_mixer_select(priv, mixer, mixer_io);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(MIXER_GLB_CTL_rt_en,\n" - ">> ?+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);\n" - ">> ?+\n" - ">> ?+ /* set the size of the frame buffer */\n" - ">> ?+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);\n" - ">> ?+ for (i = 0; i < MIXER_BLD_ATTR_N; i++)\n" - ">> ?+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_ATTRx_INSIZE(i));\n" - ">> ?+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_OUTPUT_SIZE_REG);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/* enable a LCD (DE mixer) */\n" - ">> ?+void de2_de_enable(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ mutex_lock(&lcd->priv->mutex);\n" - ">> ?+\n" - ">> ?+ de2_mixer_enable(lcd);\n" - ">> ?+\n" - ">> ?+ mutex_unlock(&lcd->priv->mutex);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/* disable a LCD (DE mixer) */\n" - ">> ?+void de2_de_disable(struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ struct priv *priv = lcd->priv;\n" - ">> ?+ void __iomem *mixer_io = priv->mmio;\n" - ">> ?+ int mixer = lcd->mixer;\n" - ">> ?+ u32 data;\n" - ">> ?+\n" - ">> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" - ">> ?+\n" - ">> ?+ mutex_lock(&priv->mutex);\n" - ">> ?+\n" - ">> ?+ de2_mixer_select(priv, mixer, mixer_io);\n" - ">> ?+\n" - ">> ?+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);\n" - ">> ?+\n" - ">> ?+ data = ~(1 << mixer);\n" - ">> ?+ andl_relaxed(priv->mmio + DE2_MOD_REG, data);\n" - ">> ?+ andl_relaxed(priv->mmio + DE2_GATE_REG, data);\n" - ">> ?+ andl_relaxed(priv->mmio + DE2_RESET_REG, data);\n" - ">> ?+\n" - ">> ?+ mutex_unlock(&priv->mutex);\n" - ">> ?+\n" - ">> ?+ /* if all mixers are disabled, stop the DE */\n" - ">> ?+ priv->started &= ~(1 << mixer);\n" - ">> ?+ if (!priv->started) {\n" - ">> ?+ clk_disable_unprepare(priv->clk);\n" - ">> ?+ clk_disable_unprepare(priv->gate);\n" - ">> ?+ reset_control_assert(priv->reset);\n" - ">> ?+ }\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_vi_update(void __iomem *chan_io,\n" - ">> ?+ struct drm_gem_cma_object *gem,\n" - ">> ?+ int layer,\n" - ">> ?+ unsigned int fmt,\n" - ">> ?+ u32 ui_sel,\n" - ">> ?+ u32 size,\n" - ">> ?+ u32 coord,\n" - ">> ?+ struct drm_framebuffer *fb,\n" - ">> ?+ u32 screen_size)\n" - ">> ?+{\n" - ">> ?+ int i;\n" - ">> ?+\n" - ">> ?+ writel_relaxed(VI_CFG_ATTR_en |\n" - ">> ?+ (fmt << VI_CFG_ATTR_fmt_SHIFT) |\n" - ">> ?+ ui_sel,\n" - ">> ?+ chan_io + VI_CFGx_ATTR(layer));\n" - ">> ?+ writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));\n" - ">> ?+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));\n" - ">> ?+ for (i = 0; i < VI_N_PLANES; i++) {\n" - ">> ?+ writel_relaxed(fb->pitches[i] ? fb->pitches[i] :\n" - ">> ?+ fb->pitches[0],\n" - ">> ?+ chan_io + VI_CFGx_PITCHy(layer, i));\n" - ">> ?+ writel_relaxed(gem->paddr + fb->offsets[i],\n" - ">> ?+ chan_io + VI_CFGx_TOP_LADDRy(layer, i));\n" - ">> ?+ }\n" - ">> ?+ writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));\n" - ">> ?+ if (layer == 0) {\n" - ">> ?+ writel_relaxed(screen_size,\n" - ">> ?+ chan_io + VI_OVL_SIZEx(0));\n" - ">> ?+ }\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_ui_update(void __iomem *chan_io,\n" - ">> ?+ struct drm_gem_cma_object *gem,\n" - ">> ?+ int layer,\n" - ">> ?+ unsigned int fmt,\n" - ">> ?+ u32 alpha_glob,\n" - ">> ?+ u32 size,\n" - ">> ?+ u32 coord,\n" - ">> ?+ struct drm_framebuffer *fb,\n" - ">> ?+ u32 screen_size)\n" - ">> ?+{\n" - ">> ?+ writel_relaxed(UI_CFG_ATTR_en |\n" - ">> ?+ (fmt << UI_CFG_ATTR_fmt_SHIFT) |\n" - ">> ?+ alpha_glob,\n" - ">> ?+ chan_io + UI_CFGx_ATTR(layer));\n" - ">> ?+ writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));\n" - ">> ?+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));\n" - ">> ?+ writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));\n" - ">> ?+ writel_relaxed(gem->paddr + fb->offsets[0],\n" - ">> ?+ chan_io + UI_CFGx_TOP_LADDR(layer));\n" - ">> ?+ if (layer == 0)\n" - ">> ?+ writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_plane_update(struct priv *priv, struct lcd *lcd,\n" - ">> ?+ int plane_num,\n" - ">> ?+ struct drm_plane_state *state,\n" - ">> ?+ struct drm_plane_state *old_state)\n" - ">> ?+{\n" - ">> ?+ void __iomem *mixer_io = priv->mmio;\n" - ">> ?+ void __iomem *chan_io;\n" - ">> ?+ struct drm_framebuffer *fb = state->fb;\n" - ">> ?+ struct drm_gem_cma_object *gem;\n" - ">> ?+ u32 size = WH(state->crtc_w, state->crtc_h);\n" - ">> ?+ u32 coord, screen_size;\n" - ">> ?+ u32 fcolor;\n" - ">> ?+ u32 ui_sel, alpha_glob;\n" - ">> ?+ int mixer = lcd->mixer;\n" - ">> ?+ int chan, layer, x, y;\n" - ">> ?+ unsigned int fmt;\n" - ">> ?+\n" - ">> ?+ chan = plane_tb[plane_num].chan;\n" - ">> ?+ layer = plane_tb[plane_num].layer;\n" - ">> ?+\n" - ">> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" - ">> ?+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;\n" - ">> ?+\n" - ">> ?+ x = state->crtc_x >= 0 ? state->crtc_x : 0;\n" - ">> ?+ y = state->crtc_y >= 0 ? state->crtc_y : 0;\n" - ">> ?+ coord = XY(x, y);\n" - ">> ?+\n" - ">> ?+ /* if plane update was delayed, force a full update */\n" - ">> ?+ if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &\n" - ">> ?+ (1 << plane_num)) {\n" - ">> ?+ priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=\n" - ">> ?+ ~(1 << plane_num);\n" - ">> ?+\n" - ">> ?+ /* handle plane move */\n" - ">> ?+ } else if (fb == old_state->fb) {\n" - ">> ?+ de2_mixer_select(priv, mixer, mixer_io);\n" - ">> ?+ if (chan == 0)\n" - ">> ?+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));\n" - ">> ?+ else\n" - ">> ?+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));\n" - ">> ?+ return;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ gem = drm_fb_cma_get_gem_obj(fb, 0);\n" - ">> ?+\n" - ">> ?+ ui_sel = alpha_glob = 0;\n" - ">> ?+\n" - ">> ?+ switch (fb->pixel_format) {\n" - ">> ?+ case DRM_FORMAT_ARGB8888:\n" - ">> ?+ fmt = DE2_FORMAT_ARGB_8888;\n" - ">> ?+ ui_sel = VI_CFG_ATTR_ui_sel;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_BGRA8888:\n" - ">> ?+ fmt = DE2_FORMAT_BGRA_8888;\n" - ">> ?+ ui_sel = VI_CFG_ATTR_ui_sel;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_XRGB8888:\n" - ">> ?+ fmt = DE2_FORMAT_XRGB_8888;\n" - ">> ?+ ui_sel = VI_CFG_ATTR_ui_sel;\n" - ">> ?+ alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |\n" - ">> ?+ (0xff << UI_CFG_ATTR_alpha_SHIFT);\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_RGB888:\n" - ">> ?+ fmt = DE2_FORMAT_RGB_888;\n" - ">> ?+ ui_sel = VI_CFG_ATTR_ui_sel;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_BGR888:\n" - ">> ?+ fmt = DE2_FORMAT_BGR_888;\n" - ">> ?+ ui_sel = VI_CFG_ATTR_ui_sel;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_YUYV:\n" - ">> ?+ fmt = DE2_FORMAT_YUV422_I_YUYV;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_YVYU:\n" - ">> ?+ fmt = DE2_FORMAT_YUV422_I_YVYU;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_YUV422:\n" - ">> ?+ fmt = DE2_FORMAT_YUV422_P;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_YUV420:\n" - ">> ?+ fmt = DE2_FORMAT_YUV420_P;\n" - ">> ?+ break;\n" - ">> ?+ case DRM_FORMAT_UYVY:\n" - ">> ?+ fmt = DE2_FORMAT_YUV422_I_UYVY;\n" - ">> ?+ break;\n" - ">> ?+ default:\n" - ">> ?+ pr_err(\"de2_plane_update: format %.4s not yet treated\\n\",\n" - ">> ?+ (char *) &fb->pixel_format);\n" - ">> ?+ return;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ /* the overlay size is the one of the primary plane */\n" - ">> ?+ screen_size = plane_num == DE2_PRIMARY_PLANE ?\n" - ">> ?+ size :\n" - ">> ?+ readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);\n" - ">> ?+\n" - ">> ?+ /* prepare pipe enable */\n" - ">> ?+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_FCOLOR_CTL_REG);\n" - ">> ?+ fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);\n" - ">> ?+\n" - ">> ?+ de2_mixer_select(priv, mixer, mixer_io);\n" - ">> ?+\n" - ">> ?+ if (chan == 0) /* VI channel */\n" - ">> ?+ de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,\n" - ">> ?+ fb, screen_size);\n" - ">> ?+ else /* UI channel */\n" - ">> ?+ de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,\n" - ">> ?+ fb, screen_size);\n" - ">> ?+ writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_FCOLOR_CTL_REG);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static int vi_nb_layers(void __iomem *chan_io)\n" - ">> ?+{\n" - ">> ?+ int layer, n = 0;\n" - ">> ?+\n" - ">> ?+ for (layer = 0; layer < 4; layer++) {\n" - ">> ?+ if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)\n" - ">> ?+ n++;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ return n;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static int ui_nb_layers(void __iomem *chan_io)\n" - ">> ?+{\n" - ">> ?+ int layer, n = 0;\n" - ">> ?+\n" - ">> ?+ for (layer = 0; layer < 4; layer++) {\n" - ">> ?+ if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)\n" - ">> ?+ n++;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ return n;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_plane_disable(struct priv *priv,\n" - ">> ?+ int mixer, int plane_num)\n" - ">> ?+{\n" - ">> ?+ void __iomem *mixer_io = priv->mmio;\n" - ">> ?+ void __iomem *chan_io;\n" - ">> ?+ u32 fcolor;\n" - ">> ?+ int chan, layer, n;\n" - ">> ?+\n" - ">> ?+ chan = plane_tb[plane_num].chan;\n" - ">> ?+ layer = plane_tb[plane_num].layer;\n" - ">> ?+\n" - ">> ?+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" - ">> ?+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;\n" - ">> ?+\n" - ">> ?+ if (chan == 0)\n" - ">> ?+ n = vi_nb_layers(chan_io);\n" - ">> ?+ else\n" - ">> ?+ n = ui_nb_layers(chan_io);\n" - ">> ?+\n" - ">> ?+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +\n" - ">> ?+ MIXER_BLD_FCOLOR_CTL_REG);\n" - ">> ?+\n" - ">> ?+ de2_mixer_select(priv, mixer, mixer_io);\n" - ">> ?+\n" - ">> ?+ if (chan == 0)\n" - ">> ?+ writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));\n" - ">> ?+ else\n" - ">> ?+ writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));\n" - ">> ?+\n" - ">> ?+ /* disable the pipe if no more active layer */\n" - ">> ?+ if (n <= 1)\n" - ">> ?+ writel_relaxed(fcolor &\n" - ">> ?+ ~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),\n" - ">> ?+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_drm_plane_update(struct drm_plane *plane,\n" - ">> ?+ struct drm_plane_state *old_state)\n" - ">> ?+{\n" - ">> ?+ struct drm_plane_state *state = plane->state;\n" - ">> ?+ struct drm_crtc *crtc = state->crtc;\n" - ">> ?+ struct lcd *lcd = crtc_to_lcd(crtc);\n" - ">> ?+ struct priv *priv = lcd->priv;\n" - ">> ?+ int plane_num = plane - lcd->planes;\n" - ">> ?+\n" - ">> ?+ /* if the crtc is disabled, mark update delayed */\n" - ">> ?+ if (!(priv->started & (1 << lcd->mixer))) {\n" - ">> ?+ lcd->delayed |= 1 << plane_num;\n" - ">> ?+ return; /* mixer disabled */\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ mutex_lock(&priv->mutex);\n" - ">> ?+\n" - ">> ?+ de2_plane_update(priv, lcd, plane_num, state, old_state);\n" - ">> ?+\n" - ">> ?+ mutex_unlock(&priv->mutex);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static void de2_drm_plane_disable(struct drm_plane *plane,\n" - ">> ?+ struct drm_plane_state *old_state)\n" - ">> ?+{\n" - ">> ?+ struct drm_crtc *crtc = old_state->crtc;\n" - ">> ?+ struct lcd *lcd = crtc_to_lcd(crtc);\n" - ">> ?+ struct priv *priv = lcd->priv;\n" - ">> ?+ int plane_num = plane - lcd->planes;\n" - ">> ?+\n" - ">> ?+ if (!(priv->started & (1 << lcd->mixer)))\n" - ">> ?+ return; /* mixer disabled */\n" - ">> ?+\n" - ">> ?+ mutex_lock(&priv->mutex);\n" - ">> ?+\n" - ">> ?+ de2_plane_disable(lcd->priv, lcd->mixer, plane_num);\n" - ">> ?+\n" - ">> ?+ mutex_unlock(&priv->mutex);\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+static const struct drm_plane_helper_funcs plane_helper_funcs = {\n" - ">> ?+ .atomic_update = de2_drm_plane_update,\n" - ">> ?+ .atomic_disable = de2_drm_plane_disable,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static const struct drm_plane_funcs plane_funcs = {\n" - ">> ?+ .update_plane = drm_atomic_helper_update_plane,\n" - ">> ?+ .disable_plane = drm_atomic_helper_disable_plane,\n" - ">> ?+ .destroy = drm_plane_cleanup,\n" - ">> ?+ .reset = drm_atomic_helper_plane_reset,\n" - ">> ?+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,\n" - ">> ?+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,\n" - ">> ?+};\n" - ">> ?+\n" - ">> ?+static int de2_one_plane_init(struct drm_device *drm,\n" - ">> ?+ struct drm_plane *plane,\n" - ">> ?+ int possible_crtcs,\n" - ">> ?+ int plane_num)\n" - ">> ?+{\n" - ">> ?+ int ret;\n" - ">> ?+\n" - ">> ?+ ret = drm_universal_plane_init(drm, plane, possible_crtcs,\n" - ">> ?+ &plane_funcs,\n" - ">> ?+ plane_tb[plane_num].formats,\n" - ">> ?+ plane_tb[plane_num].n_formats,\n" - ">> ?+ plane_tb[plane_num].type, NULL);\n" - ">> ?+ if (ret >= 0)\n" - ">> ?+ drm_plane_helper_add(plane, &plane_helper_funcs);\n" - ">> ?+\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?+\n" - ">> ?+/* initialize the planes */\n" - ">> ?+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)\n" - ">> ?+{\n" - ">> ?+ int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);\n" - ">> ?+\n" - ">> ?+ n = ARRAY_SIZE(plane_tb);\n" - ">> ?+ if (n != DE2_N_PLANES) {\n" - ">> ?+ dev_err(lcd->dev, \"Bug: incorrect number of planes %d != \"\n" - ">> ?+ __stringify(DE2_N_PLANES) \"\\n\", n);\n" - ">> ?+ return -EINVAL;\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ for (i = 0; i < n; i++) {\n" - ">> ?+ ret = de2_one_plane_init(drm, &lcd->planes[i],\n" - ">> ?+ possible_crtcs, i);\n" - ">> ?+ if (ret < 0) {\n" - ">> ?+ dev_err(lcd->dev, \"plane init failed %d\\n\", ret);\n" - ">> ?+ break;\n" - ">> ?+ }\n" - ">> ?+ }\n" - ">> ?+\n" - ">> ?+ return ret;\n" - ">> ?+}\n" - ">> ?--\n" - ">> ?2.10.2\n" + ">> \302\240\302\240source \"drivers/gpu/drm/tilcdc/Kconfig\"\n" + ">> \302\240diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile\n" + ">> \302\240index 883f3e7..3e1eaa0 100644\n" + ">> \302\240--- a/drivers/gpu/drm/Makefile\n" + ">> \302\240+++ b/drivers/gpu/drm/Makefile\n" + ">> \302\240@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/\n" + ">> \302\240\302\240obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/\n" + ">> \302\240\302\240obj-y += omapdrm/\n" + ">> \302\240\302\240obj-$(CONFIG_DRM_SUN4I) += sun4i/\n" + ">> \302\240+obj-$(CONFIG_DRM_SUN8I) += sun8i/\n" + ">> \302\240\302\240obj-y += tilcdc/\n" + ">> \302\240\302\240obj-$(CONFIG_DRM_QXL) += qxl/\n" + ">> \302\240\302\240obj-$(CONFIG_DRM_BOCHS) += bochs/\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..6940895\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/Kconfig\n" + ">> \302\240@@ -0,0 +1,19 @@\n" + ">> \302\240+#\n" + ">> \302\240+# Allwinner DE2 Video configuration\n" + ">> \302\240+#\n" + ">> \302\240+\n" + ">> \302\240+config DRM_SUN8I\n" + ">> \302\240+ bool\n" + ">> \302\240+\n" + ">> \302\240+config DRM_SUN8I_DE2\n" + ">> \302\240+ tristate \"Support for Allwinner Video with DE2 interface\"\n" + ">> \302\240+ depends on DRM && OF\n" + ">> \302\240+ depends on ARCH_SUNXI || COMPILE_TEST\n" + ">> \302\240+ select DRM_GEM_CMA_HELPER\n" + ">> \302\240+ select DRM_KMS_CMA_HELPER\n" + ">> \302\240+ select DRM_KMS_HELPER\n" + ">> \302\240+ select DRM_SUN8I\n" + ">> \302\240+ help\n" + ">> \302\240+ Choose this option if your Allwinner chipset has the DE2 interface\n" + ">> \302\240+ as the A64, A83T and H3. If M is selected the module will be called\n" + ">> \302\240+ sun8i-de2-drm.\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..f107919\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/Makefile\n" + ">> \302\240@@ -0,0 +1,7 @@\n" + ">> \302\240+#\n" + ">> \302\240+# Makefile for Allwinner's sun8i DRM device driver\n" + ">> \302\240+#\n" + ">> \302\240+\n" + ">> \302\240+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o\n" + ">> \302\240+\n" + ">> \302\240+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..4e94ccc\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/de2_crtc.c\n" + ">> \302\240@@ -0,0 +1,449 @@\n" + ">> \302\240+/*\n" + ">> \302\240+ * Allwinner DRM driver - DE2 CRTC\n" + ">> \302\240+ *\n" + ">> \302\240+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>\n" + ">> \302\240+ *\n" + ">> \302\240+ * This program is free software; you can redistribute it and/or\n" + ">> \302\240+ * modify it under the terms of the GNU General Public License as\n" + ">> \302\240+ * published by the Free Software Foundation; either version 2 of\n" + ">> \302\240+ * the License, or (at your option) any later version.\n" + ">> \302\240+ */\n" + ">> \302\240+\n" + ">> \302\240+#include <linux/component.h>\n" + ">> \302\240+#include <drm/drm_crtc_helper.h>\n" + ">> \302\240+#include <drm/drm_atomic_helper.h>\n" + ">> \302\240+#include <linux/io.h>\n" + ">> \302\240+#include <linux/of_irq.h>\n" + ">> \302\240+#include <linux/of_graph.h>\n" + ">> \302\240+\n" + ">> \302\240+#include \"de2_drv.h\"\n" + ">> \302\240+#include \"de2_crtc.h\"\n" + ">> \302\240+\n" + ">> \302\240+/* I/O map */\n" + ">> \302\240+\n" + ">> \302\240+#define TCON_GCTL_REG 0x00\n" + ">> \302\240+#define TCON_GCTL_TCON_ENABLE BIT(31)\n" + ">> \302\240+#define TCON_GINT0_REG 0x04\n" + ">> \302\240+#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)\n" + ">> \302\240+#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)\n" + ">> \302\240+#define TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)\n" + ">> \302\240+#define TCON0_CTL_REG 0x40\n" + ">> \302\240+#define TCON0_CTL_TCON_ENABLE BIT(31)\n" + ">> \302\240+#define TCON1_CTL_REG 0x90\n" + ">> \302\240+#define TCON1_CTL_TCON_ENABLE BIT(31)\n" + ">> \302\240+#define TCON1_CTL_INTERLACE_ENABLE BIT(20)\n" + ">> \302\240+#define TCON1_CTL_Start_Delay_SHIFT 4\n" + ">> \302\240+#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)\n" + ">> \302\240+#define TCON1_BASIC0_REG 0x94 /* XI/YI */\n" + ">> \302\240+#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */\n" + ">> \302\240+#define TCON1_BASIC2_REG 0x9c /* XO/YO */\n" + ">> \302\240+#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */\n" + ">> \302\240+#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */\n" + ">> \302\240+#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */\n" + ">> \302\240+#define TCON1_PS_SYNC_REG 0xb0\n" + ">> \302\240+#define TCON1_IO_POL_REG 0xf0\n" + ">> \302\240+#define TCON1_IO_POL_IO0_inv BIT(24)\n" + ">> \302\240+#define TCON1_IO_POL_IO1_inv BIT(25)\n" + ">> \302\240+#define TCON1_IO_POL_IO2_inv BIT(26)\n" + ">> \302\240+#define TCON1_IO_TRI_REG 0xf4\n" + ">> \302\240+#define TCON_CEU_CTL_REG 0x100\n" + ">> \302\240+#define TCON_CEU_CTL_ceu_en BIT(31)\n" + ">> \302\240+#define TCON1_FILL_CTL_REG 0x300\n" + ">> \302\240+#define TCON1_FILL_START0_REG 0x304\n" + ">> \302\240+#define TCON1_FILL_END0_REG 0x308\n" + ">> \302\240+#define TCON1_FILL_DATA0_REG 0x30c\n" + ">> \302\240+\n" + ">> \302\240+#define XY(x, y) (((x) << 16) | (y))\n" + ">> \302\240+\n" + ">> \302\240+#define andl_relaxed(addr, val) \\\n" + ">> \302\240+ writel_relaxed(readl_relaxed(addr) & val, addr)\n" + ">> \302\240+#define orl_relaxed(addr, val) \\\n" + ">> \302\240+ writel_relaxed(readl_relaxed(addr) | val, addr)\n" + ">> \302\240+\n" + ">> \302\240+/* vertical blank functions */\n" + ">> \302\240+\n" + ">> \302\240+static void de2_atomic_flush(struct drm_crtc *crtc,\n" + ">> \302\240+ struct drm_crtc_state *old_state)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_pending_vblank_event *event = crtc->state->event;\n" + ">> \302\240+\n" + ">> \302\240+ if (event) {\n" + ">> \302\240+ crtc->state->event = NULL;\n" + ">> \302\240+ spin_lock_irq(&crtc->dev->event_lock);\n" + ">> \302\240+ if (drm_crtc_vblank_get(crtc) == 0)\n" + ">> \302\240+ drm_crtc_arm_vblank_event(crtc, event);\n" + ">> \302\240+ else\n" + ">> \302\240+ drm_crtc_send_vblank_event(crtc, event);\n" + ">> \302\240+ spin_unlock_irq(&crtc->dev->event_lock);\n" + ">> \302\240+ }\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static irqreturn_t de2_lcd_irq(int irq, void *dev_id)\n" + ">> \302\240+{\n" + ">> \302\240+ struct lcd *lcd = (struct lcd *) dev_id;\n" + ">> \302\240+ u32 isr;\n" + ">> \302\240+\n" + ">> \302\240+ isr = readl_relaxed(lcd->mmio + TCON_GINT0_REG);\n" + ">> \302\240+\n" + ">> \302\240+ drm_crtc_handle_vblank(&lcd->crtc);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(isr &\n" + ">> \302\240+ ~(TCON_GINT0_TCON1_Vb_Int_Flag |\n" + ">> \302\240+ TCON_GINT0_TCON1_Vb_Line_Int_Flag),\n" + ">> \302\240+ lcd->mmio + TCON_GINT0_REG);\n" + ">> \302\240+\n" + ">> \302\240+ return IRQ_HANDLED;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)\n" + ">> \302\240+{\n" + ">> \302\240+ struct priv *priv = drm_to_priv(drm);\n" + ">> \302\240+ struct lcd *lcd = priv->lcds[crtc_ix];\n" + ">> \302\240+\n" + ">> \302\240+ orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);\n" + ">> \302\240+\n" + ">> \302\240+ return 0;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)\n" + ">> \302\240+{\n" + ">> \302\240+ struct priv *priv = drm_to_priv(drm);\n" + ">> \302\240+ struct lcd *lcd = priv->lcds[crtc_ix];\n" + ">> \302\240+\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+void de2_vblank_reset(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ drm_crtc_vblank_reset(&lcd->crtc);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/* frame functions */\n" + ">> \302\240+static int de2_crtc_set_clock(struct lcd *lcd, int rate)\n" + ">> \302\240+{\n" + ">> \302\240+ struct clk *parent_clk;\n" + ">> \302\240+ u32 parent_rate;\n" + ">> \302\240+ int ret;\n" + ">> \302\240+\n" + ">> \302\240+ /* determine and set the best rate for the parent clock (pll-video) */\n" + ">> \302\240+ if ((270000 * 2) % rate == 0)\n" + ">> \302\240+ parent_rate = 270000000;\n" + ">> \302\240+ else if (297000 % rate == 0)\n" + ">> \302\240+ parent_rate = 297000000;\n" + ">> \302\240+ else\n" + ">> \302\240+ return -EINVAL; /* unsupported clock */\n" + ">> \302\240+\n" + ">> \302\240+ parent_clk = clk_get_parent(lcd->clk);\n" + ">> \302\240+\n" + ">> \302\240+ ret = clk_set_rate(parent_clk, parent_rate);\n" + ">> \302\240+ if (ret) {\n" + ">> \302\240+ dev_err(lcd->dev, \"set parent rate failed %d\\n\", ret);\n" + ">> \302\240+ return ret;\n" + ">> \302\240+ }\n" + ">> \302\240+ ret = clk_set_rate(lcd->clk, rate * 1000);\n" + ">> \302\240+ if (ret) {\n" + ">> \302\240+ dev_err(lcd->dev, \"set rate failed %d\\n\", ret);\n" + ">> \302\240+ return ret;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ /* enable the clock */\n" + ">> \302\240+ reset_control_deassert(lcd->reset);\n" + ">> \302\240+ clk_prepare_enable(lcd->bus);\n" + ">> \302\240+ clk_prepare_enable(lcd->clk);\n" + ">> \302\240+\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_tcon_init(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);\n" + ">> \302\240+\n" + ">> \302\240+ /* disable/ack interrupts */\n" + ">> \302\240+ writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_tcon_enable(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_crtc *crtc = &lcd->crtc;\n" + ">> \302\240+ const struct drm_display_mode *mode = &crtc->mode;\n" + ">> \302\240+ int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;\n" + ">> \302\240+ int start_delay;\n" + ">> \302\240+ u32 data;\n" + ">> \302\240+\n" + ">> \302\240+ orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);\n" + ">> \302\240+\n" + ">> \302\240+ data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);\n" + ">> \302\240+ writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);\n" + ">> \302\240+ writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);\n" + ">> \302\240+ writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);\n" + ">> \302\240+ writel_relaxed(XY(mode->htotal - 1,\n" + ">> \302\240+ mode->htotal - mode->hsync_start - 1),\n" + ">> \302\240+ lcd->mmio + TCON1_BASIC3_REG);\n" + ">> \302\240+ writel_relaxed(XY(mode->vtotal * (3 - interlace),\n" + ">> \302\240+ mode->vtotal - mode->vsync_start - 1),\n" + ">> \302\240+ lcd->mmio + TCON1_BASIC4_REG);\n" + ">> \302\240+ writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,\n" + ">> \302\240+ mode->vsync_end - mode->vsync_start - 1),\n" + ">> \302\240+ lcd->mmio + TCON1_BASIC5_REG);\n" + ">> \302\240+\n" + ">> \302\240+ data = TCON1_IO_POL_IO2_inv;\n" + ">> \302\240+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)\n" + ">> \302\240+ data |= TCON1_IO_POL_IO0_inv;\n" + ">> \302\240+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)\n" + ">> \302\240+ data |= TCON1_IO_POL_IO1_inv;\n" + ">> \302\240+ writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);\n" + ">> \302\240+\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);\n" + ">> \302\240+\n" + ">> \302\240+ if (interlace == 2)\n" + ">> \302\240+ orl_relaxed(lcd->mmio + TCON1_CTL_REG,\n" + ">> \302\240+ TCON1_CTL_INTERLACE_ENABLE);\n" + ">> \302\240+ else\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON1_CTL_REG,\n" + ">> \302\240+ ~TCON1_CTL_INTERLACE_ENABLE);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);\n" + ">> \302\240+ writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);\n" + ">> \302\240+ writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);\n" + ">> \302\240+ writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);\n" + ">> \302\240+\n" + ">> \302\240+ start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;\n" + ">> \302\240+ if (start_delay > 31)\n" + ">> \302\240+ start_delay = 31;\n" + ">> \302\240+ data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);\n" + ">> \302\240+ data &= ~TCON1_CTL_Start_Delay_MASK;\n" + ">> \302\240+ data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;\n" + ">> \302\240+ writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);\n" + ">> \302\240+\n" + ">> \302\240+ orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_tcon_disable(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);\n" + ">> \302\240+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_crtc_enable(struct drm_crtc *crtc)\n" + ">> \302\240+{\n" + ">> \302\240+ struct lcd *lcd = crtc_to_lcd(crtc);\n" + ">> \302\240+ struct drm_display_mode *mode = &crtc->mode;\n" + ">> \302\240+\n" + ">> \302\240+ if (de2_crtc_set_clock(lcd, mode->clock) < 0)\n" + ">> \302\240+ return;\n" + ">> \302\240+ lcd->clk_enabled = true;\n" + ">> \302\240+\n" + ">> \302\240+ /* start the TCON and the DE */\n" + ">> \302\240+ de2_tcon_enable(lcd);\n" + ">> \302\240+ de2_de_enable(lcd);\n" + ">> \302\240+\n" + ">> \302\240+ /* turn on blanking interrupt */\n" + ">> \302\240+ drm_crtc_vblank_on(crtc);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_crtc_disable(struct drm_crtc *crtc,\n" + ">> \302\240+ struct drm_crtc_state *old_crtc_state)\n" + ">> \302\240+{\n" + ">> \302\240+ struct lcd *lcd = crtc_to_lcd(crtc);\n" + ">> \302\240+\n" + ">> \302\240+ if (!lcd->clk_enabled)\n" + ">> \302\240+ return; /* already disabled */\n" + ">> \302\240+ lcd->clk_enabled = false;\n" + ">> \302\240+\n" + ">> \302\240+ de2_de_disable(lcd);\n" + ">> \302\240+\n" + ">> \302\240+ drm_crtc_vblank_off(crtc);\n" + ">> \302\240+\n" + ">> \302\240+ de2_tcon_disable(lcd);\n" + ">> \302\240+\n" + ">> \302\240+ clk_disable_unprepare(lcd->clk);\n" + ">> \302\240+ clk_disable_unprepare(lcd->bus);\n" + ">> \302\240+ reset_control_assert(lcd->reset);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct drm_crtc_funcs de2_crtc_funcs = {\n" + ">> \302\240+ .destroy = drm_crtc_cleanup,\n" + ">> \302\240+ .set_config = drm_atomic_helper_set_config,\n" + ">> \302\240+ .page_flip = drm_atomic_helper_page_flip,\n" + ">> \302\240+ .reset = drm_atomic_helper_crtc_reset,\n" + ">> \302\240+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,\n" + ">> \302\240+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {\n" + ">> \302\240+ .atomic_flush = de2_atomic_flush,\n" + ">> \302\240+ .enable = de2_crtc_enable,\n" + ">> \302\240+ .atomic_disable = de2_crtc_disable,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+/* device init */\n" + ">> \302\240+static int de2_lcd_bind(struct device *dev, struct device *master,\n" + ">> \302\240+ void *data)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_device *drm = data;\n" + ">> \302\240+ struct priv *priv = drm_to_priv(drm);\n" + ">> \302\240+ struct lcd *lcd = dev_get_drvdata(dev);\n" + ">> \302\240+ struct drm_crtc *crtc = &lcd->crtc;\n" + ">> \302\240+ int ret, i, crtc_ix;\n" + ">> \302\240+\n" + ">> \302\240+ lcd->priv = priv;\n" + ">> \302\240+\n" + ">> \302\240+ /* set the CRTC reference */\n" + ">> \302\240+ crtc_ix = drm_crtc_index(crtc);\n" + ">> \302\240+ if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {\n" + ">> \302\240+ dev_err(drm->dev, \"Bad crtc index\");\n" + ">> \302\240+ return -ENOENT;\n" + ">> \302\240+ }\n" + ">> \302\240+ priv->lcds[crtc_ix] = lcd;\n" + ">> \302\240+\n" + ">> \302\240+ /* and the mixer index (DT port index in the DE) */\n" + ">> \302\240+ for (i = 0; ; i++) {\n" + ">> \302\240+ struct device_node *port;\n" + ">> \302\240+\n" + ">> \302\240+ port = of_parse_phandle(drm->dev->of_node, \"ports\", i);\n" + ">> \302\240+ if (!port)\n" + ">> \302\240+ break;\n" + ">> \302\240+ if (port == lcd->crtc.port) {\n" + ">> \302\240+ lcd->mixer = i;\n" + ">> \302\240+ break;\n" + ">> \302\240+ }\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ ret = de2_plane_init(drm, lcd);\n" + ">> \302\240+ if (ret < 0)\n" + ">> \302\240+ return ret;\n" + ">> \302\240+\n" + ">> \302\240+ drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);\n" + ">> \302\240+\n" + ">> \302\240+ return drm_crtc_init_with_planes(drm, crtc,\n" + ">> \302\240+ &lcd->planes[DE2_PRIMARY_PLANE],\n" + ">> \302\240+ &lcd->planes[DE2_CURSOR_PLANE],\n" + ">> \302\240+ &de2_crtc_funcs, NULL);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_lcd_unbind(struct device *dev, struct device *master,\n" + ">> \302\240+ void *data)\n" + ">> \302\240+{\n" + ">> \302\240+ struct platform_device *pdev = to_platform_device(dev);\n" + ">> \302\240+ struct lcd *lcd = platform_get_drvdata(pdev);\n" + ">> \302\240+\n" + ">> \302\240+ if (lcd->priv)\n" + ">> \302\240+ lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct component_ops de2_lcd_ops = {\n" + ">> \302\240+ .bind = de2_lcd_bind,\n" + ">> \302\240+ .unbind = de2_lcd_unbind,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static int de2_lcd_probe(struct platform_device *pdev)\n" + ">> \302\240+{\n" + ">> \302\240+ struct device *dev = &pdev->dev;\n" + ">> \302\240+ struct device_node *np = dev->of_node, *tmp, *parent, *port;\n" + ">> \302\240+ struct lcd *lcd;\n" + ">> \302\240+ struct resource *res;\n" + ">> \302\240+ int id, irq, ret;\n" + ">> \302\240+\n" + ">> \302\240+ lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);\n" + ">> \302\240+ if (!lcd)\n" + ">> \302\240+ return -ENOMEM;\n" + ">> \302\240+\n" + ">> \302\240+ dev_set_drvdata(dev, lcd);\n" + ">> \302\240+ lcd->dev = dev;\n" + ">> \302\240+ lcd->mixer = id;\n" + ">> \302\240+\n" + ">> \302\240+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\n" + ">> \302\240+ if (!res) {\n" + ">> \302\240+ dev_err(dev, \"failed to get memory resource\\n\");\n" + ">> \302\240+ return -EINVAL;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ lcd->mmio = devm_ioremap_resource(dev, res);\n" + ">> \302\240+ if (IS_ERR(lcd->mmio)) {\n" + ">> \302\240+ dev_err(dev, \"failed to map registers\\n\");\n" + ">> \302\240+ return PTR_ERR(lcd->mmio);\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ /* possible CRTC */\n" + ">> \302\240+ parent = np;\n" + ">> \302\240+ tmp = of_get_child_by_name(np, \"ports\");\n" + ">> \302\240+ if (tmp)\n" + ">> \302\240+ parent = tmp;\n" + ">> \302\240+ port = of_get_child_by_name(parent, \"port\");\n" + ">> \302\240+ of_node_put(tmp);\n" + ">> \302\240+ if (!port) {\n" + ">> \302\240+ dev_err(dev, \"no port node\\n\");\n" + ">> \302\240+ return -ENXIO;\n" + ">> \302\240+ }\n" + ">> \302\240+ lcd->crtc.port = port;\n" + ">> \302\240+\n" + ">> \302\240+ lcd->bus = devm_clk_get(dev, \"bus\");\n" + ">> \302\240+ if (IS_ERR(lcd->bus)) {\n" + ">> \302\240+ dev_err(dev, \"get bus clock err %d\\n\", (int) PTR_ERR(lcd->bus));\n" + ">> \302\240+ ret = PTR_ERR(lcd->bus);\n" + ">> \302\240+ goto err;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ lcd->clk = devm_clk_get(dev, \"clock\");\n" + ">> \302\240+ if (IS_ERR(lcd->clk)) {\n" + ">> \302\240+ ret = PTR_ERR(lcd->clk);\n" + ">> \302\240+ dev_err(dev, \"get video clock err %d\\n\", ret);\n" + ">> \302\240+ goto err;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ lcd->reset = devm_reset_control_get(dev, NULL);\n" + ">> \302\240+ if (IS_ERR(lcd->reset)) {\n" + ">> \302\240+ ret = PTR_ERR(lcd->reset);\n" + ">> \302\240+ dev_err(dev, \"get reset err %d\\n\", ret);\n" + ">> \302\240+ goto err;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ irq = platform_get_irq(pdev, 0);\n" + ">> \302\240+ if (irq <= 0) {\n" + ">> \302\240+ dev_err(dev, \"unable to get irq\\n\");\n" + ">> \302\240+ ret = -EINVAL;\n" + ">> \302\240+ goto err;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ de2_tcon_init(lcd); /* stop TCON and avoid interrupts */\n" + ">> \302\240+\n" + ">> \302\240+ ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,\n" + ">> \302\240+ dev_name(dev), lcd);\n" + ">> \302\240+ if (ret < 0) {\n" + ">> \302\240+ dev_err(dev, \"unable to request irq %d\\n\", irq);\n" + ">> \302\240+ goto err;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ return component_add(dev, &de2_lcd_ops);\n" + ">> \302\240+\n" + ">> \302\240+err:\n" + ">> \302\240+ of_node_put(lcd->crtc.port);\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static int de2_lcd_remove(struct platform_device *pdev)\n" + ">> \302\240+{\n" + ">> \302\240+ struct lcd *lcd = platform_get_drvdata(pdev);\n" + ">> \302\240+\n" + ">> \302\240+ component_del(&pdev->dev, &de2_lcd_ops);\n" + ">> \302\240+\n" + ">> \302\240+ of_node_put(lcd->crtc.port);\n" + ">> \302\240+\n" + ">> \302\240+ return 0;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct of_device_id de2_lcd_ids[] = {\n" + ">> \302\240+ { .compatible = \"allwinner,sun8i-a83t-tcon\", },\n" + ">> \302\240+ { }\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+struct platform_driver de2_lcd_platform_driver = {\n" + ">> \302\240+ .probe = de2_lcd_probe,\n" + ">> \302\240+ .remove = de2_lcd_remove,\n" + ">> \302\240+ .driver = {\n" + ">> \302\240+ .name = \"sun8i-de2-tcon\",\n" + ">> \302\240+ .of_match_table = of_match_ptr(de2_lcd_ids),\n" + ">> \302\240+ },\n" + ">> \302\240+};\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..c0d34a7\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/de2_crtc.h\n" + ">> \302\240@@ -0,0 +1,50 @@\n" + ">> \302\240+#ifndef __DE2_CRTC_H__\n" + ">> \302\240+#define __DE2_CRTC_H__\n" + ">> \302\240+/*\n" + ">> \302\240+ * Copyright (C) 2016 Jean-Fran??ois Moine\n" + ">> \302\240+ *\n" + ">> \302\240+ * This program is free software; you can redistribute it and/or\n" + ">> \302\240+ * modify it under the terms of the GNU General Public License as\n" + ">> \302\240+ * published by the Free Software Foundation; either version 2 of\n" + ">> \302\240+ * the License, or (at your option) any later version.\n" + ">> \302\240+ */\n" + ">> \302\240+\n" + ">> \302\240+#include <drm/drm_plane_helper.h>\n" + ">> \302\240+\n" + ">> \302\240+struct clk;\n" + ">> \302\240+struct reset_control;\n" + ">> \302\240+struct priv;\n" + ">> \302\240+\n" + ">> \302\240+/* planes */\n" + ">> \302\240+#define DE2_PRIMARY_PLANE 0\n" + ">> \302\240+#define DE2_CURSOR_PLANE 1\n" + ">> \302\240+#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */\n" + ">> \302\240+\n" + ">> \302\240+struct lcd {\n" + ">> \302\240+ void __iomem *mmio;\n" + ">> \302\240+\n" + ">> \302\240+ struct device *dev;\n" + ">> \302\240+ struct drm_crtc crtc;\n" + ">> \302\240+\n" + ">> \302\240+ struct priv *priv; /* DRM/DE private data */\n" + ">> \302\240+\n" + ">> \302\240+ u8 mixer; /* LCD (mixer) number */\n" + ">> \302\240+ u8 delayed; /* bitmap of planes with delayed update */\n" + ">> \302\240+\n" + ">> \302\240+ u8 clk_enabled; /* used for error in crtc_enable */\n" + ">> \302\240+\n" + ">> \302\240+ struct clk *clk;\n" + ">> \302\240+ struct clk *bus;\n" + ">> \302\240+ struct reset_control *reset;\n" + ">> \302\240+\n" + ">> \302\240+ struct drm_plane planes[DE2_N_PLANES];\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)\n" + ">> \302\240+\n" + ">> \302\240+/* in de2_plane.c */\n" + ">> \302\240+void de2_de_enable(struct lcd *lcd);\n" + ">> \302\240+void de2_de_disable(struct lcd *lcd);\n" + ">> \302\240+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);\n" + ">> \302\240+\n" + ">> \302\240+#endif /* __DE2_CRTC_H__ */\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..f96babe\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/de2_drv.c\n" + ">> \302\240@@ -0,0 +1,317 @@\n" + ">> \302\240+/*\n" + ">> \302\240+ * Allwinner DRM driver - DE2 DRM driver\n" + ">> \302\240+ *\n" + ">> \302\240+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>\n" + ">> \302\240+ *\n" + ">> \302\240+ * This program is free software; you can redistribute it and/or\n" + ">> \302\240+ * modify it under the terms of the GNU General Public License as\n" + ">> \302\240+ * published by the Free Software Foundation; either version 2 of\n" + ">> \302\240+ * the License, or (at your option) any later version.\n" + ">> \302\240+ */\n" + ">> \302\240+\n" + ">> \302\240+#include <linux/module.h>\n" + ">> \302\240+#include <linux/of_device.h>\n" + ">> \302\240+#include <drm/drm_of.h>\n" + ">> \302\240+#include <linux/component.h>\n" + ">> \302\240+#include <drm/drm_atomic_helper.h>\n" + ">> \302\240+#include <drm/drm_crtc_helper.h>\n" + ">> \302\240+#include <drm/drm_fb_cma_helper.h>\n" + ">> \302\240+#include <drm/drm_gem_cma_helper.h>\n" + ">> \302\240+\n" + ">> \302\240+#include \"de2_drv.h\"\n" + ">> \302\240+\n" + ">> \302\240+#define DRIVER_NAME \"sun8i-de2\"\n" + ">> \302\240+#define DRIVER_DESC \"Allwinner DRM DE2\"\n" + ">> \302\240+#define DRIVER_DATE \"20161101\"\n" + ">> \302\240+#define DRIVER_MAJOR 1\n" + ">> \302\240+#define DRIVER_MINOR 0\n" + ">> \302\240+\n" + ">> \302\240+static const struct of_device_id de2_drm_of_match[] = {\n" + ">> \302\240+ { .compatible = \"allwinner,sun8i-a83t-display-engine\",\n" + ">> \302\240+ .data = (void *) SOC_A83T },\n" + ">> \302\240+ { .compatible = \"allwinner,sun8i-h3-display-engine\",\n" + ">> \302\240+ .data = (void *) SOC_H3 },\n" + ">> \302\240+ { },\n" + ">> \302\240+};\n" + ">> \302\240+MODULE_DEVICE_TABLE(of, de2_drm_of_match);\n" + ">> \302\240+\n" + ">> \302\240+static void de2_fb_output_poll_changed(struct drm_device *drm)\n" + ">> \302\240+{\n" + ">> \302\240+ struct priv *priv = drm_to_priv(drm);\n" + ">> \302\240+\n" + ">> \302\240+ if (priv->fbdev)\n" + ">> \302\240+ drm_fbdev_cma_hotplug_event(priv->fbdev);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct drm_mode_config_funcs de2_mode_config_funcs = {\n" + ">> \302\240+ .fb_create = drm_fb_cma_create,\n" + ">> \302\240+ .output_poll_changed = de2_fb_output_poll_changed,\n" + ">> \302\240+ .atomic_check = drm_atomic_helper_check,\n" + ">> \302\240+ .atomic_commit = drm_atomic_helper_commit,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+/* -- DRM operations -- */\n" + ">> \302\240+\n" + ">> \302\240+static void de2_lastclose(struct drm_device *drm)\n" + ">> \302\240+{\n" + ">> \302\240+ struct priv *priv = drm_to_priv(drm);\n" + ">> \302\240+\n" + ">> \302\240+ if (priv->fbdev)\n" + ">> \302\240+ drm_fbdev_cma_restore_mode(priv->fbdev);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct file_operations de2_fops = {\n" + ">> \302\240+ .owner = THIS_MODULE,\n" + ">> \302\240+ .open = drm_open,\n" + ">> \302\240+ .release = drm_release,\n" + ">> \302\240+ .unlocked_ioctl = drm_ioctl,\n" + ">> \302\240+ .poll = drm_poll,\n" + ">> \302\240+ .read = drm_read,\n" + ">> \302\240+ .llseek = no_llseek,\n" + ">> \302\240+ .mmap = drm_gem_cma_mmap,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static struct drm_driver de2_drm_driver = {\n" + ">> \302\240+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |\n" + ">> \302\240+ DRIVER_ATOMIC,\n" + ">> \302\240+ .lastclose = de2_lastclose,\n" + ">> \302\240+ .get_vblank_counter = drm_vblank_no_hw_counter,\n" + ">> \302\240+ .enable_vblank = de2_enable_vblank,\n" + ">> \302\240+ .disable_vblank = de2_disable_vblank,\n" + ">> \302\240+ .gem_free_object = drm_gem_cma_free_object,\n" + ">> \302\240+ .gem_vm_ops = &drm_gem_cma_vm_ops,\n" + ">> \302\240+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,\n" + ">> \302\240+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,\n" + ">> \302\240+ .gem_prime_import = drm_gem_prime_import,\n" + ">> \302\240+ .gem_prime_export = drm_gem_prime_export,\n" + ">> \302\240+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,\n" + ">> \302\240+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,\n" + ">> \302\240+ .gem_prime_vmap = drm_gem_cma_prime_vmap,\n" + ">> \302\240+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,\n" + ">> \302\240+ .gem_prime_mmap = drm_gem_cma_prime_mmap,\n" + ">> \302\240+ .dumb_create = drm_gem_cma_dumb_create,\n" + ">> \302\240+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,\n" + ">> \302\240+ .dumb_destroy = drm_gem_dumb_destroy,\n" + ">> \302\240+ .fops = &de2_fops,\n" + ">> \302\240+ .name = DRIVER_NAME,\n" + ">> \302\240+ .desc = DRIVER_DESC,\n" + ">> \302\240+ .date = DRIVER_DATE,\n" + ">> \302\240+ .major = DRIVER_MAJOR,\n" + ">> \302\240+ .minor = DRIVER_MINOR,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+/*\n" + ">> \302\240+ * Platform driver\n" + ">> \302\240+ */\n" + ">> \302\240+\n" + ">> \302\240+static int de2_drm_bind(struct device *dev)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_device *drm;\n" + ">> \302\240+ struct priv *priv;\n" + ">> \302\240+ struct resource *res;\n" + ">> \302\240+ struct lcd *lcd;\n" + ">> \302\240+ int i, ret;\n" + ">> \302\240+\n" + ">> \302\240+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);\n" + ">> \302\240+ if (!priv)\n" + ">> \302\240+ return -ENOMEM;\n" + ">> \302\240+\n" + ">> \302\240+ drm = &priv->drm;\n" + ">> \302\240+ dev_set_drvdata(dev, drm);\n" + ">> \302\240+\n" + ">> \302\240+ /* get the resources */\n" + ">> \302\240+ priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;\n" + ">> \302\240+\n" + ">> \302\240+ res = platform_get_resource(to_platform_device(dev),\n" + ">> \302\240+ IORESOURCE_MEM, 0);\n" + ">> \302\240+ if (!res) {\n" + ">> \302\240+ dev_err(dev, \"failed to get memory resource\\n\");\n" + ">> \302\240+ ret = -EINVAL;\n" + ">> \302\240+ goto out1;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ priv->mmio = devm_ioremap_resource(dev, res);\n" + ">> \302\240+ if (IS_ERR(priv->mmio)) {\n" + ">> \302\240+ ret = PTR_ERR(priv->mmio);\n" + ">> \302\240+ dev_err(dev, \"failed to map registers %d\\n\", ret);\n" + ">> \302\240+ goto out1;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ priv->gate = devm_clk_get(dev, \"bus\");\n" + ">> \302\240+ if (IS_ERR(priv->gate)) {\n" + ">> \302\240+ ret = PTR_ERR(priv->gate);\n" + ">> \302\240+ dev_err(dev, \"bus gate err %d\\n\", ret);\n" + ">> \302\240+ goto out1;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ priv->clk = devm_clk_get(dev, \"clock\");\n" + ">> \302\240+ if (IS_ERR(priv->clk)) {\n" + ">> \302\240+ ret = PTR_ERR(priv->clk);\n" + ">> \302\240+ dev_err(dev, \"clock err %d\\n\", ret);\n" + ">> \302\240+ goto out1;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ priv->reset = devm_reset_control_get(dev, NULL);\n" + ">> \302\240+ if (IS_ERR(priv->reset)) {\n" + ">> \302\240+ ret = PTR_ERR(priv->reset);\n" + ">> \302\240+ dev_err(dev, \"reset err %d\\n\", ret);\n" + ">> \302\240+ goto out1;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ mutex_init(&priv->mutex); /* protect DE I/O accesses */\n" + ">> \302\240+\n" + ">> \302\240+ ret = drm_dev_init(drm, &de2_drm_driver, dev);\n" + ">> \302\240+ if (ret != 0) {\n" + ">> \302\240+ dev_err(dev, \"dev_init failed %d\\n\", ret);\n" + ">> \302\240+ goto out1;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ drm_mode_config_init(drm);\n" + ">> \302\240+ drm->mode_config.min_width = 32; /* needed for cursor */\n" + ">> \302\240+ drm->mode_config.min_height = 32;\n" + ">> \302\240+ drm->mode_config.max_width = 1920;\n" + ">> \302\240+ drm->mode_config.max_height = 1080;\n" + ">> \302\240+ drm->mode_config.funcs = &de2_mode_config_funcs;\n" + ">> \302\240+\n" + ">> \302\240+ drm->irq_enabled = true;\n" + ">> \302\240+\n" + ">> \302\240+ /* start the subdevices */\n" + ">> \302\240+ ret = component_bind_all(dev, drm);\n" + ">> \302\240+ if (ret < 0)\n" + ">> \302\240+ goto out2;\n" + ">> \302\240+\n" + ">> \302\240+ /* initialize and disable vertical blanking on all CRTCs */\n" + ">> \302\240+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);\n" + ">> \302\240+ if (ret < 0)\n" + ">> \302\240+ dev_warn(dev, \"vblank_init failed %d\\n\", ret);\n" + ">> \302\240+\n" + ">> \302\240+ for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {\n" + ">> \302\240+ lcd = priv->lcds[i];\n" + ">> \302\240+ if (lcd)\n" + ">> \302\240+ de2_vblank_reset(lcd);\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ drm_mode_config_reset(drm);\n" + ">> \302\240+\n" + ">> \302\240+ priv->fbdev = drm_fbdev_cma_init(drm,\n" + ">> \302\240+ 32, /* bpp */\n" + ">> \302\240+ drm->mode_config.num_crtc,\n" + ">> \302\240+ drm->mode_config.num_connector);\n" + ">> \302\240+ if (IS_ERR(priv->fbdev)) {\n" + ">> \302\240+ ret = PTR_ERR(priv->fbdev);\n" + ">> \302\240+ priv->fbdev = NULL;\n" + ">> \302\240+ goto out3;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ drm_kms_helper_poll_init(drm);\n" + ">> \302\240+\n" + ">> \302\240+ ret = drm_dev_register(drm, 0);\n" + ">> \302\240+ if (ret < 0)\n" + ">> \302\240+ goto out4;\n" + ">> \302\240+\n" + ">> \302\240+ return 0;\n" + ">> \302\240+\n" + ">> \302\240+out4:\n" + ">> \302\240+ drm_fbdev_cma_fini(priv->fbdev);\n" + ">> \302\240+out3:\n" + ">> \302\240+ component_unbind_all(dev, drm);\n" + ">> \302\240+out2:\n" + ">> \302\240+ drm_dev_unref(drm);\n" + ">> \302\240+out1:\n" + ">> \302\240+ kfree(priv);\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_drm_unbind(struct device *dev)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_device *drm = dev_get_drvdata(dev);\n" + ">> \302\240+ struct priv *priv = drm_to_priv(drm);\n" + ">> \302\240+\n" + ">> \302\240+ drm_dev_unregister(drm);\n" + ">> \302\240+\n" + ">> \302\240+ drm_fbdev_cma_fini(priv->fbdev);\n" + ">> \302\240+ drm_kms_helper_poll_fini(drm);\n" + ">> \302\240+ drm_vblank_cleanup(drm);\n" + ">> \302\240+ drm_mode_config_cleanup(drm);\n" + ">> \302\240+\n" + ">> \302\240+ component_unbind_all(dev, drm);\n" + ">> \302\240+\n" + ">> \302\240+ kfree(priv);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct component_master_ops de2_drm_comp_ops = {\n" + ">> \302\240+ .bind = de2_drm_bind,\n" + ">> \302\240+ .unbind = de2_drm_unbind,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+/*\n" + ">> \302\240+ * drm_of_component_probe() does:\n" + ">> \302\240+ * - bind of the ports (lcd-controller.port)\n" + ">> \302\240+ * - bind of the remote nodes (hdmi, tve..)\n" + ">> \302\240+ */\n" + ">> \302\240+static int compare_of(struct device *dev, void *data)\n" + ">> \302\240+{\n" + ">> \302\240+ struct device_node *np = data;\n" + ">> \302\240+\n" + ">> \302\240+ if (of_node_cmp(np->name, \"port\") == 0) {\n" + ">> \302\240+ np = of_get_parent(np);\n" + ">> \302\240+ of_node_put(np);\n" + ">> \302\240+ }\n" + ">> \302\240+ return dev->of_node == np;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static int de2_drm_probe(struct platform_device *pdev)\n" + ">> \302\240+{\n" + ">> \302\240+ int ret;\n" + ">> \302\240+\n" + ">> \302\240+ ret = drm_of_component_probe(&pdev->dev,\n" + ">> \302\240+ compare_of,\n" + ">> \302\240+ &de2_drm_comp_ops);\n" + ">> \302\240+ if (ret == -EINVAL)\n" + ">> \302\240+ ret = -ENXIO;\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static int de2_drm_remove(struct platform_device *pdev)\n" + ">> \302\240+{\n" + ">> \302\240+ component_master_del(&pdev->dev, &de2_drm_comp_ops);\n" + ">> \302\240+\n" + ">> \302\240+ return 0;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static struct platform_driver de2_drm_platform_driver = {\n" + ">> \302\240+ .probe = de2_drm_probe,\n" + ">> \302\240+ .remove = de2_drm_remove,\n" + ">> \302\240+ .driver = {\n" + ">> \302\240+ .name = DRIVER_NAME,\n" + ">> \302\240+ .of_match_table = de2_drm_of_match,\n" + ">> \302\240+ },\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static int __init de2_drm_init(void)\n" + ">> \302\240+{\n" + ">> \302\240+ int ret;\n" + ">> \302\240+\n" + ">> \302\240+ ret = platform_driver_register(&de2_lcd_platform_driver);\n" + ">> \302\240+ if (ret < 0)\n" + ">> \302\240+ return ret;\n" + ">> \302\240+\n" + ">> \302\240+ ret = platform_driver_register(&de2_drm_platform_driver);\n" + ">> \302\240+ if (ret < 0)\n" + ">> \302\240+ platform_driver_unregister(&de2_lcd_platform_driver);\n" + ">> \302\240+\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void __exit de2_drm_fini(void)\n" + ">> \302\240+{\n" + ">> \302\240+ platform_driver_unregister(&de2_lcd_platform_driver);\n" + ">> \302\240+ platform_driver_unregister(&de2_drm_platform_driver);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+module_init(de2_drm_init);\n" + ">> \302\240+module_exit(de2_drm_fini);\n" + ">> \302\240+\n" + ">> \302\240+MODULE_AUTHOR(\"Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>\");\n" + ">> \302\240+MODULE_DESCRIPTION(\"Allwinner DE2 DRM Driver\");\n" + ">> \302\240+MODULE_LICENSE(\"GPL v2\");\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..c42c30a\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/de2_drv.h\n" + ">> \302\240@@ -0,0 +1,48 @@\n" + ">> \302\240+#ifndef __DE2_DRM_H__\n" + ">> \302\240+#define __DE2_DRM_H__\n" + ">> \302\240+/*\n" + ">> \302\240+ * Copyright (C) 2016 Jean-Fran??ois Moine\n" + ">> \302\240+ *\n" + ">> \302\240+ * This program is free software; you can redistribute it and/or\n" + ">> \302\240+ * modify it under the terms of the GNU General Public License as\n" + ">> \302\240+ * published by the Free Software Foundation; either version 2 of\n" + ">> \302\240+ * the License, or (at your option) any later version.\n" + ">> \302\240+ */\n" + ">> \302\240+\n" + ">> \302\240+#include <drm/drmP.h>\n" + ">> \302\240+#include <linux/clk.h>\n" + ">> \302\240+#include <linux/reset.h>\n" + ">> \302\240+\n" + ">> \302\240+struct drm_fbdev_cma;\n" + ">> \302\240+struct lcd;\n" + ">> \302\240+\n" + ">> \302\240+#define N_LCDS 2\n" + ">> \302\240+\n" + ">> \302\240+struct priv {\n" + ">> \302\240+ struct drm_device drm;\n" + ">> \302\240+ void __iomem *mmio;\n" + ">> \302\240+ struct clk *clk;\n" + ">> \302\240+ struct clk *gate;\n" + ">> \302\240+ struct reset_control *reset;\n" + ">> \302\240+\n" + ">> \302\240+ struct mutex mutex; /* protect DE I/O access */\n" + ">> \302\240+ u8 soc_type;\n" + ">> \302\240+#define SOC_A83T 0\n" + ">> \302\240+#define SOC_H3 1\n" + ">> \302\240+ u8 started; /* bitmap of started mixers */\n" + ">> \302\240+ u8 clean; /* bitmap of clean mixers */\n" + ">> \302\240+\n" + ">> \302\240+ struct drm_fbdev_cma *fbdev;\n" + ">> \302\240+\n" + ">> \302\240+ struct lcd *lcds[N_LCDS]; /* CRTCs */\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+#define drm_to_priv(x) container_of(x, struct priv, drm)\n" + ">> \302\240+\n" + ">> \302\240+/* in de2_crtc.c */\n" + ">> \302\240+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);\n" + ">> \302\240+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);\n" + ">> \302\240+void de2_vblank_reset(struct lcd *lcd);\n" + ">> \302\240+extern struct platform_driver de2_lcd_platform_driver;\n" + ">> \302\240+\n" + ">> \302\240+#endif /* __DE2_DRM_H__ */\n" + ">> \302\240diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c\n" + ">> \302\240new file mode 100644\n" + ">> \302\240index 0000000..2fd72dc\n" + ">> \302\240--- /dev/null\n" + ">> \302\240+++ b/drivers/gpu/drm/sun8i/de2_plane.c\n" + ">> \302\240@@ -0,0 +1,734 @@\n" + ">> \302\240+/*\n" + ">> \302\240+ * Allwinner DRM driver - Display Engine 2\n" + ">> \302\240+ *\n" + ">> \302\240+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>\n" + ">> \302\240+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers\n" + ">> \302\240+ * Copyright (c) 2016 Allwinnertech Co., Ltd.\n" + ">> \302\240+ *\n" + ">> \302\240+ * This program is free software; you can redistribute it and/or\n" + ">> \302\240+ * modify it under the terms of the GNU General Public License as\n" + ">> \302\240+ * published by the Free Software Foundation; either version 2 of\n" + ">> \302\240+ * the License, or (at your option) any later version.\n" + ">> \302\240+ */\n" + ">> \302\240+\n" + ">> \302\240+#include <linux/io.h>\n" + ">> \302\240+#include <drm/drm_atomic_helper.h>\n" + ">> \302\240+#include <drm/drm_crtc_helper.h>\n" + ">> \302\240+#include <drm/drm_fb_cma_helper.h>\n" + ">> \302\240+#include <drm/drm_gem_cma_helper.h>\n" + ">> \302\240+#include <drm/drm_plane_helper.h>\n" + ">> \302\240+\n" + ">> \302\240+#include \"de2_drv.h\"\n" + ">> \302\240+#include \"de2_crtc.h\"\n" + ">> \302\240+\n" + ">> \302\240+/* DE2 I/O map */\n" + ">> \302\240+\n" + ">> \302\240+#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */\n" + ">> \302\240+#define DE2_GATE_REG 0x0004\n" + ">> \302\240+#define DE2_RESET_REG 0x0008\n" + ">> \302\240+#define DE2_DIV_REG 0x000c /* 4 bits per LCD */\n" + ">> \302\240+#define DE2_SEL_REG 0x0010\n" + ">> \302\240+\n" + ">> \302\240+#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */\n" + ">> \302\240+#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */\n" + ">> \302\240+\n" + ">> \302\240+/* mixer registers (addr / mixer base) */\n" + ">> \302\240+#define MIXER_GLB_REGS 0x00000 /* global control */\n" + ">> \302\240+#define MIXER_BLD_REGS 0x01000 /* alpha blending */\n" + ">> \302\240+#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */\n" + ">> \302\240+#define MIXER_CHAN_SZ 0x1000 /* size of a channel */\n" + ">> \302\240+#define MIXER_VSU_REGS 0x20000 /* VSU */\n" + ">> \302\240+#define MIXER_GSU1_REGS 0x30000 /* GSUs */\n" + ">> \302\240+#define MIXER_GSU2_REGS 0x40000\n" + ">> \302\240+#define MIXER_GSU3_REGS 0x50000\n" + ">> \302\240+#define MIXER_FCE_REGS 0xa0000 /* FCE */\n" + ">> \302\240+#define MIXER_BWS_REGS 0xa2000 /* BWS */\n" + ">> \302\240+#define MIXER_LTI_REGS 0xa4000 /* LTI */\n" + ">> \302\240+#define MIXER_PEAK_REGS 0xa6000 /* PEAK */\n" + ">> \302\240+#define MIXER_ASE_REGS 0xa8000 /* ASE */\n" + ">> \302\240+#define MIXER_FCC_REGS 0xaa000 /* FCC */\n" + ">> \302\240+#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */\n" + ">> \302\240+\n" + ">> \302\240+/* global control */\n" + ">> \302\240+#define MIXER_GLB_CTL_REG 0x00\n" + ">> \302\240+#define MIXER_GLB_CTL_rt_en BIT(0)\n" + ">> \302\240+#define MIXER_GLB_CTL_finish_irq_en BIT(4)\n" + ">> \302\240+#define MIXER_GLB_CTL_rtwb_port BIT(12)\n" + ">> \302\240+#define MIXER_GLB_STATUS_REG 0x04\n" + ">> \302\240+#define MIXER_GLB_DBUFF_REG 0x08\n" + ">> \302\240+#define MIXER_GLB_SIZE_REG 0x0c\n" + ">> \302\240+\n" + ">> \302\240+/* alpha blending */\n" + ">> \302\240+#define MIXER_BLD_FCOLOR_CTL_REG 0x00\n" + ">> \302\240+#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe))\n" + ">> \302\240+#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */\n" + ">> \302\240+#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */\n" + ">> \302\240+#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x))\n" + ">> \302\240+#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x))\n" + ">> \302\240+#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x))\n" + ">> \302\240+#define MIXER_BLD_ROUTE_REG 0x80\n" + ">> \302\240+#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))\n" + ">> \302\240+#define MIXER_BLD_PREMULTIPLY_REG 0x84\n" + ">> \302\240+#define MIXER_BLD_BKCOLOR_REG 0x88\n" + ">> \302\240+#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c\n" + ">> \302\240+#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */\n" + ">> \302\240+#define MIXER_BLD_MODE_SRCOVER 0x03010301\n" + ">> \302\240+#define MIXER_BLD_OUT_CTL_REG 0xfc\n" + ">> \302\240+\n" + ">> \302\240+/* VI channel (channel 0) */\n" + ">> \302\240+#define VI_CFG_N 4 /* number of layers */\n" + ">> \302\240+#define VI_CFG_SIZE 0x30 /* size of a layer */\n" + ">> \302\240+#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l))\n" + ">> \302\240+#define VI_CFG_ATTR_en BIT(0)\n" + ">> \302\240+#define VI_CFG_ATTR_fcolor_en BIT(4)\n" + ">> \302\240+#define VI_CFG_ATTR_fmt_SHIFT 8\n" + ">> \302\240+#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)\n" + ">> \302\240+#define VI_CFG_ATTR_ui_sel BIT(15)\n" + ">> \302\240+#define VI_CFG_ATTR_top_down BIT(23)\n" + ">> \302\240+#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l))\n" + ">> \302\240+#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l))\n" + ">> \302\240+#define VI_N_PLANES 3\n" + ">> \302\240+#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p))\n" + ">> \302\240+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))\n" + ">> \302\240+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))\n" + ">> \302\240+#define VI_FCOLORx(l) (0xc0 + 4 * (l))\n" + ">> \302\240+#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p))\n" + ">> \302\240+#define VI_BOT_HADDRx(p) (0xdc + 4 * (p))\n" + ">> \302\240+#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n))\n" + ">> \302\240+#define VI_HORI_DSx(n) (0xf0 + 4 * (n))\n" + ">> \302\240+#define VI_VERT_DSx(n) (0xf8 + 4 * (n))\n" + ">> \302\240+#define VI_SIZE 0x100\n" + ">> \302\240+\n" + ">> \302\240+/* UI channel (channels 1..3) */\n" + ">> \302\240+#define UI_CFG_N 4 /* number of layers */\n" + ">> \302\240+#define UI_CFG_SIZE (8 * 4) /* size of a layer */\n" + ">> \302\240+#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_CFG_ATTR_en BIT(0)\n" + ">> \302\240+#define UI_CFG_ATTR_alpmod_SHIFT 1\n" + ">> \302\240+#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)\n" + ">> \302\240+#define UI_CFG_ATTR_fcolor_en BIT(4)\n" + ">> \302\240+#define UI_CFG_ATTR_fmt_SHIFT 8\n" + ">> \302\240+#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)\n" + ">> \302\240+#define UI_CFG_ATTR_top_down BIT(23)\n" + ">> \302\240+#define UI_CFG_ATTR_alpha_SHIFT 24\n" + ">> \302\240+#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)\n" + ">> \302\240+#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l))\n" + ">> \302\240+#define UI_TOP_HADDR 0x80\n" + ">> \302\240+#define UI_BOT_HADDR 0x84\n" + ">> \302\240+#define UI_OVL_SIZE 0x88\n" + ">> \302\240+#define UI_SIZE 0x8c\n" + ">> \302\240+\n" + ">> \302\240+/* coordinates and sizes */\n" + ">> \302\240+#define XY(x, y) (((y) << 16) | (x))\n" + ">> \302\240+#define WH(w, h) ((((h) - 1) << 16) | ((w) - 1))\n" + ">> \302\240+\n" + ">> \302\240+/* UI video formats */\n" + ">> \302\240+#define DE2_FORMAT_ARGB_8888 0\n" + ">> \302\240+#define DE2_FORMAT_BGRA_8888 3\n" + ">> \302\240+#define DE2_FORMAT_XRGB_8888 4\n" + ">> \302\240+#define DE2_FORMAT_RGB_888 8\n" + ">> \302\240+#define DE2_FORMAT_BGR_888 9\n" + ">> \302\240+\n" + ">> \302\240+/* VI video formats */\n" + ">> \302\240+#define DE2_FORMAT_YUV422_I_YVYU 1 /* YVYU */\n" + ">> \302\240+#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */\n" + ">> \302\240+#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */\n" + ">> \302\240+#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */\n" + ">> \302\240+#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */\n" + ">> \302\240+\n" + ">> \302\240+/* plane formats */\n" + ">> \302\240+static const uint32_t ui_formats[] = {\n" + ">> \302\240+ DRM_FORMAT_ARGB8888,\n" + ">> \302\240+ DRM_FORMAT_BGRA8888,\n" + ">> \302\240+ DRM_FORMAT_XRGB8888,\n" + ">> \302\240+ DRM_FORMAT_RGB888,\n" + ">> \302\240+ DRM_FORMAT_BGR888,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static const uint32_t vi_formats[] = {\n" + ">> \302\240+ DRM_FORMAT_XRGB8888,\n" + ">> \302\240+ DRM_FORMAT_YUYV,\n" + ">> \302\240+ DRM_FORMAT_YVYU,\n" + ">> \302\240+ DRM_FORMAT_YUV422,\n" + ">> \302\240+ DRM_FORMAT_YUV420,\n" + ">> \302\240+ DRM_FORMAT_UYVY,\n" + ">> \302\240+ DRM_FORMAT_BGRA8888,\n" + ">> \302\240+ DRM_FORMAT_RGB888,\n" + ">> \302\240+ DRM_FORMAT_BGR888,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+/*\n" + ">> \302\240+ * plane table\n" + ">> \302\240+ *\n" + ">> \302\240+ * The chosen channel/layer assignment of the planes respects\n" + ">> \302\240+ * the following constraints:\n" + ">> \302\240+ * - the cursor must be in a channel higher than the primary channel\n" + ">> \302\240+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1\n" + ">> \302\240+ */\n" + ">> \302\240+static const struct {\n" + ">> \302\240+ u8 chan;\n" + ">> \302\240+ u8 layer;\n" + ">> \302\240+ u8 pipe;\n" + ">> \302\240+ u8 type; /* plane type */\n" + ">> \302\240+ const uint32_t *formats;\n" + ">> \302\240+ u8 n_formats;\n" + ">> \302\240+} plane_tb[] = {\n" + ">> \302\240+ [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */\n" + ">> \302\240+ 0, 0, 0,\n" + ">> \302\240+ DRM_PLANE_TYPE_PRIMARY,\n" + ">> \302\240+ ui_formats, ARRAY_SIZE(ui_formats),\n" + ">> \302\240+ },\n" + ">> \302\240+ [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */\n" + ">> \302\240+ 1, 0, 1,\n" + ">> \302\240+ DRM_PLANE_TYPE_CURSOR,\n" + ">> \302\240+ ui_formats, ARRAY_SIZE(ui_formats),\n" + ">> \302\240+ },\n" + ">> \302\240+ {\n" + ">> \302\240+ 0, 1, 0, /* 1st overlay: channel 0, layer 1 */\n" + ">> \302\240+ DRM_PLANE_TYPE_OVERLAY,\n" + ">> \302\240+ vi_formats, ARRAY_SIZE(vi_formats),\n" + ">> \302\240+ },\n" + ">> \302\240+ {\n" + ">> \302\240+ 0, 2, 0, /* 2nd overlay: channel 0, layer 2 */\n" + ">> \302\240+ DRM_PLANE_TYPE_OVERLAY,\n" + ">> \302\240+ vi_formats, ARRAY_SIZE(vi_formats),\n" + ">> \302\240+ },\n" + ">> \302\240+ {\n" + ">> \302\240+ 0, 3, 0, /* 3rd overlay: channel 0, layer 3 */\n" + ">> \302\240+ DRM_PLANE_TYPE_OVERLAY,\n" + ">> \302\240+ vi_formats, ARRAY_SIZE(vi_formats),\n" + ">> \302\240+ },\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static inline void andl_relaxed(void __iomem *addr, u32 val)\n" + ">> \302\240+{\n" + ">> \302\240+ writel_relaxed(readl_relaxed(addr) & val, addr);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static inline void orl_relaxed(void __iomem *addr, u32 val)\n" + ">> \302\240+{\n" + ">> \302\240+ writel_relaxed(readl_relaxed(addr) | val, addr);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/* alert the DE processor about changes in a mixer configuration */\n" + ">> \302\240+static void de2_mixer_select(struct priv *priv,\n" + ">> \302\240+ int mixer,\n" + ">> \302\240+ void __iomem *mixer_io)\n" + ">> \302\240+{\n" + ">> \302\240+ /* select the mixer */\n" + ">> \302\240+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);\n" + ">> \302\240+\n" + ">> \302\240+ /* double register switch */\n" + ">> \302\240+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/*\n" + ">> \302\240+ * cleanup a mixer\n" + ">> \302\240+ *\n" + ">> \302\240+ * This is needed only once after power on.\n" + ">> \302\240+ */\n" + ">> \302\240+static void de2_mixer_cleanup(struct priv *priv, int mixer,\n" + ">> \302\240+ u32 size)\n" + ">> \302\240+{\n" + ">> \302\240+ void __iomem *mixer_io = priv->mmio;\n" + ">> \302\240+ void __iomem *chan_io;\n" + ">> \302\240+ u32 data;\n" + ">> \302\240+ unsigned int i;\n" + ">> \302\240+\n" + ">> \302\240+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" + ">> \302\240+ chan_io = mixer_io + MIXER_CHAN_REGS;\n" + ">> \302\240+\n" + ">> \302\240+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);\n" + ">> \302\240+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(MIXER_GLB_CTL_rt_en,\n" + ">> \302\240+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);\n" + ">> \302\240+\n" + ">> \302\240+ /*\n" + ">> \302\240+ * clear the VI/UI channels\n" + ">> \302\240+ * LCD0: 1 VI and 3 UIs\n" + ">> \302\240+ * LCD1: 1 VI and 1 UI\n" + ">> \302\240+ */\n" + ">> \302\240+ memset_io(chan_io, 0, VI_SIZE);\n" + ">> \302\240+ memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);\n" + ">> \302\240+ if (mixer == 0) {\n" + ">> \302\240+ memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);\n" + ">> \302\240+ memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ /* alpha blending */\n" + ">> \302\240+ writel_relaxed(0x00000001 | /* fcolor for primary */\n" + ">> \302\240+ MIXER_BLD_FCOLOR_CTL_PEN(0),\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);\n" + ">> \302\240+ for (i = 0; i < MIXER_BLD_ATTR_N; i++) {\n" + ">> \302\240+ writel_relaxed(0xff000000,\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));\n" + ">> \302\240+ writel_relaxed(size,\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));\n" + ">> \302\240+ writel_relaxed(0,\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));\n" + ">> \302\240+ }\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);\n" + ">> \302\240+\n" + ">> \302\240+ /* prepare the pipe route for the planes */\n" + ">> \302\240+ data = 0;\n" + ">> \302\240+ for (i = 0; i < DE2_N_PLANES; i++)\n" + ">> \302\240+ data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);\n" + ">> \302\240+ writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_PREMULTIPLY_REG);\n" + ">> \302\240+ writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_BKCOLOR_REG);\n" + ">> \302\240+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_OUTPUT_SIZE_REG);\n" + ">> \302\240+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));\n" + ">> \302\240+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));\n" + ">> \302\240+\n" + ">> \302\240+ /* disable the enhancements */\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_VSU_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_FCE_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_BWS_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_LTI_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_ASE_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_FCC_REGS);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/* enable a mixer */\n" + ">> \302\240+static void de2_mixer_enable(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ struct priv *priv = lcd->priv;\n" + ">> \302\240+ void __iomem *mixer_io = priv->mmio;\n" + ">> \302\240+ struct drm_display_mode *mode = &lcd->crtc.mode;\n" + ">> \302\240+ u32 size = WH(mode->hdisplay, mode->vdisplay);\n" + ">> \302\240+ u32 data;\n" + ">> \302\240+ int mixer = lcd->mixer;\n" + ">> \302\240+ int i;\n" + ">> \302\240+\n" + ">> \302\240+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" + ">> \302\240+\n" + ">> \302\240+ /* if not done yet, start the DE processor */\n" + ">> \302\240+ if (!priv->started) {\n" + ">> \302\240+ reset_control_deassert(priv->reset);\n" + ">> \302\240+ clk_prepare_enable(priv->gate);\n" + ">> \302\240+ clk_prepare_enable(priv->clk);\n" + ">> \302\240+ }\n" + ">> \302\240+ priv->started |= 1 << mixer;\n" + ">> \302\240+\n" + ">> \302\240+ /* set the A83T clock divider (500 / 2) = 250MHz */\n" + ">> \302\240+ if (priv->soc_type == SOC_A83T)\n" + ">> \302\240+ writel_relaxed(0x00000011, /* div = 2 for both LCDs */\n" + ">> \302\240+ priv->mmio + DE2_DIV_REG);\n" + ">> \302\240+\n" + ">> \302\240+ /* deassert the mixer and enable its clock */\n" + ">> \302\240+ orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);\n" + ">> \302\240+ data = 1 << mixer; /* 1 bit / lcd */\n" + ">> \302\240+ orl_relaxed(priv->mmio + DE2_GATE_REG, data);\n" + ">> \302\240+ orl_relaxed(priv->mmio + DE2_MOD_REG, data);\n" + ">> \302\240+\n" + ">> \302\240+ /* if not done yet, cleanup and enable */\n" + ">> \302\240+ if (!(priv->clean & (1 << mixer))) {\n" + ">> \302\240+ priv->clean |= 1 << mixer;\n" + ">> \302\240+ de2_mixer_cleanup(priv, mixer, size);\n" + ">> \302\240+ return;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ /* enable */\n" + ">> \302\240+ de2_mixer_select(priv, mixer, mixer_io);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(MIXER_GLB_CTL_rt_en,\n" + ">> \302\240+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);\n" + ">> \302\240+\n" + ">> \302\240+ /* set the size of the frame buffer */\n" + ">> \302\240+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);\n" + ">> \302\240+ for (i = 0; i < MIXER_BLD_ATTR_N; i++)\n" + ">> \302\240+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_ATTRx_INSIZE(i));\n" + ">> \302\240+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_OUTPUT_SIZE_REG);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/* enable a LCD (DE mixer) */\n" + ">> \302\240+void de2_de_enable(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ mutex_lock(&lcd->priv->mutex);\n" + ">> \302\240+\n" + ">> \302\240+ de2_mixer_enable(lcd);\n" + ">> \302\240+\n" + ">> \302\240+ mutex_unlock(&lcd->priv->mutex);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/* disable a LCD (DE mixer) */\n" + ">> \302\240+void de2_de_disable(struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ struct priv *priv = lcd->priv;\n" + ">> \302\240+ void __iomem *mixer_io = priv->mmio;\n" + ">> \302\240+ int mixer = lcd->mixer;\n" + ">> \302\240+ u32 data;\n" + ">> \302\240+\n" + ">> \302\240+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" + ">> \302\240+\n" + ">> \302\240+ mutex_lock(&priv->mutex);\n" + ">> \302\240+\n" + ">> \302\240+ de2_mixer_select(priv, mixer, mixer_io);\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);\n" + ">> \302\240+\n" + ">> \302\240+ data = ~(1 << mixer);\n" + ">> \302\240+ andl_relaxed(priv->mmio + DE2_MOD_REG, data);\n" + ">> \302\240+ andl_relaxed(priv->mmio + DE2_GATE_REG, data);\n" + ">> \302\240+ andl_relaxed(priv->mmio + DE2_RESET_REG, data);\n" + ">> \302\240+\n" + ">> \302\240+ mutex_unlock(&priv->mutex);\n" + ">> \302\240+\n" + ">> \302\240+ /* if all mixers are disabled, stop the DE */\n" + ">> \302\240+ priv->started &= ~(1 << mixer);\n" + ">> \302\240+ if (!priv->started) {\n" + ">> \302\240+ clk_disable_unprepare(priv->clk);\n" + ">> \302\240+ clk_disable_unprepare(priv->gate);\n" + ">> \302\240+ reset_control_assert(priv->reset);\n" + ">> \302\240+ }\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_vi_update(void __iomem *chan_io,\n" + ">> \302\240+ struct drm_gem_cma_object *gem,\n" + ">> \302\240+ int layer,\n" + ">> \302\240+ unsigned int fmt,\n" + ">> \302\240+ u32 ui_sel,\n" + ">> \302\240+ u32 size,\n" + ">> \302\240+ u32 coord,\n" + ">> \302\240+ struct drm_framebuffer *fb,\n" + ">> \302\240+ u32 screen_size)\n" + ">> \302\240+{\n" + ">> \302\240+ int i;\n" + ">> \302\240+\n" + ">> \302\240+ writel_relaxed(VI_CFG_ATTR_en |\n" + ">> \302\240+ (fmt << VI_CFG_ATTR_fmt_SHIFT) |\n" + ">> \302\240+ ui_sel,\n" + ">> \302\240+ chan_io + VI_CFGx_ATTR(layer));\n" + ">> \302\240+ writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));\n" + ">> \302\240+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));\n" + ">> \302\240+ for (i = 0; i < VI_N_PLANES; i++) {\n" + ">> \302\240+ writel_relaxed(fb->pitches[i] ? fb->pitches[i] :\n" + ">> \302\240+ fb->pitches[0],\n" + ">> \302\240+ chan_io + VI_CFGx_PITCHy(layer, i));\n" + ">> \302\240+ writel_relaxed(gem->paddr + fb->offsets[i],\n" + ">> \302\240+ chan_io + VI_CFGx_TOP_LADDRy(layer, i));\n" + ">> \302\240+ }\n" + ">> \302\240+ writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));\n" + ">> \302\240+ if (layer == 0) {\n" + ">> \302\240+ writel_relaxed(screen_size,\n" + ">> \302\240+ chan_io + VI_OVL_SIZEx(0));\n" + ">> \302\240+ }\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_ui_update(void __iomem *chan_io,\n" + ">> \302\240+ struct drm_gem_cma_object *gem,\n" + ">> \302\240+ int layer,\n" + ">> \302\240+ unsigned int fmt,\n" + ">> \302\240+ u32 alpha_glob,\n" + ">> \302\240+ u32 size,\n" + ">> \302\240+ u32 coord,\n" + ">> \302\240+ struct drm_framebuffer *fb,\n" + ">> \302\240+ u32 screen_size)\n" + ">> \302\240+{\n" + ">> \302\240+ writel_relaxed(UI_CFG_ATTR_en |\n" + ">> \302\240+ (fmt << UI_CFG_ATTR_fmt_SHIFT) |\n" + ">> \302\240+ alpha_glob,\n" + ">> \302\240+ chan_io + UI_CFGx_ATTR(layer));\n" + ">> \302\240+ writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));\n" + ">> \302\240+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));\n" + ">> \302\240+ writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));\n" + ">> \302\240+ writel_relaxed(gem->paddr + fb->offsets[0],\n" + ">> \302\240+ chan_io + UI_CFGx_TOP_LADDR(layer));\n" + ">> \302\240+ if (layer == 0)\n" + ">> \302\240+ writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_plane_update(struct priv *priv, struct lcd *lcd,\n" + ">> \302\240+ int plane_num,\n" + ">> \302\240+ struct drm_plane_state *state,\n" + ">> \302\240+ struct drm_plane_state *old_state)\n" + ">> \302\240+{\n" + ">> \302\240+ void __iomem *mixer_io = priv->mmio;\n" + ">> \302\240+ void __iomem *chan_io;\n" + ">> \302\240+ struct drm_framebuffer *fb = state->fb;\n" + ">> \302\240+ struct drm_gem_cma_object *gem;\n" + ">> \302\240+ u32 size = WH(state->crtc_w, state->crtc_h);\n" + ">> \302\240+ u32 coord, screen_size;\n" + ">> \302\240+ u32 fcolor;\n" + ">> \302\240+ u32 ui_sel, alpha_glob;\n" + ">> \302\240+ int mixer = lcd->mixer;\n" + ">> \302\240+ int chan, layer, x, y;\n" + ">> \302\240+ unsigned int fmt;\n" + ">> \302\240+\n" + ">> \302\240+ chan = plane_tb[plane_num].chan;\n" + ">> \302\240+ layer = plane_tb[plane_num].layer;\n" + ">> \302\240+\n" + ">> \302\240+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" + ">> \302\240+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;\n" + ">> \302\240+\n" + ">> \302\240+ x = state->crtc_x >= 0 ? state->crtc_x : 0;\n" + ">> \302\240+ y = state->crtc_y >= 0 ? state->crtc_y : 0;\n" + ">> \302\240+ coord = XY(x, y);\n" + ">> \302\240+\n" + ">> \302\240+ /* if plane update was delayed, force a full update */\n" + ">> \302\240+ if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &\n" + ">> \302\240+ (1 << plane_num)) {\n" + ">> \302\240+ priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=\n" + ">> \302\240+ ~(1 << plane_num);\n" + ">> \302\240+\n" + ">> \302\240+ /* handle plane move */\n" + ">> \302\240+ } else if (fb == old_state->fb) {\n" + ">> \302\240+ de2_mixer_select(priv, mixer, mixer_io);\n" + ">> \302\240+ if (chan == 0)\n" + ">> \302\240+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));\n" + ">> \302\240+ else\n" + ">> \302\240+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));\n" + ">> \302\240+ return;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ gem = drm_fb_cma_get_gem_obj(fb, 0);\n" + ">> \302\240+\n" + ">> \302\240+ ui_sel = alpha_glob = 0;\n" + ">> \302\240+\n" + ">> \302\240+ switch (fb->pixel_format) {\n" + ">> \302\240+ case DRM_FORMAT_ARGB8888:\n" + ">> \302\240+ fmt = DE2_FORMAT_ARGB_8888;\n" + ">> \302\240+ ui_sel = VI_CFG_ATTR_ui_sel;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_BGRA8888:\n" + ">> \302\240+ fmt = DE2_FORMAT_BGRA_8888;\n" + ">> \302\240+ ui_sel = VI_CFG_ATTR_ui_sel;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_XRGB8888:\n" + ">> \302\240+ fmt = DE2_FORMAT_XRGB_8888;\n" + ">> \302\240+ ui_sel = VI_CFG_ATTR_ui_sel;\n" + ">> \302\240+ alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |\n" + ">> \302\240+ (0xff << UI_CFG_ATTR_alpha_SHIFT);\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_RGB888:\n" + ">> \302\240+ fmt = DE2_FORMAT_RGB_888;\n" + ">> \302\240+ ui_sel = VI_CFG_ATTR_ui_sel;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_BGR888:\n" + ">> \302\240+ fmt = DE2_FORMAT_BGR_888;\n" + ">> \302\240+ ui_sel = VI_CFG_ATTR_ui_sel;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_YUYV:\n" + ">> \302\240+ fmt = DE2_FORMAT_YUV422_I_YUYV;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_YVYU:\n" + ">> \302\240+ fmt = DE2_FORMAT_YUV422_I_YVYU;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_YUV422:\n" + ">> \302\240+ fmt = DE2_FORMAT_YUV422_P;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_YUV420:\n" + ">> \302\240+ fmt = DE2_FORMAT_YUV420_P;\n" + ">> \302\240+ break;\n" + ">> \302\240+ case DRM_FORMAT_UYVY:\n" + ">> \302\240+ fmt = DE2_FORMAT_YUV422_I_UYVY;\n" + ">> \302\240+ break;\n" + ">> \302\240+ default:\n" + ">> \302\240+ pr_err(\"de2_plane_update: format %.4s not yet treated\\n\",\n" + ">> \302\240+ (char *) &fb->pixel_format);\n" + ">> \302\240+ return;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ /* the overlay size is the one of the primary plane */\n" + ">> \302\240+ screen_size = plane_num == DE2_PRIMARY_PLANE ?\n" + ">> \302\240+ size :\n" + ">> \302\240+ readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);\n" + ">> \302\240+\n" + ">> \302\240+ /* prepare pipe enable */\n" + ">> \302\240+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_FCOLOR_CTL_REG);\n" + ">> \302\240+ fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);\n" + ">> \302\240+\n" + ">> \302\240+ de2_mixer_select(priv, mixer, mixer_io);\n" + ">> \302\240+\n" + ">> \302\240+ if (chan == 0) /* VI channel */\n" + ">> \302\240+ de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,\n" + ">> \302\240+ fb, screen_size);\n" + ">> \302\240+ else /* UI channel */\n" + ">> \302\240+ de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,\n" + ">> \302\240+ fb, screen_size);\n" + ">> \302\240+ writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_FCOLOR_CTL_REG);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static int vi_nb_layers(void __iomem *chan_io)\n" + ">> \302\240+{\n" + ">> \302\240+ int layer, n = 0;\n" + ">> \302\240+\n" + ">> \302\240+ for (layer = 0; layer < 4; layer++) {\n" + ">> \302\240+ if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)\n" + ">> \302\240+ n++;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ return n;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static int ui_nb_layers(void __iomem *chan_io)\n" + ">> \302\240+{\n" + ">> \302\240+ int layer, n = 0;\n" + ">> \302\240+\n" + ">> \302\240+ for (layer = 0; layer < 4; layer++) {\n" + ">> \302\240+ if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)\n" + ">> \302\240+ n++;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ return n;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_plane_disable(struct priv *priv,\n" + ">> \302\240+ int mixer, int plane_num)\n" + ">> \302\240+{\n" + ">> \302\240+ void __iomem *mixer_io = priv->mmio;\n" + ">> \302\240+ void __iomem *chan_io;\n" + ">> \302\240+ u32 fcolor;\n" + ">> \302\240+ int chan, layer, n;\n" + ">> \302\240+\n" + ">> \302\240+ chan = plane_tb[plane_num].chan;\n" + ">> \302\240+ layer = plane_tb[plane_num].layer;\n" + ">> \302\240+\n" + ">> \302\240+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;\n" + ">> \302\240+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;\n" + ">> \302\240+\n" + ">> \302\240+ if (chan == 0)\n" + ">> \302\240+ n = vi_nb_layers(chan_io);\n" + ">> \302\240+ else\n" + ">> \302\240+ n = ui_nb_layers(chan_io);\n" + ">> \302\240+\n" + ">> \302\240+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +\n" + ">> \302\240+ MIXER_BLD_FCOLOR_CTL_REG);\n" + ">> \302\240+\n" + ">> \302\240+ de2_mixer_select(priv, mixer, mixer_io);\n" + ">> \302\240+\n" + ">> \302\240+ if (chan == 0)\n" + ">> \302\240+ writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));\n" + ">> \302\240+ else\n" + ">> \302\240+ writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));\n" + ">> \302\240+\n" + ">> \302\240+ /* disable the pipe if no more active layer */\n" + ">> \302\240+ if (n <= 1)\n" + ">> \302\240+ writel_relaxed(fcolor &\n" + ">> \302\240+ ~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),\n" + ">> \302\240+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_drm_plane_update(struct drm_plane *plane,\n" + ">> \302\240+ struct drm_plane_state *old_state)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_plane_state *state = plane->state;\n" + ">> \302\240+ struct drm_crtc *crtc = state->crtc;\n" + ">> \302\240+ struct lcd *lcd = crtc_to_lcd(crtc);\n" + ">> \302\240+ struct priv *priv = lcd->priv;\n" + ">> \302\240+ int plane_num = plane - lcd->planes;\n" + ">> \302\240+\n" + ">> \302\240+ /* if the crtc is disabled, mark update delayed */\n" + ">> \302\240+ if (!(priv->started & (1 << lcd->mixer))) {\n" + ">> \302\240+ lcd->delayed |= 1 << plane_num;\n" + ">> \302\240+ return; /* mixer disabled */\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ mutex_lock(&priv->mutex);\n" + ">> \302\240+\n" + ">> \302\240+ de2_plane_update(priv, lcd, plane_num, state, old_state);\n" + ">> \302\240+\n" + ">> \302\240+ mutex_unlock(&priv->mutex);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static void de2_drm_plane_disable(struct drm_plane *plane,\n" + ">> \302\240+ struct drm_plane_state *old_state)\n" + ">> \302\240+{\n" + ">> \302\240+ struct drm_crtc *crtc = old_state->crtc;\n" + ">> \302\240+ struct lcd *lcd = crtc_to_lcd(crtc);\n" + ">> \302\240+ struct priv *priv = lcd->priv;\n" + ">> \302\240+ int plane_num = plane - lcd->planes;\n" + ">> \302\240+\n" + ">> \302\240+ if (!(priv->started & (1 << lcd->mixer)))\n" + ">> \302\240+ return; /* mixer disabled */\n" + ">> \302\240+\n" + ">> \302\240+ mutex_lock(&priv->mutex);\n" + ">> \302\240+\n" + ">> \302\240+ de2_plane_disable(lcd->priv, lcd->mixer, plane_num);\n" + ">> \302\240+\n" + ">> \302\240+ mutex_unlock(&priv->mutex);\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+static const struct drm_plane_helper_funcs plane_helper_funcs = {\n" + ">> \302\240+ .atomic_update = de2_drm_plane_update,\n" + ">> \302\240+ .atomic_disable = de2_drm_plane_disable,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static const struct drm_plane_funcs plane_funcs = {\n" + ">> \302\240+ .update_plane = drm_atomic_helper_update_plane,\n" + ">> \302\240+ .disable_plane = drm_atomic_helper_disable_plane,\n" + ">> \302\240+ .destroy = drm_plane_cleanup,\n" + ">> \302\240+ .reset = drm_atomic_helper_plane_reset,\n" + ">> \302\240+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,\n" + ">> \302\240+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,\n" + ">> \302\240+};\n" + ">> \302\240+\n" + ">> \302\240+static int de2_one_plane_init(struct drm_device *drm,\n" + ">> \302\240+ struct drm_plane *plane,\n" + ">> \302\240+ int possible_crtcs,\n" + ">> \302\240+ int plane_num)\n" + ">> \302\240+{\n" + ">> \302\240+ int ret;\n" + ">> \302\240+\n" + ">> \302\240+ ret = drm_universal_plane_init(drm, plane, possible_crtcs,\n" + ">> \302\240+ &plane_funcs,\n" + ">> \302\240+ plane_tb[plane_num].formats,\n" + ">> \302\240+ plane_tb[plane_num].n_formats,\n" + ">> \302\240+ plane_tb[plane_num].type, NULL);\n" + ">> \302\240+ if (ret >= 0)\n" + ">> \302\240+ drm_plane_helper_add(plane, &plane_helper_funcs);\n" + ">> \302\240+\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240+\n" + ">> \302\240+/* initialize the planes */\n" + ">> \302\240+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)\n" + ">> \302\240+{\n" + ">> \302\240+ int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);\n" + ">> \302\240+\n" + ">> \302\240+ n = ARRAY_SIZE(plane_tb);\n" + ">> \302\240+ if (n != DE2_N_PLANES) {\n" + ">> \302\240+ dev_err(lcd->dev, \"Bug: incorrect number of planes %d != \"\n" + ">> \302\240+ __stringify(DE2_N_PLANES) \"\\n\", n);\n" + ">> \302\240+ return -EINVAL;\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ for (i = 0; i < n; i++) {\n" + ">> \302\240+ ret = de2_one_plane_init(drm, &lcd->planes[i],\n" + ">> \302\240+ possible_crtcs, i);\n" + ">> \302\240+ if (ret < 0) {\n" + ">> \302\240+ dev_err(lcd->dev, \"plane init failed %d\\n\", ret);\n" + ">> \302\240+ break;\n" + ">> \302\240+ }\n" + ">> \302\240+ }\n" + ">> \302\240+\n" + ">> \302\240+ return ret;\n" + ">> \302\240+}\n" + ">> \302\240--\n" + ">> \302\2402.10.2\n" ">\n" - ">> ?_______________________________________________\n" - ">> ?dri-devel mailing list\n" - ">> ?dri-devel at lists.freedesktop.org\n" - ">> ?https://lists.freedesktop.org/mailman/listinfo/dri-devel\n" + ">> \302\240_______________________________________________\n" + ">> \302\240dri-devel mailing list\n" + ">> \302\240dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org\n" + ">> \302\240https://lists.freedesktop.org/mailman/listinfo/dri-devel\n" ">\n" "> --\n" "> Daniel Vetter\n" @@ -1757,7 +1766,12 @@ ">\n" "> --\n" "> You received this message because you are subscribed to the Google Groups \"linux-sunxi\" group.\n" - "> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.\n" - > For more options, visit https://groups.google.com/d/optout. + "> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org\n" + "> For more options, visit https://groups.google.com/d/optout.\n" + "\n" + "-- \n" + "You received this message because you are subscribed to the Google Groups \"linux-sunxi\" group.\n" + "To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org\n" + For more options, visit https://groups.google.com/d/optout. -1c96ad56c7f73cffdfcb0deff32e32dab963d25fc7e1605bd437e1842257174a +65971c0cc1f54cb5b5bc960f5fa112086de31e7df6db9fef2e7f6eca54573a42
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.