Module Name:    src
Committed By:   jmcneill
Date:           Mon Feb  4 12:10:13 UTC 2019

Modified Files:
        src/sys/arch/arm/sunxi: sunxi_drm.c sunxi_drm.h sunxi_lcdc.c
            sunxi_mixer.c

Log Message:
Add support for vblank irq and RGB overlay planes.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/sunxi/sunxi_drm.c
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/sunxi/sunxi_drm.h
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/sunxi/sunxi_lcdc.c
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/sunxi/sunxi_mixer.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/sunxi/sunxi_drm.c
diff -u src/sys/arch/arm/sunxi/sunxi_drm.c:1.5 src/sys/arch/arm/sunxi/sunxi_drm.c:1.6
--- src/sys/arch/arm/sunxi/sunxi_drm.c:1.5	Sun Feb  3 15:43:57 2019
+++ src/sys/arch/arm/sunxi/sunxi_drm.c	Mon Feb  4 12:10:13 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_drm.c,v 1.5 2019/02/03 15:43:57 jmcneill Exp $ */
+/* $NetBSD: sunxi_drm.c,v 1.6 2019/02/04 12:10:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_drm.c,v 1.5 2019/02/03 15:43:57 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_drm.c,v 1.6 2019/02/04 12:10:13 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -71,6 +71,10 @@ static void	sunxi_drm_init(device_t);
 
 static int	sunxi_drm_set_busid(struct drm_device *, struct drm_master *);
 
+static uint32_t	sunxi_drm_get_vblank_counter(struct drm_device *, unsigned int);
+static int	sunxi_drm_enable_vblank(struct drm_device *, unsigned int);
+static void	sunxi_drm_disable_vblank(struct drm_device *, unsigned int);
+
 static int	sunxi_drm_load(struct drm_device *, unsigned long);
 static int	sunxi_drm_unload(struct drm_device *);
 
@@ -88,11 +92,9 @@ static struct drm_driver sunxi_drm_drive
 	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
 
-#if notyet
 	.get_vblank_counter = sunxi_drm_get_vblank_counter,
 	.enable_vblank = sunxi_drm_enable_vblank,
 	.disable_vblank = sunxi_drm_disable_vblank,
-#endif
 
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
@@ -233,12 +235,23 @@ sunxi_drm_fb_create(struct drm_device *d
 	fb = kmem_zalloc(sizeof(*fb), KM_SLEEP);
 	fb->obj = to_drm_gem_cma_obj(gem_obj);
 	fb->base.pitches[0] = cmd->pitches[0];
+	fb->base.pitches[1] = cmd->pitches[1];
+	fb->base.pitches[2] = cmd->pitches[2];
 	fb->base.offsets[0] = cmd->offsets[0];
+	fb->base.offsets[1] = cmd->offsets[2];
+	fb->base.offsets[2] = cmd->offsets[1];
 	fb->base.width = cmd->width;
 	fb->base.height = cmd->height;
 	fb->base.pixel_format = cmd->pixel_format;
-	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
-	    &fb->base.bits_per_pixel);
+	fb->base.bits_per_pixel = drm_format_plane_cpp(fb->base.pixel_format, 0) * 8;
+
+	switch (fb->base.pixel_format) {
+	case DRM_FORMAT_XRGB8888:
+		fb->base.depth = 32;
+		break;
+	default:
+		break;
+	}
 
 	error = drm_framebuffer_init(ddev, &fb->base, &sunxi_drm_framebuffer_funcs);
 	if (error != 0)
@@ -372,6 +385,10 @@ sunxi_drm_load(struct drm_device *ddev, 
 
 	drm_fb_helper_initial_config(&fbdev->helper, 32);
 
+	/* XXX */
+	ddev->irq_enabled = true;
+	drm_vblank_init(ddev, num_crtc);
+
 	return 0;
 
 drmerr:
@@ -381,6 +398,50 @@ drmerr:
 	return error;
 }
 
+static uint32_t
+sunxi_drm_get_vblank_counter(struct drm_device *ddev, unsigned int crtc)
+{
+	struct sunxi_drm_softc * const sc = sunxi_drm_private(ddev);
+
+	if (crtc >= __arraycount(sc->sc_vbl))
+		return 0;
+
+	if (sc->sc_vbl[crtc].get_vblank_counter == NULL)
+		return 0;
+
+	return sc->sc_vbl[crtc].get_vblank_counter(sc->sc_vbl[crtc].priv);
+}
+
+static int
+sunxi_drm_enable_vblank(struct drm_device *ddev, unsigned int crtc)
+{
+	struct sunxi_drm_softc * const sc = sunxi_drm_private(ddev);
+
+	if (crtc >= __arraycount(sc->sc_vbl))
+		return 0;
+
+	if (sc->sc_vbl[crtc].enable_vblank == NULL)
+		return 0;
+
+	sc->sc_vbl[crtc].enable_vblank(sc->sc_vbl[crtc].priv);
+
+	return 0;
+}
+
+static void
+sunxi_drm_disable_vblank(struct drm_device *ddev, unsigned int crtc)
+{
+	struct sunxi_drm_softc * const sc = sunxi_drm_private(ddev);
+
+	if (crtc >= __arraycount(sc->sc_vbl))
+		return;
+
+	if (sc->sc_vbl[crtc].disable_vblank == NULL)
+		return;
+
+	sc->sc_vbl[crtc].disable_vblank(sc->sc_vbl[crtc].priv);
+}
+
 static int
 sunxi_drm_unload(struct drm_device *ddev)
 {

Index: src/sys/arch/arm/sunxi/sunxi_drm.h
diff -u src/sys/arch/arm/sunxi/sunxi_drm.h:1.1 src/sys/arch/arm/sunxi/sunxi_drm.h:1.2
--- src/sys/arch/arm/sunxi/sunxi_drm.h:1.1	Wed Jan 30 01:24:00 2019
+++ src/sys/arch/arm/sunxi/sunxi_drm.h	Mon Feb  4 12:10:13 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_drm.h,v 1.1 2019/01/30 01:24:00 jmcneill Exp $ */
+/* $NetBSD: sunxi_drm.h,v 1.2 2019/02/04 12:10:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -44,6 +44,15 @@
 
 struct sunxi_framebuffer;
 
+#define	SUNXI_DRM_MAX_CRTC	2
+
+struct sunxi_drm_vblank {
+	void			*priv;
+	void			(*enable_vblank)(void *);
+	void			(*disable_vblank)(void *);
+	uint32_t		(*get_vblank_counter)(void *);
+};
+
 struct sunxi_drm_softc {
 	device_t		sc_dev;
 	struct drm_device	*sc_ddev;
@@ -52,6 +61,8 @@ struct sunxi_drm_softc {
 	bus_dma_tag_t		sc_dmat;
 
 	int			sc_phandle;
+
+	struct sunxi_drm_vblank	sc_vbl[SUNXI_DRM_MAX_CRTC];
 };
 
 struct sunxi_drm_framebuffer {

Index: src/sys/arch/arm/sunxi/sunxi_lcdc.c
diff -u src/sys/arch/arm/sunxi/sunxi_lcdc.c:1.3 src/sys/arch/arm/sunxi/sunxi_lcdc.c:1.4
--- src/sys/arch/arm/sunxi/sunxi_lcdc.c:1.3	Sun Feb  3 13:15:19 2019
+++ src/sys/arch/arm/sunxi/sunxi_lcdc.c	Mon Feb  4 12:10:13 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $ */
+/* $NetBSD: sunxi_lcdc.c,v 1.4 2019/02/04 12:10:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.4 2019/02/04 12:10:13 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -43,11 +43,17 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c
 #include <dev/fdt/fdtvar.h>
 #include <dev/fdt/fdt_port.h>
 
+#include <arm/sunxi/sunxi_drm.h>
+
 #define	TCON_GCTL_REG		0x000
 #define	 TCON_GCTL_TCON_EN			__BIT(31)
 #define	 TCON_GCTL_GAMMA_EN			__BIT(30)
 #define	 TCON_GCTL_IO_MAP_SEL			__BIT(0)
 #define	TCON_GINT0_REG		0x004
+#define	 TCON_GINT0_TCON0_VB_INT_EN		__BIT(31)
+#define	 TCON_GINT0_TCON1_VB_INT_EN		__BIT(30)
+#define	 TCON_GINT0_TCON0_VB_INT_FLAG		__BIT(15)
+#define	 TCON_GINT0_TCON1_VB_INT_FLAG		__BIT(14)
 #define	TCON_GINT1_REG		0x008
 #define	 TCON_GINT1_TCON1_LINE_INT_NUM		__BITS(11,0)
 
@@ -129,6 +135,8 @@ struct sunxi_lcdc_softc {
 	struct drm_connector	sc_connector;
 
 	struct fdt_device_ports	sc_ports;
+
+	uint32_t		sc_vbl_counter;
 };
 
 #define	to_sunxi_lcdc_encoder(x)	container_of(x, struct sunxi_lcdc_encoder, base)
@@ -336,6 +344,50 @@ sunxi_lcdc_encoder_mode(struct fdt_endpo
 	}
 }
 
+static uint32_t
+sunxi_lcdc_get_vblank_counter(void *priv)
+{
+	struct sunxi_lcdc_softc * const sc = priv;
+
+	return sc->sc_vbl_counter;
+}
+
+static void
+sunxi_lcdc_enable_vblank(void *priv)
+{
+	struct sunxi_lcdc_softc * const sc = priv;
+        const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1;
+
+	if (crtc_index == 0)
+		TCON_WRITE(sc, TCON_GINT0_REG, TCON_GINT0_TCON0_VB_INT_EN);
+	else
+		TCON_WRITE(sc, TCON_GINT0_REG, TCON_GINT0_TCON1_VB_INT_EN);
+}
+
+static void
+sunxi_lcdc_disable_vblank(void *priv)
+{
+	struct sunxi_lcdc_softc * const sc = priv;
+
+	TCON_WRITE(sc, TCON_GINT0_REG, 0);
+}
+
+static void
+sunxi_lcdc_setup_vblank(struct sunxi_lcdc_softc *sc)
+{
+        const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1;
+	struct drm_device *ddev = sc->sc_encoder.base.dev;
+	struct sunxi_drm_softc *drm_sc;
+
+	KASSERT(ddev != NULL);
+
+	drm_sc = device_private(ddev->dev);
+	drm_sc->sc_vbl[crtc_index].priv = sc;
+	drm_sc->sc_vbl[crtc_index].get_vblank_counter = sunxi_lcdc_get_vblank_counter;
+	drm_sc->sc_vbl[crtc_index].enable_vblank = sunxi_lcdc_enable_vblank;
+	drm_sc->sc_vbl[crtc_index].disable_vblank = sunxi_lcdc_disable_vblank;
+}
+
 static int
 sunxi_lcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
 {
@@ -364,6 +416,8 @@ sunxi_lcdc_ep_activate(device_t dev, str
 		    sunxi_lcdc_encoder_mode(out_ep));
 		drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon0_helper_funcs);
 
+		sunxi_lcdc_setup_vblank(sc);
+
 		return fdt_endpoint_activate(out_ep, activate);
 	}
 
@@ -373,6 +427,8 @@ sunxi_lcdc_ep_activate(device_t dev, str
 		    sunxi_lcdc_encoder_mode(out_ep));
 		drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon1_helper_funcs);
 
+		sunxi_lcdc_setup_vblank(sc);
+
 		return fdt_endpoint_activate(out_ep, activate);
 	}
 
@@ -388,6 +444,28 @@ sunxi_lcdc_ep_get_data(device_t dev, str
 }
 
 static int
+sunxi_lcdc_intr(void *priv)
+{
+	struct sunxi_lcdc_softc * const sc = priv;
+	uint32_t val;
+	int rv = 0;
+
+	const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1;
+	const uint32_t status_mask = crtc_index == 0 ?
+	    TCON_GINT0_TCON0_VB_INT_FLAG : TCON_GINT0_TCON1_VB_INT_FLAG;
+
+	val = TCON_READ(sc, TCON_GINT0_REG);
+	if ((val & status_mask) != 0) {
+		TCON_WRITE(sc, TCON_GINT0_REG, val & ~status_mask);
+		atomic_inc_32(&sc->sc_vbl_counter);
+		drm_handle_vblank(sc->sc_encoder.base.dev, crtc_index);
+		rv = 1;
+	}
+
+	return rv;
+}
+
+static int
 sunxi_lcdc_match(device_t parent, cfdata_t cf, void *aux)
 {
 	struct fdt_attach_args * const faa = aux;
@@ -402,15 +480,22 @@ sunxi_lcdc_attach(device_t parent, devic
 	struct fdt_attach_args * const faa = aux;
 	const int phandle = faa->faa_phandle;
 	struct fdtbus_reset *rst;
+	char intrstr[128];
 	struct clk *clk;
 	bus_addr_t addr;
 	bus_size_t size;
+	void *ih;
 
 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
 		aprint_error(": couldn't get registers\n");
 		return;
 	}
 
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error(": couldn't decode interrupt\n");
+		return;
+	}
+
 	rst = fdtbus_reset_get(phandle, "lcd");
 	if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
 		aprint_error(": couldn't de-assert reset\n");
@@ -447,6 +532,15 @@ sunxi_lcdc_attach(device_t parent, devic
 	sc->sc_ports.dp_ep_activate = sunxi_lcdc_ep_activate;
 	sc->sc_ports.dp_ep_get_data = sunxi_lcdc_ep_get_data;
 	fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER);
+
+	ih = fdtbus_intr_establish(phandle, 0, IPL_VM, FDT_INTR_MPSAFE,
+	    sunxi_lcdc_intr, sc);
+	if (ih == NULL) {
+		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
+		    intrstr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
 }
 
 CFATTACH_DECL_NEW(sunxi_lcdc, sizeof(struct sunxi_lcdc_softc),

Index: src/sys/arch/arm/sunxi/sunxi_mixer.c
diff -u src/sys/arch/arm/sunxi/sunxi_mixer.c:1.2 src/sys/arch/arm/sunxi/sunxi_mixer.c:1.3
--- src/sys/arch/arm/sunxi/sunxi_mixer.c:1.2	Thu Jan 31 01:49:28 2019
+++ src/sys/arch/arm/sunxi/sunxi_mixer.c	Mon Feb  4 12:10:13 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_mixer.c,v 1.2 2019/01/31 01:49:28 jmcneill Exp $ */
+/* $NetBSD: sunxi_mixer.c,v 1.3 2019/02/04 12:10:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_mixer.c,v 1.2 2019/01/31 01:49:28 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_mixer.c,v 1.3 2019/02/04 12:10:13 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -52,6 +52,7 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_mixer.
 #define	GLB_BASE		0x00000
 #define	BLD_BASE		0x01000
 #define	OVL_BASE(n)		(0x02000 + (n) * 0x1000)
+#define	OVL_V_BASE		OVL_BASE(0)
 #define	OVL_UI_BASE		OVL_BASE(1)
 
 /* GLB registers */
@@ -64,14 +65,48 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_mixer.
 
 /* BLD registers */
 #define	BLD_FILL_COLOR_CTL	0x000
+#define	 BLD_FILL_COLOR_CTL_P1_EN		__BIT(9)
 #define	 BLD_FILL_COLOR_CTL_P0_EN		__BIT(8)
 #define	BLD_CH_ISIZE(n)		(0x008 + (n) * 0x10)
 #define	BLD_CH_OFFSET(n)	(0x00c + (n) * 0x10)
 #define	BLD_CH_RTCTL		0x080
+#define	 BLD_CH_RTCTL_P1			__BITS(7,4)
 #define	 BLD_CH_RTCTL_P0			__BITS(3,0)
 #define	BLD_SIZE		0x08c
 #define	BLD_CTL(n)		(0x090 + (n) * 0x04)
 
+/* OVL_V registers */
+#define	OVL_V_ATTCTL(n)		(0x000 + (n) * 0x30)
+#define	 OVL_V_ATTCTL_VIDEO_UI_SEL		__BIT(15)
+#define	 OVL_V_ATTCTL_LAY_FBFMT			__BITS(12,8)
+#define	  OVL_V_ATTCTL_LAY_FBFMT_VYUY		0x00
+#define	  OVL_V_ATTCTL_LAY_FBFMT_YVYU		0x01
+#define	  OVL_V_ATTCTL_LAY_FBFMT_UYVY		0x02
+#define	  OVL_V_ATTCTL_LAY_FBFMT_YUYV		0x03
+#define	  OVL_V_ATTCTL_LAY_FBFMT_YUV422		0x06
+#define	  OVL_V_ATTCTL_LAY_FBFMT_YUV420		0x0a
+#define	  OVL_V_ATTCTL_LAY_FBFMT_YUV411		0x0e
+#define	  OVL_V_ATTCTL_LAY_FBFMT_XRGB_8888	0x04
+#define	 OVL_V_ATTCTL_LAY0_EN			__BIT(0)
+#define	OVL_V_MBSIZE(n)		(0x004 + (n) * 0x30)
+#define	OVL_V_COOR(n)		(0x008 + (n) * 0x30)
+#define	OVL_V_PITCH0(n)		(0x00c + (n) * 0x30)
+#define	OVL_V_PITCH1(n)		(0x010 + (n) * 0x30)
+#define	OVL_V_PITCH2(n)		(0x014 + (n) * 0x30)
+#define	OVL_V_TOP_LADD0(n)	(0x018 + (n) * 0x30)
+#define	OVL_V_TOP_LADD1(n)	(0x01c + (n) * 0x30)
+#define	OVL_V_TOP_LADD2(n)	(0x020 + (n) * 0x30)
+#define	OVL_V_FILL_COLOR(n)	(0x0c0 + (n) * 0x4)
+#define	OVL_V_TOP_HADD0		0x0d0
+#define	OVL_V_TOP_HADD1		0x0d4
+#define	OVL_V_TOP_HADD2		0x0d8
+#define	 OVL_V_TOP_HADD_LAYER0	__BITS(7,0)
+#define	OVL_V_SIZE		0x0e8
+#define	OVL_V_HDS_CTL0		0x0f0
+#define	OVL_V_HDS_CTL1		0x0f4
+#define	OVL_V_VDS_CTL0		0x0f8
+#define	OVL_V_VDS_CTL1		0x0fc
+
 /* OVL_UI registers */
 #define	OVL_UI_ATTR_CTL(n)	(0x000 + (n) * 0x20)
 #define	 OVL_UI_ATTR_CTL_LAY_FBFMT		__BITS(12,8)
@@ -103,6 +138,11 @@ struct sunxi_mixer_crtc {
 	struct sunxi_mixer_softc *sc;
 };
 
+struct sunxi_mixer_overlay {
+	struct drm_plane	base;
+	struct sunxi_mixer_softc *sc;
+};
+
 struct sunxi_mixer_softc {
 	device_t		sc_dev;
 	bus_space_tag_t		sc_bst;
@@ -110,6 +150,7 @@ struct sunxi_mixer_softc {
 	int			sc_phandle;
 
 	struct sunxi_mixer_crtc	sc_crtc;
+	struct sunxi_mixer_overlay sc_overlay;
 
 	struct fdt_device_ports	sc_ports;
 };
@@ -124,12 +165,18 @@ struct sunxi_mixer_softc {
 #define	BLD_WRITE(sc, reg, val)				\
 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, BLD_BASE + (reg), (val))
 
+#define	OVL_V_READ(sc, reg)				\
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, OVL_V_BASE + (reg))
+#define	OVL_V_WRITE(sc, reg, val)			\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, OVL_V_BASE + (reg), (val))
+
 #define	OVL_UI_READ(sc, reg)				\
 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, OVL_UI_BASE + (reg))
 #define	OVL_UI_WRITE(sc, reg, val)			\
 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, OVL_UI_BASE + (reg), (val))
 
