From patchwork Thu Jun 23 06:17:50 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mario Kleiner X-Patchwork-Id: 9194585 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id F084E6077D for ; Thu, 23 Jun 2016 06:18:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E003F283DF for ; Thu, 23 Jun 2016 06:18:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D49812842A; Thu, 23 Jun 2016 06:18:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 11F42283DF for ; Thu, 23 Jun 2016 06:18:50 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 363DE6E8A1; Thu, 23 Jun 2016 06:18:49 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-wm0-x243.google.com (mail-wm0-x243.google.com [IPv6:2a00:1450:400c:c09::243]) by gabe.freedesktop.org (Postfix) with ESMTPS id E01426E8A3 for ; Thu, 23 Jun 2016 06:18:35 +0000 (UTC) Received: by mail-wm0-x243.google.com with SMTP id c82so7901964wme.3 for ; Wed, 22 Jun 2016 23:18:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=eVn9kusU3Nz1yRaxwEjtOv47trm0u1KhCU82CseL7vg=; b=gmXEylYppMgrYUys8fUBXNvkbEHCMpWw7UBwR/YDKCi2vEKlTRcJN3V05Q63THX6Gw XuDdzn22dAn6VkI9FEgM5CWZFH1gLTHRp/hwN6f9nBFhv/QgrhVgE91oRfrzV5rVNsYW qtA6ZoeKCf767ToqHS/vKkvfLloRsTH5l1vznyuKcHA+qs1Xd+QWQHYfsVwEDGhkWLNs FnzjHvLTnDI3aA5zpKbyw0ZTliqtBQSKh92yuhRgE02fOjRAusX58QFqsIhimPUVOhTH 4WHa/O50RG9gq9Hyw1Px8Cr2POX7IUGD9ajqPZCNAhe2vzJLx9QgtaIJqGkfn+78zV7u IJJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=eVn9kusU3Nz1yRaxwEjtOv47trm0u1KhCU82CseL7vg=; b=mI+J5TTfIxCQRuiwaQSvoqyfgY2Xj1Uk+5aXazf9NO2uCrTSn2jeCsKGMM3+enugBM 3DMIAQVfXKQghjbYOEyelbeiXemSdDA7Ds0z0AqHB7dEe9HmtMY2KTMZaYaG48Ra93sT 9Iy/Kl8ybboiU+huQbI6TeX6crltQkRR9q50yhspGGOveVjU11IA/dcu6gBmIvBmwpqu w0hTJ/GR4ZniiFruSHIJzxzBUDu+yYvfVZYNpFHeLxk59qO9VpUAT7Z5V6Duv18PUKFy 1TmfR+CbaIoDA9zsNBmHtzp4nPL0j49gZsVq/hSEKIqa297DdYwY26kdIVnXzqHarGxG 0PUg== X-Gm-Message-State: ALyK8tLn5vt3VNFqniRhuY7rM0vzHN2FKqSX4DvYgmABaB+jqVag8CrBpK5dZcL/hRz83A== X-Received: by 10.194.104.40 with SMTP id gb8mr30443182wjb.146.1466662714063; Wed, 22 Jun 2016 23:18:34 -0700 (PDT) Received: from twisty.fritz.box (x4d02df78.dyn.telefonica.de. [77.2.223.120]) by smtp.gmail.com with ESMTPSA id kd7sm2663907wjc.33.2016.06.22.23.18.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 22 Jun 2016 23:18:33 -0700 (PDT) From: Mario Kleiner To: dri-devel@lists.freedesktop.org Subject: [PATCH] drm/vc4: Implement precise vblank timestamping. Date: Thu, 23 Jun 2016 08:17:50 +0200 Message-Id: <1466662670-9884-2-git-send-email-mario.kleiner.de@gmail.com> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1466662670-9884-1-git-send-email-mario.kleiner.de@gmail.com> References: <1466662670-9884-1-git-send-email-mario.kleiner.de@gmail.com> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Precise vblank timestamping is implemented via the usual scanout position based method. On VC4 the pixelvalves PV do not have a scanout position register. Only the hardware video scaler HVS has a similar register which describes which scanline for the output is currently composited and stored in the HVS fifo for later consumption by the PV. This causes a problem in that the HVS runs at a much faster clock (system clock / audio gate) than the PV which runs at video mode dot clock, so the unless the fifo between HVS and PV is full, the HVS will progress faster in its observable read line position than video scan rate, so the HVS position reading can't be directly translated into a scanout position for timestamp correction. Additionally when the PV is in vblank, it doesn't consume from the fifo, so the fifo gets full very quickly and then the HVS stops compositing until the PV enters active scanout and starts consuming scanlines from the fifo again, making new space for the HVS to composite. Therefore a simple translation of HVS read position into elapsed time since (or to) start of active scanout does not work, but for the most interesting cases we can still get useful and sufficiently accurate results: 1. The PV enters active scanout of a new frame with the fifo of the HVS completely full, and the HVS can refill any fifo line which gets consumed and thereby freed up by the PV during active scanout very quickly. Therefore the PV and HVS work effectively in lock-step during active scanout with the fifo never having more than 1 scanline freed up by the PV before it gets refilled. The PV's real scanout position is therefore trailing the HVS compositing position as scanoutpos = hvspos - fifosize and we can get the true scanoutpos as HVS readpos minus fifo size, so precise timestamping works while in active scanout, except for the last few scanlines of the frame, when the HVS reaches end of frame, stops compositing and the PV catches up and drains the fifo. This special case would only introduce minor errors though. 2. If we are in vblank, then we can only guess something reasonable. If called from vblank irq, we assume the irq is usually dispatched with minimum delay, so we can take a timestamp taken at entry into the vblank irq handler as a baseline and then add a full vblank duration until the guessed start of active scanout. As irq dispatch is usually pretty low latency this works with relatively low jitter and good results. If we aren't called from vblank then we could be anywhere within the vblank interval, so we return a neutral result, simply the current system timestamp, and hope for the best. Measurement shows the generated timestamps to be rather precise, and at least never off more than 1 vblank duration worst-case. Limitations: Doesn't work well yet for interlaced video modes, therefore disabled in interlaced mode for now. Signed-off-by: Mario Kleiner --- drivers/gpu/drm/vc4/vc4_crtc.c | 143 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_drv.c | 2 + drivers/gpu/drm/vc4/vc4_drv.h | 7 ++ drivers/gpu/drm/vc4/vc4_regs.h | 4 ++ 4 files changed, 156 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index c82d468..c75166e 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -46,6 +46,9 @@ struct vc4_crtc { const struct vc4_crtc_data *data; void __iomem *regs; + /* Timestamp at start of vblank irq - unaffected by lock delays. */ + ktime_t t_vblank; + /* Which HVS channel we're using for our CRTC. */ int channel; @@ -146,6 +149,145 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused) } #endif +int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, + unsigned int flags, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + u32 val; + int fifo_lines; + int vblank_lines; + int ret = 0; + + /* + * XXX Doesn't work well in interlaced mode yet, partially due + * to problems in vc4 kms or drm core interlaced mode handling, + * so disable for now in interlaced mode. + */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return ret; + + /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ + + /* Get optional system timestamp before query. */ + if (stime) + *stime = ktime_get(); + + /* + * Read vertical scanline which is currently composed for our + * pixelvalve by the HVS, and also the scaler status. + */ + val = HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)); + + /* Get optional system timestamp after query. */ + if (etime) + *etime = ktime_get(); + + /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ + + /* Vertical position of hvs composed scanline. */ + *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE); + + /* No hpos info available. */ + if (hpos) + *hpos = 0; + + /* This is the offset we need for translating hvs -> pv scanout pos. */ + /* XXX Find proper formula from hw docs instead of guesstimating? */ + fifo_lines = 2048 * 7 / mode->crtc_hdisplay; + + if (fifo_lines > 0) + ret |= DRM_SCANOUTPOS_VALID; + + /* HVS more than fifo_lines into frame for compositing? */ + if (*vpos > fifo_lines) { + /* + * We are in active scanout and can get some meaningful results + * from HVS. The actual PV scanout can not trail behind more + * than fifo_lines as that is the fifo's capacity. Assume that + * in active scanout the HVS and PV work in lockstep wrt. HVS + * refilling the fifo and PV consuming from the fifo, ie. + * whenever the PV consumes and frees up a scanline in the + * fifo, the HVS will immediately refill it, therefore + * incrementing vpos. Therefore we choose HVS read position - + * fifo size in scanlines as a estimate of the real scanout + * position of the PV. + */ + *vpos -= fifo_lines + 1; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + *vpos /= 2; + + ret |= DRM_SCANOUTPOS_ACCURATE; + return ret; + } + + /* + * Less: This happens when we are in vblank and the HVS, after getting + * the VSTART restart signal from the PV, just started refilling its + * fifo with new lines from the top-most lines of the new framebuffers. + * The PV does not scan out in vblank, so does not remove lines from + * the fifo, so the fifo will be full quickly and the HVS has to pause. + * We can't get meaningful readings wrt. scanline position of the PV + * and need to make things up in a approximative but consistent way. + */ + ret |= DRM_SCANOUTPOS_IN_VBLANK; + vblank_lines = mode->crtc_vtotal - mode->crtc_vdisplay; + + if (flags & DRM_CALLED_FROM_VBLIRQ) { + /* + * Assume the irq handler got called close to first + * line of vblank, so PV has about a full vblank + * scanlines to go, and as a base timestamp use the + * one taken at entry into vblank irq handler, so it + * is not affected by random delays due to lock + * contention on event_lock or vblank_time lock in + * the core. + */ + *vpos = -vblank_lines; + + if (stime) + *stime = vc4_crtc->t_vblank; + if (etime) + *etime = vc4_crtc->t_vblank; + + /* + * If the HVS fifo is not yet full then we know for certain + * we are at the very beginning of vblank, as the hvs just + * started refilling, and the stime and etime timestamps + * truly correspond to start of vblank. + */ + if ((val & SCALER_DISPSTATX_FULL) != SCALER_DISPSTATX_FULL) + ret |= DRM_SCANOUTPOS_ACCURATE; + } else { + /* + * No clue where we are inside vblank. Return a vpos of zero, + * which will cause calling code to just return the etime + * timestamp uncorrected. At least this is no worse than the + * standard fallback. + */ + *vpos = 0; + } + + return ret; +} + +int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id, + int *max_error, struct timeval *vblank_time, + unsigned flags) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = &vc4_crtc->base; + struct drm_crtc_state *state = crtc->state; + + /* Helper routine in DRM core does all the work: */ + return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc_id, max_error, + vblank_time, flags, + &state->adjusted_mode); +} + static void vc4_crtc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); @@ -519,6 +661,7 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) irqreturn_t ret = IRQ_NONE; if (stat & PV_INT_VFP_START) { + vc4_crtc->t_vblank = ktime_get(); CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); drm_crtc_handle_vblank(&vc4_crtc->base); vc4_crtc_handle_page_flip(vc4_crtc); diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 15a4f6c..4a09bee 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -92,6 +92,8 @@ static struct drm_driver vc4_drm_driver = { .enable_vblank = vc4_enable_vblank, .disable_vblank = vc4_disable_vblank, .get_vblank_counter = drm_vblank_no_hw_counter, + .get_scanout_position = vc4_crtc_get_scanoutpos, + .get_vblank_timestamp = vc4_crtc_get_vblank_timestamp, #if defined(CONFIG_DEBUG_FS) .debugfs_init = vc4_debugfs_init, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index c799baa..8e3b84e 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -415,6 +415,13 @@ extern struct platform_driver vc4_crtc_driver; int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id); void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id); int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg); +int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, + unsigned int flags, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); +int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id, + int *max_error, struct timeval *vblank_time, + unsigned flags); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index f99eece..63cdc28 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -375,6 +375,10 @@ # define SCALER_DISPSTATX_MODE_EOF 3 # define SCALER_DISPSTATX_FULL BIT(29) # define SCALER_DISPSTATX_EMPTY BIT(28) +# define SCALER_DISPSTATX_FRAME_COUNT_MASK VC4_MASK(17, 12) +# define SCALER_DISPSTATX_FRAME_COUNT_SHIFT 12 +# define SCALER_DISPSTATX_LINE_MASK VC4_MASK(11, 0) +# define SCALER_DISPSTATX_LINE_SHIFT 0 #define SCALER_DISPCTRL1 0x00000050 #define SCALER_DISPBKGND1 0x00000054 #define SCALER_DISPBKGNDX(x) (SCALER_DISPBKGND0 + \