Module Name:    src
Committed By:   riastradh
Date:           Mon Dec 20 00:27:17 UTC 2021

Modified Files:
        src/sys/arch/arm/rockchip: rk_drm.c rk_vop.c

Log Message:
rkdrm: Implement vblank.


To generate a diff of this commit:
cvs rdiff -u -r1.17 -r1.18 src/sys/arch/arm/rockchip/rk_drm.c
cvs rdiff -u -r1.15 -r1.16 src/sys/arch/arm/rockchip/rk_vop.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/rockchip/rk_drm.c
diff -u src/sys/arch/arm/rockchip/rk_drm.c:1.17 src/sys/arch/arm/rockchip/rk_drm.c:1.18
--- src/sys/arch/arm/rockchip/rk_drm.c:1.17	Sun Dec 19 12:45:04 2021
+++ src/sys/arch/arm/rockchip/rk_drm.c	Mon Dec 20 00:27:17 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_drm.c,v 1.17 2021/12/19 12:45:04 riastradh Exp $ */
+/* $NetBSD: rk_drm.c,v 1.18 2021/12/20 00:27:17 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rk_drm.c,v 1.17 2021/12/19 12:45:04 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_drm.c,v 1.18 2021/12/20 00:27:17 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -77,10 +77,6 @@ static void	rk_drm_attach(device_t, devi
 static void	rk_drm_init(device_t);
 static vmem_t	*rk_drm_alloc_cma_pool(struct drm_device *, size_t);
 
-static uint32_t	rk_drm_get_vblank_counter(struct drm_device *, unsigned int);
-static int	rk_drm_enable_vblank(struct drm_device *, unsigned int);
-static void	rk_drm_disable_vblank(struct drm_device *, unsigned int);
-
 static int	rk_drm_load(struct drm_device *, unsigned long);
 static void	rk_drm_unload(struct drm_device *);
 
@@ -99,10 +95,6 @@ static struct drm_driver rk_drm_driver =
 	.dumb_create = drm_gem_cma_dumb_create,
 	.dumb_destroy = drm_gem_dumb_destroy,
 
-	.get_vblank_counter = rk_drm_get_vblank_counter,
-	.enable_vblank = rk_drm_enable_vblank,
-	.disable_vblank = rk_drm_disable_vblank,
-
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
 	.date = DRIVER_DATE,
@@ -440,7 +432,7 @@ rk_drm_load(struct drm_device *ddev, uns
 
 	drm_fb_helper_initial_config(&fbdev->helper, 32);
 
-	/* XXX */
+	/* XXX Delegate this to rk_vop.c?  */
 	ddev->irq_enabled = true;
 	drm_vblank_init(ddev, num_crtc);
 
@@ -454,50 +446,6 @@ drmerr:
 	return error;
 }
 
