diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index b4262ad66b18..02a65259d8fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -68,6 +68,86 @@ nouveau_display_vblank_disable(struct drm_device *dev, int head) nouveau_event_put(disp->vblank[head]); } +static inline int +calc(int blanks, int blanke, int total, int line) +{ + if (blanke >= blanks) { + if (line >= blanks) + line -= total; + } else { + if (line >= blanks) + line -= total; + line -= blanke + 1; + } + return line; +} + +int +nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime) +{ + const u32 mthd = NV04_DISP_SCANOUTPOS + nouveau_crtc(crtc)->index; + struct nouveau_display *disp = nouveau_display(crtc->dev); + struct nv04_display_scanoutpos args; + int ret, retry = 1; + + do { + ret = nv_exec(disp->core, mthd, &args, sizeof(args)); + if (ret != 0) + return 0; + + if (args.vline) { + ret |= DRM_SCANOUTPOS_ACCURATE; + ret |= DRM_SCANOUTPOS_VALID; + break; + } + + if (retry) ndelay(crtc->linedur_ns); + } while (retry--); + + *hpos = calc(args.hblanks, args.hblanke, args.htotal, args.hline); + *vpos = calc(args.vblanks, args.vblanke, args.vtotal, args.vline); + if (stime) *stime = ns_to_ktime(args.time[0]); + if (etime) *etime = ns_to_ktime(args.time[1]); + + if (*vpos < 0) + ret |= DRM_SCANOUTPOS_INVBL; + return ret; +} + +int +nouveau_display_scanoutpos(struct drm_device *dev, int head, unsigned int flags, + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(crtc)->index == head) { + return nouveau_display_scanoutpos_head(crtc, vpos, hpos, + stime, etime); + } + } + + return 0; +} + +int +nouveau_display_vblstamp(struct drm_device *dev, int head, int *max_error, + struct timeval *time, unsigned flags) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(crtc)->index == head) { + return drm_calc_vbltimestamp_from_scanoutpos(dev, + head, max_error, time, flags, crtc, + &crtc->hwmode); + } + } + + return -EINVAL; +} + static void nouveau_display_vblank_fini(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 73aa231130b8..a71cf77e55b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -64,6 +64,10 @@ void nouveau_display_repin(struct drm_device *dev); void nouveau_display_resume(struct drm_device *dev); int nouveau_display_vblank_enable(struct drm_device *, int); void nouveau_display_vblank_disable(struct drm_device *, int); +int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int, + int *, int *, ktime_t *, ktime_t *); +int nouveau_display_vblstamp(struct drm_device *, int, int *, + struct timeval *, unsigned); int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 98a22e6e27a1..eecc6ca377c8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -798,6 +798,8 @@ driver = { .get_vblank_counter = drm_vblank_count, .enable_vblank = nouveau_display_vblank_enable, .disable_vblank = nouveau_display_vblank_disable, + .get_scanout_position = nouveau_display_scanoutpos, + .get_vblank_timestamp = nouveau_display_vblstamp, .ioctls = nouveau_ioctls, .num_ioctls = ARRAY_SIZE(nouveau_ioctls),