-#define	to_sunxi_mixer_crtc(x)	container_of(x, struct sunxi_mixer_crtc, base)
+#define	to_sunxi_mixer_crtc(x)		container_of(x, struct sunxi_mixer_crtc, base)
+#define	to_sunxi_mixer_overlay(x)	container_of(x, struct sunxi_mixer_overlay, base)
 
 static void
 sunxi_mixer_destroy(struct drm_crtc *crtc)
@@ -193,14 +240,19 @@ sunxi_mixer_mode_set(struct drm_crtc *cr
 	GLB_WRITE(sc, GLB_SIZE, size);
 
 	/* Enable pipe 0 */
-	BLD_WRITE(sc, BLD_FILL_COLOR_CTL, BLD_FILL_COLOR_CTL_P0_EN);
+	val = BLD_READ(sc, BLD_FILL_COLOR_CTL);
+	val |= BLD_FILL_COLOR_CTL_P0_EN;
+	BLD_WRITE(sc, BLD_FILL_COLOR_CTL, val);
 
 	/* Set blender 0 input size */
 	BLD_WRITE(sc, BLD_CH_ISIZE(0), size);
 	/* Set blender 0 offset */
 	BLD_WRITE(sc, BLD_CH_OFFSET(0), offset);
 	/* Route channel 1 to pipe 0 */
-	BLD_WRITE(sc, BLD_CH_RTCTL, __SHIFTIN(1, BLD_CH_RTCTL_P0));
+	val = BLD_READ(sc, BLD_CH_RTCTL);
+	val &= ~BLD_CH_RTCTL_P0;
+	val |= __SHIFTIN(1, BLD_CH_RTCTL_P0);
+	BLD_WRITE(sc, BLD_CH_RTCTL, val);
 	/* Set blender output size */
 	BLD_WRITE(sc, BLD_SIZE, size);
 
@@ -288,6 +340,144 @@ static const struct drm_crtc_helper_func
 	.commit = sunxi_mixer_commit,
 };
 
