Home | History | Annotate | Line # | Download | only in rockchip
rk_vop.c revision 1.16
      1 /* $NetBSD: rk_vop.c,v 1.16 2021/12/20 00:27:17 riastradh 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: rk_vop.c,v 1.16 2021/12/20 00:27:17 riastradh 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/rockchip/rk_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	VOP_REG_CFG_DONE		0x0000
     56 #define	 REG_LOAD_EN			__BIT(0)
     57 #define	VOP_SYS_CTRL			0x0008
     58 #define	 VOP_STANDBY_EN			__BIT(22)
     59 #define	 MIPI_OUT_EN			__BIT(15)
     60 #define	 EDP_OUT_EN			__BIT(14)
     61 #define	 HDMI_OUT_EN			__BIT(13)
     62 #define	 RGB_OUT_EN			__BIT(12)
     63 #define	VOP_DSP_CTRL0			0x0010
     64 #define	 DSP_OUT_MODE			__BITS(3,0)
     65 #define	  DSP_OUT_MODE_RGB888		0
     66 #define	  DSP_OUT_MODE_RGBaaa		15
     67 #define	VOP_DSP_CTRL1			0x0014
     68 #define	VOP_WIN0_CTRL			0x0030
     69 #define	 WIN0_LB_MODE			__BITS(7,5)
     70 #define	  WIN0_LB_MODE_RGB_3840X2	2
     71 #define	  WIN0_LB_MODE_RGB_2560X4	3
     72 #define	  WIN0_LB_MODE_RGB_1920X5	4
     73 #define	  WIN0_LB_MODE_RGB_1280X8	5
     74 #define	 WIN0_DATA_FMT			__BITS(3,1)
     75 #define	  WIN0_DATA_FMT_ARGB888		0
     76 #define	 WIN0_EN			__BIT(0)
     77 #define	VOP_WIN0_COLOR_KEY		0x0038
     78 #define	VOP_WIN0_VIR			0x003c
     79 #define	 WIN0_VIR_STRIDE		__BITS(13,0)
     80 #define	VOP_WIN0_YRGB_MST		0x0040
     81 #define	VOP_WIN0_ACT_INFO		0x0048
     82 #define	 WIN0_ACT_HEIGHT		__BITS(28,16)
     83 #define	 WIN0_ACT_WIDTH			__BITS(12,0)
     84 #define	VOP_WIN0_DSP_INFO		0x004c
     85 #define	 WIN0_DSP_HEIGHT		__BITS(27,16)
     86 #define	 WIN0_DSP_WIDTH			__BITS(11,0)
     87 #define	VOP_WIN0_DSP_ST			0x0050
     88 #define	 WIN0_DSP_YST			__BITS(28,16)
     89 #define	 WIN0_DSP_XST			__BITS(12,0)
     90 #define	VOP_POST_DSP_HACT_INFO		0x0170
     91 #define	 DSP_HACT_ST_POST		__BITS(28,16)
     92 #define	 DSP_HACT_END_POST		__BITS(12,0)
     93 #define	VOP_POST_DSP_VACT_INFO		0x0174
     94 #define	 DSP_VACT_ST_POST		__BITS(28,16)
     95 #define	 DSP_VACT_END_POST		__BITS(12,0)
     96 #define	VOP_DSP_HTOTAL_HS_END		0x0188
     97 #define	 DSP_HS_END			__BITS(28,16)
     98 #define	 DSP_HTOTAL			__BITS(12,0)
     99 #define	VOP_DSP_HACT_ST_END		0x018c
    100 #define	 DSP_HACT_ST			__BITS(28,16)
    101 #define	 DSP_HACT_END			__BITS(12,0)
    102 #define	VOP_DSP_VTOTAL_VS_END		0x0190
    103 #define	 DSP_VS_END			__BITS(28,16)
    104 #define	 DSP_VTOTAL			__BITS(12,0)
    105 #define	VOP_DSP_VACT_ST_END		0x0194
    106 #define	 DSP_VACT_ST			__BITS(28,16)
    107 #define	 DSP_VACT_END			__BITS(12,0)
    108 #define	VOP_INTR_EN0			0x0280
    109 #define	VOP_INTR_CLEAR0			0x0284
    110 #define	VOP_INTR_STATUS0		0x0288
    111 #define	VOP_INTR_RAW_STATUS0		0x028c
    112 #define	 VOP_INTR0_DMA_FINISH		__BIT(15)
    113 #define	 VOP_INTR0_MMU			__BIT(14)
    114 #define	 VOP_INTR0_DSP_HOLD_VALID	__BIT(13)
    115 #define	 VOP_INTR0_FS_FIELD		__BIT(12)
    116 #define	 VOP_INTR0_POST_BUF_EMPTY	__BIT(11)
    117 #define	 VOP_INTR0_HWC_EMPTY		__BIT(10)
    118 #define	 VOP_INTR0_WIN3_EMPTY		__BIT(9)
    119 #define	 VOP_INTR0_WIN2_EMPTY		__BIT(8)
    120 #define	 VOP_INTR0_WIN1_EMPTY		__BIT(7)
    121 #define	 VOP_INTR0_WIN0_EMPTY		__BIT(6)
    122 #define	 VOP_INTR0_BUS_ERROR		__BIT(5)
    123 #define	 VOP_INTR0_LINE_FLAG1		__BIT(4)
    124 #define	 VOP_INTR0_LINE_FLAG0		__BIT(3)
    125 #define	 VOP_INTR0_ADDR_SAME		__BIT(2)
    126 #define	 VOP_INTR0_FS_NEW		__BIT(1)
    127 #define	 VOP_INTR0_FS			__BIT(0)
    128 
    129 /*
    130  * Polarity fields are in different locations depending on SoC and output type,
    131  * but always in the same order.
    132  */
    133 #define	DSP_DCLK_POL			__BIT(3)
    134 #define	DSP_DEN_POL			__BIT(2)
    135 #define	DSP_VSYNC_POL			__BIT(1)
    136 #define	DSP_HSYNC_POL			__BIT(0)
    137 
    138 enum vop_ep_type {
    139 	VOP_EP_MIPI,
    140 	VOP_EP_EDP,
    141 	VOP_EP_HDMI,
    142 	VOP_EP_MIPI1,
    143 	VOP_EP_DP,
    144 	VOP_NEP
    145 };
    146 
    147 struct rk_vop_softc;
    148 struct rk_vop_config;
    149 
    150 struct rk_vop_crtc {
    151 	struct drm_crtc		base;
    152 	struct rk_vop_softc	*sc;
    153 };
    154 
    155 struct rk_vop_plane {
    156 	struct drm_plane	base;
    157 	struct rk_vop_softc	*sc;
    158 };
    159 
    160 struct rk_vop_softc {
    161 	device_t		sc_dev;
    162 	bus_space_tag_t		sc_bst;
    163 	bus_space_handle_t	sc_bsh;
    164 	int			sc_phandle;
    165 
    166 	struct clk		*sc_dclk;
    167 
    168 	struct rk_vop_plane	sc_plane;
    169 	struct rk_vop_crtc	sc_crtc;
    170 
    171 	struct fdt_device_ports	sc_ports;
    172 
    173 	const struct rk_vop_config *sc_conf;
    174 
    175 	/* vblank */
    176 	void			*sc_ih;
    177 	kmutex_t		sc_intr_lock;
    178 	struct drm_pending_vblank_event *sc_event;
    179 };
    180 
    181 #define	to_rk_vop_crtc(x)	container_of(x, struct rk_vop_crtc, base)
    182 #define	to_rk_vop_plane(x)	container_of(x, struct rk_vop_plane, base)
    183 
    184 #define	RD4(sc, reg)				\
    185 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
    186 #define	WR4(sc, reg, val)			\
    187 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
    188 
    189 static void
    190 WR4_MASK(struct rk_vop_softc *sc, bus_size_t reg, uint16_t mask, uint16_t val)
    191 {
    192 
    193 	KASSERT(val == (mask & val));
    194 	WR4(sc, reg, ((uint32_t)mask << 16) | val);
    195 }
    196 
    197 struct rk_vop_config {
    198 	const char		*descr;
    199 	u_int			out_mode;
    200 	void			(*init)(struct rk_vop_softc *);
    201 	void			(*set_polarity)(struct rk_vop_softc *,
    202 						enum vop_ep_type, uint32_t);
    203 };
    204 
    205 static const uint32_t rk_vop_layer_formats[] = {
    206 	DRM_FORMAT_ARGB8888,
    207 	DRM_FORMAT_XRGB8888,
    208 };
    209 
    210 static const uint64_t rk_vop_layer_modifiers[] = {
    211 	DRM_FORMAT_MOD_LINEAR,
    212 	DRM_FORMAT_MOD_INVALID
    213 };
    214 
    215 #define	RK3399_VOP_MIPI_POL	__BITS(31,28)
    216 #define	RK3399_VOP_EDP_POL	__BITS(27,24)
    217 #define	RK3399_VOP_HDMI_POL	__BITS(23,20)
    218 #define	RK3399_VOP_DP_POL	__BITS(19,16)
    219 
    220 #define	RK3399_VOP_SYS_CTRL_ENABLE	__BIT(11)
    221 
    222 static void
    223 rk3399_vop_set_polarity(struct rk_vop_softc *sc, enum vop_ep_type ep_type, uint32_t pol)
    224 {
    225 	uint32_t mask, val;
    226 
    227 	switch (ep_type) {
    228 	case VOP_EP_MIPI:
    229 	case VOP_EP_MIPI1:
    230 		mask = RK3399_VOP_MIPI_POL;
    231 		break;
    232 	case VOP_EP_EDP:
    233 		mask = RK3399_VOP_EDP_POL;
    234 		break;
    235 	case VOP_EP_HDMI:
    236 		mask = RK3399_VOP_HDMI_POL;
    237 		break;
    238 	case VOP_EP_DP:
    239 		mask = RK3399_VOP_DP_POL;
    240 		break;
    241 	default:
    242 		return;
    243 	}
    244 
    245 	val = RD4(sc, VOP_DSP_CTRL1);
    246 	val &= ~mask;
    247 	val |= __SHIFTIN(pol, mask);
    248 	WR4(sc, VOP_DSP_CTRL1, val);
    249 }
    250 
    251 static void
    252 rk3399_vop_init(struct rk_vop_softc *sc)
    253 {
    254 	uint32_t val;
    255 
    256 	val = RD4(sc, VOP_SYS_CTRL);
    257 	val |= RK3399_VOP_SYS_CTRL_ENABLE;
    258 	WR4(sc, VOP_SYS_CTRL, val);
    259 }
    260 
    261 static const struct rk_vop_config rk3399_vop_lit_config = {
    262 	.descr = "RK3399 VOPL",
    263 	.out_mode = DSP_OUT_MODE_RGB888,
    264 	.init = rk3399_vop_init,
    265 	.set_polarity = rk3399_vop_set_polarity,
    266 };
    267 
    268 static const struct rk_vop_config rk3399_vop_big_config = {
    269 	.descr = "RK3399 VOPB",
    270 	.out_mode = DSP_OUT_MODE_RGBaaa,
    271 	.init = rk3399_vop_init,
    272 	.set_polarity = rk3399_vop_set_polarity,
    273 };
    274 
    275 static const struct device_compatible_entry compat_data[] = {
    276 	{ .compat = "rockchip,rk3399-vop-big",
    277 	  .data = &rk3399_vop_big_config },
    278 	{ .compat = "rockchip,rk3399-vop-lit",
    279 	  .data = &rk3399_vop_lit_config },
    280 
    281 	DEVICE_COMPAT_EOL
    282 };
    283 
    284 static int
    285 rk_vop_plane_atomic_check(struct drm_plane *plane,
    286     struct drm_plane_state *state)
    287 {
    288 	struct drm_crtc_state *crtc_state;
    289 
    290 	if (state->crtc == NULL)
    291 		return 0;
    292 
    293 	crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
    294 	if (IS_ERR(crtc_state))
    295 		return PTR_ERR(crtc_state);
    296 
    297 	return drm_atomic_helper_check_plane_state(state, crtc_state,
    298 	    DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
    299 	    false, true);
    300 }
    301 
    302 static void
    303 rk_vop_plane_atomic_update(struct drm_plane *plane,
    304     struct drm_plane_state *old_state)
    305 {
    306 	struct rk_vop_plane *vop_plane = to_rk_vop_plane(plane);
    307 	struct rk_vop_softc * const sc = vop_plane->sc;
    308 	struct rk_drm_framebuffer *sfb =
    309 	    to_rk_drm_framebuffer(plane->state->fb);
    310 	struct drm_display_mode *mode = &plane->state->crtc->mode;
    311 	struct drm_rect *src = &plane->state->src;
    312 	struct drm_rect *dst = &plane->state->dst;
    313 	uint32_t act_width, act_height, dsp_width, dsp_height;
    314 	uint32_t htotal, hsync_start;
    315 	uint32_t vtotal, vsync_start;
    316 	uint32_t lb_mode;
    317 	uint32_t block_h, block_w, x, y, block_start_y, num_hblocks;
    318 	uint64_t paddr;
    319 	uint32_t val;
    320 
    321 	act_width = drm_rect_width(src) >> 16;
    322 	act_height = drm_rect_height(src) >> 16;
    323 	val = __SHIFTIN(act_width - 1, WIN0_ACT_WIDTH) |
    324 	      __SHIFTIN(act_height - 1, WIN0_ACT_HEIGHT);
    325 	WR4(sc, VOP_WIN0_ACT_INFO, val);
    326 
    327 	dsp_width = drm_rect_width(dst);
    328 	dsp_height = drm_rect_height(dst);
    329 	val = __SHIFTIN(dsp_width - 1, WIN0_DSP_WIDTH) |
    330 	      __SHIFTIN(dsp_height - 1, WIN0_DSP_HEIGHT);
    331 	WR4(sc, VOP_WIN0_DSP_INFO, val);
    332 
    333 	htotal = mode->htotal;
    334 	hsync_start = mode->hsync_start;
    335 	vtotal = mode->vtotal;
    336 	vsync_start = mode->vsync_start;
    337 	val = __SHIFTIN(dst->x1 + htotal - hsync_start, WIN0_DSP_XST) |
    338 	      __SHIFTIN(dst->y1 + vtotal - vsync_start, WIN0_DSP_YST);
    339 	WR4(sc, VOP_WIN0_DSP_ST, val);
    340 
    341 	WR4(sc, VOP_WIN0_COLOR_KEY, 0);
    342 
    343 	if (act_width > 2560)
    344 		lb_mode = WIN0_LB_MODE_RGB_3840X2;
    345 	else if (act_width > 1920)
    346 		lb_mode = WIN0_LB_MODE_RGB_2560X4;
    347 	else if (act_width > 1280)
    348 		lb_mode = WIN0_LB_MODE_RGB_1920X5;
    349 	else
    350 		lb_mode = WIN0_LB_MODE_RGB_1280X8;
    351 	val = __SHIFTIN(lb_mode, WIN0_LB_MODE) |
    352 	      __SHIFTIN(WIN0_DATA_FMT_ARGB888, WIN0_DATA_FMT) |
    353 	      WIN0_EN;
    354 	WR4(sc, VOP_WIN0_CTRL, val);
    355 
    356 	paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr;
    357 	paddr += sfb->base.offsets[0];
    358 
    359 	block_h = drm_format_info_block_height(sfb->base.format, 0);
    360 	block_w = drm_format_info_block_width(sfb->base.format, 0);
    361 	x = plane->state->src_x >> 16;
    362 	y = plane->state->src_y >> 16;
    363 	block_start_y = (y / block_h) * block_h;
    364 	num_hblocks = x / block_w;
    365 
    366 	paddr += block_start_y * sfb->base.pitches[0];
    367 	paddr += sfb->base.format->char_per_block[0] * num_hblocks;
    368 
    369 	DRM_DEBUG_KMS("[PLANE:%s] fb=%p paddr=0x%lx\n", plane->name, sfb, paddr);
    370 
    371 	KASSERT((paddr & ~0xffffffff) == 0);
    372 
    373 	val = __SHIFTIN(sfb->base.pitches[0] / 4, WIN0_VIR_STRIDE);
    374 	WR4(sc, VOP_WIN0_VIR, val);
    375 
    376 	/* Framebuffer start address */
    377 	WR4(sc, VOP_WIN0_YRGB_MST, (uint32_t)paddr);
    378 }
    379 
    380 static void
    381 rk_vop_plane_atomic_disable(struct drm_plane *plane,
    382     struct drm_plane_state *state)
    383 {
    384 	struct rk_vop_plane *vop_plane = to_rk_vop_plane(plane);
    385 	struct rk_vop_softc * const sc = vop_plane->sc;
    386 
    387 	WR4(sc, VOP_WIN0_CTRL, 0);	/* clear WIN0_EN */
    388 }
    389 
    390 static const struct drm_plane_helper_funcs rk_vop_plane_helper_funcs = {
    391 	.atomic_check = rk_vop_plane_atomic_check,
    392 	.atomic_update = rk_vop_plane_atomic_update,
    393 	.atomic_disable = rk_vop_plane_atomic_disable,
    394 #if 0
    395 	.prepare_fb = drm_gem_vram_plane_helper_prepare_fb,
    396 	.cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb,
    397 #endif
    398 };
    399 
    400 static bool
    401 rk_vop_plane_format_mod_supported(struct drm_plane *plane, uint32_t format,
    402     uint64_t modifier)
    403 {
    404 	return modifier == DRM_FORMAT_MOD_LINEAR;
    405 }
    406 
    407 static const struct drm_plane_funcs rk_vop_plane_funcs = {
    408 	.update_plane = drm_atomic_helper_update_plane,
    409 	.disable_plane = drm_atomic_helper_disable_plane,
    410 	.destroy = drm_plane_cleanup,
    411 	.reset = drm_atomic_helper_plane_reset,
    412 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
    413 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
    414 	.format_mod_supported = rk_vop_plane_format_mod_supported,
    415 };
    416 
    417 static void
    418 rk_vop_crtc_dpms(struct drm_crtc *crtc, int mode)
    419 {
    420 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
    421 	struct rk_vop_softc * const sc = mixer_crtc->sc;
    422 	uint32_t val;
    423 
    424 	val = RD4(sc, VOP_SYS_CTRL);
    425 
    426 	switch (mode) {
    427 	case DRM_MODE_DPMS_ON:
    428 		val &= ~VOP_STANDBY_EN;
    429 		break;
    430 	case DRM_MODE_DPMS_STANDBY:
    431 	case DRM_MODE_DPMS_SUSPEND:
    432 	case DRM_MODE_DPMS_OFF:
    433 		val |= VOP_STANDBY_EN;
    434 		break;
    435 	}
    436 
    437 	WR4(sc, VOP_SYS_CTRL, val);
    438 
    439 	/* Commit settings */
    440 	WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
    441 }
    442 
    443 static int
    444 rk_vop_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
    445 {
    446 	bool enabled = state->plane_mask & drm_plane_mask(crtc->primary);
    447 
    448 	if (enabled != state->enable)
    449 		return -EINVAL;
    450 
    451 	return drm_atomic_add_affected_planes(state->state, crtc);
    452 }
    453 
    454 static void
    455 rk_vop_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *state)
    456 {
    457 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
    458 	struct rk_vop_softc * const sc = mixer_crtc->sc;
    459 	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
    460 	uint32_t val;
    461 	u_int pol;
    462 	int connector_type = 0;
    463 	struct drm_connector *connector;
    464 	struct drm_connector_list_iter conn_iter;
    465 	int error;
    466 
    467 	const u_int hactive = adjusted_mode->hdisplay;
    468 	const u_int hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
    469 	const u_int hback_porch = adjusted_mode->htotal - adjusted_mode->hsync_end;
    470 	const u_int hfront_porch = adjusted_mode->hsync_start - adjusted_mode->hdisplay;
    471 
    472 	const u_int vactive = adjusted_mode->vdisplay;
    473 	const u_int vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
    474 	const u_int vback_porch = adjusted_mode->vtotal - adjusted_mode->vsync_end;
    475 	const u_int vfront_porch = adjusted_mode->vsync_start - adjusted_mode->vdisplay;
    476 
    477 	error = clk_set_rate(sc->sc_dclk, adjusted_mode->clock * 1000);
    478 	if (error)
    479 		DRM_ERROR("couldn't set pixel clock: %d\n", error);
    480 
    481 	pol = DSP_DCLK_POL;
    482 	if ((adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) != 0)
    483 		pol |= DSP_HSYNC_POL;
    484 	if ((adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) != 0)
    485 		pol |= DSP_VSYNC_POL;
    486 
    487 	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
    488 	drm_for_each_connector_iter(connector, &conn_iter) {
    489 		if (connector->encoder == NULL)
    490 			continue;
    491 		if (connector->encoder->crtc == crtc) {
    492 			connector_type = connector->connector_type;
    493 			break;
    494 		}
    495 	}
    496 	drm_connector_list_iter_end(&conn_iter);
    497 
    498 	switch (connector_type) {
    499 	case DRM_MODE_CONNECTOR_HDMIA:
    500 		sc->sc_conf->set_polarity(sc, VOP_EP_HDMI, pol);
    501 		break;
    502 	case DRM_MODE_CONNECTOR_eDP:
    503 		sc->sc_conf->set_polarity(sc, VOP_EP_EDP, pol);
    504 		break;
    505 	}
    506 
    507 	val = RD4(sc, VOP_SYS_CTRL);
    508 	val &= ~VOP_STANDBY_EN;
    509 	val &= ~(MIPI_OUT_EN|EDP_OUT_EN|HDMI_OUT_EN|RGB_OUT_EN);
    510 
    511 	switch (connector_type) {
    512 	case DRM_MODE_CONNECTOR_HDMIA:
    513 		val |= HDMI_OUT_EN;
    514 		break;
    515 	case DRM_MODE_CONNECTOR_eDP:
    516 		val |= EDP_OUT_EN;
    517 		break;
    518 	}
    519 	WR4(sc, VOP_SYS_CTRL, val);
    520 
    521 	val = RD4(sc, VOP_DSP_CTRL0);
    522 	val &= ~DSP_OUT_MODE;
    523 	val |= __SHIFTIN(sc->sc_conf->out_mode, DSP_OUT_MODE);
    524 	WR4(sc, VOP_DSP_CTRL0, val);
    525 
    526 	val = __SHIFTIN(hsync_len + hback_porch, DSP_HACT_ST_POST) |
    527 	      __SHIFTIN(hsync_len + hback_porch + hactive, DSP_HACT_END_POST);
    528 	WR4(sc, VOP_POST_DSP_HACT_INFO, val);
    529 
    530 	val = __SHIFTIN(hsync_len + hback_porch, DSP_HACT_ST) |
    531 	      __SHIFTIN(hsync_len + hback_porch + hactive, DSP_HACT_END);
    532 	WR4(sc, VOP_DSP_HACT_ST_END, val);
    533 
    534 	val = __SHIFTIN(hsync_len, DSP_HTOTAL) |
    535 	      __SHIFTIN(hsync_len + hback_porch + hactive + hfront_porch, DSP_HS_END);
    536 	WR4(sc, VOP_DSP_HTOTAL_HS_END, val);
    537 
    538 	val = __SHIFTIN(vsync_len + vback_porch, DSP_VACT_ST_POST) |
    539 	      __SHIFTIN(vsync_len + vback_porch + vactive, DSP_VACT_END_POST);
    540 	WR4(sc, VOP_POST_DSP_VACT_INFO, val);
    541 
    542 	val = __SHIFTIN(vsync_len + vback_porch, DSP_VACT_ST) |
    543 	      __SHIFTIN(vsync_len + vback_porch + vactive, DSP_VACT_END);
    544 	WR4(sc, VOP_DSP_VACT_ST_END, val);
    545 
    546 	val = __SHIFTIN(vsync_len, DSP_VTOTAL) |
    547 	      __SHIFTIN(vsync_len + vback_porch + vactive + vfront_porch, DSP_VS_END);
    548 	WR4(sc, VOP_DSP_VTOTAL_VS_END, val);
    549 
    550 	drm_crtc_vblank_on(crtc);
    551 }
    552 
    553 static void
    554 rk_vop_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *state)
    555 {
    556 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
    557 	struct rk_vop_softc * const sc = mixer_crtc->sc;
    558 	uint32_t val;
    559 
    560 	drm_crtc_vblank_off(crtc);
    561 
    562 	val = RD4(sc, VOP_SYS_CTRL);
    563 	val |= VOP_STANDBY_EN;
    564 	WR4(sc, VOP_SYS_CTRL, val);
    565 
    566 	if (crtc->state->event && !crtc->state->active) {
    567 		spin_lock(&crtc->dev->event_lock);
    568 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
    569 		spin_unlock(&crtc->dev->event_lock);
    570 
    571 		crtc->state->event = NULL;
    572 	}
    573 }
    574 
    575 static void
    576 rk_vop_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state)
    577 {
    578 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
    579 	struct rk_vop_softc * const sc = mixer_crtc->sc;
    580 	int ret;
    581 
    582 	/* Commit settings */
    583 	WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
    584 
    585 	/*
    586 	 * If caller wants a vblank event, tell the vblank interrupt
    587 	 * handler to send it on the next interrupt.
    588 	 */
    589 	spin_lock(&crtc->dev->event_lock);
    590 	if (crtc->state->event) {
    591 		if ((ret = drm_crtc_vblank_get_locked(crtc)) != 0)
    592 			aprint_error_dev(sc->sc_dev,
    593 			    "drm_crtc_vblank_get: %d\n", ret);
    594 		if (sc->sc_event) /* XXX leaky; KASSERT? */
    595 			aprint_error_dev(sc->sc_dev, "unfinished vblank\n");
    596 		sc->sc_event = crtc->state->event;
    597 		crtc->state->event = NULL;
    598 	}
    599 	spin_unlock(&crtc->dev->event_lock);
    600 }
    601 
    602 static const struct drm_crtc_helper_funcs rk_vop_crtc_helper_funcs = {
    603 	.dpms = rk_vop_crtc_dpms,
    604 	.atomic_check = rk_vop_crtc_atomic_check,
    605 	.atomic_enable = rk_vop_crtc_atomic_enable,
    606 	.atomic_disable = rk_vop_crtc_atomic_disable,
    607 	.atomic_flush = rk_vop_crtc_atomic_flush,
    608 };
    609 
    610 static int
    611 rk_vop_crtc_enable_vblank(struct drm_crtc *crtc)
    612 {
    613 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
    614 	struct rk_vop_softc * const sc = mixer_crtc->sc;
    615 
    616 	mutex_spin_enter(&sc->sc_intr_lock);
    617 	WR4_MASK(sc, VOP_INTR_CLEAR0, VOP_INTR0_FS_NEW, VOP_INTR0_FS_NEW);
    618 	WR4_MASK(sc, VOP_INTR_EN0, VOP_INTR0_FS_NEW, VOP_INTR0_FS_NEW);
    619 	mutex_spin_exit(&sc->sc_intr_lock);
    620 
    621 	return 0;
    622 }
    623 
    624 static void
    625 rk_vop_crtc_disable_vblank(struct drm_crtc *crtc)
    626 {
    627 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
    628 	struct rk_vop_softc * const sc = mixer_crtc->sc;
    629 
    630 	mutex_spin_enter(&sc->sc_intr_lock);
    631 	WR4_MASK(sc, VOP_INTR_EN0, VOP_INTR0_FS_NEW, 0);
    632 	mutex_spin_exit(&sc->sc_intr_lock);
    633 }
    634 
    635 static const struct drm_crtc_funcs rk_vop_crtc_funcs = {
    636 	.set_config = drm_atomic_helper_set_config,
    637 	.destroy = drm_crtc_cleanup,
    638 	.page_flip = drm_atomic_helper_page_flip,
    639 	.reset = drm_atomic_helper_crtc_reset,
    640 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
    641 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
    642 	.enable_vblank = rk_vop_crtc_enable_vblank,
    643 	.disable_vblank = rk_vop_crtc_disable_vblank,
    644 };
    645 
    646 static int
    647 rk_vop_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
    648 {
    649 	struct rk_vop_softc * const sc = device_private(dev);
    650 	struct drm_device *ddev;
    651 	int error;
    652 
    653 	if (!activate)
    654 		return EINVAL;
    655 
    656 	ddev = rk_drm_port_device(&sc->sc_ports);
    657 	if (ddev == NULL) {
    658 		DRM_ERROR("couldn't find DRM device\n");
    659 		return ENXIO;
    660 	}
    661 
    662 	if (sc->sc_plane.sc == NULL) {
    663 		sc->sc_plane.sc = sc;
    664 
    665 		error = drm_universal_plane_init(ddev, &sc->sc_plane.base, 0x3,
    666 		    &rk_vop_plane_funcs,
    667 		    rk_vop_layer_formats, __arraycount(rk_vop_layer_formats),
    668 		    rk_vop_layer_modifiers,
    669 		    DRM_PLANE_TYPE_PRIMARY,
    670 		    NULL);
    671 		if (error) {
    672 			DRM_ERROR("couldn't initialize plane: %d\n", error);
    673 			return ENXIO;
    674 		}
    675 		drm_plane_helper_add(&sc->sc_plane.base, &rk_vop_plane_helper_funcs);
    676 	}
    677 
    678 	if (sc->sc_crtc.sc == NULL) {
    679 		sc->sc_crtc.sc = sc;
    680 
    681 		drm_crtc_init_with_planes(ddev, &sc->sc_crtc.base,
    682 		    &sc->sc_plane.base, NULL, &rk_vop_crtc_funcs, NULL);
    683 		drm_crtc_helper_add(&sc->sc_crtc.base, &rk_vop_crtc_helper_funcs);
    684 
    685 		aprint_debug_dev(dev, "using CRTC %d for %s\n",
    686 		    drm_crtc_index(&sc->sc_crtc.base), sc->sc_conf->descr);
    687 	}
    688 
    689 	const u_int ep_index = fdt_endpoint_index(ep);
    690 	if (ep_index >= VOP_NEP) {
    691 		DRM_ERROR("endpoint index %d out of range\n", ep_index);
    692 		return ENXIO;
    693 	}
    694 
    695 	return fdt_endpoint_activate(ep, activate);
    696 }
    697 
    698 static void *
    699 rk_vop_ep_get_data(device_t dev, struct fdt_endpoint *ep)
    700 {
    701 	struct rk_vop_softc * const sc = device_private(dev);
    702 
    703 	return &sc->sc_crtc.base;
    704 }
    705 
    706 static int
    707 rk_vop_intr(void *cookie)
    708 {
    709 	struct rk_vop_softc * const sc = cookie;
    710 	struct drm_crtc *crtc = &sc->sc_crtc.base;
    711 	struct drm_device *ddev;
    712 	uint32_t intr;
    713 	int ours = 0;
    714 
    715 	mutex_spin_enter(&sc->sc_intr_lock);
    716 	intr = RD4(sc, VOP_INTR_STATUS0);
    717 	WR4_MASK(sc, VOP_INTR_CLEAR0, intr, intr);
    718 	mutex_spin_exit(&sc->sc_intr_lock);
    719 
    720 	ddev = rk_drm_port_device(&sc->sc_ports);
    721 	KASSERT(ddev);
    722 
    723 	if (intr & VOP_INTR0_FS_NEW) {
    724 		intr &= ~VOP_INTR0_FS_NEW;
    725 		ours = 1;
    726 
    727 		/* XXX defer to softint? */
    728 		drm_crtc_handle_vblank(&sc->sc_crtc.base);
    729 		spin_lock(&ddev->event_lock);
    730 		if (sc->sc_event) {
    731 			drm_crtc_send_vblank_event(crtc, sc->sc_event);
    732 			sc->sc_event = NULL;
    733 			drm_crtc_vblank_put(crtc);
    734 		}
    735 		spin_unlock(&ddev->event_lock);
    736 	}
    737 
    738 	if (intr) {
    739 		aprint_error_dev(sc->sc_dev, "unhandled interrupts: 0x%04x\n",
    740 		    intr);
    741 	}
    742 
    743 	return ours;
    744 }
    745 
    746 static int
    747 rk_vop_match(device_t parent, cfdata_t cf, void *aux)
    748 {
    749 	struct fdt_attach_args * const faa = aux;
    750 
    751 	return of_compatible_match(faa->faa_phandle, compat_data);
    752 }
    753 
    754 static void
    755 rk_vop_attach(device_t parent, device_t self, void *aux)
    756 {
    757 	struct rk_vop_softc * const sc = device_private(self);
    758 	struct fdt_attach_args * const faa = aux;
    759 	const int phandle = faa->faa_phandle;
    760 	char intrstr[128];
    761 	const char * const reset_names[] = { "axi", "ahb", "dclk" };
    762 	const char * const clock_names[] = { "aclk_vop", "hclk_vop" };
    763 	struct fdtbus_reset *rst;
    764 	bus_addr_t addr;
    765 	bus_size_t size;
    766 	u_int n;
    767 
    768 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    769 		aprint_error(": couldn't get registers\n");
    770 		return;
    771 	}
    772 
    773 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
    774 		aprint_error(": failed to decode interrupt\n");
    775 		return;
    776 	}
    777 
    778 	fdtbus_clock_assign(phandle);
    779 
    780 	/* assert all the reset signals for 20us */
    781 	for (n = 0; n < __arraycount(reset_names); n++) {
    782 		rst = fdtbus_reset_get(phandle, reset_names[n]);
    783 		if (rst == NULL || fdtbus_reset_assert(rst) != 0) {
    784 			aprint_error(": couldn't assert reset %s\n",
    785 			    reset_names[n]);
    786 			return;
    787 		}
    788 	}
    789 	DELAY(10);
    790 	for (n = 0; n < __arraycount(reset_names); n++) {
    791 		rst = fdtbus_reset_get(phandle, reset_names[n]);
    792 		if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
    793 			aprint_error(": couldn't de-assert reset %s\n",
    794 			    reset_names[n]);
    795 			return;
    796 		}
    797 	}
    798 
    799 	for (n = 0; n < __arraycount(clock_names); n++) {
    800 		if (fdtbus_clock_enable(phandle, clock_names[n], true) != 0) {
    801 			aprint_error(": couldn't enable clock %s\n", clock_names[n]);
    802 			return;
    803 		}
    804 	}
    805 	sc->sc_dclk = fdtbus_clock_get(phandle, "dclk_vop");
    806 	if (sc->sc_dclk == NULL || clk_enable(sc->sc_dclk) != 0) {
    807 		aprint_error(": couldn't enable clock %s\n", "dclk_vop");
    808 		return;
    809 	}
    810 
    811 	sc->sc_dev = self;
    812 	sc->sc_bst = faa->faa_bst;
    813 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    814 		aprint_error(": couldn't map registers\n");
    815 		return;
    816 	}
    817 	sc->sc_phandle = faa->faa_phandle;
    818 	sc->sc_conf = of_compatible_lookup(phandle, compat_data)->data;
    819 
    820 	aprint_naive("\n");
    821 	aprint_normal(": %s\n", sc->sc_conf->descr);
    822 
    823 	if (sc->sc_conf->init != NULL)
    824 		sc->sc_conf->init(sc);
    825 
    826 	sc->sc_ports.dp_ep_activate = rk_vop_ep_activate;
    827 	sc->sc_ports.dp_ep_get_data = rk_vop_ep_get_data;
    828 	fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_CRTC);
    829 
    830 	const int port_phandle = of_find_firstchild_byname(phandle, "port");
    831 	if (port_phandle > 0)
    832 		rk_drm_register_port(port_phandle, &sc->sc_ports);
    833 
    834 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
    835 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
    836 	    FDT_INTR_MPSAFE, &rk_vop_intr, sc, device_xname(self));
    837 	if (sc->sc_ih == NULL) {
    838 		aprint_error_dev(self, "failed to establish interrupt on %s\n",
    839 		    intrstr);
    840 		return;
    841 	}
    842 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
    843 }
    844 
    845 CFATTACH_DECL_NEW(rk_vop, sizeof(struct rk_vop_softc),
    846 	rk_vop_match, rk_vop_attach, NULL, NULL);
    847