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); }