+static void
+sunxi_mixer_overlay_destroy(struct drm_plane *plane)
+{
+}
+
+static bool
+sunxi_mixer_overlay_ui(uint32_t drm_format)
+{
+	switch (drm_format) {
+	case DRM_FORMAT_XRGB8888:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static u_int
+sunxi_mixer_overlay_format(uint32_t drm_format)
+{
+	switch (drm_format) {
+	case DRM_FORMAT_XRGB8888:	return OVL_V_ATTCTL_LAY_FBFMT_XRGB_8888;
+	case DRM_FORMAT_VYUY:		return OVL_V_ATTCTL_LAY_FBFMT_VYUY;
+	case DRM_FORMAT_YVYU:		return OVL_V_ATTCTL_LAY_FBFMT_YVYU;
+	case DRM_FORMAT_UYVY:		return OVL_V_ATTCTL_LAY_FBFMT_UYVY;
+	case DRM_FORMAT_YUYV:		return OVL_V_ATTCTL_LAY_FBFMT_YUYV;
+	case DRM_FORMAT_YUV422:		return OVL_V_ATTCTL_LAY_FBFMT_YUV422;
+	case DRM_FORMAT_YUV420:		return OVL_V_ATTCTL_LAY_FBFMT_YUV420;
+	case DRM_FORMAT_YUV411:		return OVL_V_ATTCTL_LAY_FBFMT_YUV411;
+	default:			return 0;	/* shouldn't happen */
+	}
+}
+
+static int
+sunxi_mixer_overlay_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+    struct drm_framebuffer *fb, int crtc_x, int crtc_y, u_int crtc_w, u_int crtc_h,
+    uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct sunxi_mixer_overlay *overlay = to_sunxi_mixer_overlay(plane);
+	struct sunxi_mixer_softc * const sc = overlay->sc;
+	struct sunxi_drm_framebuffer *sfb = to_sunxi_drm_framebuffer(fb);
+	uint32_t val;
+
+	const u_int fbfmt = sunxi_mixer_overlay_format(fb->pixel_format);
+	const uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr;
+
+	const uint32_t input_size = (((src_h >> 16) - 1) << 16) | ((src_w >> 16) - 1);
+	const uint32_t input_pos = ((src_y >> 16) << 16) | (src_x >> 16);
+
+	OVL_V_WRITE(sc, OVL_V_MBSIZE(0), input_size);
+	OVL_V_WRITE(sc, OVL_V_COOR(0), input_pos);
+
+	OVL_V_WRITE(sc, OVL_V_PITCH0(0), fb->pitches[0]);
+	OVL_V_WRITE(sc, OVL_V_PITCH1(0), fb->pitches[1]);
+	OVL_V_WRITE(sc, OVL_V_PITCH2(0), fb->pitches[2]);
+
+	const uint64_t paddr0 = paddr + fb->offsets[0] +
+	    (src_x >> 16) * drm_format_plane_cpp(fb->pixel_format, 0) +
+	    (src_y >> 16) * fb->pitches[0];
+	const uint64_t paddr1 = paddr + fb->offsets[1] +
+	    (src_x >> 16) * drm_format_plane_cpp(fb->pixel_format, 1) +
+	    (src_y >> 16) * fb->pitches[1];
+	const uint64_t paddr2 = paddr + fb->offsets[2] +
+	    (src_x >> 16) * drm_format_plane_cpp(fb->pixel_format, 2) +
+	    (src_y >> 16) * fb->pitches[2];
+
+	OVL_V_WRITE(sc, OVL_V_TOP_HADD0, (paddr0 >> 32) & OVL_V_TOP_HADD_LAYER0);
+	OVL_V_WRITE(sc, OVL_V_TOP_HADD1, (paddr1 >> 32) & OVL_V_TOP_HADD_LAYER0);
+	OVL_V_WRITE(sc, OVL_V_TOP_HADD2, (paddr2 >> 32) & OVL_V_TOP_HADD_LAYER0);
+
+	OVL_V_WRITE(sc, OVL_V_TOP_LADD0(0), paddr0 & 0xffffffff);
+	OVL_V_WRITE(sc, OVL_V_TOP_LADD1(0), paddr1 & 0xffffffff);
+	OVL_V_WRITE(sc, OVL_V_TOP_LADD2(0), paddr2 & 0xffffffff);
+
+	OVL_V_WRITE(sc, OVL_V_SIZE, input_size);
+
+	val = OVL_V_ATTCTL_LAY0_EN;
+	val |= __SHIFTIN(fbfmt, OVL_V_ATTCTL_LAY_FBFMT);
+	if (sunxi_mixer_overlay_ui(fb->pixel_format) == true)
+		val |= OVL_V_ATTCTL_VIDEO_UI_SEL;
+	OVL_V_WRITE(sc, OVL_V_ATTCTL(0), val);
+
+	/* Set blender 1 input size */
+	BLD_WRITE(sc, BLD_CH_ISIZE(1), input_size);
+	/* Set blender 1 offset */
+	BLD_WRITE(sc, BLD_CH_OFFSET(1), (crtc_y << 16) | crtc_x);
+	/* Route channel 0 to pipe 1 */
+	val = BLD_READ(sc, BLD_CH_RTCTL);
+	val &= ~BLD_CH_RTCTL_P1;
+	val |= __SHIFTIN(0, BLD_CH_RTCTL_P1);
+	BLD_WRITE(sc, BLD_CH_RTCTL, val);
+
+        /* Enable pipe 1 */
+	val = BLD_READ(sc, BLD_FILL_COLOR_CTL);
+	val |= BLD_FILL_COLOR_CTL_P1_EN;
+	BLD_WRITE(sc, BLD_FILL_COLOR_CTL, val);
+
+	/* Commit settings */
+	GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY);
+
+	return 0;
+}
+
+static int
+sunxi_mixer_overlay_disable_plane(struct drm_plane *plane)
+{
+	struct sunxi_mixer_overlay *overlay = to_sunxi_mixer_overlay(plane);
+	struct sunxi_mixer_softc * const sc = overlay->sc;
+	uint32_t val;
+
+	val = BLD_READ(sc, BLD_FILL_COLOR_CTL);
+	val &= ~BLD_FILL_COLOR_CTL_P1_EN;
+	BLD_WRITE(sc, BLD_FILL_COLOR_CTL, val);
+
+	/* Commit settings */
+	GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY);
+
+	return 0;
+}
+
+static const struct drm_plane_funcs sunxi_mixer_overlay_funcs = {
+	.update_plane = sunxi_mixer_overlay_update_plane,
+	.disable_plane = sunxi_mixer_overlay_disable_plane,
+	.destroy = sunxi_mixer_overlay_destroy,
+};
+
+static uint32_t sunxi_mixer_overlay_formats[] = {
+	DRM_FORMAT_XRGB8888,
+#if notyet
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YUV411,
+#endif
+};
+
 static int
 sunxi_mixer_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
 {
@@ -304,10 +494,16 @@ sunxi_mixer_ep_activate(device_t dev, st
 	}
 
 	sc->sc_crtc.sc = sc;
+	sc->sc_overlay.sc = sc;
 
 	drm_crtc_init(ddev, &sc->sc_crtc.base, &sunxi_mixer_crtc_funcs);
 	drm_crtc_helper_add(&sc->sc_crtc.base, &sunxi_mixer_crtc_helper_funcs);
 
+	drm_universal_plane_init(ddev, &sc->sc_overlay.base,
+	    1 << drm_crtc_index(&sc->sc_crtc.base), &sunxi_mixer_overlay_funcs,
+	    sunxi_mixer_overlay_formats, __arraycount(sunxi_mixer_overlay_formats),
+	    DRM_PLANE_TYPE_OVERLAY);
+
 	return fdt_endpoint_activate(ep, activate);
 }
 

Reply via email to