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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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);
}