Not as thoroughly tested as I would like. Newer nvd0 and kepler are unsupported, as I don't know the registers yet. Information of the scanout position is based on Lucas Stach's original patch, with a teak to read vline twice, to prevent a race of hline with vline. Cc: Lucas Stach <dev@xxxxxxxxxx> Cc: Mario Kleiner <mario.kleiner@xxxxxxxxxxxxxxxx> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@xxxxxxxxxxxxx> --- diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index c168ae3..96268b7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -1285,6 +1285,57 @@ nv50_disp_intr(struct nouveau_subdev *subdev) } } +u32 nv50_disp_get_vblank_count(struct nouveau_disp *disp, int head) +{ + if (head < 0 || head >= 2) + return 0; + + return nv_rd32(disp, 0x616340 + head * 0x800) >> 16; +} + +int nv50_disp_get_scanoutpos(struct nouveau_disp *disp, int head, int *vpos, int *hpos) +{ + u32 reg, vbias, hbias, vbl_start, vbl_end, hline, vline; + + if (head < 0 || head >= 2) + return -1; + + reg = nv_rd32(disp, 0x610ae8 + head * 4); + vbias = reg >> 16; + hbias = reg & 0xffff; + + vbl_start = nv_rd32(disp, 0x610af0 + head * 4) >> 16; + vbl_end = nv_rd32(disp, 0x610af8 + head * 4) >> 16; + + reg = nv_rd32(disp, 0x616340 + head * 0x800) & 0xffff; + while (1) { + hline = nv_rd32(disp, 0x616344 + head * 0x800) & 0xffff; + + vline = nv_rd32(disp, 0x616340 + head * 0x800) & 0xffff; + if (vline == reg) + break; + + reg = vline; + } + + if((vline >= vbl_start) || (vline < vbias)) { + /* we are in vblank so do a neg countdown */ + vline -= vbias; + hline -= hbias; + + if (vline > 0) + vline -= vbl_end; + } else { + /* apply corrective offset */ + vline -= vbias; + hline -= hbias; + } + + *vpos = vline; + *hpos = hline; + return 0; +} + static int nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -1302,6 +1353,9 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv50_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + priv->base.max_vblank_count = 0xffff; + priv->base.get_vblank_count = nv50_disp_get_vblank_count; + priv->base.get_scanoutpos = nv50_disp_get_scanoutpos; INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nv50_disp_sclass; priv->head.nr = 2; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index 1ae6ceb..e3900ce 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -144,4 +144,7 @@ extern struct nouveau_oclass nvd0_disp_cclass; void nvd0_disp_intr_supervisor(struct work_struct *); void nvd0_disp_intr(struct nouveau_subdev *); +u32 nv50_disp_get_vblank_count(struct nouveau_disp *disp, int head); +int nv50_disp_get_scanoutpos(struct nouveau_disp *disp, int head, int *vpos, int *hpos); + #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index d8c74c0..df357cf 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -75,6 +75,9 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv84_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + priv->base.max_vblank_count = 0xffff; + priv->base.get_vblank_count = nv50_disp_get_vblank_count; + priv->base.get_scanoutpos = nv50_disp_get_scanoutpos; INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nv84_disp_sclass; priv->head.nr = 2; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index a66f949..38eafdf 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -75,6 +75,9 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv94_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + priv->base.max_vblank_count = 0xffff; + priv->base.get_vblank_count = nv50_disp_get_vblank_count; + priv->base.get_scanoutpos = nv50_disp_get_scanoutpos; INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nv94_disp_sclass; priv->head.nr = 2; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c index 6cf8eef..4f02b8d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c @@ -62,6 +62,9 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nva0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + priv->base.max_vblank_count = 0xffff; + priv->base.get_vblank_count = nv50_disp_get_vblank_count; + priv->base.get_scanoutpos = nv50_disp_get_scanoutpos; INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nva0_disp_sclass; priv->head.nr = 2; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index b754131..186f22e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -77,6 +77,9 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); + priv->base.max_vblank_count = 0xffff; + priv->base.get_vblank_count = nv50_disp_get_vblank_count; + priv->base.get_scanoutpos = nv50_disp_get_scanoutpos; priv->sclass = nva3_disp_sclass; priv->head.nr = 2; priv->dac.nr = 3; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h index 4b21fab..8ba03db 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h @@ -9,6 +9,9 @@ struct nouveau_disp { struct nouveau_engine base; struct nouveau_event *vblank; + u32 (*get_vblank_count)(struct nouveau_disp *disp, int head); + int (*get_scanoutpos)(struct nouveau_disp *disp, int head, int *vpos, int *hpos); + u32 max_vblank_count; }; static inline struct nouveau_disp * diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 2573604..edd8d07 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -579,7 +579,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Emit a page flip */ if (nv_device(drm->device)->card_type >= NV_50) { - ret = nv50_display_flip_next(crtc, fb, chan, 0); + ret = nv50_display_flip_next(crtc, fb, chan, 1); if (ret) goto fail_unreserve; } @@ -634,7 +634,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head); if (s->event) - drm_send_vblank_event(dev, -1, s->event); + drm_send_vblank_event(dev, s->crtc, s->event); list_del(&s->head); if (ps) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index bd301f4..9a73aa2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -104,6 +104,62 @@ nouveau_drm_vblank_disable(struct drm_device *dev, int head) nouveau_event_disable_locked(pdisp->vblank, head, 1); } +static u32 +nouveau_drm_vblank_count(struct drm_device *dev, int head) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + + if (!pdisp->get_vblank_count) + return drm_vblank_count(dev, head); + return pdisp->get_vblank_count(pdisp, head); +} + +static int +nouveau_drm_get_scanoutpos(struct drm_device *dev, int head, int *vpos, int *hpos) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + int ret = DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; + + if (pdisp->get_scanoutpos(pdisp, head, vpos, hpos)) + return 0; + + if (*vpos < 0) + ret |= DRM_SCANOUTPOS_INVBL; + return ret; +} + +static int +nouveau_drm_get_vblank_timestamp(struct drm_device *dev, int crtc, + int *max_error, + struct timeval *vblank_time, + unsigned flags) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + struct drm_crtc *drmcrtc; + + if (!pdisp->get_scanoutpos) + return -EOPNOTSUPP; + + list_for_each_entry(drmcrtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(drmcrtc); + + if (nv_crtc->index != crtc) + continue; + + /* Helper routine in DRM core does all the work: */ + return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, + max_error, + vblank_time, + flags, + drmcrtc); + } + return -EINVAL; +} + + static u64 nouveau_name(struct pci_dev *pdev) { @@ -701,7 +757,9 @@ driver = { .debugfs_cleanup = nouveau_debugfs_takedown, #endif - .get_vblank_counter = drm_vblank_count, + .get_vblank_counter = nouveau_drm_vblank_count, + .get_scanout_position = nouveau_drm_get_scanoutpos, + .get_vblank_timestamp = nouveau_drm_get_vblank_timestamp, .enable_vblank = nouveau_drm_vblank_enable, .disable_vblank = nouveau_drm_vblank_disable, diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 738d7a2..df8e24a 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -41,6 +41,7 @@ #include <core/class.h> #include <engine/disp.h> +#include <engine/disp.h> #include <subdev/timer.h> #include <subdev/bar.h> #include <subdev/fb.h> @@ -1030,12 +1031,14 @@ nv50_crtc_commit(struct drm_crtc *crtc) nv50_crtc_cursor_show_hide(nv_crtc, nv_crtc->cursor.visible, true); nv50_display_flip_next(crtc, crtc->fb, NULL, 1); + drm_vblank_post_modeset(crtc->dev, nv_crtc->index); } static bool nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + drm_mode_set_crtcinfo(adjusted_mode, 0); return true; } @@ -1091,9 +1094,12 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, vactive = (vactive * 2) + 1; } + drm_vblank_pre_modeset(crtc->dev, nv_crtc->index); ret = nv50_crtc_swap_fbs(crtc, old_fb); - if (ret) + if (ret) { + drm_vblank_post_modeset(crtc->dev, nv_crtc->index); return ret; + } push = evo_wait(mast, 64); if (push) { @@ -2229,9 +2235,18 @@ nv50_display_create(struct drm_device *dev) struct dcb_table *dcb = &drm->vbios.dcb; struct drm_connector *connector, *tmp; struct nv50_disp *disp; + struct nouveau_disp *dev_disp; struct dcb_output *dcbe; int crtcs, ret, i; + dev_disp = nouveau_disp(device); + if (!dev_disp) { + NV_ERROR(drm, "Cannot enable display engine without display support\n"); + return -ENODEV; + } + if (dev_disp->max_vblank_count) + dev->max_vblank_count = dev_disp->max_vblank_count; + disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) return -ENOMEM; _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel