Lucas Stach
2012-Apr-25 22:26 UTC
[Nouveau] [PATCH 1/2] drm/nouveau: Use drm_vblank_count_and_time() for pageflip completion events.
From: Mario Kleiner <mario.kleiner at tuebingen.mpg.de> Emit kms pageflip completion events with proper vblank count and timestamp for the vblank interval in which the pageflip completed. This makes the timestamps and counts consistent with what the OML_sync_control spec defines. v2 Lucas Stach: rebased on top of nouveau tree and resolved trivial conflict. Signed-off-by: Mario Kleiner <mario.kleiner at tuebingen.mpg.de> Conflicts: drivers/gpu/drm/nouveau/nouveau_display.c --- drivers/gpu/drm/nouveau/nouveau_display.c | 29 +++++++++++++++++++++++------ drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 71379f8..2c0f415 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -504,7 +504,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, *s = (struct nouveau_page_flip_state) { { }, event, nouveau_crtc(crtc)->index, fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y, - new_bo->bo.offset }; + new_bo->bo.offset, crtc->framedur_ns }; /* Choose the channel the flip will be handled in */ chan = nouveau_fence_channel(new_bo->bo.sync_obj); @@ -550,6 +550,9 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, struct drm_device *dev = chan->dev; struct nouveau_page_flip_state *s; unsigned long flags; + struct timeval tnow, tvbl; + + do_gettimeofday(&tnow); spin_lock_irqsave(&dev->event_lock, flags); @@ -563,12 +566,26 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, struct nouveau_page_flip_state, head); if (s->event) { struct drm_pending_vblank_event *e = s->event; - struct timeval now; - do_gettimeofday(&now); - e->event.sequence = 0; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; + e->event.sequence = drm_vblank_count_and_time(dev, s->crtc, &tvbl); + + /* Called before vblank count and timestamp have + * been updated for the vblank interval of flip + * completion? If so, need to increment vblank count and + * add one videorefresh duration to returned timestamp + * to account for this. We assume this happened if we + * get called over 0.9 frame durations after the last + * timestamped vblank. + */ + if (10 * (timeval_to_ns(&tnow) - timeval_to_ns(&tvbl)) > + 9 * s->framedur_ns) { + e->event.sequence++; + tvbl = ns_to_timeval(timeval_to_ns(&tvbl) + + s->framedur_ns); + } + + e->event.tv_sec = tvbl.tv_sec; + e->event.tv_usec = tvbl.tv_usec; list_add_tail(&e->base.link, &e->base.file_priv->event_list); wake_up_interruptible(&e->base.file_priv->event_wait); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index ab9f9e0..ba10ad7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -203,6 +203,7 @@ struct nouveau_page_flip_state { struct drm_pending_vblank_event *event; int crtc, bpp, pitch, x, y; uint64_t offset; + s64 framedur_ns; }; enum nouveau_channel_mutex_class { -- 1.7.10
Lucas Stach
2012-Apr-25 22:26 UTC
[Nouveau] [PATCH 2/2] drm/nouveau: implement precise vblank timestamping
This patch implements the driver hooks needed for precise vblank timestamping. This is a complementary patch to Mario Kleiner's patches to improve swap scheduling. With the complete patchset applied nouveau will be able to provide correct and precise pageflip timestamps (compliant to OML_sync_control spec) v2: - Rebase on top of nouveau tree and update to reflect Ben's review feedback. v3: - Split nv04+ and nv50+ paths into separate functions. - Do not advertise precise vblank timestamping on nvd9+, as it's not confirmed to work and the nv50 codepath may not work due to moved regs. Kudos to Mario for his many helpful comments and testing. Signed-off-by: Lucas Stach <dev at lynxeye.de> Reviewed-by: Mario Kleiner <mario.kleiner at tuebingen.mpg.de> Tested-by: Mario Kleiner <mario.kleiner at tuebingen.mpg.de> --- drivers/gpu/drm/nouveau/nouveau_display.c | 25 ++++++++++ drivers/gpu/drm/nouveau/nouveau_reg.h | 9 +++- drivers/gpu/drm/nouveau/nv04_display.c | 55 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_crtc.c | 19 ++++++++ drivers/gpu/drm/nouveau/nv50_display.c | 71 +++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nvreg.h | 1 + 6 files changed, 179 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 2c0f415..810ba72 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -258,6 +258,27 @@ nouveau_display_fini(struct drm_device *dev) } int +nouveau_get_vblank_timestamp(struct drm_device *dev, int crtc, + int *max_error, struct timeval *vblank_time, + unsigned flags) +{ + struct drm_crtc *drmcrtc; + + if (crtc < 0 || crtc >= dev->num_crtcs) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + list_for_each_entry(drmcrtc, &dev->mode_config.crtc_list, head) { + if(nouveau_crtc(drmcrtc)->index == crtc) + break; + } + + return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, + vblank_time, flags, drmcrtc); +} + +int nouveau_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -327,6 +348,10 @@ nouveau_display_create(struct drm_device *dev) if (ret) goto disp_create_err; + if (dev->driver->get_scanout_position) + dev->driver->get_vblank_timestamp + nouveau_get_vblank_timestamp; + if (dev->mode_config.num_crtc) { ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index 43a96b9..0ec1945 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -762,7 +762,7 @@ #define NV50_PDISPLAY_CRTC_CLOCK 0x00610ad0 #define NV50_PDISPLAY_CRTC_COLOR_CTRL 0x00610ae0 #define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END 0x00610ae8 -#define NV50_PDISPLAY_CRTC_MODE_UNK1 0x00610af0 +#define NV50_PDISPLAY_CRTC_VBL_START 0x00610af0 #define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL 0x00610af8 #define NV50_PDISPLAY_CRTC_SYNC_DURATION 0x00610b00 #define NV50_PDISPLAY_CRTC_MODE_UNK2 0x00610b08 @@ -800,6 +800,13 @@ #define NV50_PDISPLAY_SOR_CLK 0x00614000 #define NV50_PDISPLAY_SOR_CLK_CTRL2(i) ((i) * 0x800 + 0x614300) +#define NV50_PDISPLAY_CRTC_STAT_VERT(i0) (0x00616340 + 0x800*(i0)) +#define NV50_PDISPLAY_CRTC_STAT_VERT_VLINE__MASK 0x0000ffff +#define NV50_PDISPLAY_CRTC_STAT_VERT_VBLANK_COUNT__MASK 0xffff0000 +#define NV50_PDISPLAY_CRTC_STAT_VERT_VBLANK_COUNT__SHIFT 16 +#define NV50_PDISPLAY_CRTC_STAT_HORZ(i0) (0x00616344 + 0x800*(i0)) +#define NV50_PDISPLAY_CRTC_STAT_HORZ_HLINE__MASK 0x0000ffff + #define NV50_PDISPLAY_VGACRTC(r) ((r) + 0x619400) #define NV50_PDISPLAY_DAC 0x0061a000 diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 7047d37..2622953 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -26,6 +26,7 @@ #include "drm.h" #include "drm_crtc_helper.h" +#include "nouveau_crtc.h" #include "nouveau_drv.h" #include "nouveau_fb.h" #include "nouveau_hw.h" @@ -35,6 +36,58 @@ static void nv04_vblank_crtc0_isr(struct drm_device *); static void nv04_vblank_crtc1_isr(struct drm_device *); +int +nv04_display_scanoutpos_get(struct drm_device *dev, int crtc, + int *vpos, int *hpos) +{ + int vline, ret = 0; + u32 vbl_start, vbl_end; + struct drm_crtc *drmcrtc; + + if (crtc < 0 || crtc >= dev->num_crtcs) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + list_for_each_entry(drmcrtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(drmcrtc)->index == crtc) + break; + } + + /* get vsync area from PRAMDAC */ + vbl_start = NVReadRAMDAC(dev, crtc, NV_PRAMDAC_FP_VDISPLAY_END) + & 0xffff; + vbl_end = (NVReadRAMDAC(dev, crtc, NV_PRAMDAC_FP_VTOTAL) + & 0xffff) + 1; + + /* get current scanout position from PCRTC */ + vline = nv_rd32(dev, NV_PCRTC_STAT(crtc)) & 0xffff; + + /* + * vline == 0 could be invalid: + * Some gpu's get stuck on that value inside vblank. Try again + * after one scanline duration, if it still reads 0 give up. + */ + if (vline == 0) { + ndelay(drmcrtc->linedur_ns & 0xffff); + vline = nv_rd32(dev, NV_PCRTC_STAT(crtc)) & 0xffff; + } + + if (vline > 0) + ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; + + /* are we in vblank? if yes: do neg countdown */ + if ((vline >= vbl_start) && (vline < vbl_end)) { + ret |= DRM_SCANOUTPOS_INVBL; + vline -= vbl_end; + } + + *vpos = vline; + *hpos = 0; /* don't use hline as it's unreliable */ + + return ret; +} + static void nv04_display_store_initial_head_owner(struct drm_device *dev) { @@ -179,6 +232,8 @@ nv04_display_create(struct drm_device *dev) func->save(encoder); } + dev->driver->get_scanout_position = nv04_display_scanoutpos_get; + nouveau_irq_register(dev, 24, nv04_vblank_crtc0_isr); nouveau_irq_register(dev, 25, nv04_vblank_crtc1_isr); return 0; diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index e021b1c..0652675 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -530,6 +530,25 @@ static bool nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + /* crtc_xxx fields are needed by drm core. Init them with the + * settings we actually use for mode programming. */ + adjusted_mode->synth_clock = adjusted_mode->clock; + adjusted_mode->crtc_hdisplay = adjusted_mode->hdisplay; + adjusted_mode->crtc_hblank_start = 0; + adjusted_mode->crtc_hblank_end = 0; + adjusted_mode->crtc_hsync_start = adjusted_mode->hsync_start; + adjusted_mode->crtc_hsync_end = adjusted_mode->hsync_end; + adjusted_mode->crtc_htotal = adjusted_mode->htotal; + adjusted_mode->crtc_hskew = adjusted_mode->hskew; + adjusted_mode->crtc_vdisplay = adjusted_mode->vdisplay; + adjusted_mode->crtc_vblank_start = 0; + adjusted_mode->crtc_vblank_end = 0; + adjusted_mode->crtc_vsync_start = adjusted_mode->vsync_start; + adjusted_mode->crtc_vsync_end = adjusted_mode->vsync_end; + adjusted_mode->crtc_vtotal = adjusted_mode->vtotal; + adjusted_mode->crtc_hadjusted = 0; + adjusted_mode->crtc_vadjusted = 0; + return true; } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index b526e3f..86042f9 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -37,6 +37,75 @@ static void nv50_display_isr(struct drm_device *); static void nv50_display_bh(unsigned long); +int +nv50_display_scanoutpos_get(struct drm_device *dev, int crtc, + int *vpos, int *hpos) +{ + int vline, hline, ret = 0; + u32 vbias, hbias, reg, vbl_start, vbl_end; + struct drm_crtc *drmcrtc; + + if (crtc < 0 || crtc >= dev->num_crtcs) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + list_for_each_entry(drmcrtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(drmcrtc)->index == crtc) + break; + } + + /* get vsync and hsync area */ + reg = nv_rd32(dev, NV50_PDISPLAY_CRTC_C(crtc, + SYNC_START_TO_BLANK_END)); + vbias = (reg >> 16) & 0xffff; + hbias = reg & 0xffff; + + /* get vertical display size including bias as vbl_start + * and vtotal as vbl_end */ + vbl_start = (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(crtc, + VBL_START)) >> 16) & 0xffff; + vbl_end = (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(crtc, + DISPLAY_TOTAL)) >> 16) & 0xffff; + + /* get current scanout position from PDISPLAY */ + vline = nv_rd32(dev, NV50_PDISPLAY_CRTC_STAT_VERT(crtc)) + & NV50_PDISPLAY_CRTC_STAT_VERT_VLINE__MASK; + + /* + * vline == 0 could be invalid: + * Some gpu's get stuck on that value inside vblank. Try again + * after one scanline duration, if it still reads 0 give up. + */ + if (vline == 0) { + ndelay(drmcrtc->linedur_ns & 0xffff); + vline = nv_rd32(dev, NV50_PDISPLAY_CRTC_STAT_VERT(crtc)) + & NV50_PDISPLAY_CRTC_STAT_VERT_VLINE__MASK; + } + + hline = nv_rd32(dev, NV50_PDISPLAY_CRTC_STAT_HORZ(crtc)) + & NV50_PDISPLAY_CRTC_STAT_HORZ_HLINE__MASK; + + if ((vline > 0) && (vline < vbl_end)) + ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; + + if ((vline >= vbl_start) || (vline < vbias)) { + /* we are in vblank so do a neg countdown */ + ret |= DRM_SCANOUTPOS_INVBL; + vline -= (vline < vbias) ? vbias : (vbl_end + vbias); + hline -= hbias; + } else { + /* apply corrective offset */ + vline -= vbias; + hline -= hbias; + } + + *vpos = vline; + *hpos = hline; + + return ret; +} + static inline int nv50_sor_nr(struct drm_device *dev) { @@ -411,6 +480,8 @@ nv50_display_create(struct drm_device *dev) return ret; } + dev->driver->get_scanout_position = nv50_display_scanoutpos_get; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h index bbfb1a6..e8281c4 100644 --- a/drivers/gpu/drm/nouveau/nvreg.h +++ b/drivers/gpu/drm/nouveau/nvreg.h @@ -172,6 +172,7 @@ #define NV_PCRTC_834 0x00600834 #define NV_PCRTC_850 0x00600850 #define NV_PCRTC_ENGINE_CTRL 0x00600860 +#define NV_PCRTC_STAT(i0) (0x00600868 + 0x2000*(i0)) # define NV_CRTC_FSEL_I2C (1 << 4) # define NV_CRTC_FSEL_OVERLAY (1 << 12) -- 1.7.10
Maarten Maathuis
2012-Apr-26 21:25 UTC
[Nouveau] [PATCH 2/2] drm/nouveau: implement precise vblank timestamping
It seems a bit strange to go in between a register and defines that probably belong to that register. On Thu, Apr 26, 2012 at 12:26 AM, Lucas Stach <dev at lynxeye.de> wrote:> ?#define NV_PCRTC_ENGINE_CTRL ? ? ? ? ? ? ? ? ? ? ? ? ? 0x00600860 > +#define NV_PCRTC_STAT(i0) ? ? ? ? ? ? ? ? ? ? ?(0x00600868 + 0x2000*(i0)) > ?# ? ? ?define NV_CRTC_FSEL_I2C ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (1 << 4) > ?# ? ? ?define NV_CRTC_FSEL_OVERLAY ? ? ? ? ? ? ? ? ? ? ? ? ? ? (1 << 12)-- Far away from the primal instinct, the song seems to fade away, the river get wider between your thoughts and the things we do and say.
Maybe Matching Threads
- [Patches][nouveau/kms]: Precise Vblank and pageflip timestamping
- [Patches][nouveau/kms]: Precise Vblank and pageflip timestamping v2
- [PATCH] nouveau: implement precise vblank timestamping
- [RFC PATCH] drm/nv50-nvd0: implement precise vblank timing support on nv50/nvc0.
- [Bug 92852] New: NV34: WARNING: ... at drivers/gpu/drm/drm_irq.c:924 drm_vblank_count_and_time+0x71/0x80 [drm]()