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),