drm/nv50-nvd0: implement precise vblank timing support on nv50/nvc0.
Subsystems (3)
RFC PATCH 1/1 drm/nv50-nvd0: implement precise vblank timing support on nv50/nvc0.
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 <[email protected]>
Cc Mario Kleiner <[email protected]>
Signed-off-by Maarten Lankhorst <[email protected]>
---
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +54 -0
@@ -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 ;
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +3 -0
@@ -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
drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +3 -0
@@ -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 ;
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +3 -0
@@ -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 ;
drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +3 -0
@@ -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 ;
drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +3 -0
@@ -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 ;
drivers/gpu/drm/nouveau/core/include/engine/disp.h +3 -0
@@ -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 *
drivers/gpu/drm/nouveau/nouveau_display.c +2 -2
@@ -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)
drivers/gpu/drm/nouveau/nouveau_drm.c +59 -1
72 lines not on this page · raw message
drivers/gpu/drm/nouveau/nv50_display.c +16 -1
@@ -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 ;