Home | History | Annotate | Line # | Download | only in sunxi
      1 /* $NetBSD: sunxi_mixer.c,v 1.21 2026/04/21 10:32:16 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: sunxi_mixer.c,v 1.21 2026/04/21 10:32:16 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/conf.h>
     35 #include <sys/device.h>
     36 #include <sys/intr.h>
     37 #include <sys/kernel.h>
     38 #include <sys/sysctl.h>
     39 #include <sys/systm.h>
     40 
     41 #include <dev/fdt/fdt_port.h>
     42 #include <dev/fdt/fdtvar.h>
     43 
     44 #include <arm/sunxi/sunxi_drm.h>
     45 
     46 #include <drm/drm_atomic.h>
     47 #include <drm/drm_atomic_helper.h>
     48 #include <drm/drm_crtc.h>
     49 #include <drm/drm_crtc_helper.h>
     50 #include <drm/drm_drv.h>
     51 #include <drm/drm_fourcc.h>
     52 #include <drm/drm_plane_helper.h>
     53 #include <drm/drm_vblank.h>
     54 
     55 #define	MIXER_CURSOR_MAXWIDTH	256
     56 #define	MIXER_CURSOR_MAXHEIGHT	256
     57 
     58 #define	SUNXI_MIXER_FREQ	432000000
     59 
     60 #define	GLB_BASE		0x00000
     61 #define	BLD_BASE		0x01000
     62 #define	OVL_BASE(n)		(0x02000 + (n) * 0x1000)
     63 #define	VSU_BASE		0x20000
     64 #define	CSC_BASE(n)		((n) == 0 ? 0xaa050 : 0xa0000)
     65 
     66 /* GLB registers */
     67 #define	GLB_CTL			0x000
     68 #define	 GLB_CTL_EN				__BIT(0)
     69 #define	GLB_STS			0x004
     70 #define	GLB_DBUFFER		0x008
     71 #define	 GLB_DBUFFER_DOUBLE_BUFFER_RDY		__BIT(0)
     72 #define	GLB_SIZE		0x00c
     73 
     74 /* BLD registers */
     75 #define	BLD_FILL_COLOR_CTL	0x000
     76 #define	 BLD_FILL_COLOR_CTL_P3_EN		__BIT(11)
     77 #define	 BLD_FILL_COLOR_CTL_P2_EN		__BIT(10)
     78 #define	 BLD_FILL_COLOR_CTL_P1_EN		__BIT(9)
     79 #define	 BLD_FILL_COLOR_CTL_P0_EN		__BIT(8)
     80 #define	 BLD_FILL_COLOR_CTL_P3_FCEN		__BIT(3)
     81 #define	 BLD_FILL_COLOR_CTL_P2_FCEN		__BIT(2)
     82 #define	 BLD_FILL_COLOR_CTL_P1_FCEN		__BIT(1)
     83 #define	 BLD_FILL_COLOR_CTL_P0_FCEN		__BIT(0)
     84 #define	BLD_FILL_COLOR(n)	(0x004 + (n) * 0x10)
     85 #define	BLD_CH_ISIZE(n)		(0x008 + (n) * 0x10)
     86 #define	BLD_CH_OFFSET(n)	(0x00c + (n) * 0x10)
     87 #define	BLD_CH_RTCTL		0x080
     88 #define	 BLD_CH_RTCTL_P3			__BITS(15,12)
     89 #define	 BLD_CH_RTCTL_P2			__BITS(11,8)
     90 #define	 BLD_CH_RTCTL_P1			__BITS(7,4)
     91 #define	 BLD_CH_RTCTL_P0			__BITS(3,0)
     92 #define	BLD_SIZE		0x08c
     93 #define	BLD_CTL(n)		(0x090 + (n) * 0x04)
     94 
     95 /* OVL_V registers */
     96 #define	OVL_V_ATTCTL(n)		(0x000 + (n) * 0x30)
     97 #define	 OVL_V_ATTCTL_VIDEO_UI_SEL		__BIT(15)
     98 #define	 OVL_V_ATTCTL_LAY_FBFMT			__BITS(12,8)
     99 #define	  OVL_V_ATTCTL_LAY_FBFMT_VYUY		0x00
    100 #define	  OVL_V_ATTCTL_LAY_FBFMT_YVYU		0x01
    101 #define	  OVL_V_ATTCTL_LAY_FBFMT_UYVY		0x02
    102 #define	  OVL_V_ATTCTL_LAY_FBFMT_YUYV		0x03
    103 #define	  OVL_V_ATTCTL_LAY_FBFMT_YUV422		0x06
    104 #define	  OVL_V_ATTCTL_LAY_FBFMT_YUV420		0x0a
    105 #define	  OVL_V_ATTCTL_LAY_FBFMT_YUV411		0x0e
    106 #if BYTE_ORDER == BIG_ENDIAN
    107 #define	  OVL_V_ATTCTL_LAY_FBFMT_ARGB_8888	0x03
    108 #define	  OVL_V_ATTCTL_LAY_FBFMT_XRGB_8888	0x07
    109 #else
    110 #define	  OVL_V_ATTCTL_LAY_FBFMT_ARGB_8888	0x00
    111 #define	  OVL_V_ATTCTL_LAY_FBFMT_XRGB_8888	0x04
    112 #endif
    113 #define	 OVL_V_ATTCTL_LAY0_EN			__BIT(0)
    114 #define	OVL_V_MBSIZE(n)		(0x004 + (n) * 0x30)
    115 #define	OVL_V_COOR(n)		(0x008 + (n) * 0x30)
    116 #define	OVL_V_PITCH0(n)		(0x00c + (n) * 0x30)
    117 #define	OVL_V_PITCH1(n)		(0x010 + (n) * 0x30)
    118 #define	OVL_V_PITCH2(n)		(0x014 + (n) * 0x30)
    119 #define	OVL_V_TOP_LADD0(n)	(0x018 + (n) * 0x30)
    120 #define	OVL_V_TOP_LADD1(n)	(0x01c + (n) * 0x30)
    121 #define	OVL_V_TOP_LADD2(n)	(0x020 + (n) * 0x30)
    122 #define	OVL_V_FILL_COLOR(n)	(0x0c0 + (n) * 0x4)
    123 #define	OVL_V_TOP_HADD0		0x0d0
    124 #define	OVL_V_TOP_HADD1		0x0d4
    125 #define	OVL_V_TOP_HADD2		0x0d8
    126 #define	 OVL_V_TOP_HADD_LAYER0	__BITS(7,0)
    127 #define	OVL_V_SIZE		0x0e8
    128 #define	OVL_V_HDS_CTL0		0x0f0
    129 #define	OVL_V_HDS_CTL1		0x0f4
    130 #define	OVL_V_VDS_CTL0		0x0f8
    131 #define	OVL_V_VDS_CTL1		0x0fc
    132 
    133 /* OVL_UI registers */
    134 #define	OVL_UI_ATTR_CTL(n)	(0x000 + (n) * 0x20)
    135 #define	 OVL_UI_ATTR_CTL_LAY_FBFMT		__BITS(12,8)
    136 #if BYTE_ORDER == BIG_ENDIAN
    137 #define	  OVL_UI_ATTR_CTL_LAY_FBFMT_ARGB_8888	0x03
    138 #define	  OVL_UI_ATTR_CTL_LAY_FBFMT_XRGB_8888	0x07
    139 #else
    140 #define	  OVL_UI_ATTR_CTL_LAY_FBFMT_ARGB_8888	0x00
    141 #define	  OVL_UI_ATTR_CTL_LAY_FBFMT_XRGB_8888	0x04
    142 #endif
    143 #define	 OVL_UI_ATTR_CTL_LAY_EN			__BIT(0)
    144 #define	OVL_UI_MBSIZE(n)	(0x004 + (n) * 0x20)
    145 #define	OVL_UI_COOR(n)		(0x008 + (n) * 0x20)
    146 #define	OVL_UI_PITCH(n)		(0x00c + (n) * 0x20)
    147 #define	OVL_UI_TOP_LADD(n)	(0x010 + (n) * 0x20)
    148 #define	OVL_UI_FILL_COLOR(n)	(0x018 + (n) * 0x20)
    149 #define	OVL_UI_TOP_HADD		0x080
    150 #define	 OVL_UI_TOP_HADD_LAYER1	__BITS(15,8)
    151 #define	 OVL_UI_TOP_HADD_LAYER0	__BITS(7,0)
    152 #define	OVL_UI_SIZE		0x088
    153 
    154 /* VSU registers */
    155 #define	VS_CTRL_REG		0x000
    156 #define	 VS_CTRL_COEF_SWITCH_EN			__BIT(4)
    157 #define	 VS_CTRL_EN				__BIT(0)
    158 #define	VS_STATUS_REG		0x008
    159 #define	VS_FIELD_CTRL_REG	0x00c
    160 #define	VS_OUT_SIZE_REG		0x040
    161 #define	VS_Y_SIZE_REG		0x080
    162 #define	VS_Y_HSTEP_REG		0x088
    163 #define	VS_Y_VSTEP_REG		0x08c
    164 #define	VS_Y_HPHASE_REG		0x090
    165 #define	VS_Y_VPHASE0_REG	0x098
    166 #define	VS_Y_VPHASE1_REG	0x09c
    167 #define	VS_C_SIZE_REG		0x0c0
    168 #define	VS_C_HSTEP_REG		0x0c8
    169 #define	VS_C_VSTEP_REG		0x0cc
    170 #define	VS_C_HPHASE_REG		0x0d0
    171 #define	VS_C_VPHASE0_REG	0x0d8
    172 #define	VS_C_VPHASE1_REG	0x0dc
    173 #define	VS_Y_HCOEF0_REG(n)	(0x200 + (n) * 0x4)
    174 #define	VS_Y_HCOEF1_REG(n)	(0x300 + (n) * 0x4)
    175 #define	VS_Y_VCOEF_REG(n)	(0x400 + (n) * 0x4)
    176 #define	VS_C_HCOEF0_REG(n)	(0x600 + (n) * 0x4)
    177 #define	VS_C_HCOEF1_REG(n)	(0x700 + (n) * 0x4)
    178 #define	VS_C_VCOEF_REG(n)	(0x800 + (n) * 0x4)
    179 
    180 /* CSC registers */
    181 #define	CSC_BYPASS_REG		0x000
    182 #define	 CSC_BYPASS_DISABLE			__BIT(0)
    183 #define	CSC_COEFF0_REG(n)	(0x10 + 0x10 * (n))
    184 #define	GLB_ALPHA_REG		0x040
    185 
    186 enum {
    187 	MIXER_PORT_OUTPUT = 1,
    188 };
    189 
    190 struct sunxi_mixer_compat_data {
    191 	uint8_t ovl_ui_count;
    192 	uint8_t mixer_index;
    193 };
    194 
    195 struct sunxi_mixer_compat_data mixer0_data = {
    196 	.ovl_ui_count = 3,
    197 	.mixer_index = 0,
    198 };
    199 
    200 struct sunxi_mixer_compat_data mixer1_data = {
    201 	.ovl_ui_count = 1,
    202 	.mixer_index = 1,
    203 };
    204 
    205 static const struct device_compatible_entry compat_data[] = {
    206 	{ .compat = "allwinner,sun8i-h3-de2-mixer-0",
    207 	  .data = &mixer0_data },
    208 	{ .compat = "allwinner,sun8i-v3s-de2-mixer",
    209 	  .data = &mixer0_data },
    210 	{ .compat = "allwinner,sun50i-a64-de2-mixer-0",
    211 	  .data = &mixer0_data },
    212 	{ .compat = "allwinner,sun50i-a64-de2-mixer-1",
    213 	  .data = &mixer1_data },
    214 
    215 	DEVICE_COMPAT_EOL
    216 };
    217 
    218 struct sunxi_mixer_softc;
    219 
    220 struct sunxi_mixer_crtc {
    221 	struct drm_crtc		base;
    222 	struct sunxi_mixer_softc *sc;
    223 };
    224 
    225 struct sunxi_mixer_plane {
    226 	struct drm_plane	base;
    227 	struct sunxi_mixer_softc *sc;
    228 };
    229 
    230 struct sunxi_mixer_softc {
    231 	device_t		sc_dev;
    232 	bus_space_tag_t		sc_bst;
    233 	bus_space_handle_t	sc_bsh;
    234 	int			sc_phandle;
    235 
    236 	u_int			sc_ovl_ui_count;
    237 
    238 	struct sunxi_mixer_crtc	sc_crtc;
    239 	struct sunxi_mixer_plane sc_plane;
    240 
    241 	struct fdt_device_ports	sc_ports;
    242 };
    243 
    244 #define	GLB_READ(sc, reg)				\
    245 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, GLB_BASE + (reg))
    246 #define	GLB_WRITE(sc, reg, val)				\
    247 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, GLB_BASE + (reg), (val))
    248 
    249 #define	BLD_READ(sc, reg)				\
    250 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, BLD_BASE + (reg))
    251 #define	BLD_WRITE(sc, reg, val)				\
    252 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, BLD_BASE + (reg), (val))
    253 
    254 #define	OVL_V_READ(sc, reg)				\
    255 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE(0) + (reg))
    256 #define	OVL_V_WRITE(sc, reg, val)			\
    257 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE(0) + (reg), (val))
    258 
    259 #define	OVL_UI_READ(sc, n, reg)				\
    260 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE((n) + 1) + (reg))
    261 #define	OVL_UI_WRITE(sc, n, reg, val)			\
    262 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE((n) + 1) + (reg), (val))
    263 
    264 #define	VSU_READ(sc, reg)				\
    265 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, VSU_BASE + (reg))
    266 #define	VSU_WRITE(sc, reg, val)			\
    267 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, VSU_BASE + (reg), (val))
    268 
    269 #define	CSC_READ(sc, n, reg)				\
    270 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, CSC_BASE(n) + (reg))
    271 #define	CSC_WRITE(sc, n, reg, val)			\
    272 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, CSC_BASE(n) + (reg), (val))
    273 
    274 #define	to_sunxi_mixer_crtc(x)		container_of(x, struct sunxi_mixer_crtc, base)
    275 #define	to_sunxi_mixer_plane(x)	container_of(x, struct sunxi_mixer_plane, base)
    276 
    277 static int
    278 sunxi_mixer_plane_atomic_check(struct drm_plane *plane,
    279     struct drm_plane_state *state)
    280 {
    281 	struct drm_crtc_state *crtc_state;
    282 
    283 	if (state->crtc == NULL) {
    284 		return 0;
    285 	}
    286 
    287 	crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
    288 	if (IS_ERR(crtc_state)) {
    289 		return PTR_ERR(crtc_state);
    290 	}
    291 
    292 	return drm_atomic_helper_check_plane_state(state, crtc_state,
    293 	    DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
    294 	    false, true);
    295 }
    296 
    297 static void
    298 sunxi_mixer_plane_atomic_update(struct drm_plane *plane,
    299     struct drm_plane_state *old_state)
    300 {
    301 	struct sunxi_mixer_plane *mixer_plane = to_sunxi_mixer_plane(plane);
    302 	struct sunxi_mixer_softc * const sc = mixer_plane->sc;
    303 	struct sunxi_drm_framebuffer *sfb =
    304 	    to_sunxi_drm_framebuffer(plane->state->fb);
    305 	uint32_t block_h, block_w, x, y, block_start_y, num_hblocks;
    306 	uint32_t val;
    307 
    308 	block_h = drm_format_info_block_height(sfb->base.format, 0);
    309 	block_w = drm_format_info_block_width(sfb->base.format, 0);
    310 	x = plane->state->src_x >> 16;
    311 	y = plane->state->src_y >> 16;
    312 	block_start_y = (y / block_h) * block_h;
    313 	num_hblocks = x / block_w;
    314 
    315 	uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr;
    316 
    317 	paddr += block_start_y * sfb->base.pitches[0];
    318 	paddr += sfb->base.format->char_per_block[0] * num_hblocks;
    319 
    320 	uint32_t haddr = (paddr >> 32) & OVL_UI_TOP_HADD_LAYER0;
    321 	uint32_t laddr = paddr & 0xffffffff;
    322 
    323 	/* Enable UI overlay */
    324 	val = OVL_UI_ATTR_CTL_LAY_EN |
    325 	      __SHIFTIN(OVL_UI_ATTR_CTL_LAY_FBFMT_XRGB_8888,
    326 			OVL_UI_ATTR_CTL_LAY_FBFMT);
    327 	OVL_UI_WRITE(sc, 0, OVL_UI_ATTR_CTL(0), val);
    328 
    329 	/* Set UI overlay line size */
    330 	OVL_UI_WRITE(sc, 0, OVL_UI_PITCH(0), sfb->base.pitches[0]);
    331 
    332 	/* Framebuffer start address */
    333 	val = OVL_UI_READ(sc, 0, OVL_UI_TOP_HADD);
    334 	val &= ~OVL_UI_TOP_HADD_LAYER0;
    335 	val |= __SHIFTIN(haddr, OVL_UI_TOP_HADD_LAYER0);
    336 	OVL_UI_WRITE(sc, 0, OVL_UI_TOP_HADD, val);
    337 	OVL_UI_WRITE(sc, 0, OVL_UI_TOP_LADD(0), laddr);
    338 
    339 	/* Commit settings */
    340 	GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY);
    341 }
    342 
    343 static void
    344 sunxi_mixer_plane_atomic_disable(struct drm_plane *plane,
    345     struct drm_plane_state *state)
    346 {
    347 	struct sunxi_mixer_plane *mixer_plane = to_sunxi_mixer_plane(plane);
    348 	struct sunxi_mixer_softc * const sc = mixer_plane->sc;
    349 
    350 	/* Disable UI overlay */
    351 	OVL_UI_WRITE(sc, 0, OVL_UI_ATTR_CTL(0), 0);
    352 }
    353 
    354 static const struct drm_plane_helper_funcs sunxi_mixer_plane_helper_funcs = {
    355 	.atomic_check = sunxi_mixer_plane_atomic_check,
    356 	.atomic_update = sunxi_mixer_plane_atomic_update,
    357 	.atomic_disable = sunxi_mixer_plane_atomic_disable,
    358 };
    359 
    360 static bool
    361 sunxi_mixer_format_mod_supported(struct drm_plane *plane, uint32_t format,
    362     uint64_t modifier)
    363 {
    364 	return modifier == DRM_FORMAT_MOD_LINEAR;
    365 }
    366 
    367 static const struct drm_plane_funcs sunxi_mixer_plane_funcs = {
    368 	.update_plane = drm_atomic_helper_update_plane,
    369 	.disable_plane = drm_atomic_helper_disable_plane,
    370 	.destroy = drm_plane_cleanup,
    371 	.reset = drm_atomic_helper_plane_reset,
    372 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
    373 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
    374 	.format_mod_supported = sunxi_mixer_format_mod_supported,
    375 };
    376 
    377 static int
    378 sunxi_mixer_crtc_atomic_check(struct drm_crtc *crtc,
    379     struct drm_crtc_state *state)
    380 {
    381 	bool enabled = state->plane_mask & drm_plane_mask(crtc->primary);
    382 
    383 	if (enabled != state->enable) {
    384 		return -EINVAL;
    385 	}
    386 
    387 	return drm_atomic_add_affected_planes(state->state, crtc);
    388 }
    389 
    390 static void
    391 sunxi_mixer_crtc_atomic_enable(struct drm_crtc *crtc,
    392     struct drm_crtc_state *state)
    393 {
    394 	struct sunxi_mixer_crtc *mixer_crtc = to_sunxi_mixer_crtc(crtc);
    395 	struct sunxi_mixer_softc * const sc = mixer_crtc->sc;
    396 	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
    397 	uint32_t val;
    398 
    399 	const uint32_t size = ((adjusted_mode->vdisplay - 1) << 16) |
    400 			      (adjusted_mode->hdisplay - 1);
    401 
    402 	/* Set global size */
    403 	GLB_WRITE(sc, GLB_SIZE, size);
    404 
    405 	/* Enable pipe 0 */
    406 	val = BLD_READ(sc, BLD_FILL_COLOR_CTL);
    407 	val |= BLD_FILL_COLOR_CTL_P0_EN;
    408 	BLD_WRITE(sc, BLD_FILL_COLOR_CTL, val);
    409 
    410 	/* Set blender 0 input size */
    411 	BLD_WRITE(sc, BLD_CH_ISIZE(0), size);
    412 	/* Set blender 0 offset */
    413 	BLD_WRITE(sc, BLD_CH_OFFSET(0), 0);
    414 	/* Route channel 1 to pipe 0 */
    415 	val = BLD_READ(sc, BLD_CH_RTCTL);
    416 	val &= ~BLD_CH_RTCTL_P0;
    417 	val |= __SHIFTIN(1, BLD_CH_RTCTL_P0);
    418 	BLD_WRITE(sc, BLD_CH_RTCTL, val);
    419 	/* Set blender output size */
    420 	BLD_WRITE(sc, BLD_SIZE, size);
    421 
    422 	/* Set UI overlay layer size */
    423 	OVL_UI_WRITE(sc, 0, OVL_UI_MBSIZE(0), size);
    424 	/* Set UI overlay offset */
    425 	OVL_UI_WRITE(sc, 0, OVL_UI_COOR(0), 0);
    426 	/* Set UI overlay window size */
    427 	OVL_UI_WRITE(sc, 0, OVL_UI_SIZE, size);
    428 
    429 	/* Commit settings */
    430 	GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY);
    431 
    432 	drm_crtc_vblank_on(crtc);
    433 
    434 	if (drm_crtc_index(crtc) == 0) {
    435 		pmf_event_inject(NULL, PMFE_DISPLAY_ON);
    436 	}
    437 }
    438 
    439 static void
    440 sunxi_mixer_crtc_atomic_disable(struct drm_crtc *crtc,
    441     struct drm_crtc_state *state)
    442 {
    443 	drm_crtc_vblank_off(crtc);
    444 
    445 	if (crtc->state->event && !crtc->state->active) {
    446 		spin_lock(&crtc->dev->event_lock);
    447 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
    448 		spin_unlock(&crtc->dev->event_lock);
    449 
    450 		crtc->state->event = NULL;
    451 	}
    452 
    453 	if (drm_crtc_index(crtc) == 0) {
    454 		pmf_event_inject(NULL, PMFE_DISPLAY_OFF);
    455 	}
    456 }
    457 
    458 static void
    459 sunxi_mixer_crtc_atomic_flush(struct drm_crtc *crtc,
    460     struct drm_crtc_state *state)
    461 {
    462 	struct sunxi_mixer_crtc *mixer_crtc = to_sunxi_mixer_crtc(crtc);
    463 	struct sunxi_mixer_softc * const sc = mixer_crtc->sc;
    464 	int ret;
    465 
    466 	/* Commit settings */
    467 	GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY);
    468 
    469 	/*
    470 	 * If caller wants a vblank event, tell the vblank interrupt
    471 	 * handler to send it on the next interrupt.
    472 	 */
    473 	spin_lock(&crtc->dev->event_lock);
    474 	if (crtc->state->event) {
    475 		struct sunxi_drm_softc *drm_sc = device_private(crtc->dev->dev);
    476 		const int crtc_index = drm_crtc_index(crtc);
    477 		struct sunxi_drm_vblank *vbl = &drm_sc->sc_vbl[crtc_index];
    478 
    479 		if ((ret = drm_crtc_vblank_get_locked(crtc)) != 0) {
    480 			aprint_error_dev(sc->sc_dev,
    481 			     "drm_crtc_vblank_get: %d\n", ret);
    482 		}
    483 		if (vbl->event) { /* XXX leaky; KASSERT? */
    484 			aprint_error_dev(sc->sc_dev, "unfinished vblank\n");
    485 		}
    486 		vbl->event = crtc->state->event;
    487 		crtc->state->event = NULL;
    488 	}
    489 	spin_unlock(&crtc->dev->event_lock);
    490 }
    491 
    492 static int
    493 sunxi_mixer_crtc_enable_vblank(struct drm_crtc *crtc)
    494 {
    495 	struct drm_device *ddev = crtc->dev;
    496 	struct sunxi_drm_softc *drm_sc = device_private(ddev->dev);
    497 	const int crtc_index = drm_crtc_index(crtc);
    498 
    499 	if (drm_sc->sc_vbl[crtc_index].enable_vblank != NULL) {
    500 		drm_sc->sc_vbl[crtc_index].enable_vblank(
    501 		    drm_sc->sc_vbl[crtc_index].priv);
    502 	}
    503 
    504 	return 0;
    505 }
    506 
    507 static void
    508 sunxi_mixer_crtc_disable_vblank(struct drm_crtc *crtc)
    509 {
    510 	struct drm_device *ddev = crtc->dev;
    511 	struct sunxi_drm_softc *drm_sc = device_private(ddev->dev);
    512 	const int crtc_index = drm_crtc_index(crtc);
    513 
    514 	if (drm_sc->sc_vbl[crtc_index].disable_vblank != NULL) {
    515 		drm_sc->sc_vbl[crtc_index].disable_vblank(
    516 		    drm_sc->sc_vbl[crtc_index].priv);
    517 	}
    518 }
    519 
    520 static const struct drm_crtc_helper_funcs sunxi_mixer_crtc_helper_funcs = {
    521 	.atomic_check = sunxi_mixer_crtc_atomic_check,
    522 	.atomic_enable = sunxi_mixer_crtc_atomic_enable,
    523 	.atomic_disable = sunxi_mixer_crtc_atomic_disable,
    524 	.atomic_flush = sunxi_mixer_crtc_atomic_flush,
    525 };
    526 
    527 static const struct drm_crtc_funcs sunxi_mixer_crtc_funcs = {
    528 	.set_config = drm_atomic_helper_set_config,
    529 	.destroy = drm_crtc_cleanup,
    530 	.page_flip = drm_atomic_helper_page_flip,
    531 	.reset = drm_atomic_helper_crtc_reset,
    532 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
    533 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
    534 	.enable_vblank = sunxi_mixer_crtc_enable_vblank,
    535 	.disable_vblank = sunxi_mixer_crtc_disable_vblank,
    536 };
    537 
    538 static uint32_t sunxi_mixer_plane_formats[] = {
    539 	DRM_FORMAT_XRGB8888,
    540 };
    541 
    542 static const uint64_t sunxi_mixer_plane_modifiers[] = {
    543 	DRM_FORMAT_MOD_LINEAR,
    544 	DRM_FORMAT_MOD_INVALID,
    545 };
    546 
    547 static int
    548 sunxi_mixer_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
    549 {
    550 	struct sunxi_mixer_softc * const sc = device_private(dev);
    551 	struct drm_plane *plane, *cursor;
    552 	struct drm_device *ddev;
    553 	struct sunxi_drm_softc *drm_sc;
    554 	bus_size_t reg;
    555 
    556 	if (!activate)
    557 		return EINVAL;
    558 
    559 	ddev = sunxi_drm_endpoint_device(ep);
    560 	if (ddev == NULL) {
    561 		DRM_ERROR("couldn't find DRM device\n");
    562 		return ENXIO;
    563 	}
    564 	drm_sc = device_private(ddev->dev);
    565 
    566 	sc->sc_crtc.sc = sc;
    567 	sc->sc_plane.sc = sc;
    568 
    569 	/* Initialize registers */
    570 	for (reg = 0; reg < 0xc000; reg += 4)
    571 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, 0);
    572 	BLD_WRITE(sc, BLD_CTL(0), 0x03010301);
    573 	BLD_WRITE(sc, BLD_CTL(1), 0x03010301);
    574 	BLD_WRITE(sc, BLD_CTL(2), 0x03010301);
    575 	BLD_WRITE(sc, BLD_CTL(3), 0x03010301);
    576 
    577 	/* RT enable */
    578 	GLB_WRITE(sc, GLB_CTL, GLB_CTL_EN);
    579 
    580 	drm_universal_plane_init(ddev, &sc->sc_plane.base,
    581 	    0, &sunxi_mixer_plane_funcs,
    582 	    sunxi_mixer_plane_formats, __arraycount(sunxi_mixer_plane_formats),
    583 	    sunxi_mixer_plane_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL);
    584 	drm_plane_helper_add(&sc->sc_plane.base, &sunxi_mixer_plane_helper_funcs);
    585 	plane = &sc->sc_plane.base;
    586 
    587 	/* TODO: hardware cursor support */
    588 	cursor = NULL;
    589 
    590 	drm_crtc_init_with_planes(ddev, &sc->sc_crtc.base, plane, cursor,
    591 	    &sunxi_mixer_crtc_funcs, NULL);
    592 	drm_crtc_helper_add(&sc->sc_crtc.base, &sunxi_mixer_crtc_helper_funcs);
    593 
    594 	sc->sc_plane.base.possible_crtcs = 1 << drm_crtc_index(&sc->sc_crtc.base);
    595 
    596 	drm_sc->sc_vbl[drm_crtc_index(&sc->sc_crtc.base)].crtc = &sc->sc_crtc.base;
    597 
    598 	return fdt_endpoint_activate(ep, activate);
    599 }
    600 
    601 static void *
    602 sunxi_mixer_ep_get_data(device_t dev, struct fdt_endpoint *ep)
    603 {
    604 	struct sunxi_mixer_softc * const sc = device_private(dev);
    605 
    606 	return &sc->sc_crtc;
    607 }
    608 
    609 static int
    610 sunxi_mixer_match(device_t parent, cfdata_t cf, void *aux)
    611 {
    612 	struct fdt_attach_args * const faa = aux;
    613 
    614 	return of_compatible_match(faa->faa_phandle, compat_data);
    615 }
    616 
    617 static void
    618 sunxi_mixer_attach(device_t parent, device_t self, void *aux)
    619 {
    620 	struct sunxi_mixer_softc * const sc = device_private(self);
    621 	struct fdt_attach_args * const faa = aux;
    622 	struct fdt_endpoint *out_ep;
    623 	const int phandle = faa->faa_phandle;
    624 	const struct sunxi_mixer_compat_data * const cd =
    625 	    of_compatible_lookup(phandle, compat_data)->data;
    626 	struct clk *clk_bus, *clk_mod;
    627 	struct fdtbus_reset *rst;
    628 	bus_addr_t addr;
    629 	bus_size_t size;
    630 
    631 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    632 		aprint_error(": couldn't get registers\n");
    633 		return;
    634 	}
    635 
    636 	rst = fdtbus_reset_get_index(phandle, 0);
    637 	if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
    638 		aprint_error(": couldn't de-assert reset\n");
    639 		return;
    640 	}
    641 
    642 	clk_bus = fdtbus_clock_get(phandle, "bus");
    643 	if (clk_bus == NULL || clk_enable(clk_bus) != 0) {
    644 		aprint_error(": couldn't enable bus clock\n");
    645 		return;
    646 	}
    647 
    648 	clk_mod = fdtbus_clock_get(phandle, "mod");
    649 	if (clk_mod == NULL ||
    650 	    clk_set_rate(clk_mod, SUNXI_MIXER_FREQ) != 0 ||
    651 	    clk_enable(clk_mod) != 0) {
    652 		aprint_error(": couldn't enable mod clock\n");
    653 		return;
    654 	}
    655 
    656 	sc->sc_dev = self;
    657 	sc->sc_bst = faa->faa_bst;
    658 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    659 		aprint_error(": couldn't map registers\n");
    660 		return;
    661 	}
    662 	sc->sc_phandle = faa->faa_phandle;
    663 	sc->sc_ovl_ui_count = cd->ovl_ui_count;
    664 
    665 	aprint_naive("\n");
    666 	aprint_normal(": Display Engine Mixer\n");
    667 
    668 	sc->sc_ports.dp_ep_activate = sunxi_mixer_ep_activate;
    669 	sc->sc_ports.dp_ep_get_data = sunxi_mixer_ep_get_data;
    670 	fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_CRTC);
    671 
    672 	out_ep = fdt_endpoint_get_from_index(&sc->sc_ports,
    673 	    MIXER_PORT_OUTPUT, cd->mixer_index);
    674 	if (out_ep == NULL) {
    675 		/* Couldn't find new-style DE2 endpoint, try old style. */
    676 		out_ep = fdt_endpoint_get_from_index(&sc->sc_ports,
    677 		    MIXER_PORT_OUTPUT, 0);
    678 	}
    679 
    680 	if (out_ep != NULL)
    681 		sunxi_drm_register_endpoint(phandle, out_ep);
    682 }
    683 
    684 CFATTACH_DECL_NEW(sunxi_mixer, sizeof(struct sunxi_mixer_softc),
    685 	sunxi_mixer_match, sunxi_mixer_attach, NULL, NULL);
    686