-static uint32_t
-rk_drm_get_vblank_counter(struct drm_device *ddev, unsigned int crtc)
-{
-	struct rk_drm_softc * const sc = rk_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
-rk_drm_enable_vblank(struct drm_device *ddev, unsigned int crtc)
-{
-	struct rk_drm_softc * const sc = rk_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
-rk_drm_disable_vblank(struct drm_device *ddev, unsigned int crtc)
-{
-	struct rk_drm_softc * const sc = rk_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 void
 rk_drm_unload(struct drm_device *ddev)
 {

Index: src/sys/arch/arm/rockchip/rk_vop.c
diff -u src/sys/arch/arm/rockchip/rk_vop.c:1.15 src/sys/arch/arm/rockchip/rk_vop.c:1.16
--- src/sys/arch/arm/rockchip/rk_vop.c:1.15	Sun Dec 19 12:45:27 2021
+++ src/sys/arch/arm/rockchip/rk_vop.c	Mon Dec 20 00:27:17 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_vop.c,v 1.15 2021/12/19 12:45:27 riastradh Exp $ */
+/* $NetBSD: rk_vop.c,v 1.16 2021/12/20 00:27:17 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rk_vop.c,v 1.15 2021/12/19 12:45:27 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_vop.c,v 1.16 2021/12/20 00:27:17 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: rk_vop.c,v 1
 #include <drm/drm_drv.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/drm_vblank.h>
 
 #define	VOP_REG_CFG_DONE		0x0000
 #define	 REG_LOAD_EN			__BIT(0)
@@ -104,6 +105,26 @@ __KERNEL_RCSID(0, "$NetBSD: rk_vop.c,v 1
 #define	VOP_DSP_VACT_ST_END		0x0194
 #define	 DSP_VACT_ST			__BITS(28,16)
 #define	 DSP_VACT_END			__BITS(12,0)
+#define	VOP_INTR_EN0			0x0280
+#define	VOP_INTR_CLEAR0			0x0284
+#define	VOP_INTR_STATUS0		0x0288
+#define	VOP_INTR_RAW_STATUS0		0x028c
+#define	 VOP_INTR0_DMA_FINISH		__BIT(15)
+#define	 VOP_INTR0_MMU			__BIT(14)
+#define	 VOP_INTR0_DSP_HOLD_VALID	__BIT(13)
+#define	 VOP_INTR0_FS_FIELD		__BIT(12)
+#define	 VOP_INTR0_POST_BUF_EMPTY	__BIT(11)
+#define	 VOP_INTR0_HWC_EMPTY		__BIT(10)
+#define	 VOP_INTR0_WIN3_EMPTY		__BIT(9)
+#define	 VOP_INTR0_WIN2_EMPTY		__BIT(8)
+#define	 VOP_INTR0_WIN1_EMPTY		__BIT(7)
+#define	 VOP_INTR0_WIN0_EMPTY		__BIT(6)
+#define	 VOP_INTR0_BUS_ERROR		__BIT(5)
+#define	 VOP_INTR0_LINE_FLAG1		__BIT(4)
+#define	 VOP_INTR0_LINE_FLAG0		__BIT(3)
+#define	 VOP_INTR0_ADDR_SAME		__BIT(2)
+#define	 VOP_INTR0_FS_NEW		__BIT(1)
+#define	 VOP_INTR0_FS			__BIT(0)
 
 /*
  * Polarity fields are in different locations depending on SoC and output type,
@@ -150,6 +171,11 @@ struct rk_vop_softc {
 	struct fdt_device_ports	sc_ports;
 
 	const struct rk_vop_config *sc_conf;
+
+	/* vblank */
+	void			*sc_ih;
+	kmutex_t		sc_intr_lock;
+	struct drm_pending_vblank_event *sc_event;
 };
 
 #define	to_rk_vop_crtc(x)	container_of(x, struct rk_vop_crtc, base)
@@ -160,6 +186,14 @@ struct rk_vop_softc {
 #define	WR4(sc, reg, val)			\
 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
 
+static void
+WR4_MASK(struct rk_vop_softc *sc, bus_size_t reg, uint16_t mask, uint16_t val)
+{
+
+	KASSERT(val == (mask & val));
+	WR4(sc, reg, ((uint32_t)mask << 16) | val);
+}
+
 struct rk_vop_config {
 	const char		*descr;
 	u_int			out_mode;
@@ -364,7 +398,7 @@ static const struct drm_plane_helper_fun
 };
 
 static bool
-rk_vop_format_mod_supported(struct drm_plane *plane, uint32_t format,
+rk_vop_plane_format_mod_supported(struct drm_plane *plane, uint32_t format,
     uint64_t modifier)
 {
 	return modifier == DRM_FORMAT_MOD_LINEAR;
@@ -377,11 +411,11 @@ static const struct drm_plane_funcs rk_v
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
-	.format_mod_supported = rk_vop_format_mod_supported,
+	.format_mod_supported = rk_vop_plane_format_mod_supported,
 };
 
 static void
-rk_vop_dpms(struct drm_crtc *crtc, int mode)
+rk_vop_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
 	struct rk_vop_softc * const sc = mixer_crtc->sc;
@@ -407,7 +441,7 @@ rk_vop_dpms(struct drm_crtc *crtc, int m
 }
 
 static int
-rk_vop_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
+rk_vop_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
 {
 	bool enabled = state->plane_mask & drm_plane_mask(crtc->primary);
 
@@ -418,7 +452,7 @@ rk_vop_atomic_check(struct drm_crtc *crt
 }
 
 static void
-rk_vop_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *state)
+rk_vop_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *state)
 {
 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
 	struct rk_vop_softc * const sc = mixer_crtc->sc;
@@ -512,25 +546,92 @@ rk_vop_atomic_enable(struct drm_crtc *cr
 	val = __SHIFTIN(vsync_len, DSP_VTOTAL) |
 	      __SHIFTIN(vsync_len + vback_porch + vactive + vfront_porch, DSP_VS_END);
 	WR4(sc, VOP_DSP_VTOTAL_VS_END, val);
+
+	drm_crtc_vblank_on(crtc);
 }
 
 static void
-rk_vop_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state)
+rk_vop_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *state)
 {
 	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
 	struct rk_vop_softc * const sc = mixer_crtc->sc;
+	uint32_t val;
+
+	drm_crtc_vblank_off(crtc);
+
+	val = RD4(sc, VOP_SYS_CTRL);
+	val |= VOP_STANDBY_EN;
+	WR4(sc, VOP_SYS_CTRL, val);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static void
+rk_vop_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state)
+{
+	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
+	struct rk_vop_softc * const sc = mixer_crtc->sc;
+	int ret;
 
 	/* Commit settings */
 	WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
+
+	/*
+	 * If caller wants a vblank event, tell the vblank interrupt
+	 * handler to send it on the next interrupt.
+	 */
+	spin_lock(&crtc->dev->event_lock);
+	if (crtc->state->event) {
+		if ((ret = drm_crtc_vblank_get_locked(crtc)) != 0)
+			aprint_error_dev(sc->sc_dev,
+			    "drm_crtc_vblank_get: %d\n", ret);
+		if (sc->sc_event) /* XXX leaky; KASSERT? */
+			aprint_error_dev(sc->sc_dev, "unfinished vblank\n");
+		sc->sc_event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
+	spin_unlock(&crtc->dev->event_lock);
 }
 
 static const struct drm_crtc_helper_funcs rk_vop_crtc_helper_funcs = {
-	.dpms = rk_vop_dpms,
-	.atomic_check = rk_vop_atomic_check,
-	.atomic_enable = rk_vop_atomic_enable,
-	.atomic_flush = rk_vop_atomic_flush,
+	.dpms = rk_vop_crtc_dpms,
+	.atomic_check = rk_vop_crtc_atomic_check,
+	.atomic_enable = rk_vop_crtc_atomic_enable,
+	.atomic_disable = rk_vop_crtc_atomic_disable,
+	.atomic_flush = rk_vop_crtc_atomic_flush,
 };
 
+static int
+rk_vop_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
+	struct rk_vop_softc * const sc = mixer_crtc->sc;
+
+	mutex_spin_enter(&sc->sc_intr_lock);
+	WR4_MASK(sc, VOP_INTR_CLEAR0, VOP_INTR0_FS_NEW, VOP_INTR0_FS_NEW);
+	WR4_MASK(sc, VOP_INTR_EN0, VOP_INTR0_FS_NEW, VOP_INTR0_FS_NEW);
+	mutex_spin_exit(&sc->sc_intr_lock);
+
+	return 0;
+}
+
+static void
+rk_vop_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc);
+	struct rk_vop_softc * const sc = mixer_crtc->sc;
+
+	mutex_spin_enter(&sc->sc_intr_lock);
+	WR4_MASK(sc, VOP_INTR_EN0, VOP_INTR0_FS_NEW, 0);
+	mutex_spin_exit(&sc->sc_intr_lock);
+}
+
 static const struct drm_crtc_funcs rk_vop_crtc_funcs = {
 	.set_config = drm_atomic_helper_set_config,
 	.destroy = drm_crtc_cleanup,
@@ -538,6 +639,8 @@ static const struct drm_crtc_funcs rk_vo
 	.reset = drm_atomic_helper_crtc_reset,
 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.enable_vblank = rk_vop_crtc_enable_vblank,
+	.disable_vblank = rk_vop_crtc_disable_vblank,
 };
 
 static int
@@ -601,6 +704,46 @@ rk_vop_ep_get_data(device_t dev, struct 
 }
 
 static int
+rk_vop_intr(void *cookie)
+{
+	struct rk_vop_softc * const sc = cookie;
+	struct drm_crtc *crtc = &sc->sc_crtc.base;
+	struct drm_device *ddev;
+	uint32_t intr;
+	int ours = 0;
+
+	mutex_spin_enter(&sc->sc_intr_lock);
+	intr = RD4(sc, VOP_INTR_STATUS0);
+	WR4_MASK(sc, VOP_INTR_CLEAR0, intr, intr);
+	mutex_spin_exit(&sc->sc_intr_lock);
+
+	ddev = rk_drm_port_device(&sc->sc_ports);
+	KASSERT(ddev);
+
+	if (intr & VOP_INTR0_FS_NEW) {
+		intr &= ~VOP_INTR0_FS_NEW;
+		ours = 1;
+
+		/* XXX defer to softint? */
+		drm_crtc_handle_vblank(&sc->sc_crtc.base);
+		spin_lock(&ddev->event_lock);
+		if (sc->sc_event) {
+			drm_crtc_send_vblank_event(crtc, sc->sc_event);
+			sc->sc_event = NULL;
+			drm_crtc_vblank_put(crtc);
+		}
+		spin_unlock(&ddev->event_lock);
+	}
+
+	if (intr) {
+		aprint_error_dev(sc->sc_dev, "unhandled interrupts: 0x%04x\n",
+		    intr);
+	}
+
+	return ours;
+}
+
+static int
 rk_vop_match(device_t parent, cfdata_t cf, void *aux)
 {
 	struct fdt_attach_args * const faa = aux;
@@ -614,6 +757,7 @@ rk_vop_attach(device_t parent, device_t 
 	struct rk_vop_softc * const sc = device_private(self);
 	struct fdt_attach_args * const faa = aux;
 	const int phandle = faa->faa_phandle;
+	char intrstr[128];
 	const char * const reset_names[] = { "axi", "ahb", "dclk" };
 	const char * const clock_names[] = { "aclk_vop", "hclk_vop" };
 	struct fdtbus_reset *rst;
@@ -626,6 +770,11 @@ rk_vop_attach(device_t parent, device_t 
 		return;
 	}
 
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error(": failed to decode interrupt\n");
+		return;
+	}
+
 	fdtbus_clock_assign(phandle);
 
 	/* assert all the reset signals for 20us */
@@ -681,6 +830,16 @@ rk_vop_attach(device_t parent, device_t 
 	const int port_phandle = of_find_firstchild_byname(phandle, "port");
 	if (port_phandle > 0)
 		rk_drm_register_port(port_phandle, &sc->sc_ports);
+
+	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
+	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
+	    FDT_INTR_MPSAFE, &rk_vop_intr, sc, device_xname(self));
+	if (sc->sc_ih == NULL) {
+		aprint_error_dev(self, "failed to establish interrupt on %s\n",
+		    intrstr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
 }
 
 CFATTACH_DECL_NEW(rk_vop, sizeof(struct rk_vop_softc),

Reply via email to