All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related
@ 2017-05-24 14:51 Daniel Vetter
  2017-05-24 14:51 ` [PATCH 01/37] drm/doc: move printf helpers out of drmP.h Daniel Vetter
                   ` (40 more replies)
  0 siblings, 41 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development

Hi all,

So the first part of this patch series is the usual deal of decrufting some part
of drm and attempting to improve the docs. Mostly around vblank. I seem to be
the ever constant 5% of finally being able to document drm_driver/device
properly, oh well.

By patch count the bigger part is trying to get rid of drm_vblank_cleanup. That
was initially exported to drivers for a radeon ums irq failover hack, and then
seems to have been cargo-culted all over the place. It's a bit of churn, but it
will get us one step closer to tracking vblanks as part of drm_crtc. The other
bit would be to get rid of drm_vblank_init, and instead auto-setup the relevant
vblank stuff in drm_crtc_init (iff the driver supports vblanks ofc).

I'd also like to cleanup the vblank locking a bit, I think switching to per-pipe
locks from the current nested scheme will be both easier to understand and more
concurrent than what we have right now.

Anyway, stuff was getting out of hand so want to flush this out.

Review, comments and testing very much welcome.

Thanks, Daniel

Daniel Vetter (37):
  drm/doc: move printf helpers out of drmP.h
  drm: Remove drm_device->virtdev
  drm/udl: Remove dummy busid callback
  drm: Remove drm_driver->set_busid hook
  drm/pci: Deprecate drm_pci_init/exit completely
  drm/doc: Improve ioctl/fops docs a bit more
  drm: Extract drm_vblank.[hc]
  drm/doc: Polish irq helper documentation
  drm/doc: Drop empty include for drm_color_mgmt.h
  drm/doc: vblank cleanup
  drm/vblank: _ioctl posfix for ioctl handler
  drm/vblank: Consistent drm_crtc_ prefix
  drm: better document how to send out the crtc disable event
  drm/amd|radeon: Drop drm_vblank_cleanup
  drm/arcgpu: Drop drm_vblank_cleanup
  drm/hdlcd|mali: Drop drm_vblank_cleanup
  drm/atmel: Drop drm_vblank_cleanup
  drm/exynos: Drop drm_vblank_cleanup
  drm/fsl: Drop drm_vblank_cleanup
  drm/hibmc: Drop drm_vblank_cleanup
  drm/kirin: Drop drm_vblank_cleanup
  drm/i915: Drop drm_vblank_cleanup
  drm/imx: Drop drm_vblank_cleanup
  drm/mtk: Drop drm_vblank_cleanup
  drm/meson: Drop drm_vblank_cleanup
  drm/mxsfb: Drop drm_vblank_cleanup
  drm/nouveau: Drop drm_vblank_cleanup
  drm/rockchip: Drop drm_vblank_cleanup
  drm/shmob: Drop drm_vblank_cleanup
  drm/sti: Drop drm_vblank_cleanup
  drm/stm: Drop drm_vblank_cleanup
  drm/sun4i: Drop drm_vblank_cleanup
  drm/tegra: Drop drm_vblank_cleanup
  drm/udl: Drop drm_vblank_cleanup
  drm/vmwgfx: Drop drm_vblank_cleanup
  drm/zte: Drop drm_vblank_cleanup
  drm/vblank: Unexport drm_vblank_cleanup

 Documentation/gpu/drm-internals.rst             |   64 +-
 Documentation/gpu/drm-kms.rst                   |   63 +-
 Documentation/gpu/drm-uapi.rst                  |    2 +
 drivers/gpu/drm/Makefile                        |    2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c         |    5 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c         |    1 -
 drivers/gpu/drm/arc/arcpgu_drv.c                |    1 -
 drivers/gpu/drm/arm/hdlcd_drv.c                 |    2 -
 drivers/gpu/drm/arm/malidp_drv.c                |    2 -
 drivers/gpu/drm/ast/ast_drv.c                   |    5 +-
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c    |    1 -
 drivers/gpu/drm/bochs/bochs_drv.c               |    5 +-
 drivers/gpu/drm/cirrus/cirrus_drv.c             |    5 +-
 drivers/gpu/drm/drm_file.c                      |    7 +-
 drivers/gpu/drm/drm_internal.h                  |   17 +-
 drivers/gpu/drm/drm_ioctl.c                     |   13 +-
 drivers/gpu/drm/drm_irq.c                       | 1643 +----------------------
 drivers/gpu/drm/drm_pci.c                       |   40 +-
 drivers/gpu/drm/drm_vblank.c                    | 1611 ++++++++++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_drv.c         |    4 +-
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c       |    2 -
 drivers/gpu/drm/gma500/psb_drv.c                |    5 +-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |    3 +-
 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c |    1 -
 drivers/gpu/drm/i810/i810_drv.c                 |    5 +-
 drivers/gpu/drm/i915/i915_drv.c                 |    7 +-
 drivers/gpu/drm/i915/i915_irq.c                 |    2 +-
 drivers/gpu/drm/i915/intel_display.c            |    2 +-
 drivers/gpu/drm/imx/imx-drm-core.c              |    4 +-
 drivers/gpu/drm/mediatek/mtk_drm_drv.c          |    1 -
 drivers/gpu/drm/meson/meson_drv.c               |    1 -
 drivers/gpu/drm/mga/mga_drv.c                   |    5 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c           |    6 +-
 drivers/gpu/drm/mxsfb/mxsfb_drv.c               |    1 -
 drivers/gpu/drm/nouveau/nouveau_display.c       |    2 -
 drivers/gpu/drm/nouveau/nouveau_drm.c           |   12 +-
 drivers/gpu/drm/nouveau/nv50_display.c          |    2 +-
 drivers/gpu/drm/qxl/qxl_drv.c                   |    6 +-
 drivers/gpu/drm/r128/r128_drv.c                 |    5 +-
 drivers/gpu/drm/radeon/radeon_drv.c             |    6 +-
 drivers/gpu/drm/radeon/radeon_irq_kms.c         |    1 -
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c     |    2 -
 drivers/gpu/drm/savage/savage_drv.c             |    5 +-
 drivers/gpu/drm/shmobile/shmob_drm_drv.c        |    4 +-
 drivers/gpu/drm/sis/sis_drv.c                   |    5 +-
 drivers/gpu/drm/sti/sti_drv.c                   |    1 -
 drivers/gpu/drm/stm/ltdc.c                      |    2 -
 drivers/gpu/drm/sun4i/sun4i_drv.c               |    2 -
 drivers/gpu/drm/tdfx/tdfx_drv.c                 |    5 +-
 drivers/gpu/drm/tegra/drm.c                     |    5 +-
 drivers/gpu/drm/udl/udl_drv.c                   |    6 -
 drivers/gpu/drm/udl/udl_main.c                  |    2 -
 drivers/gpu/drm/via/via_drv.c                   |    5 +-
 drivers/gpu/drm/virtio/virtgpu_drm_bus.c        |    1 -
 drivers/gpu/drm/virtio/virtgpu_kms.c            |    4 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.c             |    5 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c             |    9 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h             |    2 -
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c             |    4 -
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c            |    9 -
 drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c            |   27 +-
 drivers/gpu/drm/zte/zx_drm_drv.c                |    2 -
 include/drm/drmP.h                              |   70 +-
 include/drm/drm_crtc.h                          |    7 +-
 include/drm/drm_drv.h                           |   53 +-
 include/drm/drm_file.h                          |    1 +
 include/drm/drm_irq.h                           |  158 +--
 include/drm/drm_modes.h                         |   17 +
 include/drm/drm_pci.h                           |   11 +-
 include/drm/drm_prime.h                         |    2 +
 include/drm/drm_rect.h                          |   27 +
 include/drm/drm_vblank.h                        |  180 +++
 72 files changed, 2093 insertions(+), 2112 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_vblank.c
 create mode 100644 include/drm/drm_vblank.h

-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 01/37] drm/doc: move printf helpers out of drmP.h
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-30  7:33   ` Neil Armstrong
  2017-05-24 14:51 ` [PATCH 02/37] drm: Remove drm_device->virtdev Daniel Vetter
                   ` (39 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

And document them lightly. Unfortunately kernel-doc isn't the most
awesome for documenting #defines that don't look like functions, it
makes functions out of them :-/

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 include/drm/drmP.h      | 17 -----------------
 include/drm/drm_modes.h | 17 +++++++++++++++++
 include/drm/drm_rect.h  | 27 +++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 17 deletions(-)

diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index b9b5566acfe6..575b29b47811 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -292,23 +292,6 @@ struct pci_controller;
 /* Format strings and argument splitters to simplify printing
  * various "complex" objects
  */
-#define DRM_MODE_FMT    "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x"
-#define DRM_MODE_ARG(m) \
-	(m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \
-	(m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \
-	(m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \
-	(m)->type, (m)->flags
-
-#define DRM_RECT_FMT    "%dx%d%+d%+d"
-#define DRM_RECT_ARG(r) drm_rect_width(r), drm_rect_height(r), (r)->x1, (r)->y1
-
-/* for rect's in fixed-point format: */
-#define DRM_RECT_FP_FMT "%d.%06ux%d.%06u%+d.%06u%+d.%06u"
-#define DRM_RECT_FP_ARG(r) \
-		drm_rect_width(r) >> 16, ((drm_rect_width(r) & 0xffff) * 15625) >> 10, \
-		drm_rect_height(r) >> 16, ((drm_rect_height(r) & 0xffff) * 15625) >> 10, \
-		(r)->x1 >> 16, (((r)->x1 & 0xffff) * 15625) >> 10, \
-		(r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10
 
 /*@}*/
 
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index 6dd34280e892..94ac771fe460 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -197,6 +197,8 @@ enum drm_mode_status {
  * there's the hardware timings, which are corrected for interlacing,
  * double-clocking and similar things. They are provided as a convenience, and
  * can be appropriately computed using drm_mode_set_crtcinfo().
+ *
+ * For printing you can use %DRM_MODE_FMT and DRM_MODE_ARG().
  */
 struct drm_display_mode {
 	/**
@@ -407,6 +409,21 @@ struct drm_display_mode {
 	enum hdmi_picture_aspect picture_aspect_ratio;
 };
 
+/**
+ * DRM_MODE_FMT - printf string for &struct drm_display_mode
+ */
+#define DRM_MODE_FMT    "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x"
+
+/**
+ * DRM_MODE_ARG - printf arguments for &struct drm_display_mode
+ * @m: display mode
+ */
+#define DRM_MODE_ARG(m) \
+	(m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \
+	(m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \
+	(m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \
+	(m)->type, (m)->flags
+
 #define obj_to_mode(x) container_of(x, struct drm_display_mode, base)
 
 /**
diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h
index 83bb156d4356..44bc122b9ee0 100644
--- a/include/drm/drm_rect.h
+++ b/include/drm/drm_rect.h
@@ -43,6 +43,33 @@ struct drm_rect {
 };
 
 /**
+ * DRM_RECT_FMT - printf string for &struct drm_rect
+ */
+#define DRM_RECT_FMT    "%dx%d%+d%+d"
+/**
+ * DRM_RECT_ARG - printf arguments for &struct drm_rect
+ * @r: rectangle struct
+ */
+#define DRM_RECT_ARG(r) drm_rect_width(r), drm_rect_height(r), (r)->x1, (r)->y1
+
+/**
+ * DRM_RECT_FP_FMT - printf string for &struct drm_rect in 16.16 fixed point
+ */
+#define DRM_RECT_FP_FMT "%d.%06ux%d.%06u%+d.%06u%+d.%06u"
+/**
+ * DRM_RECT_FP_ARG - printf arguments for &struct drm_rect in 16.16 fixed point
+ * @r: rectangle struct
+ *
+ * This is useful for e.g. printing plane source rectangles, which are in 16.16
+ * fixed point.
+ */
+#define DRM_RECT_FP_ARG(r) \
+		drm_rect_width(r) >> 16, ((drm_rect_width(r) & 0xffff) * 15625) >> 10, \
+		drm_rect_height(r) >> 16, ((drm_rect_height(r) & 0xffff) * 15625) >> 10, \
+		(r)->x1 >> 16, (((r)->x1 & 0xffff) * 15625) >> 10, \
+		(r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10
+
+/**
  * drm_rect_adjust_size - adjust the size of the rectangle
  * @r: rectangle to be adjusted
  * @dw: horizontal adjustment
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 02/37] drm: Remove drm_device->virtdev
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
  2017-05-24 14:51 ` [PATCH 01/37] drm/doc: move printf helpers out of drmP.h Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` Daniel Vetter
                   ` (38 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: David Airlie, Daniel Vetter, Intel Graphics Development,
	virtualization, Daniel Vetter

This is a leftover from the drm_bus days, where we've had a
bus-specific device type for every bus type in drm_device. Except for
pci (which we can't remove because dri1 drivers) this is all gone. And
the virt driver also doesn't really need it, dev_to_virtio works
perfectly fine.

Cc: David Airlie <airlied@linux.ie>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 1 -
 drivers/gpu/drm/virtio/virtgpu_kms.c     | 4 ++--
 include/drm/drmP.h                       | 2 --
 3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
index 43e1d5916c6c..7df8d0c9026a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
@@ -56,7 +56,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
 	dev = drm_dev_alloc(driver, &vdev->dev);
 	if (IS_ERR(dev))
 		return PTR_ERR(dev);
-	dev->virtdev = vdev;
 	vdev->priv = dev;
 
 	if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 1e1c90b30d4a..6400506a06b0 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -138,7 +138,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 	u32 num_scanouts, num_capsets;
 	int ret;
 
-	if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
+	if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1))
 		return -ENODEV;
 
 	vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
@@ -147,7 +147,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 
 	vgdev->ddev = dev;
 	dev->dev_private = vgdev;
-	vgdev->vdev = dev->virtdev;
+	vgdev->vdev = dev_to_virtio(dev->dev);
 	vgdev->dev = dev->dev;
 
 	spin_lock_init(&vgdev->display_info_lock);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 575b29b47811..c363f2fdff31 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -412,8 +412,6 @@ struct drm_device {
 	struct pci_controller *hose;
 #endif
 
-	struct virtio_device *virtdev;
-
 	struct drm_sg_mem *sg;	/**< Scatter gather memory */
 	unsigned int num_crtcs;                  /**< Number of CRTCs on this device */
 
-- 
2.11.0

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 02/37] drm: Remove drm_device->virtdev
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
  2017-05-24 14:51 ` [PATCH 01/37] drm/doc: move printf helpers out of drmP.h Daniel Vetter
  2017-05-24 14:51 ` [PATCH 02/37] drm: Remove drm_device->virtdev Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-29  6:52   ` Gerd Hoffmann
                     ` (3 more replies)
  2017-05-24 14:51 ` [PATCH 03/37] drm/udl: Remove dummy busid callback Daniel Vetter
                   ` (37 subsequent siblings)
  40 siblings, 4 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: David Airlie, Daniel Vetter, Intel Graphics Development,
	virtualization, Gerd Hoffmann, Daniel Vetter

This is a leftover from the drm_bus days, where we've had a
bus-specific device type for every bus type in drm_device. Except for
pci (which we can't remove because dri1 drivers) this is all gone. And
the virt driver also doesn't really need it, dev_to_virtio works
perfectly fine.

Cc: David Airlie <airlied@linux.ie>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 1 -
 drivers/gpu/drm/virtio/virtgpu_kms.c     | 4 ++--
 include/drm/drmP.h                       | 2 --
 3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
index 43e1d5916c6c..7df8d0c9026a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
@@ -56,7 +56,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
 	dev = drm_dev_alloc(driver, &vdev->dev);
 	if (IS_ERR(dev))
 		return PTR_ERR(dev);
-	dev->virtdev = vdev;
 	vdev->priv = dev;
 
 	if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 1e1c90b30d4a..6400506a06b0 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -138,7 +138,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 	u32 num_scanouts, num_capsets;
 	int ret;
 
-	if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
+	if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1))
 		return -ENODEV;
 
 	vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
@@ -147,7 +147,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 
 	vgdev->ddev = dev;
 	dev->dev_private = vgdev;
-	vgdev->vdev = dev->virtdev;
+	vgdev->vdev = dev_to_virtio(dev->dev);
 	vgdev->dev = dev->dev;
 
 	spin_lock_init(&vgdev->display_info_lock);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 575b29b47811..c363f2fdff31 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -412,8 +412,6 @@ struct drm_device {
 	struct pci_controller *hose;
 #endif
 
-	struct virtio_device *virtdev;
-
 	struct drm_sg_mem *sg;	/**< Scatter gather memory */
 	unsigned int num_crtcs;                  /**< Number of CRTCs on this device */
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 03/37] drm/udl: Remove dummy busid callback
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (2 preceding siblings ...)
  2017-05-24 14:51 ` Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 04/37] drm: Remove drm_driver->set_busid hook Daniel Vetter
                   ` (36 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

Since

commit ca8e2ad71013049bc88a10b11d83712bfe56cdd4
Author: Thierry Reding <treding@nvidia.com>
Date:   Fri Apr 11 15:23:00 2014 +0200

    drm: Introduce drm_dev_set_unique()

the ->set_busid callback is optional. On top of that the udl one isn't
really fully compliant with the drm uabi, but since only modesetting
ever binds to it (there's no 3d accel on udl) it doesn't matter.

Still, can't harm to aling and use the default used by everyone else.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/udl/udl_drv.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index cd8b01727734..0f02e1acf0ba 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -11,11 +11,6 @@
 #include <drm/drm_crtc_helper.h>
 #include "udl_drv.h"
 
-static int udl_driver_set_busid(struct drm_device *d, struct drm_master *m)
-{
-	return 0;
-}
-
 static int udl_usb_suspend(struct usb_interface *interface,
 			   pm_message_t message)
 {
@@ -52,7 +47,6 @@ static struct drm_driver driver = {
 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
 	.load = udl_driver_load,
 	.unload = udl_driver_unload,
-	.set_busid = udl_driver_set_busid,
 
 	/* gem hooks */
 	.gem_free_object = udl_gem_free_object,
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 04/37] drm: Remove drm_driver->set_busid hook
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (3 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 03/37] drm/udl: Remove dummy busid callback Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 05/37] drm/pci: Deprecate drm_pci_init/exit completely Daniel Vetter
                   ` (35 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

The only special-case is pci devices, and we can easily handle this in
the core. Do so and drop a pile of boilerplate from drivers.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 -
 drivers/gpu/drm/ast/ast_drv.c           | 1 -
 drivers/gpu/drm/bochs/bochs_drv.c       | 1 -
 drivers/gpu/drm/cirrus/cirrus_drv.c     | 1 -
 drivers/gpu/drm/drm_internal.h          | 1 +
 drivers/gpu/drm/drm_ioctl.c             | 4 ++--
 drivers/gpu/drm/drm_pci.c               | 1 -
 drivers/gpu/drm/gma500/psb_drv.c        | 1 -
 drivers/gpu/drm/i810/i810_drv.c         | 1 -
 drivers/gpu/drm/i915/i915_drv.c         | 1 -
 drivers/gpu/drm/mga/mga_drv.c           | 1 -
 drivers/gpu/drm/mgag200/mgag200_drv.c   | 1 -
 drivers/gpu/drm/nouveau/nouveau_drm.c   | 1 -
 drivers/gpu/drm/qxl/qxl_drv.c           | 2 --
 drivers/gpu/drm/r128/r128_drv.c         | 1 -
 drivers/gpu/drm/radeon/radeon_drv.c     | 1 -
 drivers/gpu/drm/savage/savage_drv.c     | 1 -
 drivers/gpu/drm/sis/sis_drv.c           | 1 -
 drivers/gpu/drm/tdfx/tdfx_drv.c         | 1 -
 drivers/gpu/drm/via/via_drv.c           | 1 -
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.c     | 1 -
 include/drm/drm_drv.h                   | 2 --
 include/drm/drm_pci.h                   | 7 -------
 23 files changed, 3 insertions(+), 31 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 31eddd85eb40..e7a4bce6358d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -734,7 +734,6 @@ static struct drm_driver kms_driver = {
 	.open = amdgpu_driver_open_kms,
 	.postclose = amdgpu_driver_postclose_kms,
 	.lastclose = amdgpu_driver_lastclose_kms,
-	.set_busid = drm_pci_set_busid,
 	.unload = amdgpu_driver_unload_kms,
 	.get_vblank_counter = amdgpu_get_vblank_counter_kms,
 	.enable_vblank = amdgpu_enable_vblank_kms,
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index fd7c9eec92e4..f6794745a024 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -197,7 +197,6 @@ static struct drm_driver driver = {
 
 	.load = ast_driver_load,
 	.unload = ast_driver_unload,
-	.set_busid = drm_pci_set_busid,
 
 	.fops = &ast_fops,
 	.name = DRIVER_NAME,
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c
index aa342515ddf4..8fccd3cf000d 100644
--- a/drivers/gpu/drm/bochs/bochs_drv.c
+++ b/drivers/gpu/drm/bochs/bochs_drv.c
@@ -84,7 +84,6 @@ static struct drm_driver bochs_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET,
 	.load			= bochs_load,
 	.unload			= bochs_unload,
-	.set_busid		= drm_pci_set_busid,
 	.fops			= &bochs_fops,
 	.name			= "bochs-drm",
 	.desc			= "bochs dispi vga interface (qemu stdvga)",
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index d893ea21a359..c48b9eb76712 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -132,7 +132,6 @@ static struct drm_driver driver = {
 	.driver_features = DRIVER_MODESET | DRIVER_GEM,
 	.load = cirrus_driver_load,
 	.unload = cirrus_driver_unload,
-	.set_busid = drm_pci_set_busid,
 	.fops = &cirrus_driver_fops,
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 3d8e8f878924..bca2c66c5d28 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -32,6 +32,7 @@ void drm_lastclose(struct drm_device *dev);
 int drm_irq_by_busid(struct drm_device *dev, void *data,
 		     struct drm_file *file_priv);
 void drm_pci_agp_destroy(struct drm_device *dev);
+int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master);
 
 /* drm_prime.c */
 int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 505c0eda2551..a423bf60e230 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -143,8 +143,8 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
 	if (master->unique != NULL)
 		drm_unset_busid(dev, master);
 
-	if (dev->driver->set_busid) {
-		ret = dev->driver->set_busid(dev, master);
+	if (dev_is_pci(dev->dev)) {
+		ret = drm_pci_set_busid(dev, master);
 		if (ret) {
 			drm_unset_busid(dev, master);
 			return ret;
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 1eb4fc3eee20..ad31d95e77c9 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -149,7 +149,6 @@ int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
 	master->unique_len = strlen(master->unique);
 	return 0;
 }
-EXPORT_SYMBOL(drm_pci_set_busid);
 
 static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
 {
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 1f9b35afefee..37d4c36c80f2 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -480,7 +480,6 @@ static struct drm_driver driver = {
 	.load = psb_driver_load,
 	.unload = psb_driver_unload,
 	.lastclose = psb_driver_lastclose,
-	.set_busid = drm_pci_set_busid,
 
 	.num_ioctls = ARRAY_SIZE(psb_ioctls),
 	.irq_preinstall = psb_irq_preinstall,
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
index 37fd0906f807..e1c35c710e24 100644
--- a/drivers/gpu/drm/i810/i810_drv.c
+++ b/drivers/gpu/drm/i810/i810_drv.c
@@ -59,7 +59,6 @@ static struct drm_driver driver = {
 	.load = i810_driver_load,
 	.lastclose = i810_driver_lastclose,
 	.preclose = i810_driver_preclose,
-	.set_busid = drm_pci_set_busid,
 	.dma_quiescent = i810_driver_dma_quiescent,
 	.ioctls = i810_ioctls,
 	.fops = &i810_driver_fops,
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index d703897786e9..cd0bbec36853 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2632,7 +2632,6 @@ static struct drm_driver driver = {
 	.open = i915_driver_open,
 	.lastclose = i915_driver_lastclose,
 	.postclose = i915_driver_postclose,
-	.set_busid = drm_pci_set_busid,
 
 	.gem_close_object = i915_gem_close_object,
 	.gem_free_object_unlocked = i915_gem_free_object,
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
index 63ba0699d107..2a36ec611a44 100644
--- a/drivers/gpu/drm/mga/mga_drv.c
+++ b/drivers/gpu/drm/mga/mga_drv.c
@@ -62,7 +62,6 @@ static struct drm_driver driver = {
 	.load = mga_driver_load,
 	.unload = mga_driver_unload,
 	.lastclose = mga_driver_lastclose,
-	.set_busid = drm_pci_set_busid,
 	.dma_quiescent = mga_driver_dma_quiescent,
 	.get_vblank_counter = mga_get_vblank_counter,
 	.enable_vblank = mga_enable_vblank,
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 9ac007880328..53a5982a04c7 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -91,7 +91,6 @@ static struct drm_driver driver = {
 	.driver_features = DRIVER_GEM | DRIVER_MODESET,
 	.load = mgag200_driver_load,
 	.unload = mgag200_driver_unload,
-	.set_busid = drm_pci_set_busid,
 	.fops = &mgag200_driver_fops,
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 6844372366d3..c31f974c59bb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -1102,7 +1102,6 @@ static int __init
 nouveau_drm_init(void)
 {
 	driver_pci = driver_stub;
-	driver_pci.set_busid = drm_pci_set_busid;
 	driver_platform = driver_stub;
 
 	nouveau_display_options();
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index c2fc201d9e1b..bb2d8da7e553 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -262,8 +262,6 @@ static struct drm_driver qxl_driver = {
 			   DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
 			   DRIVER_ATOMIC,
 
-	.set_busid = drm_pci_set_busid,
-
 	.dumb_create = qxl_mode_dumb_create,
 	.dumb_map_offset = qxl_mode_dumb_mmap,
 	.dumb_destroy = drm_gem_dumb_destroy,
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
index a982be57d1ef..1d43c434328d 100644
--- a/drivers/gpu/drm/r128/r128_drv.c
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -62,7 +62,6 @@ static struct drm_driver driver = {
 	.load = r128_driver_load,
 	.preclose = r128_driver_preclose,
 	.lastclose = r128_driver_lastclose,
-	.set_busid = drm_pci_set_busid,
 	.get_vblank_counter = r128_get_vblank_counter,
 	.enable_vblank = r128_enable_vblank,
 	.disable_vblank = r128_disable_vblank,
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 6f906abd612b..dd5e86dafb29 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -545,7 +545,6 @@ static struct drm_driver kms_driver = {
 	.open = radeon_driver_open_kms,
 	.postclose = radeon_driver_postclose_kms,
 	.lastclose = radeon_driver_lastclose_kms,
-	.set_busid = drm_pci_set_busid,
 	.unload = radeon_driver_unload_kms,
 	.get_vblank_counter = radeon_get_vblank_counter_kms,
 	.enable_vblank = radeon_enable_vblank_kms,
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
index 78c6d8e9b42c..2a08da09dbcf 100644
--- a/drivers/gpu/drm/savage/savage_drv.c
+++ b/drivers/gpu/drm/savage/savage_drv.c
@@ -55,7 +55,6 @@ static struct drm_driver driver = {
 	.preclose = savage_reclaim_buffers,
 	.lastclose = savage_driver_lastclose,
 	.unload = savage_driver_unload,
-	.set_busid = drm_pci_set_busid,
 	.ioctls = savage_ioctls,
 	.dma_ioctl = savage_bci_buffers,
 	.fops = &savage_driver_fops,
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index 7f05da13ea5e..cdaced381f5d 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -104,7 +104,6 @@ static struct drm_driver driver = {
 	.open = sis_driver_open,
 	.preclose = sis_reclaim_buffers_locked,
 	.postclose = sis_driver_postclose,
-	.set_busid = drm_pci_set_busid,
 	.dma_quiescent = sis_idle,
 	.lastclose = sis_lastclose,
 	.ioctls = sis_ioctls,
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
index c54138c3a376..acd5f8162bb6 100644
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
@@ -55,7 +55,6 @@ static const struct file_operations tdfx_driver_fops = {
 
 static struct drm_driver driver = {
 	.driver_features = DRIVER_LEGACY,
-	.set_busid = drm_pci_set_busid,
 	.fops = &tdfx_driver_fops,
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index 9e0e5392b6ec..0ca4e0489c0b 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -77,7 +77,6 @@ static struct drm_driver driver = {
 	.open = via_driver_open,
 	.preclose = via_reclaim_buffers_locked,
 	.postclose = via_driver_postclose,
-	.set_busid = drm_pci_set_busid,
 	.context_dtor = via_final_context,
 	.get_vblank_counter = via_get_vblank_counter,
 	.enable_vblank = via_enable_vblank,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 4a641555b960..63218033b0be 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1531,7 +1531,6 @@ static struct drm_driver driver = {
 	.master_drop = vmw_master_drop,
 	.open = vmw_driver_open,
 	.postclose = vmw_postclose,
-	.set_busid = drm_pci_set_busid,
 
 	.dumb_create = vmw_dumb_create,
 	.dumb_map_offset = vmw_dumb_map_offset,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index e64e33b9dd26..5d06c68bb00b 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -172,8 +172,6 @@ struct drm_driver {
 	 */
 	void (*release) (struct drm_device *);
 
-	int (*set_busid)(struct drm_device *dev, struct drm_master *master);
-
 	/**
 	 * @get_vblank_counter:
 	 *
diff --git a/include/drm/drm_pci.h b/include/drm/drm_pci.h
index 4579fac1080c..961b16f9b553 100644
--- a/include/drm/drm_pci.h
+++ b/include/drm/drm_pci.h
@@ -49,7 +49,6 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver);
 int drm_get_pci_dev(struct pci_dev *pdev,
 		    const struct pci_device_id *ent,
 		    struct drm_driver *driver);
-int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master);
 #else
 static inline int drm_get_pci_dev(struct pci_dev *pdev,
 				  const struct pci_device_id *ent,
@@ -57,12 +56,6 @@ static inline int drm_get_pci_dev(struct pci_dev *pdev,
 {
 	return -ENOSYS;
 }
-
-static inline int drm_pci_set_busid(struct drm_device *dev,
-				    struct drm_master *master)
-{
-	return -ENOSYS;
-}
 #endif
 
 #define DRM_PCIE_SPEED_25 1
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 05/37] drm/pci: Deprecate drm_pci_init/exit completely
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (4 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 04/37] drm: Remove drm_driver->set_busid hook Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 06/37] drm/doc: Improve ioctl/fops docs a bit more Daniel Vetter
                   ` (34 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

The magic switching between proper pci driver and shadow-attach isn't
useful anymore since there's no ums+kms drivers left. Let's split this
up properly, calling pci_register_driver for kms drivers and renaming
the shadow-attach init to drm_legacy_pci_init/exit.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c |  4 ++--
 drivers/gpu/drm/ast/ast_drv.c           |  4 ++--
 drivers/gpu/drm/bochs/bochs_drv.c       |  4 ++--
 drivers/gpu/drm/cirrus/cirrus_drv.c     |  4 ++--
 drivers/gpu/drm/drm_pci.c               | 39 ++++++++++-----------------------
 drivers/gpu/drm/gma500/psb_drv.c        |  4 ++--
 drivers/gpu/drm/i810/i810_drv.c         |  4 ++--
 drivers/gpu/drm/mga/mga_drv.c           |  4 ++--
 drivers/gpu/drm/mgag200/mgag200_drv.c   |  5 +++--
 drivers/gpu/drm/nouveau/nouveau_drm.c   | 11 ++++++++--
 drivers/gpu/drm/qxl/qxl_drv.c           |  4 ++--
 drivers/gpu/drm/r128/r128_drv.c         |  4 ++--
 drivers/gpu/drm/radeon/radeon_drv.c     |  5 ++---
 drivers/gpu/drm/savage/savage_drv.c     |  4 ++--
 drivers/gpu/drm/sis/sis_drv.c           |  4 ++--
 drivers/gpu/drm/tdfx/tdfx_drv.c         |  4 ++--
 drivers/gpu/drm/via/via_drv.c           |  4 ++--
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.c     |  4 ++--
 include/drm/drm_pci.h                   |  4 ++--
 19 files changed, 56 insertions(+), 64 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index e7a4bce6358d..4911d304d8b9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -816,7 +816,7 @@ static int __init amdgpu_init(void)
 	driver->num_ioctls = amdgpu_max_kms_ioctl;
 	amdgpu_register_atpx_handler();
 	/* let modprobe override vga console setting */
-	return drm_pci_init(driver, pdriver);
+	return pci_register_driver(pdriver);
 
 error_sched:
 	amdgpu_fence_slab_fini();
@@ -831,7 +831,7 @@ static int __init amdgpu_init(void)
 static void __exit amdgpu_exit(void)
 {
 	amdgpu_amdkfd_fini();
-	drm_pci_exit(driver, pdriver);
+	pci_unregister_driver(pdriver);
 	amdgpu_unregister_atpx_handler();
 	amdgpu_sync_fini();
 	amd_sched_fence_slab_fini();
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index f6794745a024..3022b39c00f3 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -220,11 +220,11 @@ static int __init ast_init(void)
 
 	if (ast_modeset == 0)
 		return -EINVAL;
-	return drm_pci_init(&driver, &ast_pci_driver);
+	return pci_register_driver(&ast_pci_driver);
 }
 static void __exit ast_exit(void)
 {
-	drm_pci_exit(&driver, &ast_pci_driver);
+	pci_unregister_driver(&ast_pci_driver);
 }
 
 module_init(ast_init);
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c
index 8fccd3cf000d..a1d28845da5f 100644
--- a/drivers/gpu/drm/bochs/bochs_drv.c
+++ b/drivers/gpu/drm/bochs/bochs_drv.c
@@ -223,12 +223,12 @@ static int __init bochs_init(void)
 	if (bochs_modeset == 0)
 		return -EINVAL;
 
-	return drm_pci_init(&bochs_driver, &bochs_pci_driver);
+	return pci_register_driver(&bochs_pci_driver);
 }
 
 static void __exit bochs_exit(void)
 {
-	drm_pci_exit(&bochs_driver, &bochs_pci_driver);
+	pci_unregister_driver(&bochs_pci_driver);
 }
 
 module_init(bochs_init);
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index c48b9eb76712..910c300f5c37 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -165,12 +165,12 @@ static int __init cirrus_init(void)
 
 	if (cirrus_modeset == 0)
 		return -EINVAL;
-	return drm_pci_init(&driver, &cirrus_pci_driver);
+	return pci_register_driver(&cirrus_pci_driver);
 }
 
 static void __exit cirrus_exit(void)
 {
-	drm_pci_exit(&driver, &cirrus_pci_driver);
+	pci_unregister_driver(&cirrus_pci_driver);
 }
 
 module_init(cirrus_init);
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index ad31d95e77c9..1235c9877d6f 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -280,20 +280,15 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 EXPORT_SYMBOL(drm_get_pci_dev);
 
 /**
- * drm_pci_init - Register matching PCI devices with the DRM subsystem
+ * drm_legacy_pci_init - shadow-attach a legacy DRM PCI driver
  * @driver: DRM device driver
  * @pdriver: PCI device driver
  *
- * Initializes a drm_device structures, registering the stubs and initializing
- * the AGP device.
- *
- * NOTE: This function is deprecated. Modern modesetting drm drivers should use
- * pci_register_driver() directly, this function only provides shadow-binding
- * support for old legacy drivers on top of that core pci function.
+ * This is only used by legacy dri1 drivers and deprecated.
  *
  * Return: 0 on success or a negative error code on failure.
  */
-int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
+int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 {
 	struct pci_dev *pdev = NULL;
 	const struct pci_device_id *pid;
@@ -301,8 +296,8 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 
 	DRM_DEBUG("\n");
 
-	if (!(driver->driver_features & DRIVER_LEGACY))
-		return pci_register_driver(pdriver);
+	if (WARN_ON(!(driver->driver_features & DRIVER_LEGACY)))
+		return -EINVAL;
 
 	/* If not using KMS, fall back to stealth mode manual scanning. */
 	INIT_LIST_HEAD(&driver->legacy_dev_list);
@@ -329,6 +324,7 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 	}
 	return 0;
 }
+EXPORT_SYMBOL(drm_legacy_pci_init);
 
 int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask)
 {
@@ -390,11 +386,6 @@ EXPORT_SYMBOL(drm_pcie_get_max_link_width);
 
 #else
 
-int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
-{
-	return -1;
-}
-
 void drm_pci_agp_destroy(struct drm_device *dev) {}
 
 int drm_irq_by_busid(struct drm_device *dev, void *data,
@@ -404,27 +395,21 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
 }
 #endif
 
-EXPORT_SYMBOL(drm_pci_init);
-
 /**
- * drm_pci_exit - Unregister matching PCI devices from the DRM subsystem
+ * drm_legacy_pci_exit - unregister shadow-attach legacy DRM driver
  * @driver: DRM device driver
  * @pdriver: PCI device driver
  *
- * Unregisters one or more devices matched by a PCI driver from the DRM
- * subsystem.
- *
- * NOTE: This function is deprecated. Modern modesetting drm drivers should use
- * pci_unregister_driver() directly, this function only provides shadow-binding
- * support for old legacy drivers on top of that core pci function.
+ * Unregister a DRM driver shadow-attached through drm_legacy_pci_init(). This
+ * is deprecated and only used by dri1 drivers.
  */
-void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
+void drm_legacy_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
 {
 	struct drm_device *dev, *tmp;
 	DRM_DEBUG("\n");
 
 	if (!(driver->driver_features & DRIVER_LEGACY)) {
-		pci_unregister_driver(pdriver);
+		WARN_ON(1);
 	} else {
 		list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list,
 					 legacy_dev_list) {
@@ -434,4 +419,4 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
 	}
 	DRM_INFO("Module unloaded\n");
 }
-EXPORT_SYMBOL(drm_pci_exit);
+EXPORT_SYMBOL(drm_legacy_pci_exit);
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 37d4c36c80f2..747c06b227c5 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -516,12 +516,12 @@ static struct pci_driver psb_pci_driver = {
 
 static int __init psb_init(void)
 {
-	return drm_pci_init(&driver, &psb_pci_driver);
+	return pci_register_driver(&psb_pci_driver);
 }
 
 static void __exit psb_exit(void)
 {
-	drm_pci_exit(&driver, &psb_pci_driver);
+	pci_unregister_driver(&psb_pci_driver);
 }
 
 late_initcall(psb_init);
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
index e1c35c710e24..c69d5c487f51 100644
--- a/drivers/gpu/drm/i810/i810_drv.c
+++ b/drivers/gpu/drm/i810/i810_drv.c
@@ -82,12 +82,12 @@ static int __init i810_init(void)
 		return -EINVAL;
 	}
 	driver.num_ioctls = i810_max_ioctl;
-	return drm_pci_init(&driver, &i810_pci_driver);
+	return drm_legacy_pci_init(&driver, &i810_pci_driver);
 }
 
 static void __exit i810_exit(void)
 {
-	drm_pci_exit(&driver, &i810_pci_driver);
+	drm_legacy_pci_exit(&driver, &i810_pci_driver);
 }
 
 module_init(i810_init);
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
index 2a36ec611a44..1aad27813c23 100644
--- a/drivers/gpu/drm/mga/mga_drv.c
+++ b/drivers/gpu/drm/mga/mga_drv.c
@@ -89,12 +89,12 @@ static struct pci_driver mga_pci_driver = {
 static int __init mga_init(void)
 {
 	driver.num_ioctls = mga_max_ioctl;
-	return drm_pci_init(&driver, &mga_pci_driver);
+	return drm_legacy_pci_init(&driver, &mga_pci_driver);
 }
 
 static void __exit mga_exit(void)
 {
-	drm_pci_exit(&driver, &mga_pci_driver);
+	drm_legacy_pci_exit(&driver, &mga_pci_driver);
 }
 
 module_init(mga_init);
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 53a5982a04c7..4189160af726 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -119,12 +119,13 @@ static int __init mgag200_init(void)
 
 	if (mgag200_modeset == 0)
 		return -EINVAL;
-	return drm_pci_init(&driver, &mgag200_pci_driver);
+
+	return pci_register_driver(&mgag200_pci_driver);
 }
 
 static void __exit mgag200_exit(void)
 {
-	drm_pci_exit(&driver, &mgag200_pci_driver);
+	pci_unregister_driver(&mgag200_pci_driver);
 }
 
 module_init(mgag200_init);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index c31f974c59bb..31359fd972e2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -1120,7 +1120,12 @@ nouveau_drm_init(void)
 
 	nouveau_register_dsm_handler();
 	nouveau_backlight_ctor();
-	return drm_pci_init(&driver_pci, &nouveau_drm_pci_driver);
+
+#ifdef CONFIG_PCI
+	return pci_register_driver(&nouveau_drm_pci_driver);
+#else
+	return 0;
+#endif
 }
 
 static void __exit
@@ -1129,7 +1134,9 @@ nouveau_drm_exit(void)
 	if (!nouveau_modeset)
 		return;
 
-	drm_pci_exit(&driver_pci, &nouveau_drm_pci_driver);
+#ifdef CONFIG_PCI
+	pci_unregister_driver(&nouveau_drm_pci_driver);
+#endif
 	nouveau_backlight_dtor();
 	nouveau_unregister_dsm_handler();
 
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index bb2d8da7e553..15c97b16ee21 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -301,12 +301,12 @@ static int __init qxl_init(void)
 	if (qxl_modeset == 0)
 		return -EINVAL;
 	qxl_driver.num_ioctls = qxl_max_ioctls;
-	return drm_pci_init(&qxl_driver, &qxl_pci_driver);
+	return pci_register_driver(&qxl_pci_driver);
 }
 
 static void __exit qxl_exit(void)
 {
-	drm_pci_exit(&qxl_driver, &qxl_pci_driver);
+	pci_unregister_driver(&qxl_pci_driver);
 }
 
 module_init(qxl_init);
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
index 1d43c434328d..0d2b7e42b3a7 100644
--- a/drivers/gpu/drm/r128/r128_drv.c
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -95,12 +95,12 @@ static int __init r128_init(void)
 {
 	driver.num_ioctls = r128_max_ioctl;
 
-	return drm_pci_init(&driver, &r128_pci_driver);
+	return drm_legacy_pci_init(&driver, &r128_pci_driver);
 }
 
 static void __exit r128_exit(void)
 {
-	drm_pci_exit(&driver, &r128_pci_driver);
+	drm_legacy_pci_exit(&driver, &r128_pci_driver);
 }
 
 module_init(r128_init);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index dd5e86dafb29..0d02349674f6 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -619,14 +619,13 @@ static int __init radeon_init(void)
 		return -EINVAL;
 	}
 
-	/* let modprobe override vga console setting */
-	return drm_pci_init(driver, pdriver);
+	return pci_register_driver(pdriver);
 }
 
 static void __exit radeon_exit(void)
 {
 	radeon_kfd_fini();
-	drm_pci_exit(driver, pdriver);
+	pci_unregister_driver(pdriver);
 	radeon_unregister_atpx_handler();
 }
 
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
index 2a08da09dbcf..2bddeb8bf457 100644
--- a/drivers/gpu/drm/savage/savage_drv.c
+++ b/drivers/gpu/drm/savage/savage_drv.c
@@ -74,12 +74,12 @@ static struct pci_driver savage_pci_driver = {
 static int __init savage_init(void)
 {
 	driver.num_ioctls = savage_max_ioctl;
-	return drm_pci_init(&driver, &savage_pci_driver);
+	return drm_legacy_pci_init(&driver, &savage_pci_driver);
 }
 
 static void __exit savage_exit(void)
 {
-	drm_pci_exit(&driver, &savage_pci_driver);
+	drm_legacy_pci_exit(&driver, &savage_pci_driver);
 }
 
 module_init(savage_init);
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index cdaced381f5d..e04a92658cd7 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -124,12 +124,12 @@ static struct pci_driver sis_pci_driver = {
 static int __init sis_init(void)
 {
 	driver.num_ioctls = sis_max_ioctl;
-	return drm_pci_init(&driver, &sis_pci_driver);
+	return drm_legacy_pci_init(&driver, &sis_pci_driver);
 }
 
 static void __exit sis_exit(void)
 {
-	drm_pci_exit(&driver, &sis_pci_driver);
+	drm_legacy_pci_exit(&driver, &sis_pci_driver);
 }
 
 module_init(sis_init);
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
index acd5f8162bb6..3a1476818c65 100644
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
@@ -71,12 +71,12 @@ static struct pci_driver tdfx_pci_driver = {
 
 static int __init tdfx_init(void)
 {
-	return drm_pci_init(&driver, &tdfx_pci_driver);
+	return drm_legacy_pci_init(&driver, &tdfx_pci_driver);
 }
 
 static void __exit tdfx_exit(void)
 {
-	drm_pci_exit(&driver, &tdfx_pci_driver);
+	drm_legacy_pci_exit(&driver, &tdfx_pci_driver);
 }
 
 module_init(tdfx_init);
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index 0ca4e0489c0b..aaf766f7cca2 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -106,12 +106,12 @@ static int __init via_init(void)
 {
 	driver.num_ioctls = via_max_ioctl;
 	via_init_command_verifier();
-	return drm_pci_init(&driver, &via_pci_driver);
+	return drm_legacy_pci_init(&driver, &via_pci_driver);
 }
 
 static void __exit via_exit(void)
 {
-	drm_pci_exit(&driver, &via_pci_driver);
+	drm_legacy_pci_exit(&driver, &via_pci_driver);
 }
 
 module_init(via_init);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 63218033b0be..204bf181b69e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1570,7 +1570,7 @@ static int __init vmwgfx_init(void)
 	if (vgacon_text_force())
 		return -EINVAL;
 
-	ret = drm_pci_init(&driver, &vmw_pci_driver);
+	ret = pci_register_driver(&vmw_pci_driver);
 	if (ret)
 		DRM_ERROR("Failed initializing DRM.\n");
 	return ret;
@@ -1578,7 +1578,7 @@ static int __init vmwgfx_init(void)
 
 static void __exit vmwgfx_exit(void)
 {
-	drm_pci_exit(&driver, &vmw_pci_driver);
+	pci_unregister_driver(&vmw_pci_driver);
 }
 
 module_init(vmwgfx_init);
diff --git a/include/drm/drm_pci.h b/include/drm/drm_pci.h
index 961b16f9b553..674599025d7d 100644
--- a/include/drm/drm_pci.h
+++ b/include/drm/drm_pci.h
@@ -43,8 +43,8 @@ struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size,
 				     size_t align);
 void drm_pci_free(struct drm_device *dev, struct drm_dma_handle * dmah);
 
-int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver);
-void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver);
+int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver);
+void drm_legacy_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver);
 #ifdef CONFIG_PCI
 int drm_get_pci_dev(struct pci_dev *pdev,
 		    const struct pci_device_id *ent,
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 06/37] drm/doc: Improve ioctl/fops docs a bit more
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (5 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 05/37] drm/pci: Deprecate drm_pci_init/exit completely Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-31  9:20   ` [PATCH] " Daniel Vetter
  2017-05-24 14:51 ` [PATCH 07/37] drm: Extract drm_vblank.[hc] Daniel Vetter
                   ` (33 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

I spotted a markup issue, plus adding the descriptions in drm_driver.
Plus a few more links while at it.

I'm still mildly unhappy with the split between fops and ioctls, but I
still think having the ioctls in the uapi chapter makes more sense. Oh
well ...

v2: Rebase.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-internals.rst |  2 ++
 Documentation/gpu/drm-uapi.rst      |  2 ++
 drivers/gpu/drm/Makefile            |  2 +-
 drivers/gpu/drm/drm_file.c          |  7 ++++++-
 drivers/gpu/drm/drm_internal.h      |  3 ++-
 drivers/gpu/drm/drm_ioctl.c         |  5 ++++-
 include/drm/drm_drv.h               | 18 ++++++++++++++++++
 7 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index babfb6143bd9..d218dd29221a 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -243,6 +243,8 @@ drivers.
 Open/Close, File Operations and IOCTLs
 ======================================
 
+.. _drm_driver_fops:
+
 File Operations
 ---------------
 
diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst
index 858457567d3d..679373b4a03f 100644
--- a/Documentation/gpu/drm-uapi.rst
+++ b/Documentation/gpu/drm-uapi.rst
@@ -160,6 +160,8 @@ other hand, a driver requires shared state between clients which is
 visible to user-space and accessible beyond open-file boundaries, they
 cannot support render nodes.
 
+.. _drm_driver_ioctl:
+
 IOCTL Support on Device Nodes
 =============================
 
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c156fecfb362..acc88942c2e5 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -16,7 +16,7 @@ drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
 		drm_framebuffer.o drm_connector.o drm_blend.o \
 		drm_encoder.o drm_mode_object.o drm_property.o \
 		drm_plane.o drm_color_mgmt.o drm_print.o \
-		drm_dumb_buffers.o drm_mode_config.o
+		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o
 
 drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
 drm-$(CONFIG_DRM_VM) += drm_vm.o
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index caad93dab54b..6631f61b66ca 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -75,7 +75,7 @@ DEFINE_MUTEX(drm_global_mutex);
  * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap().
  *
  * No other file operations are supported by the DRM userspace API. Overall the
- * following is an example #file_operations structure::
+ * following is an example &file_operations structure::
  *
  *     static const example_drm_fops = {
  *             .owner = THIS_MODULE,
@@ -92,6 +92,11 @@ DEFINE_MUTEX(drm_global_mutex);
  * For plain GEM based drivers there is the DEFINE_DRM_GEM_FOPS() macro, and for
  * CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make this
  * simpler.
+ *
+ * The driver's &file_operations must be stored in &drm_driver.fops.
+ *
+ * For driver-private IOCTL handling see the more detailed discussion in
+ * :ref:`IOCTL support in the userland interfaces chapter<drm_driver_ioctl>`.
  */
 
 static int drm_open_helper(struct file *filp, struct drm_minor *minor);
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index bca2c66c5d28..6a0cbcc84534 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -54,8 +54,9 @@ int drm_name_info(struct seq_file *m, void *data);
 int drm_clients_info(struct seq_file *m, void* data);
 int drm_gem_name_info(struct seq_file *m, void *data);
 
-/* drm_irq.c */
+/* drm_vblank.c */
 extern unsigned int drm_timestamp_monotonic;
+void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
 
 /* IOCTLS */
 int drm_wait_vblank(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index a423bf60e230..3690706f051d 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -683,7 +683,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
  * 
  * DRM driver private IOCTL must be in the range from DRM_COMMAND_BASE to
  * DRM_COMMAND_END. Finally you need an array of &struct drm_ioctl_desc to wire
- * up the handlers and set the access rights:
+ * up the handlers and set the access rights::
  *
  *     static const struct drm_ioctl_desc my_driver_ioctls[] = {
  *         DRM_IOCTL_DEF_DRV(MY_DRIVER_OPERATION, my_driver_operation,
@@ -692,6 +692,9 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
  *
  * And then assign this to the &drm_driver.ioctls field in your driver
  * structure.
+ *
+ * See the separate chapter on :ref:`file operations<drm_driver_fops>` for how
+ * the driver-specific IOCTLs are wired up.
  */
 
 /**
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 5d06c68bb00b..ebb41688581b 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -486,8 +486,26 @@ struct drm_driver {
 	char *date;
 
 	u32 driver_features;
+
+	/**
+	 * @ioctls:
+	 *
+	 * Array of driver-private IOCTL description entries. See the chapter on
+	 * :ref:`IOCTL support in the userland interfaces
+	 * chapter<drm_driver_ioctl>` for the full details.
+	 */
+
 	const struct drm_ioctl_desc *ioctls;
+	/** @num_ioctls: Number of entries in @ioctls. */
 	int num_ioctls;
+
+	/**
+	 * @fops:
+	 *
+	 * File operations for the DRM device node. See the discussion in
+	 * :ref:`file operations<drm_driver_fops>` for in-depth coverage and
+	 * some examples.
+	 */
 	const struct file_operations *fops;
 
 	/* Everything below here is for legacy driver, never use! */
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 07/37] drm: Extract drm_vblank.[hc]
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (6 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 06/37] drm/doc: Improve ioctl/fops docs a bit more Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-29 19:36   ` Stefan Agner
  2017-05-31  9:21   ` [PATCH] " Daniel Vetter
  2017-05-24 14:51 ` [PATCH 08/37] drm/doc: Polish irq helper documentation Daniel Vetter
                   ` (32 subsequent siblings)
  40 siblings, 2 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

drm_irq.c contains both the irq helper library (optional) and the
vblank support (optional, but part of the modeset uapi, and doesn't
require the use of the irq helpers at all.

Split this up for more clarity of the scope of the individual bits.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-kms.rst |    4 +-
 drivers/gpu/drm/drm_irq.c     | 1623 +---------------------------------------
 drivers/gpu/drm/drm_vblank.c  | 1645 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drmP.h            |    5 +-
 include/drm/drm_file.h        |    1 +
 include/drm/drm_irq.h         |  158 +---
 include/drm/drm_prime.h       |    2 +
 include/drm/drm_vblank.h      |  181 +++++
 8 files changed, 1857 insertions(+), 1762 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_vblank.c
 create mode 100644 include/drm/drm_vblank.h

diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index bfecd21a8cdf..2d77c9580164 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -612,8 +612,8 @@ operation handler.
 Vertical Blanking and Interrupt Handling Functions Reference
 ------------------------------------------------------------
 
-.. kernel-doc:: include/drm/drm_irq.h
+.. kernel-doc:: include/drm/drm_vblank.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_irq.c
+.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
    :export:
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index c7debaad67f8..28d736c3fcb4 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -3,6 +3,25 @@
  *
  * \author Rickard E. (Rik) Faith <faith@valinux.com>
  * \author Gareth Hughes <gareth@valinux.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  */
 
 /*
@@ -32,429 +51,15 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <drm/drm_irq.h>
 #include <drm/drmP.h>
-#include "drm_trace.h"
-#include "drm_internal.h"
 
 #include <linux/interrupt.h>	/* For task queue support */
-#include <linux/slab.h>
 
 #include <linux/vgaarb.h>
 #include <linux/export.h>
 
-/* Retry timestamp calculation up to 3 times to satisfy
- * drm_timestamp_precision before giving up.
- */
-#define DRM_TIMESTAMP_MAXRETRIES 3
-
-/* Threshold in nanoseconds for detection of redundant
- * vblank irq in drm_handle_vblank(). 1 msec should be ok.
- */
-#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
-
-static bool
-drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-			  struct timeval *tvblank, bool in_vblank_irq);
-
-static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
-
-/*
- * Default to use monotonic timestamps for wait-for-vblank and page-flip
- * complete events.
- */
-unsigned int drm_timestamp_monotonic = 1;
-
-static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
-
-module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
-module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
-module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
-MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
-MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
-MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
-
-static void store_vblank(struct drm_device *dev, unsigned int pipe,
-			 u32 vblank_count_inc,
-			 struct timeval *t_vblank, u32 last)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	assert_spin_locked(&dev->vblank_time_lock);
-
-	vblank->last = last;
-
-	write_seqlock(&vblank->seqlock);
-	vblank->time = *t_vblank;
-	vblank->count += vblank_count_inc;
-	write_sequnlock(&vblank->seqlock);
-}
-
-/*
- * "No hw counter" fallback implementation of .get_vblank_counter() hook,
- * if there is no useable hardware frame counter available.
- */
-static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
-{
-	WARN_ON_ONCE(dev->max_vblank_count != 0);
-	return 0;
-}
-
-static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
-{
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
-
-		if (crtc->funcs->get_vblank_counter)
-			return crtc->funcs->get_vblank_counter(crtc);
-	}
-
-	if (dev->driver->get_vblank_counter)
-		return dev->driver->get_vblank_counter(dev, pipe);
-
-	return drm_vblank_no_hw_counter(dev, pipe);
-}
-
-/*
- * Reset the stored timestamp for the current vblank count to correspond
- * to the last vblank occurred.
- *
- * Only to be called from drm_crtc_vblank_on().
- *
- * Note: caller must hold &drm_device.vbl_lock since this reads & writes
- * device vblank fields.
- */
-static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe)
-{
-	u32 cur_vblank;
-	bool rc;
-	struct timeval t_vblank;
-	int count = DRM_TIMESTAMP_MAXRETRIES;
-
-	spin_lock(&dev->vblank_time_lock);
-
-	/*
-	 * sample the current counter to avoid random jumps
-	 * when drm_vblank_enable() applies the diff
-	 */
-	do {
-		cur_vblank = __get_vblank_counter(dev, pipe);
-		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
-	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
-
-	/*
-	 * Only reinitialize corresponding vblank timestamp if high-precision query
-	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
-	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
-	 */
-	if (!rc)
-		t_vblank = (struct timeval) {0, 0};
-
-	/*
-	 * +1 to make sure user will never see the same
-	 * vblank counter value before and after a modeset
-	 */
-	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
-
-	spin_unlock(&dev->vblank_time_lock);
-}
-
-/*
- * Call back into the driver to update the appropriate vblank counter
- * (specified by @pipe).  Deal with wraparound, if it occurred, and
- * update the last read value so we can deal with wraparound on the next
- * call if necessary.
- *
- * Only necessary when going from off->on, to account for frames we
- * didn't get an interrupt for.
- *
- * Note: caller must hold &drm_device.vbl_lock since this reads & writes
- * device vblank fields.
- */
-static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
-				    bool in_vblank_irq)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	u32 cur_vblank, diff;
-	bool rc;
-	struct timeval t_vblank;
-	int count = DRM_TIMESTAMP_MAXRETRIES;
-	int framedur_ns = vblank->framedur_ns;
-
-	/*
-	 * Interrupts were disabled prior to this call, so deal with counter
-	 * wrap if needed.
-	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
-	 * here if the register is small or we had vblank interrupts off for
-	 * a long time.
-	 *
-	 * We repeat the hardware vblank counter & timestamp query until
-	 * we get consistent results. This to prevent races between gpu
-	 * updating its hardware counter while we are retrieving the
-	 * corresponding vblank timestamp.
-	 */
-	do {
-		cur_vblank = __get_vblank_counter(dev, pipe);
-		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
-	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
-
-	if (dev->max_vblank_count != 0) {
-		/* trust the hw counter when it's around */
-		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
-	} else if (rc && framedur_ns) {
-		const struct timeval *t_old;
-		u64 diff_ns;
-
-		t_old = &vblank->time;
-		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
-
-		/*
-		 * Figure out how many vblanks we've missed based
-		 * on the difference in the timestamps and the
-		 * frame/field duration.
-		 */
-		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
-
-		if (diff == 0 && in_vblank_irq)
-			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
-				      " diff_ns = %lld, framedur_ns = %d)\n",
-				      pipe, (long long) diff_ns, framedur_ns);
-	} else {
-		/* some kind of default for drivers w/o accurate vbl timestamping */
-		diff = in_vblank_irq ? 1 : 0;
-	}
-
-	/*
-	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
-	 * interval? If so then vblank irqs keep running and it will likely
-	 * happen that the hardware vblank counter is not trustworthy as it
-	 * might reset at some point in that interval and vblank timestamps
-	 * are not trustworthy either in that interval. Iow. this can result
-	 * in a bogus diff >> 1 which must be avoided as it would cause
-	 * random large forward jumps of the software vblank counter.
-	 */
-	if (diff > 1 && (vblank->inmodeset & 0x2)) {
-		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
-			      " due to pre-modeset.\n", pipe, diff);
-		diff = 1;
-	}
-
-	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
-		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
-		      pipe, vblank->count, diff, cur_vblank, vblank->last);
-
-	if (diff == 0) {
-		WARN_ON_ONCE(cur_vblank != vblank->last);
-		return;
-	}
-
-	/*
-	 * Only reinitialize corresponding vblank timestamp if high-precision query
-	 * available and didn't fail, or we were called from the vblank interrupt.
-	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
-	 * for now, to mark the vblanktimestamp as invalid.
-	 */
-	if (!rc && in_vblank_irq)
-		t_vblank = (struct timeval) {0, 0};
-
-	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
-}
-
-static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return 0;
-
-	return vblank->count;
-}
-
-/**
- * drm_accurate_vblank_count - retrieve the master vblank counter
- * @crtc: which counter to retrieve
- *
- * This function is similar to @drm_crtc_vblank_count but this
- * function interpolates to handle a race with vblank irq's.
- *
- * This is mostly useful for hardware that can obtain the scanout
- * position, but doesn't have a frame counter.
- */
-u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	u32 vblank;
-	unsigned long flags;
-
-	WARN(!dev->driver->get_vblank_timestamp,
-	     "This function requires support for accurate vblank timestamps.");
-
-	spin_lock_irqsave(&dev->vblank_time_lock, flags);
-
-	drm_update_vblank_count(dev, pipe, false);
-	vblank = drm_vblank_count(dev, pipe);
-
-	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
-
-	return vblank;
-}
-EXPORT_SYMBOL(drm_accurate_vblank_count);
-
-static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
-
-		if (crtc->funcs->disable_vblank) {
-			crtc->funcs->disable_vblank(crtc);
-			return;
-		}
-	}
-
-	dev->driver->disable_vblank(dev, pipe);
-}
-
-/*
- * Disable vblank irq's on crtc, make sure that last vblank count
- * of hardware and corresponding consistent software vblank counter
- * are preserved, even if there are any spurious vblank irq's after
- * disable.
- */
-static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-
-	assert_spin_locked(&dev->vbl_lock);
-
-	/* Prevent vblank irq processing while disabling vblank irqs,
-	 * so no updates of timestamps or count can happen after we've
-	 * disabled. Needed to prevent races in case of delayed irq's.
-	 */
-	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
-
-	/*
-	 * Only disable vblank interrupts if they're enabled. This avoids
-	 * calling the ->disable_vblank() operation in atomic context with the
-	 * hardware potentially runtime suspended.
-	 */
-	if (vblank->enabled) {
-		__disable_vblank(dev, pipe);
-		vblank->enabled = false;
-	}
-
-	/*
-	 * Always update the count and timestamp to maintain the
-	 * appearance that the counter has been ticking all along until
-	 * this time. This makes the count account for the entire time
-	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
-	 */
-	drm_update_vblank_count(dev, pipe, false);
-
-	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
-}
-
-static void vblank_disable_fn(unsigned long arg)
-{
-	struct drm_vblank_crtc *vblank = (void *)arg;
-	struct drm_device *dev = vblank->dev;
-	unsigned int pipe = vblank->pipe;
-	unsigned long irqflags;
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
-		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
-		vblank_disable_and_save(dev, pipe);
-	}
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-}
-
-/**
- * drm_vblank_cleanup - cleanup vblank support
- * @dev: DRM device
- *
- * This function cleans up any resources allocated in drm_vblank_init.
- */
-void drm_vblank_cleanup(struct drm_device *dev)
-{
-	unsigned int pipe;
-
-	/* Bail if the driver didn't call drm_vblank_init() */
-	if (dev->num_crtcs == 0)
-		return;
-
-	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
-		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-		WARN_ON(READ_ONCE(vblank->enabled) &&
-			drm_core_check_feature(dev, DRIVER_MODESET));
-
-		del_timer_sync(&vblank->disable_timer);
-	}
-
-	kfree(dev->vblank);
-
-	dev->num_crtcs = 0;
-}
-EXPORT_SYMBOL(drm_vblank_cleanup);
-
-/**
- * drm_vblank_init - initialize vblank support
- * @dev: DRM device
- * @num_crtcs: number of CRTCs supported by @dev
- *
- * This function initializes vblank support for @num_crtcs display pipelines.
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
-{
-	int ret = -ENOMEM;
-	unsigned int i;
-
-	spin_lock_init(&dev->vbl_lock);
-	spin_lock_init(&dev->vblank_time_lock);
-
-	dev->num_crtcs = num_crtcs;
-
-	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
-	if (!dev->vblank)
-		goto err;
-
-	for (i = 0; i < num_crtcs; i++) {
-		struct drm_vblank_crtc *vblank = &dev->vblank[i];
-
-		vblank->dev = dev;
-		vblank->pipe = i;
-		init_waitqueue_head(&vblank->queue);
-		setup_timer(&vblank->disable_timer, vblank_disable_fn,
-			    (unsigned long)vblank);
-		seqlock_init(&vblank->seqlock);
-	}
-
-	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
-
-	/* Driver specific high-precision vblank timestamping supported? */
-	if (dev->driver->get_vblank_timestamp)
-		DRM_INFO("Driver supports precise vblank timestamp query.\n");
-	else
-		DRM_INFO("No driver support for vblank timestamp query.\n");
-
-	/* Must have precise timestamping for reliable vblank instant disable */
-	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
-		dev->vblank_disable_immediate = false;
-		DRM_INFO("Setting vblank_disable_immediate to false because "
-			 "get_vblank_timestamp == NULL\n");
-	}
-
-	return 0;
-
-err:
-	dev->num_crtcs = 0;
-	return ret;
-}
-EXPORT_SYMBOL(drm_vblank_init);
+#include "drm_internal.h"
 
 /**
  * drm_irq_install - install IRQ handler
@@ -571,7 +176,7 @@ int drm_irq_uninstall(struct drm_device *dev)
 
 			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
 
-			vblank_disable_and_save(dev, i);
+			drm_vblank_disable_and_save(dev, i);
 			wake_up(&vblank->queue);
 		}
 		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -634,1187 +239,3 @@ int drm_legacy_irq_control(struct drm_device *dev, void *data,
 		return -EINVAL;
 	}
 }
-
-/**
- * drm_calc_timestamping_constants - calculate vblank timestamp constants
- * @crtc: drm_crtc whose timestamp constants should be updated.
- * @mode: display mode containing the scanout timings
- *
- * Calculate and store various constants which are later
- * needed by vblank and swap-completion timestamping, e.g,
- * by drm_calc_vbltimestamp_from_scanoutpos(). They are
- * derived from CRTC's true scanout timing, so they take
- * things like panel scaling or other adjustments into account.
- */
-void drm_calc_timestamping_constants(struct drm_crtc *crtc,
-				     const struct drm_display_mode *mode)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int linedur_ns = 0, framedur_ns = 0;
-	int dotclock = mode->crtc_clock;
-
-	if (!dev->num_crtcs)
-		return;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	/* Valid dotclock? */
-	if (dotclock > 0) {
-		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
-
-		/*
-		 * Convert scanline length in pixels and video
-		 * dot clock to line duration and frame duration
-		 * in nanoseconds:
-		 */
-		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
-		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
-
-		/*
-		 * Fields of interlaced scanout modes are only half a frame duration.
-		 */
-		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-			framedur_ns /= 2;
-	} else
-		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
-			  crtc->base.id);
-
-	vblank->linedur_ns  = linedur_ns;
-	vblank->framedur_ns = framedur_ns;
-	vblank->hwmode = *mode;
-
-	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
-		  crtc->base.id, mode->crtc_htotal,
-		  mode->crtc_vtotal, mode->crtc_vdisplay);
-	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
-		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
-}
-EXPORT_SYMBOL(drm_calc_timestamping_constants);
-
-/**
- * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
- * @dev: DRM device
- * @pipe: index of CRTC whose vblank timestamp to retrieve
- * @max_error: Desired maximum allowable error in timestamps (nanosecs)
- *             On return contains true maximum error of timestamp
- * @vblank_time: Pointer to struct timeval which should receive the timestamp
- * @in_vblank_irq:
- *     True when called from drm_crtc_handle_vblank().  Some drivers
- *     need to apply some workarounds for gpu-specific vblank irq quirks
- *     if flag is set.
- *
- * Implements calculation of exact vblank timestamps from given drm_display_mode
- * timings and current video scanout position of a CRTC. This can be called from
- * within get_vblank_timestamp() implementation of a kms driver to implement the
- * actual timestamping.
- *
- * Should return timestamps conforming to the OML_sync_control OpenML
- * extension specification. The timestamp corresponds to the end of
- * the vblank interval, aka start of scanout of topmost-leftmost display
- * pixel in the following video frame.
- *
- * Requires support for optional dev->driver->get_scanout_position()
- * in kms driver, plus a bit of setup code to provide a drm_display_mode
- * that corresponds to the true scanout timing.
- *
- * The current implementation only handles standard video modes. It
- * returns as no operation if a doublescan or interlaced video mode is
- * active. Higher level code is expected to handle this.
- *
- * This function can be used to implement the &drm_driver.get_vblank_timestamp
- * directly, if the driver implements the &drm_driver.get_scanout_position hook.
- *
- * Note that atomic drivers must call drm_calc_timestamping_constants() before
- * enabling a CRTC. The atomic helpers already take care of that in
- * drm_atomic_helper_update_legacy_modeset_state().
- *
- * Returns:
- *
- * Returns true on success, and false on failure, i.e. when no accurate
- * timestamp could be acquired.
- */
-bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-					   unsigned int pipe,
-					   int *max_error,
-					   struct timeval *vblank_time,
-					   bool in_vblank_irq)
-{
-	struct timeval tv_etime;
-	ktime_t stime, etime;
-	bool vbl_status;
-	struct drm_crtc *crtc;
-	const struct drm_display_mode *mode;
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int vpos, hpos, i;
-	int delta_ns, duration_ns;
-
-	if (!drm_core_check_feature(dev, DRIVER_MODESET))
-		return false;
-
-	crtc = drm_crtc_from_index(dev, pipe);
-
-	if (pipe >= dev->num_crtcs || !crtc) {
-		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return false;
-	}
-
-	/* Scanout position query not supported? Should not happen. */
-	if (!dev->driver->get_scanout_position) {
-		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
-		return false;
-	}
-
-	if (drm_drv_uses_atomic_modeset(dev))
-		mode = &vblank->hwmode;
-	else
-		mode = &crtc->hwmode;
-
-	/* If mode timing undefined, just return as no-op:
-	 * Happens during initial modesetting of a crtc.
-	 */
-	if (mode->crtc_clock == 0) {
-		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
-		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
-
-		return false;
-	}
-
-	/* Get current scanout position with system timestamp.
-	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
-	 * if single query takes longer than max_error nanoseconds.
-	 *
-	 * This guarantees a tight bound on maximum error if
-	 * code gets preempted or delayed for some reason.
-	 */
-	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
-		/*
-		 * Get vertical and horizontal scanout position vpos, hpos,
-		 * and bounding timestamps stime, etime, pre/post query.
-		 */
-		vbl_status = dev->driver->get_scanout_position(dev, pipe,
-							       in_vblank_irq,
-							       &vpos, &hpos,
-							       &stime, &etime,
-							       mode);
-
-		/* Return as no-op if scanout query unsupported or failed. */
-		if (!vbl_status) {
-			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
-				  pipe);
-			return false;
-		}
-
-		/* Compute uncertainty in timestamp of scanout position query. */
-		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
-
-		/* Accept result with <  max_error nsecs timing uncertainty. */
-		if (duration_ns <= *max_error)
-			break;
-	}
-
-	/* Noisy system timing? */
-	if (i == DRM_TIMESTAMP_MAXRETRIES) {
-		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
-			  pipe, duration_ns/1000, *max_error/1000, i);
-	}
-
-	/* Return upper bound of timestamp precision error. */
-	*max_error = duration_ns;
-
-	/* Convert scanout position into elapsed time at raw_time query
-	 * since start of scanout at first display scanline. delta_ns
-	 * can be negative if start of scanout hasn't happened yet.
-	 */
-	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
-			   mode->crtc_clock);
-
-	if (!drm_timestamp_monotonic)
-		etime = ktime_mono_to_real(etime);
-
-	/* save this only for debugging purposes */
-	tv_etime = ktime_to_timeval(etime);
-	/* Subtract time delta from raw timestamp to get final
-	 * vblank_time timestamp for end of vblank.
-	 */
-	etime = ktime_sub_ns(etime, delta_ns);
-	*vblank_time = ktime_to_timeval(etime);
-
-	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
-		      pipe, hpos, vpos,
-		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
-		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
-		      duration_ns/1000, i);
-
-	return true;
-}
-EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
-
-static struct timeval get_drm_timestamp(void)
-{
-	ktime_t now;
-
-	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
-	return ktime_to_timeval(now);
-}
-
-/**
- * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
- *                             vblank interval
- * @dev: DRM device
- * @pipe: index of CRTC whose vblank timestamp to retrieve
- * @tvblank: Pointer to target struct timeval which should receive the timestamp
- * @in_vblank_irq:
- *     True when called from drm_crtc_handle_vblank().  Some drivers
- *     need to apply some workarounds for gpu-specific vblank irq quirks
- *     if flag is set.
- *
- * Fetches the system timestamp corresponding to the time of the most recent
- * vblank interval on specified CRTC. May call into kms-driver to
- * compute the timestamp with a high-precision GPU specific method.
- *
- * Returns zero if timestamp originates from uncorrected do_gettimeofday()
- * call, i.e., it isn't very precisely locked to the true vblank.
- *
- * Returns:
- * True if timestamp is considered to be very precise, false otherwise.
- */
-static bool
-drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-			  struct timeval *tvblank, bool in_vblank_irq)
-{
-	bool ret = false;
-
-	/* Define requested maximum error on timestamps (nanoseconds). */
-	int max_error = (int) drm_timestamp_precision * 1000;
-
-	/* Query driver if possible and precision timestamping enabled. */
-	if (dev->driver->get_vblank_timestamp && (max_error > 0))
-		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
-							tvblank, in_vblank_irq);
-
-	/* GPU high precision timestamp query unsupported or failed.
-	 * Return current monotonic/gettimeofday timestamp as best estimate.
-	 */
-	if (!ret)
-		*tvblank = get_drm_timestamp();
-
-	return ret;
-}
-
-/**
- * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
- * @crtc: which counter to retrieve
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity.
- *
- * Returns:
- * The software vblank counter.
- */
-u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
-{
-	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_count);
-
-/**
- * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
- *     system timestamp corresponding to that vblank counter value.
- * @dev: DRM device
- * @pipe: index of CRTC whose counter to retrieve
- * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current vblank counter value.
- *
- * This is the legacy version of drm_crtc_vblank_count_and_time().
- */
-static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
-				     struct timeval *vblanktime)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	u32 vblank_count;
-	unsigned int seq;
-
-	if (WARN_ON(pipe >= dev->num_crtcs)) {
-		*vblanktime = (struct timeval) { 0 };
-		return 0;
-	}
-
-	do {
-		seq = read_seqbegin(&vblank->seqlock);
-		vblank_count = vblank->count;
-		*vblanktime = vblank->time;
-	} while (read_seqretry(&vblank->seqlock, seq));
-
-	return vblank_count;
-}
-
-/**
- * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
- *     and the system timestamp corresponding to that vblank counter value
- * @crtc: which counter to retrieve
- * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current vblank counter value.
- */
-u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
-				   struct timeval *vblanktime)
-{
-	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
-					 vblanktime);
-}
-EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
-
-static void send_vblank_event(struct drm_device *dev,
-		struct drm_pending_vblank_event *e,
-		unsigned long seq, struct timeval *now)
-{
-	e->event.sequence = seq;
-	e->event.tv_sec = now->tv_sec;
-	e->event.tv_usec = now->tv_usec;
-
-	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
-					 e->event.sequence);
-
-	drm_send_event_locked(dev, &e->base);
-}
-
-/**
- * drm_crtc_arm_vblank_event - arm vblank event after pageflip
- * @crtc: the source CRTC of the vblank event
- * @e: the event to send
- *
- * A lot of drivers need to generate vblank events for the very next vblank
- * interrupt. For example when the page flip interrupt happens when the page
- * flip gets armed, but not when it actually executes within the next vblank
- * period. This helper function implements exactly the required vblank arming
- * behaviour.
- *
- * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
- * atomic commit must ensure that the next vblank happens at exactly the same
- * time as the atomic commit is committed to the hardware. This function itself
- * does **not** protect again the next vblank interrupt racing with either this
- * function call or the atomic commit operation. A possible sequence could be:
- *
- * 1. Driver commits new hardware state into vblank-synchronized registers.
- * 2. A vblank happens, committing the hardware state. Also the corresponding
- *    vblank interrupt is fired off and fully processed by the interrupt
- *    handler.
- * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
- * 4. The event is only send out for the next vblank, which is wrong.
- *
- * An equivalent race can happen when the driver calls
- * drm_crtc_arm_vblank_event() before writing out the new hardware state.
- *
- * The only way to make this work safely is to prevent the vblank from firing
- * (and the hardware from committing anything else) until the entire atomic
- * commit sequence has run to completion. If the hardware does not have such a
- * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
- * Instead drivers need to manually send out the event from their interrupt
- * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
- * possible race with the hardware committing the atomic update.
- *
- * Caller must hold event lock. Caller must also hold a vblank reference for
- * the event @e, which will be dropped when the next vblank arrives.
- */
-void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
-			       struct drm_pending_vblank_event *e)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-
-	assert_spin_locked(&dev->event_lock);
-
-	e->pipe = pipe;
-	e->event.sequence = drm_vblank_count(dev, pipe);
-	e->event.crtc_id = crtc->base.id;
-	list_add_tail(&e->base.link, &dev->vblank_event_list);
-}
-EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
-
-/**
- * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
- * @crtc: the source CRTC of the vblank event
- * @e: the event to send
- *
- * Updates sequence # and timestamp on event for the most recently processed
- * vblank, and sends it to userspace.  Caller must hold event lock.
- *
- * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
- * situation, especially to send out events for atomic commit operations.
- */
-void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
-				struct drm_pending_vblank_event *e)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int seq, pipe = drm_crtc_index(crtc);
-	struct timeval now;
-
-	if (dev->num_crtcs > 0) {
-		seq = drm_vblank_count_and_time(dev, pipe, &now);
-	} else {
-		seq = 0;
-
-		now = get_drm_timestamp();
-	}
-	e->pipe = pipe;
-	e->event.crtc_id = crtc->base.id;
-	send_vblank_event(dev, e, seq, &now);
-}
-EXPORT_SYMBOL(drm_crtc_send_vblank_event);
-
-static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
-
-		if (crtc->funcs->enable_vblank)
-			return crtc->funcs->enable_vblank(crtc);
-	}
-
-	return dev->driver->enable_vblank(dev, pipe);
-}
-
-/**
- * drm_vblank_enable - enable the vblank interrupt on a CRTC
- * @dev: DRM device
- * @pipe: CRTC index
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int ret = 0;
-
-	assert_spin_locked(&dev->vbl_lock);
-
-	spin_lock(&dev->vblank_time_lock);
-
-	if (!vblank->enabled) {
-		/*
-		 * Enable vblank irqs under vblank_time_lock protection.
-		 * All vblank count & timestamp updates are held off
-		 * until we are done reinitializing master counter and
-		 * timestamps. Filtercode in drm_handle_vblank() will
-		 * prevent double-accounting of same vblank interval.
-		 */
-		ret = __enable_vblank(dev, pipe);
-		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
-		if (ret) {
-			atomic_dec(&vblank->refcount);
-		} else {
-			drm_update_vblank_count(dev, pipe, 0);
-			/* drm_update_vblank_count() includes a wmb so we just
-			 * need to ensure that the compiler emits the write
-			 * to mark the vblank as enabled after the call
-			 * to drm_update_vblank_count().
-			 */
-			WRITE_ONCE(vblank->enabled, true);
-		}
-	}
-
-	spin_unlock(&dev->vblank_time_lock);
-
-	return ret;
-}
-
-/**
- * drm_vblank_get - get a reference count on vblank events
- * @dev: DRM device
- * @pipe: index of CRTC to own
- *
- * Acquire a reference count on vblank events to avoid having them disabled
- * while in use.
- *
- * This is the legacy version of drm_crtc_vblank_get().
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-	int ret = 0;
-
-	if (!dev->num_crtcs)
-		return -EINVAL;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return -EINVAL;
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	/* Going from 0->1 means we have to enable interrupts again */
-	if (atomic_add_return(1, &vblank->refcount) == 1) {
-		ret = drm_vblank_enable(dev, pipe);
-	} else {
-		if (!vblank->enabled) {
-			atomic_dec(&vblank->refcount);
-			ret = -EINVAL;
-		}
-	}
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-	return ret;
-}
-
-/**
- * drm_crtc_vblank_get - get a reference count on vblank events
- * @crtc: which CRTC to own
- *
- * Acquire a reference count on vblank events to avoid having them disabled
- * while in use.
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-int drm_crtc_vblank_get(struct drm_crtc *crtc)
-{
-	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_get);
-
-/**
- * drm_vblank_put - release ownership of vblank events
- * @dev: DRM device
- * @pipe: index of CRTC to release
- *
- * Release ownership of a given vblank counter, turning off interrupts
- * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
- *
- * This is the legacy version of drm_crtc_vblank_put().
- */
-static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
-		return;
-
-	/* Last user schedules interrupt disable */
-	if (atomic_dec_and_test(&vblank->refcount)) {
-		if (drm_vblank_offdelay == 0)
-			return;
-		else if (drm_vblank_offdelay < 0)
-			vblank_disable_fn((unsigned long)vblank);
-		else if (!dev->vblank_disable_immediate)
-			mod_timer(&vblank->disable_timer,
-				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
-	}
-}
-
-/**
- * drm_crtc_vblank_put - give up ownership of vblank events
- * @crtc: which counter to give up
- *
- * Release ownership of a given vblank counter, turning off interrupts
- * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
- */
-void drm_crtc_vblank_put(struct drm_crtc *crtc)
-{
-	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_put);
-
-/**
- * drm_wait_one_vblank - wait for one vblank
- * @dev: DRM device
- * @pipe: CRTC index
- *
- * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
- * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
- * due to lack of driver support or because the crtc is off.
- */
-void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int ret;
-	u32 last;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	ret = drm_vblank_get(dev, pipe);
-	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
-		return;
-
-	last = drm_vblank_count(dev, pipe);
-
-	ret = wait_event_timeout(vblank->queue,
-				 last != drm_vblank_count(dev, pipe),
-				 msecs_to_jiffies(100));
-
-	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
-
-	drm_vblank_put(dev, pipe);
-}
-EXPORT_SYMBOL(drm_wait_one_vblank);
-
-/**
- * drm_crtc_wait_one_vblank - wait for one vblank
- * @crtc: DRM crtc
- *
- * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
- * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
- * due to lack of driver support or because the crtc is off.
- */
-void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
-{
-	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
-
-/**
- * drm_crtc_vblank_off - disable vblank events on a CRTC
- * @crtc: CRTC in question
- *
- * Drivers can use this function to shut down the vblank interrupt handling when
- * disabling a crtc. This function ensures that the latest vblank frame count is
- * stored so that drm_vblank_on can restore it again.
- *
- * Drivers must use this function when the hardware vblank counter can get
- * reset, e.g. when suspending.
- */
-void drm_crtc_vblank_off(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	struct drm_pending_vblank_event *e, *t;
-	struct timeval now;
-	unsigned long irqflags;
-	unsigned int seq;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	spin_lock_irqsave(&dev->event_lock, irqflags);
-
-	spin_lock(&dev->vbl_lock);
-	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
-		      pipe, vblank->enabled, vblank->inmodeset);
-
-	/* Avoid redundant vblank disables without previous
-	 * drm_crtc_vblank_on(). */
-	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
-		vblank_disable_and_save(dev, pipe);
-
-	wake_up(&vblank->queue);
-
-	/*
-	 * Prevent subsequent drm_vblank_get() from re-enabling
-	 * the vblank interrupt by bumping the refcount.
-	 */
-	if (!vblank->inmodeset) {
-		atomic_inc(&vblank->refcount);
-		vblank->inmodeset = 1;
-	}
-	spin_unlock(&dev->vbl_lock);
-
-	/* Send any queued vblank events, lest the natives grow disquiet */
-	seq = drm_vblank_count_and_time(dev, pipe, &now);
-
-	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
-		if (e->pipe != pipe)
-			continue;
-		DRM_DEBUG("Sending premature vblank event on disable: "
-			  "wanted %u, current %u\n",
-			  e->event.sequence, seq);
-		list_del(&e->base.link);
-		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
-	}
-	spin_unlock_irqrestore(&dev->event_lock, irqflags);
-
-	/* Will be reset by the modeset helpers when re-enabling the crtc by
-	 * calling drm_calc_timestamping_constants(). */
-	vblank->hwmode.crtc_clock = 0;
-}
-EXPORT_SYMBOL(drm_crtc_vblank_off);
-
-/**
- * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
- * @crtc: CRTC in question
- *
- * Drivers can use this function to reset the vblank state to off at load time.
- * Drivers should use this together with the drm_crtc_vblank_off() and
- * drm_crtc_vblank_on() functions. The difference compared to
- * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
- * and hence doesn't need to call any driver hooks.
- */
-void drm_crtc_vblank_reset(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned long irqflags;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	/*
-	 * Prevent subsequent drm_vblank_get() from enabling the vblank
-	 * interrupt by bumping the refcount.
-	 */
-	if (!vblank->inmodeset) {
-		atomic_inc(&vblank->refcount);
-		vblank->inmodeset = 1;
-	}
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-	WARN_ON(!list_empty(&dev->vblank_event_list));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_reset);
-
-/**
- * drm_crtc_vblank_on - enable vblank events on a CRTC
- * @crtc: CRTC in question
- *
- * This functions restores the vblank interrupt state captured with
- * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
- * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called
- * in driver load code to reflect the current hardware state of the crtc.
- */
-void drm_crtc_vblank_on(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
-		      pipe, vblank->enabled, vblank->inmodeset);
-
-	/* Drop our private "prevent drm_vblank_get" refcount */
-	if (vblank->inmodeset) {
-		atomic_dec(&vblank->refcount);
-		vblank->inmodeset = 0;
-	}
-
-	drm_reset_vblank_timestamp(dev, pipe);
-
-	/*
-	 * re-enable interrupts if there are users left, or the
-	 * user wishes vblank interrupts to be enabled all the time.
-	 */
-	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
-		WARN_ON(drm_vblank_enable(dev, pipe));
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-}
-EXPORT_SYMBOL(drm_crtc_vblank_on);
-
-static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
-					  unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	/* vblank is not initialized (IRQ not installed ?), or has been freed */
-	if (!dev->num_crtcs)
-		return;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	/*
-	 * To avoid all the problems that might happen if interrupts
-	 * were enabled/disabled around or between these calls, we just
-	 * have the kernel take a reference on the CRTC (just once though
-	 * to avoid corrupting the count if multiple, mismatch calls occur),
-	 * so that interrupts remain enabled in the interim.
-	 */
-	if (!vblank->inmodeset) {
-		vblank->inmodeset = 0x1;
-		if (drm_vblank_get(dev, pipe) == 0)
-			vblank->inmodeset |= 0x2;
-	}
-}
-
-static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
-					   unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-
-	/* vblank is not initialized (IRQ not installed ?), or has been freed */
-	if (!dev->num_crtcs)
-		return;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	if (vblank->inmodeset) {
-		spin_lock_irqsave(&dev->vbl_lock, irqflags);
-		drm_reset_vblank_timestamp(dev, pipe);
-		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-		if (vblank->inmodeset & 0x2)
-			drm_vblank_put(dev, pipe);
-
-		vblank->inmodeset = 0;
-	}
-}
-
-int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
-			   struct drm_file *file_priv)
-{
-	struct drm_modeset_ctl *modeset = data;
-	unsigned int pipe;
-
-	/* If drm_vblank_init() hasn't been called yet, just no-op */
-	if (!dev->num_crtcs)
-		return 0;
-
-	/* KMS drivers handle this internally */
-	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
-		return 0;
-
-	pipe = modeset->crtc;
-	if (pipe >= dev->num_crtcs)
-		return -EINVAL;
-
-	switch (modeset->cmd) {
-	case _DRM_PRE_MODESET:
-		drm_legacy_vblank_pre_modeset(dev, pipe);
-		break;
-	case _DRM_POST_MODESET:
-		drm_legacy_vblank_post_modeset(dev, pipe);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static inline bool vblank_passed(u32 seq, u32 ref)
-{
-	return (seq - ref) <= (1 << 23);
-}
-
-static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
-				  union drm_wait_vblank *vblwait,
-				  struct drm_file *file_priv)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	struct drm_pending_vblank_event *e;
-	struct timeval now;
-	unsigned long flags;
-	unsigned int seq;
-	int ret;
-
-	e = kzalloc(sizeof(*e), GFP_KERNEL);
-	if (e == NULL) {
-		ret = -ENOMEM;
-		goto err_put;
-	}
-
-	e->pipe = pipe;
-	e->event.base.type = DRM_EVENT_VBLANK;
-	e->event.base.length = sizeof(e->event);
-	e->event.user_data = vblwait->request.signal;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-
-	/*
-	 * drm_crtc_vblank_off() might have been called after we called
-	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
-	 * vblank disable, so no need for further locking.  The reference from
-	 * drm_vblank_get() protects against vblank disable from another source.
-	 */
-	if (!READ_ONCE(vblank->enabled)) {
-		ret = -EINVAL;
-		goto err_unlock;
-	}
-
-	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
-					    &e->event.base);
-
-	if (ret)
-		goto err_unlock;
-
-	seq = drm_vblank_count_and_time(dev, pipe, &now);
-
-	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
-		  vblwait->request.sequence, seq, pipe);
-
-	trace_drm_vblank_event_queued(file_priv, pipe,
-				      vblwait->request.sequence);
-
-	e->event.sequence = vblwait->request.sequence;
-	if (vblank_passed(seq, vblwait->request.sequence)) {
-		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
-		vblwait->reply.sequence = seq;
-	} else {
-		/* drm_handle_vblank_events will call drm_vblank_put */
-		list_add_tail(&e->base.link, &dev->vblank_event_list);
-		vblwait->reply.sequence = vblwait->request.sequence;
-	}
-
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-
-	return 0;
-
-err_unlock:
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-	kfree(e);
-err_put:
-	drm_vblank_put(dev, pipe);
-	return ret;
-}
-
-static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
-{
-	if (vblwait->request.sequence)
-		return false;
-
-	return _DRM_VBLANK_RELATIVE ==
-		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
-					  _DRM_VBLANK_EVENT |
-					  _DRM_VBLANK_NEXTONMISS));
-}
-
-/*
- * Wait for VBLANK.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param data user argument, pointing to a drm_wait_vblank structure.
- * \return zero on success or a negative number on failure.
- *
- * This function enables the vblank interrupt on the pipe requested, then
- * sleeps waiting for the requested sequence number to occur, and drops
- * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
- * after a timeout with no further vblank waits scheduled).
- */
-int drm_wait_vblank(struct drm_device *dev, void *data,
-		    struct drm_file *file_priv)
-{
-	struct drm_vblank_crtc *vblank;
-	union drm_wait_vblank *vblwait = data;
-	int ret;
-	unsigned int flags, seq, pipe, high_pipe;
-
-	if (!dev->irq_enabled)
-		return -EINVAL;
-
-	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
-		return -EINVAL;
-
-	if (vblwait->request.type &
-	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
-	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
-		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
-			  vblwait->request.type,
-			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
-			   _DRM_VBLANK_HIGH_CRTC_MASK));
-		return -EINVAL;
-	}
-
-	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
-	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
-	if (high_pipe)
-		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
-	else
-		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
-	if (pipe >= dev->num_crtcs)
-		return -EINVAL;
-
-	vblank = &dev->vblank[pipe];
-
-	/* If the counter is currently enabled and accurate, short-circuit
-	 * queries to return the cached timestamp of the last vblank.
-	 */
-	if (dev->vblank_disable_immediate &&
-	    drm_wait_vblank_is_query(vblwait) &&
-	    READ_ONCE(vblank->enabled)) {
-		struct timeval now;
-
-		vblwait->reply.sequence =
-			drm_vblank_count_and_time(dev, pipe, &now);
-		vblwait->reply.tval_sec = now.tv_sec;
-		vblwait->reply.tval_usec = now.tv_usec;
-		return 0;
-	}
-
-	ret = drm_vblank_get(dev, pipe);
-	if (ret) {
-		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
-		return ret;
-	}
-	seq = drm_vblank_count(dev, pipe);
-
-	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
-	case _DRM_VBLANK_RELATIVE:
-		vblwait->request.sequence += seq;
-		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
-	case _DRM_VBLANK_ABSOLUTE:
-		break;
-	default:
-		ret = -EINVAL;
-		goto done;
-	}
-
-	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
-	    vblank_passed(seq, vblwait->request.sequence))
-		vblwait->request.sequence = seq + 1;
-
-	if (flags & _DRM_VBLANK_EVENT) {
-		/* must hold on to the vblank ref until the event fires
-		 * drm_vblank_put will be called asynchronously
-		 */
-		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
-	}
-
-	if (vblwait->request.sequence != seq) {
-		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
-			  vblwait->request.sequence, pipe);
-		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
-			    vblank_passed(drm_vblank_count(dev, pipe),
-					  vblwait->request.sequence) ||
-			    !READ_ONCE(vblank->enabled));
-	}
-
-	if (ret != -EINTR) {
-		struct timeval now;
-
-		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
-		vblwait->reply.tval_sec = now.tv_sec;
-		vblwait->reply.tval_usec = now.tv_usec;
-
-		DRM_DEBUG("crtc %d returning %u to client\n",
-			  pipe, vblwait->reply.sequence);
-	} else {
-		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
-	}
-
-done:
-	drm_vblank_put(dev, pipe);
-	return ret;
-}
-
-static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_pending_vblank_event *e, *t;
-	struct timeval now;
-	unsigned int seq;
-
-	assert_spin_locked(&dev->event_lock);
-
-	seq = drm_vblank_count_and_time(dev, pipe, &now);
-
-	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
-		if (e->pipe != pipe)
-			continue;
-		if (!vblank_passed(seq, e->event.sequence))
-			continue;
-
-		DRM_DEBUG("vblank event on %u, current %u\n",
-			  e->event.sequence, seq);
-
-		list_del(&e->base.link);
-		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
-	}
-
-	trace_drm_vblank_event(pipe, seq);
-}
-
-/**
- * drm_handle_vblank - handle a vblank event
- * @dev: DRM device
- * @pipe: index of CRTC where this event occurred
- *
- * Drivers should call this routine in their vblank interrupt handlers to
- * update the vblank counter and send any signals that may be pending.
- *
- * This is the legacy version of drm_crtc_handle_vblank().
- */
-bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-	bool disable_irq;
-
-	if (WARN_ON_ONCE(!dev->num_crtcs))
-		return false;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return false;
-
-	spin_lock_irqsave(&dev->event_lock, irqflags);
-
-	/* Need timestamp lock to prevent concurrent execution with
-	 * vblank enable/disable, as this would cause inconsistent
-	 * or corrupted timestamps and vblank counts.
-	 */
-	spin_lock(&dev->vblank_time_lock);
-
-	/* Vblank irq handling disabled. Nothing to do. */
-	if (!vblank->enabled) {
-		spin_unlock(&dev->vblank_time_lock);
-		spin_unlock_irqrestore(&dev->event_lock, irqflags);
-		return false;
-	}
-
-	drm_update_vblank_count(dev, pipe, true);
-
-	spin_unlock(&dev->vblank_time_lock);
-
-	wake_up(&vblank->queue);
-
-	/* With instant-off, we defer disabling the interrupt until after
-	 * we finish processing the following vblank after all events have
-	 * been signaled. The disable has to be last (after
-	 * drm_handle_vblank_events) so that the timestamp is always accurate.
-	 */
-	disable_irq = (dev->vblank_disable_immediate &&
-		       drm_vblank_offdelay > 0 &&
-		       !atomic_read(&vblank->refcount));
-
-	drm_handle_vblank_events(dev, pipe);
-
-	spin_unlock_irqrestore(&dev->event_lock, irqflags);
-
-	if (disable_irq)
-		vblank_disable_fn((unsigned long)vblank);
-
-	return true;
-}
-EXPORT_SYMBOL(drm_handle_vblank);
-
-/**
- * drm_crtc_handle_vblank - handle a vblank event
- * @crtc: where this event occurred
- *
- * Drivers should call this routine in their vblank interrupt handlers to
- * update the vblank counter and send any signals that may be pending.
- *
- * This is the native KMS version of drm_handle_vblank().
- *
- * Returns:
- * True if the event was successfully handled, false on failure.
- */
-bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
-{
-	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_handle_vblank);
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
new file mode 100644
index 000000000000..630dc26379b7
--- /dev/null
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -0,0 +1,1645 @@
+/*
+ * drm_irq.c IRQ and vblank support
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drm_vblank.h>
+#include <drm/drmP.h>
+#include <linux/export.h>
+
+#include "drm_trace.h"
+#include "drm_internal.h"
+
+/* Retry timestamp calculation up to 3 times to satisfy
+ * drm_timestamp_precision before giving up.
+ */
+#define DRM_TIMESTAMP_MAXRETRIES 3
+
+/* Threshold in nanoseconds for detection of redundant
+ * vblank irq in drm_handle_vblank(). 1 msec should be ok.
+ */
+#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
+
+static bool
+drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
+			  struct timeval *tvblank, bool in_vblank_irq);
+
+static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
+
+/*
+ * Default to use monotonic timestamps for wait-for-vblank and page-flip
+ * complete events.
+ */
+unsigned int drm_timestamp_monotonic = 1;
+
+static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
+
+module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
+module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
+module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
+MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
+MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
+MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
+
+static void store_vblank(struct drm_device *dev, unsigned int pipe,
+			 u32 vblank_count_inc,
+			 struct timeval *t_vblank, u32 last)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	assert_spin_locked(&dev->vblank_time_lock);
+
+	vblank->last = last;
+
+	write_seqlock(&vblank->seqlock);
+	vblank->time = *t_vblank;
+	vblank->count += vblank_count_inc;
+	write_sequnlock(&vblank->seqlock);
+}
+
+/*
+ * "No hw counter" fallback implementation of .get_vblank_counter() hook,
+ * if there is no useable hardware frame counter available.
+ */
+static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
+{
+	WARN_ON_ONCE(dev->max_vblank_count != 0);
+	return 0;
+}
+
+static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
+
+		if (crtc->funcs->get_vblank_counter)
+			return crtc->funcs->get_vblank_counter(crtc);
+	}
+
+	if (dev->driver->get_vblank_counter)
+		return dev->driver->get_vblank_counter(dev, pipe);
+
+	return drm_vblank_no_hw_counter(dev, pipe);
+}
+
+/*
+ * Reset the stored timestamp for the current vblank count to correspond
+ * to the last vblank occurred.
+ *
+ * Only to be called from drm_crtc_vblank_on().
+ *
+ * Note: caller must hold &drm_device.vbl_lock since this reads & writes
+ * device vblank fields.
+ */
+static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe)
+{
+	u32 cur_vblank;
+	bool rc;
+	struct timeval t_vblank;
+	int count = DRM_TIMESTAMP_MAXRETRIES;
+
+	spin_lock(&dev->vblank_time_lock);
+
+	/*
+	 * sample the current counter to avoid random jumps
+	 * when drm_vblank_enable() applies the diff
+	 */
+	do {
+		cur_vblank = __get_vblank_counter(dev, pipe);
+		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
+	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+
+	/*
+	 * Only reinitialize corresponding vblank timestamp if high-precision query
+	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
+	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
+	 */
+	if (!rc)
+		t_vblank = (struct timeval) {0, 0};
+
+	/*
+	 * +1 to make sure user will never see the same
+	 * vblank counter value before and after a modeset
+	 */
+	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
+
+	spin_unlock(&dev->vblank_time_lock);
+}
+
+/*
+ * Call back into the driver to update the appropriate vblank counter
+ * (specified by @pipe).  Deal with wraparound, if it occurred, and
+ * update the last read value so we can deal with wraparound on the next
+ * call if necessary.
+ *
+ * Only necessary when going from off->on, to account for frames we
+ * didn't get an interrupt for.
+ *
+ * Note: caller must hold &drm_device.vbl_lock since this reads & writes
+ * device vblank fields.
+ */
+static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
+				    bool in_vblank_irq)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	u32 cur_vblank, diff;
+	bool rc;
+	struct timeval t_vblank;
+	int count = DRM_TIMESTAMP_MAXRETRIES;
+	int framedur_ns = vblank->framedur_ns;
+
+	/*
+	 * Interrupts were disabled prior to this call, so deal with counter
+	 * wrap if needed.
+	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
+	 * here if the register is small or we had vblank interrupts off for
+	 * a long time.
+	 *
+	 * We repeat the hardware vblank counter & timestamp query until
+	 * we get consistent results. This to prevent races between gpu
+	 * updating its hardware counter while we are retrieving the
+	 * corresponding vblank timestamp.
+	 */
+	do {
+		cur_vblank = __get_vblank_counter(dev, pipe);
+		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
+	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+
+	if (dev->max_vblank_count != 0) {
+		/* trust the hw counter when it's around */
+		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
+	} else if (rc && framedur_ns) {
+		const struct timeval *t_old;
+		u64 diff_ns;
+
+		t_old = &vblank->time;
+		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
+
+		/*
+		 * Figure out how many vblanks we've missed based
+		 * on the difference in the timestamps and the
+		 * frame/field duration.
+		 */
+		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
+
+		if (diff == 0 && in_vblank_irq)
+			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
+				      " diff_ns = %lld, framedur_ns = %d)\n",
+				      pipe, (long long) diff_ns, framedur_ns);
+	} else {
+		/* some kind of default for drivers w/o accurate vbl timestamping */
+		diff = in_vblank_irq ? 1 : 0;
+	}
+
+	/*
+	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
+	 * interval? If so then vblank irqs keep running and it will likely
+	 * happen that the hardware vblank counter is not trustworthy as it
+	 * might reset at some point in that interval and vblank timestamps
+	 * are not trustworthy either in that interval. Iow. this can result
+	 * in a bogus diff >> 1 which must be avoided as it would cause
+	 * random large forward jumps of the software vblank counter.
+	 */
+	if (diff > 1 && (vblank->inmodeset & 0x2)) {
+		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
+			      " due to pre-modeset.\n", pipe, diff);
+		diff = 1;
+	}
+
+	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
+		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
+		      pipe, vblank->count, diff, cur_vblank, vblank->last);
+
+	if (diff == 0) {
+		WARN_ON_ONCE(cur_vblank != vblank->last);
+		return;
+	}
+
+	/*
+	 * Only reinitialize corresponding vblank timestamp if high-precision query
+	 * available and didn't fail, or we were called from the vblank interrupt.
+	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
+	 * for now, to mark the vblanktimestamp as invalid.
+	 */
+	if (!rc && in_vblank_irq)
+		t_vblank = (struct timeval) {0, 0};
+
+	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
+}
+
+static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return 0;
+
+	return vblank->count;
+}
+
+/**
+ * drm_accurate_vblank_count - retrieve the master vblank counter
+ * @crtc: which counter to retrieve
+ *
+ * This function is similar to @drm_crtc_vblank_count but this
+ * function interpolates to handle a race with vblank irq's.
+ *
+ * This is mostly useful for hardware that can obtain the scanout
+ * position, but doesn't have a frame counter.
+ */
+u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	u32 vblank;
+	unsigned long flags;
+
+	WARN(!dev->driver->get_vblank_timestamp,
+	     "This function requires support for accurate vblank timestamps.");
+
+	spin_lock_irqsave(&dev->vblank_time_lock, flags);
+
+	drm_update_vblank_count(dev, pipe, false);
+	vblank = drm_vblank_count(dev, pipe);
+
+	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
+
+	return vblank;
+}
+EXPORT_SYMBOL(drm_accurate_vblank_count);
+
+static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
+
+		if (crtc->funcs->disable_vblank) {
+			crtc->funcs->disable_vblank(crtc);
+			return;
+		}
+	}
+
+	dev->driver->disable_vblank(dev, pipe);
+}
+
+/*
+ * Disable vblank irq's on crtc, make sure that last vblank count
+ * of hardware and corresponding consistent software vblank counter
+ * are preserved, even if there are any spurious vblank irq's after
+ * disable.
+ */
+void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+
+	assert_spin_locked(&dev->vbl_lock);
+
+	/* Prevent vblank irq processing while disabling vblank irqs,
+	 * so no updates of timestamps or count can happen after we've
+	 * disabled. Needed to prevent races in case of delayed irq's.
+	 */
+	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
+
+	/*
+	 * Only disable vblank interrupts if they're enabled. This avoids
+	 * calling the ->disable_vblank() operation in atomic context with the
+	 * hardware potentially runtime suspended.
+	 */
+	if (vblank->enabled) {
+		__disable_vblank(dev, pipe);
+		vblank->enabled = false;
+	}
+
+	/*
+	 * Always update the count and timestamp to maintain the
+	 * appearance that the counter has been ticking all along until
+	 * this time. This makes the count account for the entire time
+	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
+	 */
+	drm_update_vblank_count(dev, pipe, false);
+
+	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
+}
+
+static void vblank_disable_fn(unsigned long arg)
+{
+	struct drm_vblank_crtc *vblank = (void *)arg;
+	struct drm_device *dev = vblank->dev;
+	unsigned int pipe = vblank->pipe;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
+		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
+		drm_vblank_disable_and_save(dev, pipe);
+	}
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+
+/**
+ * drm_vblank_cleanup - cleanup vblank support
+ * @dev: DRM device
+ *
+ * This function cleans up any resources allocated in drm_vblank_init.
+ */
+void drm_vblank_cleanup(struct drm_device *dev)
+{
+	unsigned int pipe;
+
+	/* Bail if the driver didn't call drm_vblank_init() */
+	if (dev->num_crtcs == 0)
+		return;
+
+	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
+		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+		WARN_ON(READ_ONCE(vblank->enabled) &&
+			drm_core_check_feature(dev, DRIVER_MODESET));
+
+		del_timer_sync(&vblank->disable_timer);
+	}
+
+	kfree(dev->vblank);
+
+	dev->num_crtcs = 0;
+}
+EXPORT_SYMBOL(drm_vblank_cleanup);
+
+/**
+ * drm_vblank_init - initialize vblank support
+ * @dev: DRM device
+ * @num_crtcs: number of CRTCs supported by @dev
+ *
+ * This function initializes vblank support for @num_crtcs display pipelines.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
+{
+	int ret = -ENOMEM;
+	unsigned int i;
+
+	spin_lock_init(&dev->vbl_lock);
+	spin_lock_init(&dev->vblank_time_lock);
+
+	dev->num_crtcs = num_crtcs;
+
+	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
+	if (!dev->vblank)
+		goto err;
+
+	for (i = 0; i < num_crtcs; i++) {
+		struct drm_vblank_crtc *vblank = &dev->vblank[i];
+
+		vblank->dev = dev;
+		vblank->pipe = i;
+		init_waitqueue_head(&vblank->queue);
+		setup_timer(&vblank->disable_timer, vblank_disable_fn,
+			    (unsigned long)vblank);
+		seqlock_init(&vblank->seqlock);
+	}
+
+	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
+
+	/* Driver specific high-precision vblank timestamping supported? */
+	if (dev->driver->get_vblank_timestamp)
+		DRM_INFO("Driver supports precise vblank timestamp query.\n");
+	else
+		DRM_INFO("No driver support for vblank timestamp query.\n");
+
+	/* Must have precise timestamping for reliable vblank instant disable */
+	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
+		dev->vblank_disable_immediate = false;
+		DRM_INFO("Setting vblank_disable_immediate to false because "
+			 "get_vblank_timestamp == NULL\n");
+	}
+
+	return 0;
+
+err:
+	dev->num_crtcs = 0;
+	return ret;
+}
+EXPORT_SYMBOL(drm_vblank_init);
+
+/**
+ * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
+ * @crtc: which CRTC's vblank waitqueue to retrieve
+ *
+ * This function returns a pointer to the vblank waitqueue for the CRTC.
+ * Drivers can use this to implement vblank waits using wait_event() and related
+ * functions.
+ */
+wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
+{
+	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
+
+
+/**
+ * drm_calc_timestamping_constants - calculate vblank timestamp constants
+ * @crtc: drm_crtc whose timestamp constants should be updated.
+ * @mode: display mode containing the scanout timings
+ *
+ * Calculate and store various constants which are later
+ * needed by vblank and swap-completion timestamping, e.g,
+ * by drm_calc_vbltimestamp_from_scanoutpos(). They are
+ * derived from CRTC's true scanout timing, so they take
+ * things like panel scaling or other adjustments into account.
+ */
+void drm_calc_timestamping_constants(struct drm_crtc *crtc,
+				     const struct drm_display_mode *mode)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int linedur_ns = 0, framedur_ns = 0;
+	int dotclock = mode->crtc_clock;
+
+	if (!dev->num_crtcs)
+		return;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	/* Valid dotclock? */
+	if (dotclock > 0) {
+		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
+
+		/*
+		 * Convert scanline length in pixels and video
+		 * dot clock to line duration and frame duration
+		 * in nanoseconds:
+		 */
+		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
+		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
+
+		/*
+		 * Fields of interlaced scanout modes are only half a frame duration.
+		 */
+		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+			framedur_ns /= 2;
+	} else
+		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
+			  crtc->base.id);
+
+	vblank->linedur_ns  = linedur_ns;
+	vblank->framedur_ns = framedur_ns;
+	vblank->hwmode = *mode;
+
+	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
+		  crtc->base.id, mode->crtc_htotal,
+		  mode->crtc_vtotal, mode->crtc_vdisplay);
+	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
+		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
+}
+EXPORT_SYMBOL(drm_calc_timestamping_constants);
+
+/**
+ * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
+ * @dev: DRM device
+ * @pipe: index of CRTC whose vblank timestamp to retrieve
+ * @max_error: Desired maximum allowable error in timestamps (nanosecs)
+ *             On return contains true maximum error of timestamp
+ * @vblank_time: Pointer to struct timeval which should receive the timestamp
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
+ *
+ * Implements calculation of exact vblank timestamps from given drm_display_mode
+ * timings and current video scanout position of a CRTC. This can be called from
+ * within get_vblank_timestamp() implementation of a kms driver to implement the
+ * actual timestamping.
+ *
+ * Should return timestamps conforming to the OML_sync_control OpenML
+ * extension specification. The timestamp corresponds to the end of
+ * the vblank interval, aka start of scanout of topmost-leftmost display
+ * pixel in the following video frame.
+ *
+ * Requires support for optional dev->driver->get_scanout_position()
+ * in kms driver, plus a bit of setup code to provide a drm_display_mode
+ * that corresponds to the true scanout timing.
+ *
+ * The current implementation only handles standard video modes. It
+ * returns as no operation if a doublescan or interlaced video mode is
+ * active. Higher level code is expected to handle this.
+ *
+ * This function can be used to implement the &drm_driver.get_vblank_timestamp
+ * directly, if the driver implements the &drm_driver.get_scanout_position hook.
+ *
+ * Note that atomic drivers must call drm_calc_timestamping_constants() before
+ * enabling a CRTC. The atomic helpers already take care of that in
+ * drm_atomic_helper_update_legacy_modeset_state().
+ *
+ * Returns:
+ *
+ * Returns true on success, and false on failure, i.e. when no accurate
+ * timestamp could be acquired.
+ */
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+					   unsigned int pipe,
+					   int *max_error,
+					   struct timeval *vblank_time,
+					   bool in_vblank_irq)
+{
+	struct timeval tv_etime;
+	ktime_t stime, etime;
+	bool vbl_status;
+	struct drm_crtc *crtc;
+	const struct drm_display_mode *mode;
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int vpos, hpos, i;
+	int delta_ns, duration_ns;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return false;
+
+	crtc = drm_crtc_from_index(dev, pipe);
+
+	if (pipe >= dev->num_crtcs || !crtc) {
+		DRM_ERROR("Invalid crtc %u\n", pipe);
+		return false;
+	}
+
+	/* Scanout position query not supported? Should not happen. */
+	if (!dev->driver->get_scanout_position) {
+		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
+		return false;
+	}
+
+	if (drm_drv_uses_atomic_modeset(dev))
+		mode = &vblank->hwmode;
+	else
+		mode = &crtc->hwmode;
+
+	/* If mode timing undefined, just return as no-op:
+	 * Happens during initial modesetting of a crtc.
+	 */
+	if (mode->crtc_clock == 0) {
+		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
+		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
+
+		return false;
+	}
+
+	/* Get current scanout position with system timestamp.
+	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
+	 * if single query takes longer than max_error nanoseconds.
+	 *
+	 * This guarantees a tight bound on maximum error if
+	 * code gets preempted or delayed for some reason.
+	 */
+	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
+		/*
+		 * Get vertical and horizontal scanout position vpos, hpos,
+		 * and bounding timestamps stime, etime, pre/post query.
+		 */
+		vbl_status = dev->driver->get_scanout_position(dev, pipe,
+							       in_vblank_irq,
+							       &vpos, &hpos,
+							       &stime, &etime,
+							       mode);
+
+		/* Return as no-op if scanout query unsupported or failed. */
+		if (!vbl_status) {
+			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
+				  pipe);
+			return false;
+		}
+
+		/* Compute uncertainty in timestamp of scanout position query. */
+		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
+
+		/* Accept result with <  max_error nsecs timing uncertainty. */
+		if (duration_ns <= *max_error)
+			break;
+	}
+
+	/* Noisy system timing? */
+	if (i == DRM_TIMESTAMP_MAXRETRIES) {
+		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
+			  pipe, duration_ns/1000, *max_error/1000, i);
+	}
+
+	/* Return upper bound of timestamp precision error. */
+	*max_error = duration_ns;
+
+	/* Convert scanout position into elapsed time at raw_time query
+	 * since start of scanout at first display scanline. delta_ns
+	 * can be negative if start of scanout hasn't happened yet.
+	 */
+	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
+			   mode->crtc_clock);
+
+	if (!drm_timestamp_monotonic)
+		etime = ktime_mono_to_real(etime);
+
+	/* save this only for debugging purposes */
+	tv_etime = ktime_to_timeval(etime);
+	/* Subtract time delta from raw timestamp to get final
+	 * vblank_time timestamp for end of vblank.
+	 */
+	etime = ktime_sub_ns(etime, delta_ns);
+	*vblank_time = ktime_to_timeval(etime);
+
+	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+		      pipe, hpos, vpos,
+		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
+		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
+		      duration_ns/1000, i);
+
+	return true;
+}
+EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
+
+static struct timeval get_drm_timestamp(void)
+{
+	ktime_t now;
+
+	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
+	return ktime_to_timeval(now);
+}
+
+/**
+ * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
+ *                             vblank interval
+ * @dev: DRM device
+ * @pipe: index of CRTC whose vblank timestamp to retrieve
+ * @tvblank: Pointer to target struct timeval which should receive the timestamp
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
+ *
+ * Fetches the system timestamp corresponding to the time of the most recent
+ * vblank interval on specified CRTC. May call into kms-driver to
+ * compute the timestamp with a high-precision GPU specific method.
+ *
+ * Returns zero if timestamp originates from uncorrected do_gettimeofday()
+ * call, i.e., it isn't very precisely locked to the true vblank.
+ *
+ * Returns:
+ * True if timestamp is considered to be very precise, false otherwise.
+ */
+static bool
+drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
+			  struct timeval *tvblank, bool in_vblank_irq)
+{
+	bool ret = false;
+
+	/* Define requested maximum error on timestamps (nanoseconds). */
+	int max_error = (int) drm_timestamp_precision * 1000;
+
+	/* Query driver if possible and precision timestamping enabled. */
+	if (dev->driver->get_vblank_timestamp && (max_error > 0))
+		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
+							tvblank, in_vblank_irq);
+
+	/* GPU high precision timestamp query unsupported or failed.
+	 * Return current monotonic/gettimeofday timestamp as best estimate.
+	 */
+	if (!ret)
+		*tvblank = get_drm_timestamp();
+
+	return ret;
+}
+
+/**
+ * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
+ * @crtc: which counter to retrieve
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity.
+ *
+ * Returns:
+ * The software vblank counter.
+ */
+u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
+{
+	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_count);
+
+/**
+ * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
+ *     system timestamp corresponding to that vblank counter value.
+ * @dev: DRM device
+ * @pipe: index of CRTC whose counter to retrieve
+ * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity. Returns corresponding system timestamp of the time
+ * of the vblank interval that corresponds to the current vblank counter value.
+ *
+ * This is the legacy version of drm_crtc_vblank_count_and_time().
+ */
+static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
+				     struct timeval *vblanktime)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	u32 vblank_count;
+	unsigned int seq;
+
+	if (WARN_ON(pipe >= dev->num_crtcs)) {
+		*vblanktime = (struct timeval) { 0 };
+		return 0;
+	}
+
+	do {
+		seq = read_seqbegin(&vblank->seqlock);
+		vblank_count = vblank->count;
+		*vblanktime = vblank->time;
+	} while (read_seqretry(&vblank->seqlock, seq));
+
+	return vblank_count;
+}
+
+/**
+ * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
+ *     and the system timestamp corresponding to that vblank counter value
+ * @crtc: which counter to retrieve
+ * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity. Returns corresponding system timestamp of the time
+ * of the vblank interval that corresponds to the current vblank counter value.
+ */
+u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+				   struct timeval *vblanktime)
+{
+	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
+					 vblanktime);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
+
+static void send_vblank_event(struct drm_device *dev,
+		struct drm_pending_vblank_event *e,
+		unsigned long seq, struct timeval *now)
+{
+	e->event.sequence = seq;
+	e->event.tv_sec = now->tv_sec;
+	e->event.tv_usec = now->tv_usec;
+
+	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
+					 e->event.sequence);
+
+	drm_send_event_locked(dev, &e->base);
+}
+
+/**
+ * drm_crtc_arm_vblank_event - arm vblank event after pageflip
+ * @crtc: the source CRTC of the vblank event
+ * @e: the event to send
+ *
+ * A lot of drivers need to generate vblank events for the very next vblank
+ * interrupt. For example when the page flip interrupt happens when the page
+ * flip gets armed, but not when it actually executes within the next vblank
+ * period. This helper function implements exactly the required vblank arming
+ * behaviour.
+ *
+ * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
+ * atomic commit must ensure that the next vblank happens at exactly the same
+ * time as the atomic commit is committed to the hardware. This function itself
+ * does **not** protect again the next vblank interrupt racing with either this
+ * function call or the atomic commit operation. A possible sequence could be:
+ *
+ * 1. Driver commits new hardware state into vblank-synchronized registers.
+ * 2. A vblank happens, committing the hardware state. Also the corresponding
+ *    vblank interrupt is fired off and fully processed by the interrupt
+ *    handler.
+ * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
+ * 4. The event is only send out for the next vblank, which is wrong.
+ *
+ * An equivalent race can happen when the driver calls
+ * drm_crtc_arm_vblank_event() before writing out the new hardware state.
+ *
+ * The only way to make this work safely is to prevent the vblank from firing
+ * (and the hardware from committing anything else) until the entire atomic
+ * commit sequence has run to completion. If the hardware does not have such a
+ * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
+ * Instead drivers need to manually send out the event from their interrupt
+ * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
+ * possible race with the hardware committing the atomic update.
+ *
+ * Caller must hold event lock. Caller must also hold a vblank reference for
+ * the event @e, which will be dropped when the next vblank arrives.
+ */
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+			       struct drm_pending_vblank_event *e)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+
+	assert_spin_locked(&dev->event_lock);
+
+	e->pipe = pipe;
+	e->event.sequence = drm_vblank_count(dev, pipe);
+	e->event.crtc_id = crtc->base.id;
+	list_add_tail(&e->base.link, &dev->vblank_event_list);
+}
+EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
+
+/**
+ * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
+ * @crtc: the source CRTC of the vblank event
+ * @e: the event to send
+ *
+ * Updates sequence # and timestamp on event for the most recently processed
+ * vblank, and sends it to userspace.  Caller must hold event lock.
+ *
+ * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
+ * situation, especially to send out events for atomic commit operations.
+ */
+void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
+				struct drm_pending_vblank_event *e)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int seq, pipe = drm_crtc_index(crtc);
+	struct timeval now;
+
+	if (dev->num_crtcs > 0) {
+		seq = drm_vblank_count_and_time(dev, pipe, &now);
+	} else {
+		seq = 0;
+
+		now = get_drm_timestamp();
+	}
+	e->pipe = pipe;
+	e->event.crtc_id = crtc->base.id;
+	send_vblank_event(dev, e, seq, &now);
+}
+EXPORT_SYMBOL(drm_crtc_send_vblank_event);
+
+static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
+
+		if (crtc->funcs->enable_vblank)
+			return crtc->funcs->enable_vblank(crtc);
+	}
+
+	return dev->driver->enable_vblank(dev, pipe);
+}
+
+/**
+ * drm_vblank_enable - enable the vblank interrupt on a CRTC
+ * @dev: DRM device
+ * @pipe: CRTC index
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int ret = 0;
+
+	assert_spin_locked(&dev->vbl_lock);
+
+	spin_lock(&dev->vblank_time_lock);
+
+	if (!vblank->enabled) {
+		/*
+		 * Enable vblank irqs under vblank_time_lock protection.
+		 * All vblank count & timestamp updates are held off
+		 * until we are done reinitializing master counter and
+		 * timestamps. Filtercode in drm_handle_vblank() will
+		 * prevent double-accounting of same vblank interval.
+		 */
+		ret = __enable_vblank(dev, pipe);
+		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
+		if (ret) {
+			atomic_dec(&vblank->refcount);
+		} else {
+			drm_update_vblank_count(dev, pipe, 0);
+			/* drm_update_vblank_count() includes a wmb so we just
+			 * need to ensure that the compiler emits the write
+			 * to mark the vblank as enabled after the call
+			 * to drm_update_vblank_count().
+			 */
+			WRITE_ONCE(vblank->enabled, true);
+		}
+	}
+
+	spin_unlock(&dev->vblank_time_lock);
+
+	return ret;
+}
+
+/**
+ * drm_vblank_get - get a reference count on vblank events
+ * @dev: DRM device
+ * @pipe: index of CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * This is the legacy version of drm_crtc_vblank_get().
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+	int ret = 0;
+
+	if (!dev->num_crtcs)
+		return -EINVAL;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	/* Going from 0->1 means we have to enable interrupts again */
+	if (atomic_add_return(1, &vblank->refcount) == 1) {
+		ret = drm_vblank_enable(dev, pipe);
+	} else {
+		if (!vblank->enabled) {
+			atomic_dec(&vblank->refcount);
+			ret = -EINVAL;
+		}
+	}
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+	return ret;
+}
+
+/**
+ * drm_crtc_vblank_get - get a reference count on vblank events
+ * @crtc: which CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+int drm_crtc_vblank_get(struct drm_crtc *crtc)
+{
+	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get);
+
+/**
+ * drm_vblank_put - release ownership of vblank events
+ * @dev: DRM device
+ * @pipe: index of CRTC to release
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the legacy version of drm_crtc_vblank_put().
+ */
+static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
+		return;
+
+	/* Last user schedules interrupt disable */
+	if (atomic_dec_and_test(&vblank->refcount)) {
+		if (drm_vblank_offdelay == 0)
+			return;
+		else if (drm_vblank_offdelay < 0)
+			vblank_disable_fn((unsigned long)vblank);
+		else if (!dev->vblank_disable_immediate)
+			mod_timer(&vblank->disable_timer,
+				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
+	}
+}
+
+/**
+ * drm_crtc_vblank_put - give up ownership of vblank events
+ * @crtc: which counter to give up
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ */
+void drm_crtc_vblank_put(struct drm_crtc *crtc)
+{
+	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_put);
+
+/**
+ * drm_wait_one_vblank - wait for one vblank
+ * @dev: DRM device
+ * @pipe: CRTC index
+ *
+ * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int ret;
+	u32 last;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	ret = drm_vblank_get(dev, pipe);
+	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
+		return;
+
+	last = drm_vblank_count(dev, pipe);
+
+	ret = wait_event_timeout(vblank->queue,
+				 last != drm_vblank_count(dev, pipe),
+				 msecs_to_jiffies(100));
+
+	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
+
+	drm_vblank_put(dev, pipe);
+}
+EXPORT_SYMBOL(drm_wait_one_vblank);
+
+/**
+ * drm_crtc_wait_one_vblank - wait for one vblank
+ * @crtc: DRM crtc
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
+{
+	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
+
+/**
+ * drm_crtc_vblank_off - disable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ */
+void drm_crtc_vblank_off(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	struct drm_pending_vblank_event *e, *t;
+	struct timeval now;
+	unsigned long irqflags;
+	unsigned int seq;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	spin_lock_irqsave(&dev->event_lock, irqflags);
+
+	spin_lock(&dev->vbl_lock);
+	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
+		      pipe, vblank->enabled, vblank->inmodeset);
+
+	/* Avoid redundant vblank disables without previous
+	 * drm_crtc_vblank_on(). */
+	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
+		drm_vblank_disable_and_save(dev, pipe);
+
+	wake_up(&vblank->queue);
+
+	/*
+	 * Prevent subsequent drm_vblank_get() from re-enabling
+	 * the vblank interrupt by bumping the refcount.
+	 */
+	if (!vblank->inmodeset) {
+		atomic_inc(&vblank->refcount);
+		vblank->inmodeset = 1;
+	}
+	spin_unlock(&dev->vbl_lock);
+
+	/* Send any queued vblank events, lest the natives grow disquiet */
+	seq = drm_vblank_count_and_time(dev, pipe, &now);
+
+	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+		if (e->pipe != pipe)
+			continue;
+		DRM_DEBUG("Sending premature vblank event on disable: "
+			  "wanted %u, current %u\n",
+			  e->event.sequence, seq);
+		list_del(&e->base.link);
+		drm_vblank_put(dev, pipe);
+		send_vblank_event(dev, e, seq, &now);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+	/* Will be reset by the modeset helpers when re-enabling the crtc by
+	 * calling drm_calc_timestamping_constants(). */
+	vblank->hwmode.crtc_clock = 0;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_off);
+
+/**
+ * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to reset the vblank state to off at load time.
+ * Drivers should use this together with the drm_crtc_vblank_off() and
+ * drm_crtc_vblank_on() functions. The difference compared to
+ * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
+ * and hence doesn't need to call any driver hooks.
+ */
+void drm_crtc_vblank_reset(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned long irqflags;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	/*
+	 * Prevent subsequent drm_vblank_get() from enabling the vblank
+	 * interrupt by bumping the refcount.
+	 */
+	if (!vblank->inmodeset) {
+		atomic_inc(&vblank->refcount);
+		vblank->inmodeset = 1;
+	}
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+	WARN_ON(!list_empty(&dev->vblank_event_list));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_reset);
+
+/**
+ * drm_crtc_vblank_on - enable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
+ * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called
+ * in driver load code to reflect the current hardware state of the crtc.
+ */
+void drm_crtc_vblank_on(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
+		      pipe, vblank->enabled, vblank->inmodeset);
+
+	/* Drop our private "prevent drm_vblank_get" refcount */
+	if (vblank->inmodeset) {
+		atomic_dec(&vblank->refcount);
+		vblank->inmodeset = 0;
+	}
+
+	drm_reset_vblank_timestamp(dev, pipe);
+
+	/*
+	 * re-enable interrupts if there are users left, or the
+	 * user wishes vblank interrupts to be enabled all the time.
+	 */
+	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
+		WARN_ON(drm_vblank_enable(dev, pipe));
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_on);
+
+static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
+					  unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	/* vblank is not initialized (IRQ not installed ?), or has been freed */
+	if (!dev->num_crtcs)
+		return;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	/*
+	 * To avoid all the problems that might happen if interrupts
+	 * were enabled/disabled around or between these calls, we just
+	 * have the kernel take a reference on the CRTC (just once though
+	 * to avoid corrupting the count if multiple, mismatch calls occur),
+	 * so that interrupts remain enabled in the interim.
+	 */
+	if (!vblank->inmodeset) {
+		vblank->inmodeset = 0x1;
+		if (drm_vblank_get(dev, pipe) == 0)
+			vblank->inmodeset |= 0x2;
+	}
+}
+
+static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
+					   unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+
+	/* vblank is not initialized (IRQ not installed ?), or has been freed */
+	if (!dev->num_crtcs)
+		return;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	if (vblank->inmodeset) {
+		spin_lock_irqsave(&dev->vbl_lock, irqflags);
+		drm_reset_vblank_timestamp(dev, pipe);
+		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+		if (vblank->inmodeset & 0x2)
+			drm_vblank_put(dev, pipe);
+
+		vblank->inmodeset = 0;
+	}
+}
+
+int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_modeset_ctl *modeset = data;
+	unsigned int pipe;
+
+	/* If drm_vblank_init() hasn't been called yet, just no-op */
+	if (!dev->num_crtcs)
+		return 0;
+
+	/* KMS drivers handle this internally */
+	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
+		return 0;
+
+	pipe = modeset->crtc;
+	if (pipe >= dev->num_crtcs)
+		return -EINVAL;
+
+	switch (modeset->cmd) {
+	case _DRM_PRE_MODESET:
+		drm_legacy_vblank_pre_modeset(dev, pipe);
+		break;
+	case _DRM_POST_MODESET:
+		drm_legacy_vblank_post_modeset(dev, pipe);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline bool vblank_passed(u32 seq, u32 ref)
+{
+	return (seq - ref) <= (1 << 23);
+}
+
+static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
+				  union drm_wait_vblank *vblwait,
+				  struct drm_file *file_priv)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+	unsigned int seq;
+	int ret;
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (e == NULL) {
+		ret = -ENOMEM;
+		goto err_put;
+	}
+
+	e->pipe = pipe;
+	e->event.base.type = DRM_EVENT_VBLANK;
+	e->event.base.length = sizeof(e->event);
+	e->event.user_data = vblwait->request.signal;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	/*
+	 * drm_crtc_vblank_off() might have been called after we called
+	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
+	 * vblank disable, so no need for further locking.  The reference from
+	 * drm_vblank_get() protects against vblank disable from another source.
+	 */
+	if (!READ_ONCE(vblank->enabled)) {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
+					    &e->event.base);
+
+	if (ret)
+		goto err_unlock;
+
+	seq = drm_vblank_count_and_time(dev, pipe, &now);
+
+	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
+		  vblwait->request.sequence, seq, pipe);
+
+	trace_drm_vblank_event_queued(file_priv, pipe,
+				      vblwait->request.sequence);
+
+	e->event.sequence = vblwait->request.sequence;
+	if (vblank_passed(seq, vblwait->request.sequence)) {
+		drm_vblank_put(dev, pipe);
+		send_vblank_event(dev, e, seq, &now);
+		vblwait->reply.sequence = seq;
+	} else {
+		/* drm_handle_vblank_events will call drm_vblank_put */
+		list_add_tail(&e->base.link, &dev->vblank_event_list);
+		vblwait->reply.sequence = vblwait->request.sequence;
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	return 0;
+
+err_unlock:
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+	kfree(e);
+err_put:
+	drm_vblank_put(dev, pipe);
+	return ret;
+}
+
+static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
+{
+	if (vblwait->request.sequence)
+		return false;
+
+	return _DRM_VBLANK_RELATIVE ==
+		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
+					  _DRM_VBLANK_EVENT |
+					  _DRM_VBLANK_NEXTONMISS));
+}
+
+/*
+ * Wait for VBLANK.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param data user argument, pointing to a drm_wait_vblank structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * This function enables the vblank interrupt on the pipe requested, then
+ * sleeps waiting for the requested sequence number to occur, and drops
+ * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
+ * after a timeout with no further vblank waits scheduled).
+ */
+int drm_wait_vblank(struct drm_device *dev, void *data,
+		    struct drm_file *file_priv)
+{
+	struct drm_vblank_crtc *vblank;
+	union drm_wait_vblank *vblwait = data;
+	int ret;
+	unsigned int flags, seq, pipe, high_pipe;
+
+	if (!dev->irq_enabled)
+		return -EINVAL;
+
+	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
+		return -EINVAL;
+
+	if (vblwait->request.type &
+	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
+	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
+		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
+			  vblwait->request.type,
+			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
+			   _DRM_VBLANK_HIGH_CRTC_MASK));
+		return -EINVAL;
+	}
+
+	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
+	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
+	if (high_pipe)
+		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else
+		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
+	if (pipe >= dev->num_crtcs)
+		return -EINVAL;
+
+	vblank = &dev->vblank[pipe];
+
+	/* If the counter is currently enabled and accurate, short-circuit
+	 * queries to return the cached timestamp of the last vblank.
+	 */
+	if (dev->vblank_disable_immediate &&
+	    drm_wait_vblank_is_query(vblwait) &&
+	    READ_ONCE(vblank->enabled)) {
+		struct timeval now;
+
+		vblwait->reply.sequence =
+			drm_vblank_count_and_time(dev, pipe, &now);
+		vblwait->reply.tval_sec = now.tv_sec;
+		vblwait->reply.tval_usec = now.tv_usec;
+		return 0;
+	}
+
+	ret = drm_vblank_get(dev, pipe);
+	if (ret) {
+		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
+		return ret;
+	}
+	seq = drm_vblank_count(dev, pipe);
+
+	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
+	case _DRM_VBLANK_RELATIVE:
+		vblwait->request.sequence += seq;
+		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
+	case _DRM_VBLANK_ABSOLUTE:
+		break;
+	default:
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
+	    vblank_passed(seq, vblwait->request.sequence))
+		vblwait->request.sequence = seq + 1;
+
+	if (flags & _DRM_VBLANK_EVENT) {
+		/* must hold on to the vblank ref until the event fires
+		 * drm_vblank_put will be called asynchronously
+		 */
+		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
+	}
+
+	if (vblwait->request.sequence != seq) {
+		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
+			  vblwait->request.sequence, pipe);
+		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
+			    vblank_passed(drm_vblank_count(dev, pipe),
+					  vblwait->request.sequence) ||
+			    !READ_ONCE(vblank->enabled));
+	}
+
+	if (ret != -EINTR) {
+		struct timeval now;
+
+		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
+		vblwait->reply.tval_sec = now.tv_sec;
+		vblwait->reply.tval_usec = now.tv_usec;
+
+		DRM_DEBUG("crtc %d returning %u to client\n",
+			  pipe, vblwait->reply.sequence);
+	} else {
+		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
+	}
+
+done:
+	drm_vblank_put(dev, pipe);
+	return ret;
+}
+
+static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_pending_vblank_event *e, *t;
+	struct timeval now;
+	unsigned int seq;
+
+	assert_spin_locked(&dev->event_lock);
+
+	seq = drm_vblank_count_and_time(dev, pipe, &now);
+
+	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+		if (e->pipe != pipe)
+			continue;
+		if (!vblank_passed(seq, e->event.sequence))
+			continue;
+
+		DRM_DEBUG("vblank event on %u, current %u\n",
+			  e->event.sequence, seq);
+
+		list_del(&e->base.link);
+		drm_vblank_put(dev, pipe);
+		send_vblank_event(dev, e, seq, &now);
+	}
+
+	trace_drm_vblank_event(pipe, seq);
+}
+
+/**
+ * drm_handle_vblank - handle a vblank event
+ * @dev: DRM device
+ * @pipe: index of CRTC where this event occurred
+ *
+ * Drivers should call this routine in their vblank interrupt handlers to
+ * update the vblank counter and send any signals that may be pending.
+ *
+ * This is the legacy version of drm_crtc_handle_vblank().
+ */
+bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+	bool disable_irq;
+
+	if (WARN_ON_ONCE(!dev->num_crtcs))
+		return false;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return false;
+
+	spin_lock_irqsave(&dev->event_lock, irqflags);
+
+	/* Need timestamp lock to prevent concurrent execution with
+	 * vblank enable/disable, as this would cause inconsistent
+	 * or corrupted timestamps and vblank counts.
+	 */
+	spin_lock(&dev->vblank_time_lock);
+
+	/* Vblank irq handling disabled. Nothing to do. */
+	if (!vblank->enabled) {
+		spin_unlock(&dev->vblank_time_lock);
+		spin_unlock_irqrestore(&dev->event_lock, irqflags);
+		return false;
+	}
+
+	drm_update_vblank_count(dev, pipe, true);
+
+	spin_unlock(&dev->vblank_time_lock);
+
+	wake_up(&vblank->queue);
+
+	/* With instant-off, we defer disabling the interrupt until after
+	 * we finish processing the following vblank after all events have
+	 * been signaled. The disable has to be last (after
+	 * drm_handle_vblank_events) so that the timestamp is always accurate.
+	 */
+	disable_irq = (dev->vblank_disable_immediate &&
+		       drm_vblank_offdelay > 0 &&
+		       !atomic_read(&vblank->refcount));
+
+	drm_handle_vblank_events(dev, pipe);
+
+	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+	if (disable_irq)
+		vblank_disable_fn((unsigned long)vblank);
+
+	return true;
+}
+EXPORT_SYMBOL(drm_handle_vblank);
+
+/**
+ * drm_crtc_handle_vblank - handle a vblank event
+ * @crtc: where this event occurred
+ *
+ * Drivers should call this routine in their vblank interrupt handlers to
+ * update the vblank counter and send any signals that may be pending.
+ *
+ * This is the native KMS version of drm_handle_vblank().
+ *
+ * Returns:
+ * True if the event was successfully handled, false on failure.
+ */
+bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
+{
+	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_handle_vblank);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index c363f2fdff31..2e0b76cceb97 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -80,6 +80,9 @@
 #include <drm/drm_debugfs.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_sysfs.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_irq.h>
+
 
 struct module;
 
@@ -447,8 +450,6 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev)
 	return dev->mode_config.funcs->atomic_commit != NULL;
 }
 
-#include <drm/drm_irq.h>
-
 #define DRM_SWITCH_POWER_ON 0
 #define DRM_SWITCH_POWER_OFF 1
 #define DRM_SWITCH_POWER_CHANGING 2
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
index 5dd27ae5c47c..d66f7ee07fb5 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -40,6 +40,7 @@
 struct dma_fence;
 struct drm_file;
 struct drm_device;
+struct device;
 
 /*
  * FIXME: Not sure we want to have drm_minor here in the end, but to avoid
diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h
index 569ca86d4e1f..d77f6e65b1c6 100644
--- a/include/drm/drm_irq.h
+++ b/include/drm/drm_irq.h
@@ -24,165 +24,9 @@
 #ifndef _DRM_IRQ_H_
 #define _DRM_IRQ_H_
 
-#include <linux/seqlock.h>
-
-/**
- * struct drm_pending_vblank_event - pending vblank event tracking
- */
-struct drm_pending_vblank_event {
-	/**
-	 * @base: Base structure for tracking pending DRM events.
-	 */
-	struct drm_pending_event base;
-	/**
-	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
-	 */
-	unsigned int pipe;
-	/**
-	 * @event: Actual event which will be sent to userspace.
-	 */
-	struct drm_event_vblank event;
-};
-
-/**
- * struct drm_vblank_crtc - vblank tracking for a CRTC
- *
- * This structure tracks the vblank state for one CRTC.
- *
- * Note that for historical reasons - the vblank handling code is still shared
- * with legacy/non-kms drivers - this is a free-standing structure not directly
- * connected to &struct drm_crtc. But all public interface functions are taking
- * a &struct drm_crtc to hide this implementation detail.
- */
-struct drm_vblank_crtc {
-	/**
-	 * @dev: Pointer to the &drm_device.
-	 */
-	struct drm_device *dev;
-	/**
-	 * @queue: Wait queue for vblank waiters.
-	 */
-	wait_queue_head_t queue;	/**< VBLANK wait queue */
-	/**
-	 * @disable_timer: Disable timer for the delayed vblank disabling
-	 * hysteresis logic. Vblank disabling is controlled through the
-	 * drm_vblank_offdelay module option and the setting of the
-	 * &drm_device.max_vblank_count value.
-	 */
-	struct timer_list disable_timer;
-
-	/**
-	 * @seqlock: Protect vblank count and time.
-	 */
-	seqlock_t seqlock;		/* protects vblank count and time */
-
-	/**
-	 * @count: Current software vblank counter.
-	 */
-	u32 count;
-	/**
-	 * @time: Vblank timestamp corresponding to @count.
-	 */
-	struct timeval time;
-
-	/**
-	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
-	 * this refcount reaches 0 can the hardware interrupt be disabled using
-	 * @disable_timer.
-	 */
-	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
-	/**
-	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
-	 */
-	u32 last;
-	/**
-	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
-	 * For legacy driver bit 2 additionally tracks whether an additional
-	 * temporary vblank reference has been acquired to paper over the
-	 * hardware counter resetting/jumping. KMS drivers should instead just
-	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
-	 * save and restore the vblank count.
-	 */
-	unsigned int inmodeset;		/* Display driver is setting mode */
-	/**
-	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
-	 * structure.
-	 */
-	unsigned int pipe;
-	/**
-	 * @framedur_ns: Frame/Field duration in ns, used by
-	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
-	 * drm_calc_timestamping_constants().
-	 */
-	int framedur_ns;
-	/**
-	 * @linedur_ns: Line duration in ns, used by
-	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
-	 * drm_calc_timestamping_constants().
-	 */
-	int linedur_ns;
-
-	/**
-	 * @hwmode:
-	 *
-	 * Cache of the current hardware display mode. Only valid when @enabled
-	 * is set. This is used by helpers like
-	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
-	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
-	 * because that one is really hard to get from interrupt context.
-	 */
-	struct drm_display_mode hwmode;
-
-	/**
-	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
-	 * avoid double-disabling and hence corrupting saved state. Needed by
-	 * drivers not using atomic KMS, since those might go through their CRTC
-	 * disabling functions multiple times.
-	 */
-	bool enabled;
-};
+struct drm_device;
 
 int drm_irq_install(struct drm_device *dev, int irq);
 int drm_irq_uninstall(struct drm_device *dev);
 
-int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
-u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
-u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
-				   struct timeval *vblanktime);
-void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
-			       struct drm_pending_vblank_event *e);
-void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
-			      struct drm_pending_vblank_event *e);
-bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
-bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
-int drm_crtc_vblank_get(struct drm_crtc *crtc);
-void drm_crtc_vblank_put(struct drm_crtc *crtc);
-void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
-void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
-void drm_crtc_vblank_off(struct drm_crtc *crtc);
-void drm_crtc_vblank_reset(struct drm_crtc *crtc);
-void drm_crtc_vblank_on(struct drm_crtc *crtc);
-void drm_vblank_cleanup(struct drm_device *dev);
-u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
-
-bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-					   unsigned int pipe, int *max_error,
-					   struct timeval *vblank_time,
-					   bool in_vblank_irq);
-void drm_calc_timestamping_constants(struct drm_crtc *crtc,
-				     const struct drm_display_mode *mode);
-
-/**
- * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
- * @crtc: which CRTC's vblank waitqueue to retrieve
- *
- * This function returns a pointer to the vblank waitqueue for the CRTC.
- * Drivers can use this to implement vblank waits using wait_event() and related
- * functions.
- */
-static inline wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
-{
-	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
-}
-
 #endif
diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h
index 59ccab402e85..9cd9e36f77b5 100644
--- a/include/drm/drm_prime.h
+++ b/include/drm/drm_prime.h
@@ -59,6 +59,8 @@ struct drm_device;
 struct drm_gem_object;
 struct drm_file;
 
+struct device;
+
 struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
 				     struct drm_gem_object *obj,
 				     int flags);
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
new file mode 100644
index 000000000000..4cde47332dfa
--- /dev/null
+++ b/include/drm/drm_vblank.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2016 Intel Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DRM_VBLANK_H_
+#define _DRM_VBLANK_H_
+
+#include <linux/seqlock.h>
+#include <linux/idr.h>
+#include <linux/poll.h>
+
+#include <drm/drm_file.h>
+#include <drm/drm_modes.h>
+#include <uapi/drm/drm.h>
+
+struct drm_device;
+struct drm_crtc;
+
+/**
+ * struct drm_pending_vblank_event - pending vblank event tracking
+ */
+struct drm_pending_vblank_event {
+	/**
+	 * @base: Base structure for tracking pending DRM events.
+	 */
+	struct drm_pending_event base;
+	/**
+	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
+	 */
+	unsigned int pipe;
+	/**
+	 * @event: Actual event which will be sent to userspace.
+	 */
+	struct drm_event_vblank event;
+};
+
+/**
+ * struct drm_vblank_crtc - vblank tracking for a CRTC
+ *
+ * This structure tracks the vblank state for one CRTC.
+ *
+ * Note that for historical reasons - the vblank handling code is still shared
+ * with legacy/non-kms drivers - this is a free-standing structure not directly
+ * connected to &struct drm_crtc. But all public interface functions are taking
+ * a &struct drm_crtc to hide this implementation detail.
+ */
+struct drm_vblank_crtc {
+	/**
+	 * @dev: Pointer to the &drm_device.
+	 */
+	struct drm_device *dev;
+	/**
+	 * @queue: Wait queue for vblank waiters.
+	 */
+	wait_queue_head_t queue;	/**< VBLANK wait queue */
+	/**
+	 * @disable_timer: Disable timer for the delayed vblank disabling
+	 * hysteresis logic. Vblank disabling is controlled through the
+	 * drm_vblank_offdelay module option and the setting of the
+	 * &drm_device.max_vblank_count value.
+	 */
+	struct timer_list disable_timer;
+
+	/**
+	 * @seqlock: Protect vblank count and time.
+	 */
+	seqlock_t seqlock;		/* protects vblank count and time */
+
+	/**
+	 * @count: Current software vblank counter.
+	 */
+	u32 count;
+	/**
+	 * @time: Vblank timestamp corresponding to @count.
+	 */
+	struct timeval time;
+
+	/**
+	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
+	 * this refcount reaches 0 can the hardware interrupt be disabled using
+	 * @disable_timer.
+	 */
+	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
+	/**
+	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
+	 */
+	u32 last;
+	/**
+	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
+	 * For legacy driver bit 2 additionally tracks whether an additional
+	 * temporary vblank reference has been acquired to paper over the
+	 * hardware counter resetting/jumping. KMS drivers should instead just
+	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
+	 * save and restore the vblank count.
+	 */
+	unsigned int inmodeset;		/* Display driver is setting mode */
+	/**
+	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
+	 * structure.
+	 */
+	unsigned int pipe;
+	/**
+	 * @framedur_ns: Frame/Field duration in ns, used by
+	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
+	 * drm_calc_timestamping_constants().
+	 */
+	int framedur_ns;
+	/**
+	 * @linedur_ns: Line duration in ns, used by
+	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
+	 * drm_calc_timestamping_constants().
+	 */
+	int linedur_ns;
+
+	/**
+	 * @hwmode:
+	 *
+	 * Cache of the current hardware display mode. Only valid when @enabled
+	 * is set. This is used by helpers like
+	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
+	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
+	 * because that one is really hard to get from interrupt context.
+	 */
+	struct drm_display_mode hwmode;
+
+	/**
+	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
+	 * avoid double-disabling and hence corrupting saved state. Needed by
+	 * drivers not using atomic KMS, since those might go through their CRTC
+	 * disabling functions multiple times.
+	 */
+	bool enabled;
+};
+
+int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
+u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
+u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+				   struct timeval *vblanktime);
+void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
+			       struct drm_pending_vblank_event *e);
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+			      struct drm_pending_vblank_event *e);
+bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
+bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
+int drm_crtc_vblank_get(struct drm_crtc *crtc);
+void drm_crtc_vblank_put(struct drm_crtc *crtc);
+void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
+void drm_crtc_vblank_off(struct drm_crtc *crtc);
+void drm_crtc_vblank_reset(struct drm_crtc *crtc);
+void drm_crtc_vblank_on(struct drm_crtc *crtc);
+void drm_vblank_cleanup(struct drm_device *dev);
+u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
+
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+					   unsigned int pipe, int *max_error,
+					   struct timeval *vblank_time,
+					   bool in_vblank_irq);
+void drm_calc_timestamping_constants(struct drm_crtc *crtc,
+				     const struct drm_display_mode *mode);
+wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
+#endif
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 08/37] drm/doc: Polish irq helper documentation
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (7 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 07/37] drm: Extract drm_vblank.[hc] Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-25  7:46   ` Stefan Agner
  2017-05-31  9:22   ` [PATCH] " Daniel Vetter
  2017-05-24 14:51 ` [PATCH 09/37] drm/doc: Drop empty include for drm_color_mgmt.h Daniel Vetter
                   ` (31 subsequent siblings)
  40 siblings, 2 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

Pull a (much shorter) overview into drm_irq.c, and instead put the
callback documentation into in-line comments in drm_drv.h.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-internals.rst | 62 +++++--------------------------------
 drivers/gpu/drm/drm_irq.c           | 30 +++++++++++++++---
 drivers/gpu/drm/drm_vblank.c        |  3 ++
 include/drm/drmP.h                  |  9 ++++--
 include/drm/drm_drv.h               | 33 ++++++++++++++++++--
 5 files changed, 74 insertions(+), 63 deletions(-)

diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index d218dd29221a..82b406d3d377 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -149,60 +149,14 @@ Device Instance and Driver Handling
 Driver Load
 -----------
 
-IRQ Registration
-~~~~~~~~~~~~~~~~
-
-The DRM core tries to facilitate IRQ handler registration and
-unregistration by providing :c:func:`drm_irq_install()` and
-:c:func:`drm_irq_uninstall()` functions. Those functions only
-support a single interrupt per device, devices that use more than one
-IRQs need to be handled manually.
-
-Managed IRQ Registration
-''''''''''''''''''''''''
-
-:c:func:`drm_irq_install()` starts by calling the irq_preinstall
-driver operation. The operation is optional and must make sure that the
-interrupt will not get fired by clearing all pending interrupt flags or
-disabling the interrupt.
-
-The passed-in IRQ will then be requested by a call to
-:c:func:`request_irq()`. If the DRIVER_IRQ_SHARED driver feature
-flag is set, a shared (IRQF_SHARED) IRQ handler will be requested.
-
-The IRQ handler function must be provided as the mandatory irq_handler
-driver operation. It will get passed directly to
-:c:func:`request_irq()` and thus has the same prototype as all IRQ
-handlers. It will get called with a pointer to the DRM device as the
-second argument.
-
-Finally the function calls the optional irq_postinstall driver
-operation. The operation usually enables interrupts (excluding the
-vblank interrupt, which is enabled separately), but drivers may choose
-to enable/disable interrupts at a different time.
-
-:c:func:`drm_irq_uninstall()` is similarly used to uninstall an
-IRQ handler. It starts by waking up all processes waiting on a vblank
-interrupt to make sure they don't hang, and then calls the optional
-irq_uninstall driver operation. The operation must disable all hardware
-interrupts. Finally the function frees the IRQ by calling
-:c:func:`free_irq()`.
-
-Manual IRQ Registration
-'''''''''''''''''''''''
-
-Drivers that require multiple interrupt handlers can't use the managed
-IRQ registration functions. In that case IRQs must be registered and
-unregistered manually (usually with the :c:func:`request_irq()` and
-:c:func:`free_irq()` functions, or their :c:func:`devm_request_irq()` and
-:c:func:`devm_free_irq()` equivalents).
-
-When manually registering IRQs, drivers must not set the
-DRIVER_HAVE_IRQ driver feature flag, and must not provide the
-irq_handler driver operation. They must set the :c:type:`struct
-drm_device <drm_device>` irq_enabled field to 1 upon
-registration of the IRQs, and clear it to 0 after unregistering the
-IRQs.
+IRQ Helper Library
+~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: drivers/gpu/drm/drm_irq.c
+   :doc: irq helpers
+
+.. kernel-doc:: drivers/gpu/drm/drm_irq.c
+   :export:
 
 Memory Manager Initialization
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 28d736c3fcb4..3b04c25100ae 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -62,19 +62,39 @@
 #include "drm_internal.h"
 
 /**
+ * DOC: irq helpers
+ *
+ * The DRM core provides very simple support helpers to enable IRQ handling on a
+ * device through the drm_irq_install() and drm_irq_uninstall() functions. This
+ * only supports devices with a single interrupt on the main device stored in
+ * &drm_device.dev and set as the device paramter in drm_dev_alloc().
+ *
+ * These IRQ helpers are strictly optional. Drivers which roll their own only
+ * need to set &drm_device.irq_enabled to signal the DRM core that vblank
+ * interrupts are working. Since these helpers don't automatically clean up the
+ * requested interrupt like e.g. devm_request_irq() they're not really
+ * recommended.
+ */
+
+/**
  * drm_irq_install - install IRQ handler
  * @dev: DRM device
  * @irq: IRQ number to install the handler for
  *
  * Initializes the IRQ related data. Installs the handler, calling the driver
- * irq_preinstall() and irq_postinstall() functions before and after the
- * installation.
+ * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
+ * and after the installation.
  *
  * This is the simplified helper interface provided for drivers with no special
  * needs. Drivers which need to install interrupt handlers for multiple
  * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
  * that vblank interrupts are available.
  *
+ * @irq must match the interrupt number that would be passed to request_irq(),
+ * if called directly instead of using this helper function.
+ *
+ * &drm_driver.irq_handler is called to handle the registered interrupt.
+ *
  * Returns:
  * Zero on success or a negative error code on failure.
  */
@@ -136,9 +156,9 @@ EXPORT_SYMBOL(drm_irq_install);
  * drm_irq_uninstall - uninstall the IRQ handler
  * @dev: DRM device
  *
- * Calls the driver's irq_uninstall() function and unregisters the IRQ handler.
- * This should only be called by drivers which used drm_irq_install() to set up
- * their interrupt handler. Other drivers must only reset
+ * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ
+ * handler.  This should only be called by drivers which used drm_irq_install()
+ * to set up their interrupt handler. Other drivers must only reset
  * &drm_device.irq_enabled to false.
  *
  * Note that for kernel modesetting drivers it is a bug if this function fails.
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 630dc26379b7..463e4d81fb0d 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -363,6 +363,9 @@ static void vblank_disable_fn(unsigned long arg)
  * @dev: DRM device
  *
  * This function cleans up any resources allocated in drm_vblank_init.
+ *
+ * Drivers which don't use drm_irq_install() need to set &drm_device.irq_enabled
+ * themselves, to signal to the DRM core that vblank interrupts are enabled.
  */
 void drm_vblank_cleanup(struct drm_device *dev)
 {
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 2e0b76cceb97..39df16af7a4a 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -377,8 +377,13 @@ struct drm_device {
 	int last_context;		/**< Last current context */
 	/*@} */
 
-	/** \name VBLANK IRQ support */
-	/*@{ */
+	/**
+	 * @irq_enabled:
+	 *
+	 * Indicates that interrupt handling is enabled, specifically vblank
+	 * handling. Drivers which don't use drm_irq_install() need to set this
+	 * to true manually.
+	 */
 	bool irq_enabled;
 	int irq;
 
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index ebb41688581b..f495eee01302 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -325,11 +325,40 @@ struct drm_driver {
 				     struct timeval *vblank_time,
 				     bool in_vblank_irq);
 
-	/* these have to be filled in */
-
+	/**
+	 * @irq_handler:
+	 *
+	 * Interrupt handler called when using drm_irq_install(). Not used by
+	 * drivers which implement their own interrupt handling.
+	 */
 	irqreturn_t(*irq_handler) (int irq, void *arg);
+
+	/**
+	 * @irq_preinstall:
+	 *
+	 * Optional callback used by drm_irq_install() which is called before
+	 * the interrupt handler is registered. This should be used to clear out
+	 * any pending interrupts (from e.g. firmware based drives) and reset
+	 * the interrupt handling registers.
+	 */
 	void (*irq_preinstall) (struct drm_device *dev);
+
+	/**
+	 * @irq_postinstall:
+	 *
+	 * Optional callback used by drm_irq_install() which is called after
+	 * the interrupt handler is registered. This should be used to enable
+	 * interrupt generation in the hardware.
+	 */
 	int (*irq_postinstall) (struct drm_device *dev);
+
+	/**
+	 * @irq_uninstall:
+	 *
+	 * Optional callback used by drm_irq_uninstall() which is called before
+	 * the interrupt handler is unregistered. This should be used to disable
+	 * interrupt generation in the hardware.
+	 */
 	void (*irq_uninstall) (struct drm_device *dev);
 
 	/**
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 09/37] drm/doc: Drop empty include for drm_color_mgmt.h
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (8 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 08/37] drm/doc: Polish irq helper documentation Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 10/37] drm/doc: vblank cleanup Daniel Vetter
                   ` (30 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development

I'm fed up staring at the error message from kernel-doc that
it can't find anything.

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 Documentation/gpu/drm-kms.rst | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index 2d77c9580164..0749000ab3d7 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -523,9 +523,6 @@ Color Management Properties
 .. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
    :doc: overview
 
-.. kernel-doc:: include/drm/drm_color_mgmt.h
-   :internal:
-
 .. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
    :export:
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 10/37] drm/doc: vblank cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (9 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 09/37] drm/doc: Drop empty include for drm_color_mgmt.h Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-06-15 12:58   ` Thierry Reding
  2017-05-24 14:51 ` [PATCH 11/37] drm/vblank: _ioctl posfix for ioctl handler Daniel Vetter
                   ` (29 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

Unify and review everything, plus make sure it's all correct markup.
Drop the kernel-doc for internal functions. Also rework the overview
section, it's become rather outdated.

Unfortuantely the kernel-doc in drm_driver isn't rendered yet, but
that will change as soon as drm_driver is kernel-docified properly.

Also document properly that drm_vblank_cleanup is optional, the core
calls this already.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-kms.rst |  56 +--------------
 drivers/gpu/drm/drm_vblank.c  | 157 ++++++++++++++++++++----------------------
 include/drm/drmP.h            |  37 ++++++++--
 include/drm/drm_crtc.h        |   3 +
 4 files changed, 112 insertions(+), 141 deletions(-)

diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index 0749000ab3d7..307284125d7a 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -551,60 +551,8 @@ various modules/drivers.
 Vertical Blanking
 =================
 
-Vertical blanking plays a major role in graphics rendering. To achieve
-tear-free display, users must synchronize page flips and/or rendering to
-vertical blanking. The DRM API offers ioctls to perform page flips
-synchronized to vertical blanking and wait for vertical blanking.
-
-The DRM core handles most of the vertical blanking management logic,
-which involves filtering out spurious interrupts, keeping race-free
-blanking counters, coping with counter wrap-around and resets and
-keeping use counts. It relies on the driver to generate vertical
-blanking interrupts and optionally provide a hardware vertical blanking
-counter. Drivers must implement the following operations.
-
--  int (\*enable_vblank) (struct drm_device \*dev, int crtc); void
-   (\*disable_vblank) (struct drm_device \*dev, int crtc);
-   Enable or disable vertical blanking interrupts for the given CRTC.
-
--  u32 (\*get_vblank_counter) (struct drm_device \*dev, int crtc);
-   Retrieve the value of the vertical blanking counter for the given
-   CRTC. If the hardware maintains a vertical blanking counter its value
-   should be returned. Otherwise drivers can use the
-   :c:func:`drm_vblank_count()` helper function to handle this
-   operation.
-
-Drivers must initialize the vertical blanking handling core with a call
-to :c:func:`drm_vblank_init()` in their load operation.
-
-Vertical blanking interrupts can be enabled by the DRM core or by
-drivers themselves (for instance to handle page flipping operations).
-The DRM core maintains a vertical blanking use count to ensure that the
-interrupts are not disabled while a user still needs them. To increment
-the use count, drivers call :c:func:`drm_vblank_get()`. Upon
-return vertical blanking interrupts are guaranteed to be enabled.
-
-To decrement the use count drivers call
-:c:func:`drm_vblank_put()`. Only when the use count drops to zero
-will the DRM core disable the vertical blanking interrupts after a delay
-by scheduling a timer. The delay is accessible through the
-vblankoffdelay module parameter or the ``drm_vblank_offdelay`` global
-variable and expressed in milliseconds. Its default value is 5000 ms.
-Zero means never disable, and a negative value means disable
-immediately. Drivers may override the behaviour by setting the
-:c:type:`struct drm_device <drm_device>`
-vblank_disable_immediate flag, which when set causes vblank interrupts
-to be disabled immediately regardless of the drm_vblank_offdelay
-value. The flag should only be set if there's a properly working
-hardware vblank counter present.
-
-When a vertical blanking interrupt occurs drivers only need to call the
-:c:func:`drm_handle_vblank()` function to account for the
-interrupt.
-
-Resources allocated by :c:func:`drm_vblank_init()` must be freed
-with a call to :c:func:`drm_vblank_cleanup()` in the driver unload
-operation handler.
+.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
+   :doc: vblank handling
 
 Vertical Blanking and Interrupt Handling Functions Reference
 ------------------------------------------------------------
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 463e4d81fb0d..73023d463dc7 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -31,6 +31,41 @@
 #include "drm_trace.h"
 #include "drm_internal.h"
 
+/**
+ * DOC: vblank handling
+ *
+ * Vertical blanking plays a major role in graphics rendering. To achieve
+ * tear-free display, users must synchronize page flips and/or rendering to
+ * vertical blanking. The DRM API offers ioctls to perform page flips
+ * synchronized to vertical blanking and wait for vertical blanking.
+ *
+ * The DRM core handles most of the vertical blanking management logic, which
+ * involves filtering out spurious interrupts, keeping race-free blanking
+ * counters, coping with counter wrap-around and resets and keeping use counts.
+ * It relies on the driver to generate vertical blanking interrupts and
+ * optionally provide a hardware vertical blanking counter.
+ *
+ * Drivers must initialize the vertical blanking handling core with a call to
+ * drm_vblank_init(). Minimally, a driver needs to implement
+ * &drm_crtc_funcs.enable_vblank and &drm_crtc_funcs.disable_vblank plus call
+ * drm_crtc_handle_vblank() in it's vblank interrupt handler for working vblank
+ * support.
+ *
+ * Vertical blanking interrupts can be enabled by the DRM core or by drivers
+ * themselves (for instance to handle page flipping operations).  The DRM core
+ * maintains a vertical blanking use count to ensure that the interrupts are not
+ * disabled while a user still needs them. To increment the use count, drivers
+ * call drm_crtc_vblank_get() and release the vblank reference again with
+ * drm_crtc_vblank_put(). In between these two calls vblank interrupts are
+ * guaranteed to be enabled.
+ *
+ * On many hardware disabling the vblank interrupt cannot be done in a race-free
+ * manner, see &drm_driver.vblank_disable_immediate and
+ * &drm_driver.max_vblank_count. In that case the vblank core only disables the
+ * vblanks after a timer has expired, which can be configured through the
+ * ``vblankoffdelay`` module parameter.
+ */
+
 /* Retry timestamp calculation up to 3 times to satisfy
  * drm_timestamp_precision before giving up.
  */
@@ -262,11 +297,12 @@ static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
  * drm_accurate_vblank_count - retrieve the master vblank counter
  * @crtc: which counter to retrieve
  *
- * This function is similar to @drm_crtc_vblank_count but this
- * function interpolates to handle a race with vblank irq's.
+ * This function is similar to drm_crtc_vblank_count() but this function
+ * interpolates to handle a race with vblank interrupts using the high precision
+ * timestamping support.
  *
- * This is mostly useful for hardware that can obtain the scanout
- * position, but doesn't have a frame counter.
+ * This is mostly useful for hardware that can obtain the scanout position, but
+ * doesn't have a hardware frame counter.
  */
 u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
 {
@@ -362,10 +398,14 @@ static void vblank_disable_fn(unsigned long arg)
  * drm_vblank_cleanup - cleanup vblank support
  * @dev: DRM device
  *
- * This function cleans up any resources allocated in drm_vblank_init.
+ * This function cleans up any resources allocated in drm_vblank_init(). It is
+ * called by the DRM core when @dev is finalized.
+ *
+ * Drivers can call drm_vblank_cleanup() if they need to quiescent the vblank
+ * interrupt in their unload code. But in general this should be handled by
+ * disabling all active &drm_crtc through e.g. drm_atomic_helper_shutdown, which
+ * should end up calling drm_crtc_vblank_off().
  *
- * Drivers which don't use drm_irq_install() need to set &drm_device.irq_enabled
- * themselves, to signal to the DRM core that vblank interrupts are enabled.
  */
 void drm_vblank_cleanup(struct drm_device *dev)
 {
@@ -396,6 +436,8 @@ EXPORT_SYMBOL(drm_vblank_cleanup);
  * @num_crtcs: number of CRTCs supported by @dev
  *
  * This function initializes vblank support for @num_crtcs display pipelines.
+ * Drivers do not need to call drm_vblank_cleanup(), cleanup is already handled
+ * by the DRM core.
  *
  * Returns:
  * Zero on success or a negative error code on failure.
@@ -468,11 +510,11 @@ EXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
  * @crtc: drm_crtc whose timestamp constants should be updated.
  * @mode: display mode containing the scanout timings
  *
- * Calculate and store various constants which are later
- * needed by vblank and swap-completion timestamping, e.g,
- * by drm_calc_vbltimestamp_from_scanoutpos(). They are
- * derived from CRTC's true scanout timing, so they take
- * things like panel scaling or other adjustments into account.
+ * Calculate and store various constants which are later needed by vblank and
+ * swap-completion timestamping, e.g, by
+ * drm_calc_vbltimestamp_from_scanoutpos(). They are derived from CRTC's true
+ * scanout timing, so they take things like panel scaling or other adjustments
+ * into account.
  */
 void drm_calc_timestamping_constants(struct drm_crtc *crtc,
 				     const struct drm_display_mode *mode)
@@ -535,25 +577,14 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  *     if flag is set.
  *
  * Implements calculation of exact vblank timestamps from given drm_display_mode
- * timings and current video scanout position of a CRTC. This can be called from
- * within get_vblank_timestamp() implementation of a kms driver to implement the
- * actual timestamping.
- *
- * Should return timestamps conforming to the OML_sync_control OpenML
- * extension specification. The timestamp corresponds to the end of
- * the vblank interval, aka start of scanout of topmost-leftmost display
- * pixel in the following video frame.
- *
- * Requires support for optional dev->driver->get_scanout_position()
- * in kms driver, plus a bit of setup code to provide a drm_display_mode
- * that corresponds to the true scanout timing.
- *
- * The current implementation only handles standard video modes. It
- * returns as no operation if a doublescan or interlaced video mode is
- * active. Higher level code is expected to handle this.
+ * timings and current video scanout position of a CRTC. This can be directly
+ * used as the &drm_driver.get_vblank_timestamp implementation of a kms driver
+ * if &drm_driver.get_scanout_position is implemented.
  *
- * This function can be used to implement the &drm_driver.get_vblank_timestamp
- * directly, if the driver implements the &drm_driver.get_scanout_position hook.
+ * The current implementation only handles standard video modes. For double scan
+ * and interlaced modes the driver is supposed to adjust the hardware mode
+ * (taken from &drm_crtc_state.adjusted mode for atomic modeset drivers) to
+ * match the scanout position reported.
  *
  * Note that atomic drivers must call drm_calc_timestamping_constants() before
  * enabling a CRTC. The atomic helpers already take care of that in
@@ -738,7 +769,9 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
  *
  * Fetches the "cooked" vblank count value that represents the number of
  * vblank events since the system was booted, including lost events due to
- * modesetting activity.
+ * modesetting activity. Note that this timer isn't correct against a racing
+ * vblank interrupt (since it only reports the software vblank counter), see
+ * drm_accurate_vblank_count() for such use-cases.
  *
  * Returns:
  * The software vblank counter.
@@ -749,20 +782,6 @@ u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_vblank_count);
 
-/**
- * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
- *     system timestamp corresponding to that vblank counter value.
- * @dev: DRM device
- * @pipe: index of CRTC whose counter to retrieve
- * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current vblank counter value.
- *
- * This is the legacy version of drm_crtc_vblank_count_and_time().
- */
 static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
 				     struct timeval *vblanktime)
 {
@@ -852,8 +871,8 @@ static void send_vblank_event(struct drm_device *dev,
  * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
  * possible race with the hardware committing the atomic update.
  *
- * Caller must hold event lock. Caller must also hold a vblank reference for
- * the event @e, which will be dropped when the next vblank arrives.
+ * Caller must hold a vblank reference for the event @e, which will be dropped
+ * when the next vblank arrives.
  */
 void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
 			       struct drm_pending_vblank_event *e)
@@ -913,14 +932,6 @@ static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
 	return dev->driver->enable_vblank(dev, pipe);
 }
 
-/**
- * drm_vblank_enable - enable the vblank interrupt on a CRTC
- * @dev: DRM device
- * @pipe: CRTC index
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
 static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
@@ -958,19 +969,6 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
 	return ret;
 }
 
-/**
- * drm_vblank_get - get a reference count on vblank events
- * @dev: DRM device
- * @pipe: index of CRTC to own
- *
- * Acquire a reference count on vblank events to avoid having them disabled
- * while in use.
- *
- * This is the legacy version of drm_crtc_vblank_get().
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
 static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
@@ -1014,16 +1012,6 @@ int drm_crtc_vblank_get(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_vblank_get);
 
-/**
- * drm_vblank_put - release ownership of vblank events
- * @dev: DRM device
- * @pipe: index of CRTC to release
- *
- * Release ownership of a given vblank counter, turning off interrupts
- * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
- *
- * This is the legacy version of drm_crtc_vblank_put().
- */
 static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
@@ -1067,6 +1055,8 @@ EXPORT_SYMBOL(drm_crtc_vblank_put);
  * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
  * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
  * due to lack of driver support or because the crtc is off.
+ *
+ * This is the legacy version of drm_crtc_wait_one_vblank().
  */
 void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
 {
@@ -1116,7 +1106,7 @@ EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
  * stored so that drm_vblank_on can restore it again.
  *
  * Drivers must use this function when the hardware vblank counter can get
- * reset, e.g. when suspending.
+ * reset, e.g. when suspending or disabling the @crtc in general.
  */
 void drm_crtc_vblank_off(struct drm_crtc *crtc)
 {
@@ -1184,6 +1174,8 @@ EXPORT_SYMBOL(drm_crtc_vblank_off);
  * drm_crtc_vblank_on() functions. The difference compared to
  * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
  * and hence doesn't need to call any driver hooks.
+ *
+ * This is useful for recovering driver state e.g. on driver load, or on resume.
  */
 void drm_crtc_vblank_reset(struct drm_crtc *crtc)
 {
@@ -1212,9 +1204,10 @@ EXPORT_SYMBOL(drm_crtc_vblank_reset);
  * @crtc: CRTC in question
  *
  * This functions restores the vblank interrupt state captured with
- * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
- * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called
- * in driver load code to reflect the current hardware state of the crtc.
+ * drm_crtc_vblank_off() again and is generally called when enabling @crtc. Note
+ * that calls to drm_crtc_vblank_on() and drm_crtc_vblank_off() can be
+ * unbalanced and so can also be unconditionally called in driver load code to
+ * reflect the current hardware state of the crtc.
  */
 void drm_crtc_vblank_on(struct drm_crtc *crtc)
 {
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 39df16af7a4a..3aa3809ab524 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -387,22 +387,49 @@ struct drm_device {
 	bool irq_enabled;
 	int irq;
 
-	/*
+	/**
+	 * @vblank_disable_immediate:
+	 *
 	 * If true, vblank interrupt will be disabled immediately when the
 	 * refcount drops to zero, as opposed to via the vblank disable
 	 * timer.
-	 * This can be set to true it the hardware has a working vblank
-	 * counter and the driver uses drm_vblank_on() and drm_vblank_off()
-	 * appropriately.
+	 *
+	 * This can be set to true it the hardware has a working vblank counter
+	 * with high-precision timestamping (otherwise there are races) and the
+	 * driver uses drm_crtc_vblank_on() and drm_crtc_vblank_off()
+	 * appropriately. See also @max_vblank_count and
+	 * &drm_crtc_funcs.get_vblank_counter.
 	 */
 	bool vblank_disable_immediate;
 
-	/* array of size num_crtcs */
+	/**
+	 * @vblank:
+	 *
+	 * Array of vblank tracking structures, one per &struct drm_crtc. For
+	 * historical reasons (vblank support predates kernel modesetting) this
+	 * is free-standing and not part of &struct drm_crtc itself. It must be
+	 * initialized explicitly by calling drm_vblank_init().
+	 */
 	struct drm_vblank_crtc *vblank;
 
 	spinlock_t vblank_time_lock;    /**< Protects vblank count and time updates during vblank enable/disable */
 	spinlock_t vbl_lock;
 
+	/**
+	 * @max_vblank_count:
+	 *
+	 * Maximum value of the vblank registers. This value +1 will result in a
+	 * wrap-around of the vblank register. It is used by the vblank core to
+	 * handle wrap-arounds.
+	 *
+	 * If set to zero the vblank core will try to guess the elapsed vblanks
+	 * between times when the vblank interrupt is disabled through
+	 * high-precision timestamps. That approach is suffering from small
+	 * races and imprecision over longer time periods, hence exposing a
+	 * hardware vblank counter is always recommended.
+	 *
+	 * If non-zeor, &drm_crtc_funcs.get_vblank_counter must be set.
+	 */
 	u32 max_vblank_count;           /**< size of vblank counter register */
 
 	/**
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index b6e3713bd7a9..5f5d53973ca5 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -683,6 +683,9 @@ struct drm_crtc_funcs {
 	 * drm_crtc_vblank_off() and drm_crtc_vblank_on() when disabling or
 	 * enabling a CRTC.
 	 *
+	 * See also &drm_device.vblank_disable_immediate and
+	 * &drm_device.max_vblank_count.
+	 *
 	 * Returns:
 	 *
 	 * Raw vblank counter value.
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 11/37] drm/vblank: _ioctl posfix for ioctl handler
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (10 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 10/37] drm/doc: vblank cleanup Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 12/37] drm/vblank: Consistent drm_crtc_ prefix Daniel Vetter
                   ` (28 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

I alwasy get confused about drm_wait_vblank for a split second until I
realize it's the ioctl handler. Unconfuse me, and do it for the legacy
modeset vblank control ioctl too.

While at it also noticed that I misplaced the irq ioctl handler in the
internal header file.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/drm_internal.h | 12 ++++++++----
 drivers/gpu/drm/drm_ioctl.c    |  4 ++--
 drivers/gpu/drm/drm_vblank.c   | 22 ++++------------------
 3 files changed, 14 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 6a0cbcc84534..116de27cf8f3 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -59,12 +59,16 @@ extern unsigned int drm_timestamp_monotonic;
 void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
 
 /* IOCTLS */
-int drm_wait_vblank(struct drm_device *dev, void *data,
-		    struct drm_file *filp);
+int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *filp);
+int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data,
+				 struct drm_file *file_priv);
+
+/* drm_irq.c */
+
+/* IOCTLS */
 int drm_legacy_irq_control(struct drm_device *dev, void *data,
 			   struct drm_file *file_priv);
-int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
-			   struct drm_file *file_priv);
 
 /* drm_auth.c */
 int drm_getmagic(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 3690706f051d..306c29bd0eb1 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -600,9 +600,9 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_legacy_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_legacy_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 
-	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_MASTER|DRM_UNLOCKED),
+	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank_ioctl, DRM_MASTER|DRM_UNLOCKED),
 
-	DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_legacy_modeset_ctl, 0),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_legacy_modeset_ctl_ioctl, 0),
 
 	DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 73023d463dc7..7883f3218105 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -1292,8 +1292,8 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
 	}
 }
 
-int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
-			   struct drm_file *file_priv)
+int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data,
+				 struct drm_file *file_priv)
 {
 	struct drm_modeset_ctl *modeset = data;
 	unsigned int pipe;
@@ -1412,22 +1412,8 @@ static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
 					  _DRM_VBLANK_NEXTONMISS));
 }
 
-/*
- * Wait for VBLANK.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param data user argument, pointing to a drm_wait_vblank structure.
- * \return zero on success or a negative number on failure.
- *
- * This function enables the vblank interrupt on the pipe requested, then
- * sleeps waiting for the requested sequence number to occur, and drops
- * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
- * after a timeout with no further vblank waits scheduled).
- */
-int drm_wait_vblank(struct drm_device *dev, void *data,
-		    struct drm_file *file_priv)
+int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
 {
 	struct drm_vblank_crtc *vblank;
 	union drm_wait_vblank *vblwait = data;
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 12/37] drm/vblank: Consistent drm_crtc_ prefix
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (11 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 11/37] drm/vblank: _ioctl posfix for ioctl handler Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 13/37] drm: better document how to send out the crtc disable event Daniel Vetter
                   ` (27 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

We use drm_crtc_ for all the new-style vblank functions which directly
take a struct drm_crtc *. drm_accurate_vblank_count was the odd one
out, correct this to appease my OCD.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/drm_vblank.c           | 8 ++++----
 drivers/gpu/drm/i915/i915_irq.c        | 2 +-
 drivers/gpu/drm/i915/intel_display.c   | 2 +-
 drivers/gpu/drm/nouveau/nv50_display.c | 2 +-
 include/drm/drm_vblank.h               | 2 +-
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 7883f3218105..9263bacb6ebd 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -294,7 +294,7 @@ static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
 }
 
 /**
- * drm_accurate_vblank_count - retrieve the master vblank counter
+ * drm_crtc_accurate_vblank_count - retrieve the master vblank counter
  * @crtc: which counter to retrieve
  *
  * This function is similar to drm_crtc_vblank_count() but this function
@@ -304,7 +304,7 @@ static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
  * This is mostly useful for hardware that can obtain the scanout position, but
  * doesn't have a hardware frame counter.
  */
-u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
+u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
 	unsigned int pipe = drm_crtc_index(crtc);
@@ -323,7 +323,7 @@ u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
 
 	return vblank;
 }
-EXPORT_SYMBOL(drm_accurate_vblank_count);
+EXPORT_SYMBOL(drm_crtc_accurate_vblank_count);
 
 static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
 {
@@ -771,7 +771,7 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
  * vblank events since the system was booted, including lost events due to
  * modesetting activity. Note that this timer isn't correct against a racing
  * vblank interrupt (since it only reports the software vblank counter), see
- * drm_accurate_vblank_count() for such use-cases.
+ * drm_crtc_accurate_vblank_count() for such use-cases.
  *
  * Returns:
  * The software vblank counter.
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 69400ee928e5..912905fa6e3b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1603,7 +1603,7 @@ static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
 		crcs[3] = crc3;
 		crcs[4] = crc4;
 		drm_crtc_add_crc_entry(&crtc->base, true,
-				       drm_accurate_vblank_count(&crtc->base),
+				       drm_crtc_accurate_vblank_count(&crtc->base),
 				       crcs);
 	}
 }
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 7fa21df5bcd7..eeb766fd4a16 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12753,7 +12753,7 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
 	struct drm_device *dev = crtc->base.dev;
 
 	if (!dev->max_vblank_count)
-		return drm_accurate_vblank_count(&crtc->base);
+		return drm_crtc_accurate_vblank_count(&crtc->base);
 
 	return dev->driver->get_vblank_counter(dev, crtc->pipe);
 }
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 9303daa79aba..0ea17ad97819 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -4029,7 +4029,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 		if (crtc->state->event) {
 			unsigned long flags;
 			/* Get correct count/ts if racing with vblank irq */
-			drm_accurate_vblank_count(crtc);
+			drm_crtc_accurate_vblank_count(crtc);
 			spin_lock_irqsave(&crtc->dev->event_lock, flags);
 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
 			spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
index 4cde47332dfa..4ceef128582f 100644
--- a/include/drm/drm_vblank.h
+++ b/include/drm/drm_vblank.h
@@ -169,7 +169,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc);
 void drm_crtc_vblank_reset(struct drm_crtc *crtc);
 void drm_crtc_vblank_on(struct drm_crtc *crtc);
 void drm_vblank_cleanup(struct drm_device *dev);
-u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
+u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
 
 bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 					   unsigned int pipe, int *max_error,
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 13/37] drm: better document how to send out the crtc disable event
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (12 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 12/37] drm/vblank: Consistent drm_crtc_ prefix Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:54   ` Boris Brezillon
  2017-05-30  7:35   ` Neil Armstrong
  2017-05-24 14:51 ` [PATCH 14/37] drm/amd|radeon: Drop drm_vblank_cleanup Daniel Vetter
                   ` (26 subsequent siblings)
  40 siblings, 2 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Daniel Vetter, Intel Graphics Development,
	Boris Brezillon

The kernel doc explained what needs to happen, but not how to most
easily accomplish that using the functions. Fix that.

Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 include/drm/drm_crtc.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 5f5d53973ca5..3a911a64c257 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -214,7 +214,9 @@ struct drm_crtc_state {
 	 *    atomic commit. In that case the event can be send out any time
 	 *    after the hardware has stopped scanning out the current
 	 *    framebuffers. It should contain the timestamp and counter for the
-	 *    last vblank before the display pipeline was shut off.
+	 *    last vblank before the display pipeline was shut off. The simplest
+	 *    way to achieve that is calling drm_crtc_send_vblank_event()
+	 *    somewhen after drm_crtc_vblank_off() has been called.
 	 *
 	 *  - For a CRTC which is enabled at the end of the commit (even when it
 	 *    undergoes an full modeset) the vblank timestamp and counter must
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 14/37] drm/amd|radeon: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (13 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 13/37] drm: better document how to send out the crtc disable event Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 15/37] drm/arcgpu: " Daniel Vetter
                   ` (25 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Alex Deucher, Daniel Vetter, Intel Graphics Development,
	Christian König, Daniel Vetter

Both drivers shut down all crtc beforehand already, which will shut up
any pending vblank (the only thing vblank_cleanup really does is
disable the disable timer). Hence we don't need this here and can
remove it.

Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 1 -
 drivers/gpu/drm/radeon/radeon_irq_kms.c | 1 -
 2 files changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
index a6b7e367a860..4a15be658935 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
@@ -262,7 +262,6 @@ void amdgpu_irq_fini(struct amdgpu_device *adev)
 {
 	unsigned i, j;
 
-	drm_vblank_cleanup(adev->ddev);
 	if (adev->irq.installed) {
 		drm_irq_uninstall(adev->ddev);
 		adev->irq.installed = false;
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 1b7528df7f7f..3efe07f3e3db 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -324,7 +324,6 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
  */
 void radeon_irq_kms_fini(struct radeon_device *rdev)
 {
-	drm_vblank_cleanup(rdev->ddev);
 	if (rdev->irq.installed) {
 		drm_irq_uninstall(rdev->ddev);
 		rdev->irq.installed = false;
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 15/37] drm/arcgpu: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (14 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 14/37] drm/amd|radeon: Drop drm_vblank_cleanup Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:57   ` Alexey Brodkin
  2017-05-24 14:51 ` [PATCH 16/37] drm/hdlcd|mali: " Daniel Vetter
                   ` (24 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Alexey Brodkin, Daniel Vetter

CRTC don't seem to get shut down in a controlled fashion, but no one
bothers to stop interrupts either so this races no matter what. Might
as well remove it. A call to drm_atomic_helper_shutdown would be
pretty sweet somewhere (and maybe getting rid of the load/unload
callbacks while at it).

Cc: Alexey Brodkin <abrodkin@synopsys.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/arc/arcpgu_drv.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c
index 1926b200e4cb..3e43a5d4fb09 100644
--- a/drivers/gpu/drm/arc/arcpgu_drv.c
+++ b/drivers/gpu/drm/arc/arcpgu_drv.c
@@ -155,7 +155,6 @@ static int arcpgu_unload(struct drm_device *drm)
 		arcpgu->fbdev = NULL;
 	}
 	drm_kms_helper_poll_fini(drm);
-	drm_vblank_cleanup(drm);
 	drm_mode_config_cleanup(drm);
 
 	return 0;
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (15 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 15/37] drm/arcgpu: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-31 10:57   ` Liviu Dudau
  2017-05-31 16:37   ` Liviu Dudau
  2017-05-24 14:51 ` [PATCH 17/37] drm/atmel: " Daniel Vetter
                   ` (23 subsequent siblings)
  40 siblings, 2 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Liviu Dudau, Daniel Vetter

IRQs are properly shut down, so it almost works as race-free shutdown.
Except the irq is stopped after the vblank stuff, so boom anyway.
Proper way would be to call drm_atomic_helper_shutdown before any of
the kms things gets stopped. So no harm in removing the
drm_vblank_cleanup here really.

Same story for both hdlcd and mali.

v2: Move misplaced malidp hunk to this patch (Liviu).

Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/arm/hdlcd_drv.c  | 2 --
 drivers/gpu/drm/arm/malidp_drv.c | 2 --
 2 files changed, 4 deletions(-)

diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
index 0f49c4b12772..345c8357b273 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.c
+++ b/drivers/gpu/drm/arm/hdlcd_drv.c
@@ -340,7 +340,6 @@ static int hdlcd_drm_bind(struct device *dev)
 	}
 err_fbdev:
 	drm_kms_helper_poll_fini(drm);
-	drm_vblank_cleanup(drm);
 err_vblank:
 	pm_runtime_disable(drm->dev);
 err_pm_active:
@@ -368,7 +367,6 @@ static void hdlcd_drm_unbind(struct device *dev)
 	}
 	drm_kms_helper_poll_fini(drm);
 	component_unbind_all(dev, drm);
-	drm_vblank_cleanup(drm);
 	pm_runtime_get_sync(drm->dev);
 	drm_irq_uninstall(drm);
 	pm_runtime_put_sync(drm->dev);
diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
index 0d3eb537d08b..01b13d219917 100644
--- a/drivers/gpu/drm/arm/malidp_drv.c
+++ b/drivers/gpu/drm/arm/malidp_drv.c
@@ -652,7 +652,6 @@ static int malidp_bind(struct device *dev)
 	drm_kms_helper_poll_fini(drm);
 fbdev_fail:
 	pm_runtime_get_sync(dev);
-	drm_vblank_cleanup(drm);
 vblank_fail:
 	malidp_se_irq_fini(drm);
 	malidp_de_irq_fini(drm);
@@ -692,7 +691,6 @@ static void malidp_unbind(struct device *dev)
 	}
 	drm_kms_helper_poll_fini(drm);
 	pm_runtime_get_sync(dev);
-	drm_vblank_cleanup(drm);
 	malidp_se_irq_fini(drm);
 	malidp_de_irq_fini(drm);
 	component_unbind_all(dev, drm);
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 17/37] drm/atmel: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (16 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 16/37] drm/hdlcd|mali: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 15:19   ` Boris Brezillon
  2017-05-24 14:51 ` [PATCH 18/37] drm/exynos: " Daniel Vetter
                   ` (22 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Daniel Vetter, Intel Graphics Development

Again almost correct, but since interrupts are shut down after vblank
still a race. Proper cleanup would call drm_atomic_helper_shutdown to
make sure this really is safe.

v2: Remove misplace malidp hunk (Liviu).

Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
index f4a3065f7f51..288dd4b70126 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -678,7 +678,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)
 	flush_workqueue(dc->wq);
 	drm_kms_helper_poll_fini(dev);
 	drm_mode_config_cleanup(dev);
-	drm_vblank_cleanup(dev);
 
 	pm_runtime_get_sync(dev->dev);
 	drm_irq_uninstall(dev);
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 18/37] drm/exynos: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (17 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 17/37] drm/atmel: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-30  0:03   ` Inki Dae
  2017-05-24 14:51 ` [PATCH 19/37] drm/fsl: " Daniel Vetter
                   ` (21 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Seung-Woo Kim,
	Kyungmin Park, Daniel Vetter

Only in the load failure path, where the hardware is quiet anyway.

Cc: Inki Dae <inki.dae@samsung.com>
Cc: Joonyoung Shim <jy0922.shim@samsung.com>
Cc: Seung-Woo Kim <sw0312.kim@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/exynos/exynos_drm_drv.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 50294a7bd29d..1c814b9342af 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -376,7 +376,7 @@ static int exynos_drm_bind(struct device *dev)
 	/* Probe non kms sub drivers and virtual display driver. */
 	ret = exynos_drm_device_subdrv_probe(drm);
 	if (ret)
-		goto err_cleanup_vblank;
+		goto err_unbind_all;
 
 	drm_mode_config_reset(drm);
 
@@ -407,8 +407,6 @@ static int exynos_drm_bind(struct device *dev)
 	exynos_drm_fbdev_fini(drm);
 	drm_kms_helper_poll_fini(drm);
 	exynos_drm_device_subdrv_remove(drm);
-err_cleanup_vblank:
-	drm_vblank_cleanup(drm);
 err_unbind_all:
 	component_unbind_all(drm->dev, drm);
 err_mode_config_cleanup:
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 19/37] drm/fsl: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (18 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 18/37] drm/exynos: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-25  8:18   ` Stefan Agner
  2017-05-24 14:51 ` [PATCH 20/37] drm/hibmc: " Daniel Vetter
                   ` (20 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development

Again cleanup before irq disabling doesn't really stop the races,
so just drop it. Proper fix would be to put drm_atomic_helper_shutdown
before everything gets cleaned up.

Cc: Stefan Agner <stefan@agner.ch>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
index 6e00f4b267f1..b34d09b59eee 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
@@ -109,7 +109,6 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
 		drm_fbdev_cma_fini(fsl_dev->fbdev);
 
 	drm_mode_config_cleanup(dev);
-	drm_vblank_cleanup(dev);
 	drm_irq_uninstall(dev);
 	dev->dev_private = NULL;
 
@@ -127,7 +126,6 @@ static void fsl_dcu_unload(struct drm_device *dev)
 		drm_fbdev_cma_fini(fsl_dev->fbdev);
 
 	drm_mode_config_cleanup(dev);
-	drm_vblank_cleanup(dev);
 	drm_irq_uninstall(dev);
 
 	dev->dev_private = NULL;
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 20/37] drm/hibmc: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (19 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 19/37] drm/fsl: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 21/37] drm/kirin: " Daniel Vetter
                   ` (19 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Xinliang Liu,
	Xinwei Kong, Chen Feng, Rongrong Zou, Daniel Vetter

So this seems to be the first driver that does it the right way round,
so fix it up by calling drm_atomic_helper_shutdown instead. We need to
do that before the last kms user is gone (fbdev emulation), but before
we start shutting down hw stuff like interrupts.

Cc: Xinliang Liu <z.liuxinliang@hisilicon.com>
Cc: Rongrong Zou <zourongrong@gmail.com>
Cc: Xinwei Kong <kong.kongxinwei@hisilicon.com>
Cc: Chen Feng <puck.chen@hisilicon.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 2ffdbf9801bd..4d018ca98581 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -276,11 +276,12 @@ static int hibmc_unload(struct drm_device *dev)
 
 	hibmc_fbdev_fini(priv);
 
+	drm_atomic_helper_shutdown(dev);
+
 	if (dev->irq_enabled)
 		drm_irq_uninstall(dev);
 	if (priv->msi_enabled)
 		pci_disable_msi(dev->pdev);
-	drm_vblank_cleanup(dev);
 
 	hibmc_kms_fini(priv);
 	hibmc_mm_fini(priv);
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 21/37] drm/kirin: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (20 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 20/37] drm/hibmc: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 22/37] drm/i915: " Daniel Vetter
                   ` (18 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Xinliang Liu,
	Xinwei Kong, Chen Feng, Rongrong Zou, Daniel Vetter

Again we probably want a drm_atomic_helper_shutdown somewhere, but
that's a bit more analysis.

Cc: Xinliang Liu <z.liuxinliang@hisilicon.com>
Cc: Rongrong Zou <zourongrong@gmail.com>
Cc: Xinwei Kong <kong.kongxinwei@hisilicon.com>
Cc: Chen Feng <puck.chen@hisilicon.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
index 9c903672f582..8065d6cb1d7f 100644
--- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
@@ -41,7 +41,6 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev)
 	}
 #endif
 	drm_kms_helper_poll_fini(dev);
-	drm_vblank_cleanup(dev);
 	dc_ops->cleanup(to_platform_device(dev->dev));
 	drm_mode_config_cleanup(dev);
 	devm_kfree(dev->dev, priv);
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 22/37] drm/i915: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (21 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 21/37] drm/kirin: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:51 ` [PATCH 23/37] drm/imx: " Daniel Vetter
                   ` (17 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

On the load error path we can't have pending vblank interrupts, and on
unload we already call drm_atomic_helper_shutdown beforehand! So all
good to nuke it.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index cd0bbec36853..6052798bb626 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1278,7 +1278,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	ret = i915_load_modeset_init(&dev_priv->drm);
 	if (ret < 0)
-		goto out_cleanup_vblank;
+		goto out_cleanup_hw;
 
 	i915_driver_register(dev_priv);
 
@@ -1295,8 +1295,6 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	return 0;
 
-out_cleanup_vblank:
-	drm_vblank_cleanup(&dev_priv->drm);
 out_cleanup_hw:
 	i915_driver_cleanup_hw(dev_priv);
 out_cleanup_mmio:
@@ -1332,8 +1330,6 @@ void i915_driver_unload(struct drm_device *dev)
 
 	i915_driver_unregister(dev_priv);
 
-	drm_vblank_cleanup(dev);
-
 	intel_modeset_cleanup(dev);
 
 	/*
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 23/37] drm/imx: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (22 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 22/37] drm/i915: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-29 11:07   ` Philipp Zabel
  2017-05-24 14:51 ` [PATCH 24/37] drm/mtk: " Daniel Vetter
                   ` (16 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

It's only done in the driver load error path, where vblanks don't need
to be quiescent anyway. And that's all drm_vblank_cleanup does, since
the core will release the vblank allocations on its own already. So
drop it.

Cc: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/imx/imx-drm-core.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 50add2f9e250..95e2181963d9 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -278,7 +278,7 @@ static int imx_drm_bind(struct device *dev)
 	/* Now try and bind all our sub-components */
 	ret = component_bind_all(dev, drm);
 	if (ret)
-		goto err_vblank;
+		goto err_kms;
 
 	drm_mode_config_reset(drm);
 
@@ -316,8 +316,6 @@ static int imx_drm_bind(struct device *dev)
 err_unbind:
 #endif
 	component_unbind_all(drm->dev, drm);
-err_vblank:
-	drm_vblank_cleanup(drm);
 err_kms:
 	drm_mode_config_cleanup(drm);
 err_unref:
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 24/37] drm/mtk: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (23 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 23/37] drm/imx: " Daniel Vetter
@ 2017-05-24 14:51 ` Daniel Vetter
  2017-05-24 14:52 ` [PATCH 25/37] drm/meson: " Daniel Vetter
                   ` (15 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:51 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

Seems entirely cargo-culted.

Cc: CK Hu <ck.hu@mediatek.com>
Cc: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_drv.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index f6c8ec4c7dbc..56f802d0a51c 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -266,7 +266,6 @@ static void mtk_drm_kms_deinit(struct drm_device *drm)
 {
 	drm_kms_helper_poll_fini(drm);
 
-	drm_vblank_cleanup(drm);
 	component_unbind_all(drm->dev, drm);
 	drm_mode_config_cleanup(drm);
 }
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 25/37] drm/meson: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (24 preceding siblings ...)
  2017-05-24 14:51 ` [PATCH 24/37] drm/mtk: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-24 15:46   ` Neil Armstrong
  2017-05-24 14:52 ` [PATCH 26/37] drm/mxsfb: " Daniel Vetter
                   ` (14 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Daniel Vetter, Intel Graphics Development, Neil Armstrong

Again seems just cargo-culted.

Cc: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/meson/meson_drv.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 75382f5f0fce..2c605a406ad5 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -285,7 +285,6 @@ static void meson_drv_unbind(struct device *dev)
 	drm_kms_helper_poll_fini(drm);
 	drm_fbdev_cma_fini(priv->fbdev);
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 	drm_dev_unref(drm);
 
 }
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 26/37] drm/mxsfb: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (25 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 25/37] drm/meson: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
       [not found] ` <20170524145212.27837-1-daniel.vetter-/w4YWyX8dFk@public.gmane.org>
                   ` (13 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Marek Vasut, Daniel Vetter, Intel Graphics Development, Daniel Vetter

Almost right but still racy, it's called before the interrupts are
uninstalled. So let's just drop it.

Cc: Marek Vasut <marex@denx.de>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/mxsfb/mxsfb_drv.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index d1b9c34c7c00..1853557fef46 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
@@ -256,7 +256,6 @@ static void mxsfb_unload(struct drm_device *drm)
 
 	drm_kms_helper_poll_fini(drm);
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 
 	pm_runtime_get_sync(drm->dev);
 	drm_irq_uninstall(drm);
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 27/37] drm/nouveau: Drop drm_vblank_cleanup
       [not found] ` <20170524145212.27837-1-daniel.vetter-/w4YWyX8dFk@public.gmane.org>
@ 2017-05-24 14:52   ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Ben Skeggs,
	nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

nouveau_display_vblank_fini is called in the load error path (where it
doesn't matter) and module unload (where vblanks have been shut down
correctly already through drm_vblank_off), we can drop it.

Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: nouveau@lists.freedesktop.org
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/nouveau/nouveau_display.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 8d1df5678eaa..b9a109be989c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -159,8 +159,6 @@ nouveau_display_vblank_fini(struct drm_device *dev)
 {
 	struct drm_crtc *crtc;
 
-	drm_vblank_cleanup(dev);
-
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
 		nvif_notify_fini(&nv_crtc->vblank);
-- 
2.11.0

_______________________________________________
Nouveau mailing list
Nouveau@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/nouveau

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 28/37] drm/rockchip: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (27 preceding siblings ...)
       [not found] ` <20170524145212.27837-1-daniel.vetter-/w4YWyX8dFk@public.gmane.org>
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-24 14:52 ` [PATCH 29/37] drm/shmob: " Daniel Vetter
                   ` (11 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Daniel Vetter, Intel Graphics Development

Either not relevant (in the load error paths) or done better already
(in the unload code, by calling drm_atomic_helper_shutdown). Drop it.

Cc: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index c6b1b7f3a2a3..b9fbf4b1e8f0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -177,7 +177,6 @@ static int rockchip_drm_bind(struct device *dev)
 	rockchip_drm_fbdev_fini(drm_dev);
 err_kms_helper_poll_fini:
 	drm_kms_helper_poll_fini(drm_dev);
-	drm_vblank_cleanup(drm_dev);
 err_unbind_all:
 	component_unbind_all(dev, drm_dev);
 err_mode_config_cleanup:
@@ -200,7 +199,6 @@ static void rockchip_drm_unbind(struct device *dev)
 	drm_kms_helper_poll_fini(drm_dev);
 
 	drm_atomic_helper_shutdown(drm_dev);
-	drm_vblank_cleanup(drm_dev);
 	component_unbind_all(dev, drm_dev);
 	drm_mode_config_cleanup(drm_dev);
 	rockchip_iommu_cleanup(drm_dev);
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 29/37] drm/shmob: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (28 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 28/37] drm/rockchip: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-24 14:52 ` [PATCH 30/37] drm/sti: " Daniel Vetter
                   ` (10 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Laurent Pinchart,
	Daniel Vetter

It doesn't do anything in the driver load error paths that the drm
core doesn't also do.

Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/shmobile/shmob_drm_drv.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
index 800d1d2c435d..c2ca07357aac 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
@@ -277,7 +277,7 @@ static int shmob_drm_probe(struct platform_device *pdev)
 	ret = drm_irq_install(ddev, platform_get_irq(pdev, 0));
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to install IRQ handler\n");
-		goto err_vblank_cleanup;
+		goto err_modeset_cleanup;
 	}
 
 	/*
@@ -292,8 +292,6 @@ static int shmob_drm_probe(struct platform_device *pdev)
 
 err_irq_uninstall:
 	drm_irq_uninstall(ddev);
-err_vblank_cleanup:
-	drm_vblank_cleanup(ddev);
 err_modeset_cleanup:
 	drm_kms_helper_poll_fini(ddev);
 	drm_mode_config_cleanup(ddev);
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 30/37] drm/sti: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (29 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 29/37] drm/shmob: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-06-01 15:37   ` Vincent ABRIOU
  2017-05-24 14:52 ` [PATCH 31/37] drm/stm: " Daniel Vetter
                   ` (9 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Vincent Abriou, Daniel Vetter

Seems entirely cargo-culted.

Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Cc: Vincent Abriou <vincent.abriou@st.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/sti/sti_drv.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
index a4b574283269..06ef1e3886cf 100644
--- a/drivers/gpu/drm/sti/sti_drv.c
+++ b/drivers/gpu/drm/sti/sti_drv.c
@@ -237,7 +237,6 @@ static void sti_cleanup(struct drm_device *ddev)
 	}
 
 	drm_kms_helper_poll_fini(ddev);
-	drm_vblank_cleanup(ddev);
 	component_unbind_all(ddev->dev, ddev);
 	kfree(private);
 	ddev->dev_private = NULL;
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 31/37] drm/stm: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (30 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 30/37] drm/sti: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-29  8:09   ` Philippe CORNU
  2017-05-24 14:52 ` [PATCH 32/37] drm/sun4i: " Daniel Vetter
                   ` (8 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Yannick Fertre, Daniel Vetter, Intel Graphics Development,
	Philippe Cornu, Daniel Vetter

Again seems just cargo-culted.

Cc: Yannick Fertre <yannick.fertre@st.com>
Cc: Philippe Cornu <philippe.cornu@st.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/stm/ltdc.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 700cc0800e51..1b9483d4f2a4 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -1144,8 +1144,6 @@ void ltdc_unload(struct drm_device *ddev)
 
 	DRM_DEBUG_DRIVER("\n");
 
-	drm_vblank_cleanup(ddev);
-
 	if (ldev->panel)
 		drm_panel_detach(ldev->panel);
 
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 32/37] drm/sun4i: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (31 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 31/37] drm/stm: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-29  7:43   ` Maxime Ripard
  2017-05-24 14:52 ` [PATCH 33/37] drm/tegra: " Daniel Vetter
                   ` (7 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Maxime Ripard, Daniel Vetter

Again seems just cargo-culted ... It's not ordered against any
irq/vblank/modeset shutdown.

Cc: Maxime Ripard  <maxime.ripard@free-electrons.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/sun4i/sun4i_drv.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 8ddd72cd5873..c26d5888f8e1 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -138,7 +138,6 @@ static int sun4i_drv_bind(struct device *dev)
 	sun4i_framebuffer_free(drm);
 cleanup_mode_config:
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 free_mem_region:
 	of_reserved_mem_device_release(dev);
 free_drm:
@@ -154,7 +153,6 @@ static void sun4i_drv_unbind(struct device *dev)
 	drm_kms_helper_poll_fini(drm);
 	sun4i_framebuffer_free(drm);
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 	of_reserved_mem_device_release(dev);
 	drm_dev_unref(drm);
 }
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 33/37] drm/tegra: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (32 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 32/37] drm/sun4i: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-06-15 13:00   ` Thierry Reding
  2017-05-24 14:52 ` [PATCH 34/37] drm/udl: " Daniel Vetter
                   ` (6 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

Again, doesn't seem to serve a purpose.

Cc: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/tegra/drm.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 51c48a8e00ec..0d8839244c3a 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -213,12 +213,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	err = tegra_drm_fb_init(drm);
 	if (err < 0)
-		goto vblank;
+		goto device;
 
 	return 0;
 
-vblank:
-	drm_vblank_cleanup(drm);
 device:
 	host1x_device_exit(device);
 fbdev:
@@ -247,7 +245,6 @@ static void tegra_drm_unload(struct drm_device *drm)
 	drm_kms_helper_poll_fini(drm);
 	tegra_drm_fb_exit(drm);
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 
 	err = host1x_device_exit(device);
 	if (err < 0)
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 34/37] drm/udl: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (33 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 33/37] drm/tegra: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-24 14:52 ` [PATCH 35/37] drm/vmwgfx: " Daniel Vetter
                   ` (5 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Daniel Vetter, Intel Graphics Development, Dave Airlie

udl doesn't shut down the display, so stopping the vblank isn't going
to do much good either. Just drop it.

Cc: Dave Airlie <airlied@redhat.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/udl/udl_main.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
index a9d93b871a15..0328b2c7b210 100644
--- a/drivers/gpu/drm/udl/udl_main.c
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -371,8 +371,6 @@ void udl_driver_unload(struct drm_device *dev)
 {
 	struct udl_device *udl = dev->dev_private;
 
-	drm_vblank_cleanup(dev);
-
 	if (udl->urbs.count)
 		udl_free_urb_list(dev);
 
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 35/37] drm/vmwgfx: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (34 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 34/37] drm/udl: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-06-03  5:10   ` Sinclair Yeh
  2017-05-24 14:52 ` [PATCH 36/37] drm/zte: " Daniel Vetter
                   ` (4 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Daniel Vetter, Intel Graphics Development,
	Thomas Hellstrom, Sinclair Yeh

Again stopping the vblank before uninstalling the irq handler is kinda
the wrong way round, but the fb_off stuff should take care of
disabling the dsiplay at least in most cases. So drop the
drm_vblank_cleanup code since it's not really doing anything, it looks
all cargo-culted.

v2: Appease gcc better.

Cc: Sinclair Yeh <syeh@vmware.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  |  9 +++------
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h  |  2 --
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c  |  4 ----
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c |  9 ---------
 drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 27 +--------------------------
 5 files changed, 4 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index a8876b070168..d1f742478b03 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1771,7 +1771,7 @@ int vmw_kms_init(struct vmw_private *dev_priv)
 
 int vmw_kms_close(struct vmw_private *dev_priv)
 {
-	int ret;
+	int ret = 0;
 
 	/*
 	 * Docs says we should take the lock before calling this function
@@ -1779,11 +1779,8 @@ int vmw_kms_close(struct vmw_private *dev_priv)
 	 * drm_encoder_cleanup which takes the lock we deadlock.
 	 */
 	drm_mode_config_cleanup(dev_priv->dev);
-	if (dev_priv->active_display_unit == vmw_du_screen_object)
-		ret = vmw_kms_sou_close_display(dev_priv);
-	else if (dev_priv->active_display_unit == vmw_du_screen_target)
-		ret = vmw_kms_stdu_close_display(dev_priv);
-	else
+	if (dev_priv->active_display_unit != vmw_du_screen_object &&
+	    dev_priv->active_display_unit != vmw_du_screen_target)
 		ret = vmw_kms_ldu_close_display(dev_priv);
 
 	return ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index 13f2f1d2818a..f94b4ca38ab2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -405,7 +405,6 @@ int vmw_kms_update_proxy(struct vmw_resource *res,
  * Screen Objects display functions - vmwgfx_scrn.c
  */
 int vmw_kms_sou_init_display(struct vmw_private *dev_priv);
-int vmw_kms_sou_close_display(struct vmw_private *dev_priv);
 int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
 				 struct vmw_framebuffer *framebuffer,
 				 struct drm_clip_rect *clips,
@@ -433,7 +432,6 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv,
  * Screen Target Display Unit functions - vmwgfx_stdu.c
  */
 int vmw_kms_stdu_init_display(struct vmw_private *dev_priv);
-int vmw_kms_stdu_close_display(struct vmw_private *dev_priv);
 int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,
 			       struct vmw_framebuffer *framebuffer,
 			       struct drm_clip_rect *clips,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index d3987bcf53f8..449ed4fba0f2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -582,13 +582,9 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv)
 
 int vmw_kms_ldu_close_display(struct vmw_private *dev_priv)
 {
-	struct drm_device *dev = dev_priv->dev;
-
 	if (!dev_priv->ldu_priv)
 		return -ENOSYS;
 
-	drm_vblank_cleanup(dev);
-
 	BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
 
 	kfree(dev_priv->ldu_priv);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index 8d7dc9def7c2..3b917c9b0c21 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -746,15 +746,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv)
 	return 0;
 }
 
-int vmw_kms_sou_close_display(struct vmw_private *dev_priv)
-{
-	struct drm_device *dev = dev_priv->dev;
-
-	drm_vblank_cleanup(dev);
-
-	return 0;
-}
-
 static int do_dmabuf_define_gmrfb(struct vmw_private *dev_priv,
 				  struct vmw_framebuffer *framebuffer)
 {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index bad31bdf09b6..b4d4074c0ae0 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -1634,36 +1634,11 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv)
 
 		if (unlikely(ret != 0)) {
 			DRM_ERROR("Failed to initialize STDU %d", i);
-			goto err_vblank_cleanup;
+			return ret;
 		}
 	}
 
 	DRM_INFO("Screen Target Display device initialized\n");
 
 	return 0;
-
-err_vblank_cleanup:
-	drm_vblank_cleanup(dev);
-	return ret;
-}
-
-
-
-/**
- * vmw_kms_stdu_close_display - Cleans up after vmw_kms_stdu_init_display
- *
- * @dev_priv: VMW DRM device
- *
- * Frees up any resources allocated by vmw_kms_stdu_init_display
- *
- * RETURNS:
- * 0 on success
- */
-int vmw_kms_stdu_close_display(struct vmw_private *dev_priv)
-{
-	struct drm_device *dev = dev_priv->dev;
-
-	drm_vblank_cleanup(dev);
-
-	return 0;
 }
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 36/37] drm/zte: Drop drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (35 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 35/37] drm/vmwgfx: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-25  3:01   ` Shawn Guo
  2017-05-24 14:52 ` [PATCH 37/37] drm/vblank: Unexport drm_vblank_cleanup Daniel Vetter
                   ` (3 subsequent siblings)
  40 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Shawn Guo, Daniel Vetter

It again looks all cargo-culted for no good reasons.

Cc: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/zte/zx_drm_drv.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
index 490aafc99610..696836303229 100644
--- a/drivers/gpu/drm/zte/zx_drm_drv.c
+++ b/drivers/gpu/drm/zte/zx_drm_drv.c
@@ -149,7 +149,6 @@ static int zx_drm_bind(struct device *dev)
 out_poll_fini:
 	drm_kms_helper_poll_fini(drm);
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 out_unbind:
 	component_unbind_all(dev, drm);
 out_unregister:
@@ -171,7 +170,6 @@ static void zx_drm_unbind(struct device *dev)
 	}
 	drm_kms_helper_poll_fini(drm);
 	drm_mode_config_cleanup(drm);
-	drm_vblank_cleanup(drm);
 	component_unbind_all(dev, drm);
 	dev_set_drvdata(dev, NULL);
 	drm->dev_private = NULL;
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 37/37] drm/vblank: Unexport drm_vblank_cleanup
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (36 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 36/37] drm/zte: " Daniel Vetter
@ 2017-05-24 14:52 ` Daniel Vetter
  2017-05-24 15:12 ` ✓ Fi.CI.BAT: success for drm: more doc work&cleanup, mostly vblank related Patchwork
                   ` (2 subsequent siblings)
  40 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 14:52 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Jerome Glisse, Daniel Vetter

There's no reason for drivers to call this, and all the ones I've
removed looked very fishy:
- Proper quiescenting of the vblank machinery should be done by
  calling drm_crtc_vblank_off(), which is best done by shutting down
  the entire display engine with drm_atomic_helper_shutdown.

- Releasing of allocated memory is done by the core already, it calls
  drm_vblank_cleanup as a fallback.

- drm_vblank_cleanup also has checks for drivers which forget to clean
  up vblank interrupts.

This essentially reverts

commit e77cef9c2d87db835ad9d70cde4a9b00b0ca2262
Author: Jerome Glisse <jglisse@redhat.com>
Date:   Thu Jan 7 15:39:13 2010 +0100

    drm: Avoid calling vblank function is vblank wasn't initialized

which was done to fix a bug in radeon code with msi interrupts:

commit 003e69f9862bcda89a75c27750efdbc17ac02945
Author: Jerome Glisse <jglisse@redhat.com>
Date:   Thu Jan 7 15:39:14 2010 +0100

    drm/radeon/kms: Don't try to enable IRQ if we have no handler installed

Afaict from digging around in old code, this was needed to avoid
blowing up in the ums fallback, and has stopped serving it's purpose
long ago - if irq init fails, the driver fails to load, and there's
really no way to blow up anymore.

Long story short, this was most likely a small ums compat/fallback
hack that became a thing of it's own and got cargo-cult duplicated all
over the drm codebase for essentially no gain at all.

Cc: Jerome Glisse <jglisse@redhat.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/drm_internal.h |  1 +
 drivers/gpu/drm/drm_vblank.c   | 16 ----------------
 include/drm/drm_vblank.h       |  1 -
 3 files changed, 1 insertion(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 116de27cf8f3..61666e669b43 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -57,6 +57,7 @@ int drm_gem_name_info(struct seq_file *m, void *data);
 /* drm_vblank.c */
 extern unsigned int drm_timestamp_monotonic;
 void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
+void drm_vblank_cleanup(struct drm_device *dev);
 
 /* IOCTLS */
 int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 9263bacb6ebd..b387814da47d 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -394,19 +394,6 @@ static void vblank_disable_fn(unsigned long arg)
 	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 }
 
-/**
- * drm_vblank_cleanup - cleanup vblank support
- * @dev: DRM device
- *
- * This function cleans up any resources allocated in drm_vblank_init(). It is
- * called by the DRM core when @dev is finalized.
- *
- * Drivers can call drm_vblank_cleanup() if they need to quiescent the vblank
- * interrupt in their unload code. But in general this should be handled by
- * disabling all active &drm_crtc through e.g. drm_atomic_helper_shutdown, which
- * should end up calling drm_crtc_vblank_off().
- *
- */
 void drm_vblank_cleanup(struct drm_device *dev)
 {
 	unsigned int pipe;
@@ -428,7 +415,6 @@ void drm_vblank_cleanup(struct drm_device *dev)
 
 	dev->num_crtcs = 0;
 }
-EXPORT_SYMBOL(drm_vblank_cleanup);
 
 /**
  * drm_vblank_init - initialize vblank support
@@ -436,8 +422,6 @@ EXPORT_SYMBOL(drm_vblank_cleanup);
  * @num_crtcs: number of CRTCs supported by @dev
  *
  * This function initializes vblank support for @num_crtcs display pipelines.
- * Drivers do not need to call drm_vblank_cleanup(), cleanup is already handled
- * by the DRM core.
  *
  * Returns:
  * Zero on success or a negative error code on failure.
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
index 4ceef128582f..7fba9efe4951 100644
--- a/include/drm/drm_vblank.h
+++ b/include/drm/drm_vblank.h
@@ -168,7 +168,6 @@ void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
 void drm_crtc_vblank_off(struct drm_crtc *crtc);
 void drm_crtc_vblank_reset(struct drm_crtc *crtc);
 void drm_crtc_vblank_on(struct drm_crtc *crtc);
-void drm_vblank_cleanup(struct drm_device *dev);
 u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
 
 bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 13/37] drm: better document how to send out the crtc disable event
  2017-05-24 14:51 ` [PATCH 13/37] drm: better document how to send out the crtc disable event Daniel Vetter
@ 2017-05-24 14:54   ` Boris Brezillon
  2017-05-30  7:35   ` Neil Armstrong
  1 sibling, 0 replies; 94+ messages in thread
From: Boris Brezillon @ 2017-05-24 14:54 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, 24 May 2017 16:51:48 +0200
Daniel Vetter <daniel.vetter@ffwll.ch> wrote:

> The kernel doc explained what needs to happen, but not how to most
> easily accomplish that using the functions. Fix that.
> 
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>

Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
>  include/drm/drm_crtc.h | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 5f5d53973ca5..3a911a64c257 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -214,7 +214,9 @@ struct drm_crtc_state {
>  	 *    atomic commit. In that case the event can be send out any time
>  	 *    after the hardware has stopped scanning out the current
>  	 *    framebuffers. It should contain the timestamp and counter for the
> -	 *    last vblank before the display pipeline was shut off.
> +	 *    last vblank before the display pipeline was shut off. The simplest
> +	 *    way to achieve that is calling drm_crtc_send_vblank_event()
> +	 *    somewhen after drm_crtc_vblank_off() has been called.
>  	 *
>  	 *  - For a CRTC which is enabled at the end of the commit (even when it
>  	 *    undergoes an full modeset) the vblank timestamp and counter must

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 15/37] drm/arcgpu: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 15/37] drm/arcgpu: " Daniel Vetter
@ 2017-05-24 14:57   ` Alexey Brodkin
  0 siblings, 0 replies; 94+ messages in thread
From: Alexey Brodkin @ 2017-05-24 14:57 UTC (permalink / raw)
  To: daniel.vetter; +Cc: daniel.vetter, intel-gfx, dri-devel

Hi Daniel,

On Wed, 2017-05-24 at 16:51 +0200, Daniel Vetter wrote:
> CRTC don't seem to get shut down in a controlled fashion, but no one
> bothers to stop interrupts either so this races no matter what. Might
> as well remove it. A call to drm_atomic_helper_shutdown would be
> pretty sweet somewhere (and maybe getting rid of the load/unload
> callbacks while at it).
> 
> Cc: Alexey Brodkin <abrodkin@synopsys.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/arc/arcpgu_drv.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c
> index 1926b200e4cb..3e43a5d4fb09 100644
> --- a/drivers/gpu/drm/arc/arcpgu_drv.c
> +++ b/drivers/gpu/drm/arc/arcpgu_drv.c
> @@ -155,7 +155,6 @@ static int arcpgu_unload(struct drm_device *drm)
>  		arcpgu->fbdev = NULL;
>  	}
>  	drm_kms_helper_poll_fini(drm);
> -	drm_vblank_cleanup(drm);
>  	drm_mode_config_cleanup(drm);
>  
>  	return 0;

Reviewed-by: Alexey Brodkin <abrodkin@synopsys.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* ✓ Fi.CI.BAT: success for drm: more doc work&cleanup, mostly vblank related
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (37 preceding siblings ...)
  2017-05-24 14:52 ` [PATCH 37/37] drm/vblank: Unexport drm_vblank_cleanup Daniel Vetter
@ 2017-05-24 15:12 ` Patchwork
  2017-05-24 15:19 ` [PATCH 00/37] " Chris Wilson
  2017-05-31 10:05 ` ✓ Fi.CI.BAT: success for drm: more doc work&cleanup, mostly vblank related (rev4) Patchwork
  40 siblings, 0 replies; 94+ messages in thread
From: Patchwork @ 2017-05-24 15:12 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx

== Series Details ==

Series: drm: more doc work&cleanup, mostly vblank related
URL   : https://patchwork.freedesktop.org/series/24877/
State : success

== Summary ==

Series 24877v1 drm: more doc work&cleanup, mostly vblank related
https://patchwork.freedesktop.org/api/1.0/series/24877/revisions/1/mbox/

fi-bdw-5557u     total:278  pass:267  dwarn:0   dfail:0   fail:0   skip:11  time:441s
fi-bsw-n3050     total:278  pass:242  dwarn:0   dfail:0   fail:0   skip:36  time:581s
fi-bxt-j4205     total:278  pass:259  dwarn:0   dfail:0   fail:0   skip:19  time:512s
fi-byt-j1900     total:278  pass:254  dwarn:0   dfail:0   fail:0   skip:24  time:483s
fi-byt-n2820     total:278  pass:250  dwarn:0   dfail:0   fail:0   skip:28  time:477s
fi-hsw-4770      total:278  pass:262  dwarn:0   dfail:0   fail:0   skip:16  time:415s
fi-hsw-4770r     total:278  pass:262  dwarn:0   dfail:0   fail:0   skip:16  time:415s
fi-ilk-650       total:278  pass:228  dwarn:0   dfail:0   fail:0   skip:50  time:411s
fi-ivb-3520m     total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:488s
fi-ivb-3770      total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:462s
fi-kbl-7500u     total:278  pass:255  dwarn:5   dfail:0   fail:0   skip:18  time:460s
fi-kbl-7560u     total:278  pass:263  dwarn:5   dfail:0   fail:0   skip:10  time:565s
fi-skl-6260u     total:278  pass:268  dwarn:0   dfail:0   fail:0   skip:10  time:455s
fi-skl-6700hq    total:278  pass:239  dwarn:0   dfail:1   fail:17  skip:21  time:434s
fi-skl-6700k     total:278  pass:256  dwarn:4   dfail:0   fail:0   skip:18  time:462s
fi-skl-6770hq    total:278  pass:268  dwarn:0   dfail:0   fail:0   skip:10  time:502s
fi-skl-gvtdvm    total:278  pass:265  dwarn:0   dfail:0   fail:0   skip:13  time:436s
fi-snb-2520m     total:278  pass:250  dwarn:0   dfail:0   fail:0   skip:28  time:528s
fi-snb-2600      total:278  pass:249  dwarn:0   dfail:0   fail:0   skip:29  time:405s

7808a0f3330f5bdf11b5b6880af8407ad6200989 drm-tip: 2017y-05m-24d-11h-04m-21s UTC integration manifest
3df964b drm/doc: vblank cleanup
2baf417 drm/doc: Drop empty include for drm_color_mgmt.h
adacf45 drm/doc: Polish irq helper documentation
8c7743b drm: Extract drm_vblank.[hc]
270da12 drm/doc: Improve ioctl/fops docs a bit more
6a11f4d drm/pci: Deprecate drm_pci_init/exit completely
218b7fa drm: Remove drm_driver->set_busid hook
78a4bf4 drm/udl: Remove dummy busid callback
d974c13 drm: Remove drm_device->virtdev
dc79a30 drm/doc: move printf helpers out of drmP.h

== Logs ==

For more details see: https://intel-gfx-ci.01.org/CI/Patchwork_4801/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (38 preceding siblings ...)
  2017-05-24 15:12 ` ✓ Fi.CI.BAT: success for drm: more doc work&cleanup, mostly vblank related Patchwork
@ 2017-05-24 15:19 ` Chris Wilson
  2017-05-24 15:54   ` [Intel-gfx] " Daniel Vetter
  2017-05-31 10:05 ` ✓ Fi.CI.BAT: success for drm: more doc work&cleanup, mostly vblank related (rev4) Patchwork
  40 siblings, 1 reply; 94+ messages in thread
From: Chris Wilson @ 2017-05-24 15:19 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Intel Graphics Development, DRI Development

On Wed, May 24, 2017 at 04:51:35PM +0200, Daniel Vetter wrote:
> Hi all,
> 
> So the first part of this patch series is the usual deal of decrufting some part
> of drm and attempting to improve the docs. Mostly around vblank. I seem to be
> the ever constant 5% of finally being able to document drm_driver/device
> properly, oh well.
> 
> By patch count the bigger part is trying to get rid of drm_vblank_cleanup. That
> was initially exported to drivers for a radeon ums irq failover hack, and then
> seems to have been cargo-culted all over the place. It's a bit of churn, but it
> will get us one step closer to tracking vblanks as part of drm_crtc. The other
> bit would be to get rid of drm_vblank_init, and instead auto-setup the relevant
> vblank stuff in drm_crtc_init (iff the driver supports vblanks ofc).

Whilst I laud the motive, afaict drm_vblank_cleanup() is still required
after drm_vblank_init(), if only to kfree(dev->vblank). I didn't see
that change in the earlier patches...
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 17/37] drm/atmel: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 17/37] drm/atmel: " Daniel Vetter
@ 2017-05-24 15:19   ` Boris Brezillon
  0 siblings, 0 replies; 94+ messages in thread
From: Boris Brezillon @ 2017-05-24 15:19 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, 24 May 2017 16:51:52 +0200
Daniel Vetter <daniel.vetter@ffwll.ch> wrote:

> Again almost correct, but since interrupts are shut down after vblank
> still a race. Proper cleanup would call drm_atomic_helper_shutdown to
> make sure this really is safe.
> 
> v2: Remove misplace malidp hunk (Liviu).
> 
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> index f4a3065f7f51..288dd4b70126 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> @@ -678,7 +678,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)
>  	flush_workqueue(dc->wq);
>  	drm_kms_helper_poll_fini(dev);
>  	drm_mode_config_cleanup(dev);
> -	drm_vblank_cleanup(dev);
>  
>  	pm_runtime_get_sync(dev->dev);
>  	drm_irq_uninstall(dev);

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 25/37] drm/meson: Drop drm_vblank_cleanup
  2017-05-24 14:52 ` [PATCH 25/37] drm/meson: " Daniel Vetter
@ 2017-05-24 15:46   ` Neil Armstrong
  0 siblings, 0 replies; 94+ messages in thread
From: Neil Armstrong @ 2017-05-24 15:46 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development; +Cc: Daniel Vetter, Intel Graphics Development

On 05/24/2017 04:52 PM, Daniel Vetter wrote:
> Again seems just cargo-culted.
> 
> Cc: Neil Armstrong <narmstrong@baylibre.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/meson/meson_drv.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 75382f5f0fce..2c605a406ad5 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -285,7 +285,6 @@ static void meson_drv_unbind(struct device *dev)
>  	drm_kms_helper_poll_fini(drm);
>  	drm_fbdev_cma_fini(priv->fbdev);
>  	drm_mode_config_cleanup(drm);
> -	drm_vblank_cleanup(drm);
>  	drm_dev_unref(drm);
>  
>  }
> 

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [Intel-gfx] [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related
  2017-05-24 15:19 ` [PATCH 00/37] " Chris Wilson
@ 2017-05-24 15:54   ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-24 15:54 UTC (permalink / raw)
  To: Chris Wilson, Daniel Vetter, DRI Development, Intel Graphics Development

On Wed, May 24, 2017 at 04:19:31PM +0100, Chris Wilson wrote:
> On Wed, May 24, 2017 at 04:51:35PM +0200, Daniel Vetter wrote:
> > Hi all,
> > 
> > So the first part of this patch series is the usual deal of decrufting some part
> > of drm and attempting to improve the docs. Mostly around vblank. I seem to be
> > the ever constant 5% of finally being able to document drm_driver/device
> > properly, oh well.
> > 
> > By patch count the bigger part is trying to get rid of drm_vblank_cleanup. That
> > was initially exported to drivers for a radeon ums irq failover hack, and then
> > seems to have been cargo-culted all over the place. It's a bit of churn, but it
> > will get us one step closer to tracking vblanks as part of drm_crtc. The other
> > bit would be to get rid of drm_vblank_init, and instead auto-setup the relevant
> > vblank stuff in drm_crtc_init (iff the driver supports vblanks ofc).
> 
> Whilst I laud the motive, afaict drm_vblank_cleanup() is still required
> after drm_vblank_init(), if only to kfree(dev->vblank). I didn't see
> that change in the earlier patches...

drm_dev_release -> drm_dev_fini -> drm_vblank_cleanup, and has been there
even before drm_vblank_cleanup was exported to drivers (which only
happened recent-ish, see the last patch).

I should probably dump some of the history digging into the earlier
patches, atm the patch series reads a bit backwards :-/

Or did I miss something in my auditing?
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 36/37] drm/zte: Drop drm_vblank_cleanup
  2017-05-24 14:52 ` [PATCH 36/37] drm/zte: " Daniel Vetter
@ 2017-05-25  3:01   ` Shawn Guo
  2017-05-26  6:57     ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Shawn Guo @ 2017-05-25  3:01 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, May 24, 2017 at 04:52:11PM +0200, Daniel Vetter wrote:
> It again looks all cargo-culted for no good reasons.

drm_vblank_cleanup() is called to release the resources allocated by
drm_vblank_init().  I think that's the reason, no?

Shawn

> 
> Cc: Shawn Guo <shawnguo@kernel.org>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/zte/zx_drm_drv.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
> index 490aafc99610..696836303229 100644
> --- a/drivers/gpu/drm/zte/zx_drm_drv.c
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.c
> @@ -149,7 +149,6 @@ static int zx_drm_bind(struct device *dev)
>  out_poll_fini:
>  	drm_kms_helper_poll_fini(drm);
>  	drm_mode_config_cleanup(drm);
> -	drm_vblank_cleanup(drm);
>  out_unbind:
>  	component_unbind_all(dev, drm);
>  out_unregister:
> @@ -171,7 +170,6 @@ static void zx_drm_unbind(struct device *dev)
>  	}
>  	drm_kms_helper_poll_fini(drm);
>  	drm_mode_config_cleanup(drm);
> -	drm_vblank_cleanup(drm);
>  	component_unbind_all(dev, drm);
>  	dev_set_drvdata(dev, NULL);
>  	drm->dev_private = NULL;
> -- 
> 2.11.0
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 08/37] drm/doc: Polish irq helper documentation
  2017-05-24 14:51 ` [PATCH 08/37] drm/doc: Polish irq helper documentation Daniel Vetter
@ 2017-05-25  7:46   ` Stefan Agner
  2017-05-29 18:58     ` Daniel Vetter
  2017-05-31  9:22   ` [PATCH] " Daniel Vetter
  1 sibling, 1 reply; 94+ messages in thread
From: Stefan Agner @ 2017-05-25  7:46 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On 2017-05-24 07:51, Daniel Vetter wrote:
> Pull a (much shorter) overview into drm_irq.c, and instead put the
> callback documentation into in-line comments in drm_drv.h.

Looks good and just found all I needed to know to fix IRQ registration
in fsl dcu.

Reviewed-by: Stefan Agner <stefan@agner.ch>

> 
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  Documentation/gpu/drm-internals.rst | 62 +++++--------------------------------
>  drivers/gpu/drm/drm_irq.c           | 30 +++++++++++++++---
>  drivers/gpu/drm/drm_vblank.c        |  3 ++
>  include/drm/drmP.h                  |  9 ++++--
>  include/drm/drm_drv.h               | 33 ++++++++++++++++++--
>  5 files changed, 74 insertions(+), 63 deletions(-)
> 
> diff --git a/Documentation/gpu/drm-internals.rst
> b/Documentation/gpu/drm-internals.rst
> index d218dd29221a..82b406d3d377 100644
> --- a/Documentation/gpu/drm-internals.rst
> +++ b/Documentation/gpu/drm-internals.rst
> @@ -149,60 +149,14 @@ Device Instance and Driver Handling
>  Driver Load
>  -----------
>  
> -IRQ Registration
> -~~~~~~~~~~~~~~~~
> -
> -The DRM core tries to facilitate IRQ handler registration and
> -unregistration by providing :c:func:`drm_irq_install()` and
> -:c:func:`drm_irq_uninstall()` functions. Those functions only
> -support a single interrupt per device, devices that use more than one
> -IRQs need to be handled manually.
> -
> -Managed IRQ Registration
> -''''''''''''''''''''''''
> -
> -:c:func:`drm_irq_install()` starts by calling the irq_preinstall
> -driver operation. The operation is optional and must make sure that the
> -interrupt will not get fired by clearing all pending interrupt flags or
> -disabling the interrupt.
> -
> -The passed-in IRQ will then be requested by a call to
> -:c:func:`request_irq()`. If the DRIVER_IRQ_SHARED driver feature
> -flag is set, a shared (IRQF_SHARED) IRQ handler will be requested.
> -
> -The IRQ handler function must be provided as the mandatory irq_handler
> -driver operation. It will get passed directly to
> -:c:func:`request_irq()` and thus has the same prototype as all IRQ
> -handlers. It will get called with a pointer to the DRM device as the
> -second argument.
> -
> -Finally the function calls the optional irq_postinstall driver
> -operation. The operation usually enables interrupts (excluding the
> -vblank interrupt, which is enabled separately), but drivers may choose
> -to enable/disable interrupts at a different time.
> -
> -:c:func:`drm_irq_uninstall()` is similarly used to uninstall an
> -IRQ handler. It starts by waking up all processes waiting on a vblank
> -interrupt to make sure they don't hang, and then calls the optional
> -irq_uninstall driver operation. The operation must disable all hardware
> -interrupts. Finally the function frees the IRQ by calling
> -:c:func:`free_irq()`.
> -
> -Manual IRQ Registration
> -'''''''''''''''''''''''
> -
> -Drivers that require multiple interrupt handlers can't use the managed
> -IRQ registration functions. In that case IRQs must be registered and
> -unregistered manually (usually with the :c:func:`request_irq()` and
> -:c:func:`free_irq()` functions, or their :c:func:`devm_request_irq()` and
> -:c:func:`devm_free_irq()` equivalents).
> -
> -When manually registering IRQs, drivers must not set the
> -DRIVER_HAVE_IRQ driver feature flag, and must not provide the
> -irq_handler driver operation. They must set the :c:type:`struct
> -drm_device <drm_device>` irq_enabled field to 1 upon
> -registration of the IRQs, and clear it to 0 after unregistering the
> -IRQs.
> +IRQ Helper Library
> +~~~~~~~~~~~~~~~~~~
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> +   :doc: irq helpers
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> +   :export:
>  
>  Memory Manager Initialization
>  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> index 28d736c3fcb4..3b04c25100ae 100644
> --- a/drivers/gpu/drm/drm_irq.c
> +++ b/drivers/gpu/drm/drm_irq.c
> @@ -62,19 +62,39 @@
>  #include "drm_internal.h"
>  
>  /**
> + * DOC: irq helpers
> + *
> + * The DRM core provides very simple support helpers to enable IRQ
> handling on a
> + * device through the drm_irq_install() and drm_irq_uninstall() functions. This
> + * only supports devices with a single interrupt on the main device stored in
> + * &drm_device.dev and set as the device paramter in drm_dev_alloc().
> + *
> + * These IRQ helpers are strictly optional. Drivers which roll their own only
> + * need to set &drm_device.irq_enabled to signal the DRM core that vblank
> + * interrupts are working. Since these helpers don't automatically clean up the
> + * requested interrupt like e.g. devm_request_irq() they're not really
> + * recommended.
> + */
> +
> +/**
>   * drm_irq_install - install IRQ handler
>   * @dev: DRM device
>   * @irq: IRQ number to install the handler for
>   *
>   * Initializes the IRQ related data. Installs the handler, calling the driver
> - * irq_preinstall() and irq_postinstall() functions before and after the
> - * installation.
> + * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
> + * and after the installation.
>   *
>   * This is the simplified helper interface provided for drivers with no special
>   * needs. Drivers which need to install interrupt handlers for multiple
>   * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
>   * that vblank interrupts are available.
>   *
> + * @irq must match the interrupt number that would be passed to request_irq(),
> + * if called directly instead of using this helper function.
> + *
> + * &drm_driver.irq_handler is called to handle the registered interrupt.
> + *
>   * Returns:
>   * Zero on success or a negative error code on failure.
>   */
> @@ -136,9 +156,9 @@ EXPORT_SYMBOL(drm_irq_install);
>   * drm_irq_uninstall - uninstall the IRQ handler
>   * @dev: DRM device
>   *
> - * Calls the driver's irq_uninstall() function and unregisters the IRQ handler.
> - * This should only be called by drivers which used drm_irq_install() to set up
> - * their interrupt handler. Other drivers must only reset
> + * Calls the driver's &drm_driver.irq_uninstall function and
> unregisters the IRQ
> + * handler.  This should only be called by drivers which used drm_irq_install()
> + * to set up their interrupt handler. Other drivers must only reset
>   * &drm_device.irq_enabled to false.
>   *
>   * Note that for kernel modesetting drivers it is a bug if this function fails.
> diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
> index 630dc26379b7..463e4d81fb0d 100644
> --- a/drivers/gpu/drm/drm_vblank.c
> +++ b/drivers/gpu/drm/drm_vblank.c
> @@ -363,6 +363,9 @@ static void vblank_disable_fn(unsigned long arg)
>   * @dev: DRM device
>   *
>   * This function cleans up any resources allocated in drm_vblank_init.
> + *
> + * Drivers which don't use drm_irq_install() need to set
> &drm_device.irq_enabled
> + * themselves, to signal to the DRM core that vblank interrupts are enabled.
>   */
>  void drm_vblank_cleanup(struct drm_device *dev)
>  {
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 2e0b76cceb97..39df16af7a4a 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -377,8 +377,13 @@ struct drm_device {
>  	int last_context;		/**< Last current context */
>  	/*@} */
>  
> -	/** \name VBLANK IRQ support */
> -	/*@{ */
> +	/**
> +	 * @irq_enabled:
> +	 *
> +	 * Indicates that interrupt handling is enabled, specifically vblank
> +	 * handling. Drivers which don't use drm_irq_install() need to set this
> +	 * to true manually.
> +	 */
>  	bool irq_enabled;
>  	int irq;
>  
> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> index ebb41688581b..f495eee01302 100644
> --- a/include/drm/drm_drv.h
> +++ b/include/drm/drm_drv.h
> @@ -325,11 +325,40 @@ struct drm_driver {
>  				     struct timeval *vblank_time,
>  				     bool in_vblank_irq);
>  
> -	/* these have to be filled in */
> -
> +	/**
> +	 * @irq_handler:
> +	 *
> +	 * Interrupt handler called when using drm_irq_install(). Not used by
> +	 * drivers which implement their own interrupt handling.
> +	 */
>  	irqreturn_t(*irq_handler) (int irq, void *arg);
> +
> +	/**
> +	 * @irq_preinstall:
> +	 *
> +	 * Optional callback used by drm_irq_install() which is called before
> +	 * the interrupt handler is registered. This should be used to clear out
> +	 * any pending interrupts (from e.g. firmware based drives) and reset
> +	 * the interrupt handling registers.
> +	 */
>  	void (*irq_preinstall) (struct drm_device *dev);
> +
> +	/**
> +	 * @irq_postinstall:
> +	 *
> +	 * Optional callback used by drm_irq_install() which is called after
> +	 * the interrupt handler is registered. This should be used to enable
> +	 * interrupt generation in the hardware.
> +	 */
>  	int (*irq_postinstall) (struct drm_device *dev);
> +
> +	/**
> +	 * @irq_uninstall:
> +	 *
> +	 * Optional callback used by drm_irq_uninstall() which is called before
> +	 * the interrupt handler is unregistered. This should be used to disable
> +	 * interrupt generation in the hardware.
> +	 */
>  	void (*irq_uninstall) (struct drm_device *dev);
>  
>  	/**
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 19/37] drm/fsl: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 19/37] drm/fsl: " Daniel Vetter
@ 2017-05-25  8:18   ` Stefan Agner
  2017-05-26  7:00     ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Stefan Agner @ 2017-05-25  8:18 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Intel Graphics Development, DRI Development

On 2017-05-24 07:51, Daniel Vetter wrote:
> Again cleanup before irq disabling doesn't really stop the races,
> so just drop it. Proper fix would be to put drm_atomic_helper_shutdown
> before everything gets cleaned up.

Hm, I already use the non-atomic drm_crtc_force_disable_all before, I
think that fixed the races I saw.

But I guess what you are saying instead of using
drm_crtc_force_disable_all I should use drm_atomic_helper_shutdown...?

Will try that.

FWIW,

Acked-by: Stefan Agner <stefan@agner.ch>

--
Stefan

> 
> Cc: Stefan Agner <stefan@agner.ch>
> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
> b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
> index 6e00f4b267f1..b34d09b59eee 100644
> --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
> +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
> @@ -109,7 +109,6 @@ static int fsl_dcu_load(struct drm_device *dev,
> unsigned long flags)
>  		drm_fbdev_cma_fini(fsl_dev->fbdev);
>  
>  	drm_mode_config_cleanup(dev);
> -	drm_vblank_cleanup(dev);
>  	drm_irq_uninstall(dev);
>  	dev->dev_private = NULL;
>  
> @@ -127,7 +126,6 @@ static void fsl_dcu_unload(struct drm_device *dev)
>  		drm_fbdev_cma_fini(fsl_dev->fbdev);
>  
>  	drm_mode_config_cleanup(dev);
> -	drm_vblank_cleanup(dev);
>  	drm_irq_uninstall(dev);
>  
>  	dev->dev_private = NULL;
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 36/37] drm/zte: Drop drm_vblank_cleanup
  2017-05-25  3:01   ` Shawn Guo
@ 2017-05-26  6:57     ` Daniel Vetter
  2017-05-26 11:04       ` Shawn Guo
  0 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-26  6:57 UTC (permalink / raw)
  To: Shawn Guo; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Thu, May 25, 2017 at 5:01 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> On Wed, May 24, 2017 at 04:52:11PM +0200, Daniel Vetter wrote:
>> It again looks all cargo-culted for no good reasons.
>
> drm_vblank_cleanup() is called to release the resources allocated by
> drm_vblank_init().  I think that's the reason, no?

Pls read the code and the last patch in this series. The drm core
already calls drm_vblank_cleanup for you, the only reason it was
exported was to handle certain usersmodesetting fallback scenarios in
radeon.ko.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 19/37] drm/fsl: Drop drm_vblank_cleanup
  2017-05-25  8:18   ` Stefan Agner
@ 2017-05-26  7:00     ` Daniel Vetter
  2017-05-30 21:17       ` Stefan Agner
  0 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-26  7:00 UTC (permalink / raw)
  To: Stefan Agner; +Cc: Intel Graphics Development, DRI Development

On Thu, May 25, 2017 at 10:18 AM, Stefan Agner <stefan@agner.ch> wrote:
> On 2017-05-24 07:51, Daniel Vetter wrote:
>> Again cleanup before irq disabling doesn't really stop the races,
>> so just drop it. Proper fix would be to put drm_atomic_helper_shutdown
>> before everything gets cleaned up.
>
> Hm, I already use the non-atomic drm_crtc_force_disable_all before, I
> think that fixed the races I saw.
>
> But I guess what you are saying instead of using
> drm_crtc_force_disable_all I should use drm_atomic_helper_shutdown...?

Yes. I thought I audited all existing users of the legacy
force_disable (it won't work correctly for atomic drivers), but
somehow I missed the one in fsl. See

commit 18dddadc78c91a91b546acc48506c24f5f840c4f
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date:   Tue Mar 21 17:41:49 2017 +0100

    drm/atomic: Introduce drm_atomic_helper_shutdown

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 36/37] drm/zte: Drop drm_vblank_cleanup
  2017-05-26  6:57     ` Daniel Vetter
@ 2017-05-26 11:04       ` Shawn Guo
  0 siblings, 0 replies; 94+ messages in thread
From: Shawn Guo @ 2017-05-26 11:04 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Fri, May 26, 2017 at 08:57:32AM +0200, Daniel Vetter wrote:
> On Thu, May 25, 2017 at 5:01 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> > On Wed, May 24, 2017 at 04:52:11PM +0200, Daniel Vetter wrote:
> >> It again looks all cargo-culted for no good reasons.
> >
> > drm_vblank_cleanup() is called to release the resources allocated by
> > drm_vblank_init().  I think that's the reason, no?
> 
> Pls read the code and the last patch in this series. The drm core
> already calls drm_vblank_cleanup for you, the only reason it was
> exported was to handle certain usersmodesetting fallback scenarios in
> radeon.ko.

Ah, okay.  The drm_dev_unref() in the error and unbind path will
eventually triggers drm_dev_fini() where drm_vblank_cleanup() will get
called.

Thanks for the hint.  FWIW,

Acked-by: Shawn Guo <shawnguo@kernel.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/37] drm: Remove drm_device->virtdev
  2017-05-24 14:51 ` Daniel Vetter
  2017-05-29  6:52   ` Gerd Hoffmann
@ 2017-05-29  6:52   ` Gerd Hoffmann
  2017-05-30  7:33   ` Neil Armstrong
  2017-05-30  7:33   ` Neil Armstrong
  3 siblings, 0 replies; 94+ messages in thread
From: Gerd Hoffmann @ 2017-05-29  6:52 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development
  Cc: David Airlie, Daniel Vetter, Intel Graphics Development, virtualization

On Wed, 2017-05-24 at 16:51 +0200, Daniel Vetter wrote:
> This is a leftover from the drm_bus days, where we've had a
> bus-specific device type for every bus type in drm_device. Except for
> pci (which we can't remove because dri1 drivers) this is all gone.
> And
> the virt driver also doesn't really need it, dev_to_virtio works
> perfectly fine.

Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/37] drm: Remove drm_device->virtdev
  2017-05-24 14:51 ` Daniel Vetter
@ 2017-05-29  6:52   ` Gerd Hoffmann
  2017-05-29  6:52   ` Gerd Hoffmann
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 94+ messages in thread
From: Gerd Hoffmann @ 2017-05-29  6:52 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development
  Cc: David Airlie, Daniel Vetter, Intel Graphics Development, virtualization

On Wed, 2017-05-24 at 16:51 +0200, Daniel Vetter wrote:
> This is a leftover from the drm_bus days, where we've had a
> bus-specific device type for every bus type in drm_device. Except for
> pci (which we can't remove because dri1 drivers) this is all gone.
> And
> the virt driver also doesn't really need it, dev_to_virtio works
> perfectly fine.

Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 32/37] drm/sun4i: Drop drm_vblank_cleanup
  2017-05-24 14:52 ` [PATCH 32/37] drm/sun4i: " Daniel Vetter
@ 2017-05-29  7:43   ` Maxime Ripard
  0 siblings, 0 replies; 94+ messages in thread
From: Maxime Ripard @ 2017-05-29  7:43 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development


[-- Attachment #1.1: Type: text/plain, Size: 457 bytes --]

On Wed, May 24, 2017 at 04:52:07PM +0200, Daniel Vetter wrote:
> Again seems just cargo-culted ... It's not ordered against any
> irq/vblank/modeset shutdown.
> 
> Cc: Maxime Ripard  <maxime.ripard@free-electrons.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>

Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 31/37] drm/stm: Drop drm_vblank_cleanup
  2017-05-24 14:52 ` [PATCH 31/37] drm/stm: " Daniel Vetter
@ 2017-05-29  8:09   ` Philippe CORNU
  0 siblings, 0 replies; 94+ messages in thread
From: Philippe CORNU @ 2017-05-29  8:09 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development
  Cc: Yannick FERTRE, Daniel Vetter, Intel Graphics Development



On 05/24/2017 04:52 PM, Daniel Vetter wrote:
> Again seems just cargo-culted.
> 
> Cc: Yannick Fertre <yannick.fertre@st.com>
> Cc: Philippe Cornu <philippe.cornu@st.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>   drivers/gpu/drm/stm/ltdc.c | 2 --
>   1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
> index 700cc0800e51..1b9483d4f2a4 100644
> --- a/drivers/gpu/drm/stm/ltdc.c
> +++ b/drivers/gpu/drm/stm/ltdc.c
> @@ -1144,8 +1144,6 @@ void ltdc_unload(struct drm_device *ddev)
>   
>   	DRM_DEBUG_DRIVER("\n");
>   
> -	drm_vblank_cleanup(ddev);
> -
>   	if (ldev->panel)
>   		drm_panel_detach(ldev->panel);
>   
> 

Acked-by: Philippe Cornu <philippe.cornu@st.com>

Many thanks
Philippe
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 23/37] drm/imx: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 23/37] drm/imx: " Daniel Vetter
@ 2017-05-29 11:07   ` Philipp Zabel
  2017-05-31  8:51     ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Philipp Zabel @ 2017-05-29 11:07 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

Hi Daniel,

On Wed, 2017-05-24 at 16:51 +0200, Daniel Vetter wrote:
> It's only done in the driver load error path, where vblanks don't need
> to be quiescent anyway. And that's all drm_vblank_cleanup does, since
> the core will release the vblank allocations on its own already. So
> drop it.

Thank you for cleaning this up and improving the docs.
From the function name and kerneldoc comment, it was really not clear
that this function is already called in the drm_device release path.

I think the comment is slightly misleading though, as drm_vblank_cleanup
does call kfree(dev->vblank).

> Cc: Philipp Zabel <p.zabel@pengutronix.de>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
>
> ---
>  drivers/gpu/drm/imx/imx-drm-core.c | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
> index 50add2f9e250..95e2181963d9 100644
> --- a/drivers/gpu/drm/imx/imx-drm-core.c
> +++ b/drivers/gpu/drm/imx/imx-drm-core.c
> @@ -278,7 +278,7 @@ static int imx_drm_bind(struct device *dev)
>  	/* Now try and bind all our sub-components */
>  	ret = component_bind_all(dev, drm);
>  	if (ret)
> -		goto err_vblank;
> +		goto err_kms;
>  
>  	drm_mode_config_reset(drm);
>  
> @@ -316,8 +316,6 @@ static int imx_drm_bind(struct device *dev)
>  err_unbind:
>  #endif
>  	component_unbind_all(drm->dev, drm);
> -err_vblank:
> -	drm_vblank_cleanup(drm);
>  err_kms:
>  	drm_mode_config_cleanup(drm);
>  err_unref:

As I understand, the drm_dev_unref(drm) that follows this causes
drm_dev_release -> drm_dev_fini -> drm_vblank_cleanup to be called, so

Acked-by: Philipp Zabel <p.zabel@pengutronix.de>

regards
Philipp

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 08/37] drm/doc: Polish irq helper documentation
  2017-05-25  7:46   ` Stefan Agner
@ 2017-05-29 18:58     ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-29 18:58 UTC (permalink / raw)
  To: Stefan Agner
  Cc: Daniel Vetter, Intel Graphics Development, DRI Development,
	Daniel Vetter

On Thu, May 25, 2017 at 12:46:29AM -0700, Stefan Agner wrote:
> On 2017-05-24 07:51, Daniel Vetter wrote:
> > Pull a (much shorter) overview into drm_irq.c, and instead put the
> > callback documentation into in-line comments in drm_drv.h.
> 
> Looks good and just found all I needed to know to fix IRQ registration
> in fsl dcu.
> 
> Reviewed-by: Stefan Agner <stefan@agner.ch>

Can you pls ack/review the previous patch 7 too? I can't apply this one
here with the other one ...

Thanks, Daniel

> 
> > 
> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> > ---
> >  Documentation/gpu/drm-internals.rst | 62 +++++--------------------------------
> >  drivers/gpu/drm/drm_irq.c           | 30 +++++++++++++++---
> >  drivers/gpu/drm/drm_vblank.c        |  3 ++
> >  include/drm/drmP.h                  |  9 ++++--
> >  include/drm/drm_drv.h               | 33 ++++++++++++++++++--
> >  5 files changed, 74 insertions(+), 63 deletions(-)
> > 
> > diff --git a/Documentation/gpu/drm-internals.rst
> > b/Documentation/gpu/drm-internals.rst
> > index d218dd29221a..82b406d3d377 100644
> > --- a/Documentation/gpu/drm-internals.rst
> > +++ b/Documentation/gpu/drm-internals.rst
> > @@ -149,60 +149,14 @@ Device Instance and Driver Handling
> >  Driver Load
> >  -----------
> >  
> > -IRQ Registration
> > -~~~~~~~~~~~~~~~~
> > -
> > -The DRM core tries to facilitate IRQ handler registration and
> > -unregistration by providing :c:func:`drm_irq_install()` and
> > -:c:func:`drm_irq_uninstall()` functions. Those functions only
> > -support a single interrupt per device, devices that use more than one
> > -IRQs need to be handled manually.
> > -
> > -Managed IRQ Registration
> > -''''''''''''''''''''''''
> > -
> > -:c:func:`drm_irq_install()` starts by calling the irq_preinstall
> > -driver operation. The operation is optional and must make sure that the
> > -interrupt will not get fired by clearing all pending interrupt flags or
> > -disabling the interrupt.
> > -
> > -The passed-in IRQ will then be requested by a call to
> > -:c:func:`request_irq()`. If the DRIVER_IRQ_SHARED driver feature
> > -flag is set, a shared (IRQF_SHARED) IRQ handler will be requested.
> > -
> > -The IRQ handler function must be provided as the mandatory irq_handler
> > -driver operation. It will get passed directly to
> > -:c:func:`request_irq()` and thus has the same prototype as all IRQ
> > -handlers. It will get called with a pointer to the DRM device as the
> > -second argument.
> > -
> > -Finally the function calls the optional irq_postinstall driver
> > -operation. The operation usually enables interrupts (excluding the
> > -vblank interrupt, which is enabled separately), but drivers may choose
> > -to enable/disable interrupts at a different time.
> > -
> > -:c:func:`drm_irq_uninstall()` is similarly used to uninstall an
> > -IRQ handler. It starts by waking up all processes waiting on a vblank
> > -interrupt to make sure they don't hang, and then calls the optional
> > -irq_uninstall driver operation. The operation must disable all hardware
> > -interrupts. Finally the function frees the IRQ by calling
> > -:c:func:`free_irq()`.
> > -
> > -Manual IRQ Registration
> > -'''''''''''''''''''''''
> > -
> > -Drivers that require multiple interrupt handlers can't use the managed
> > -IRQ registration functions. In that case IRQs must be registered and
> > -unregistered manually (usually with the :c:func:`request_irq()` and
> > -:c:func:`free_irq()` functions, or their :c:func:`devm_request_irq()` and
> > -:c:func:`devm_free_irq()` equivalents).
> > -
> > -When manually registering IRQs, drivers must not set the
> > -DRIVER_HAVE_IRQ driver feature flag, and must not provide the
> > -irq_handler driver operation. They must set the :c:type:`struct
> > -drm_device <drm_device>` irq_enabled field to 1 upon
> > -registration of the IRQs, and clear it to 0 after unregistering the
> > -IRQs.
> > +IRQ Helper Library
> > +~~~~~~~~~~~~~~~~~~
> > +
> > +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> > +   :doc: irq helpers
> > +
> > +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> > +   :export:
> >  
> >  Memory Manager Initialization
> >  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> > index 28d736c3fcb4..3b04c25100ae 100644
> > --- a/drivers/gpu/drm/drm_irq.c
> > +++ b/drivers/gpu/drm/drm_irq.c
> > @@ -62,19 +62,39 @@
> >  #include "drm_internal.h"
> >  
> >  /**
> > + * DOC: irq helpers
> > + *
> > + * The DRM core provides very simple support helpers to enable IRQ
> > handling on a
> > + * device through the drm_irq_install() and drm_irq_uninstall() functions. This
> > + * only supports devices with a single interrupt on the main device stored in
> > + * &drm_device.dev and set as the device paramter in drm_dev_alloc().
> > + *
> > + * These IRQ helpers are strictly optional. Drivers which roll their own only
> > + * need to set &drm_device.irq_enabled to signal the DRM core that vblank
> > + * interrupts are working. Since these helpers don't automatically clean up the
> > + * requested interrupt like e.g. devm_request_irq() they're not really
> > + * recommended.
> > + */
> > +
> > +/**
> >   * drm_irq_install - install IRQ handler
> >   * @dev: DRM device
> >   * @irq: IRQ number to install the handler for
> >   *
> >   * Initializes the IRQ related data. Installs the handler, calling the driver
> > - * irq_preinstall() and irq_postinstall() functions before and after the
> > - * installation.
> > + * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
> > + * and after the installation.
> >   *
> >   * This is the simplified helper interface provided for drivers with no special
> >   * needs. Drivers which need to install interrupt handlers for multiple
> >   * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
> >   * that vblank interrupts are available.
> >   *
> > + * @irq must match the interrupt number that would be passed to request_irq(),
> > + * if called directly instead of using this helper function.
> > + *
> > + * &drm_driver.irq_handler is called to handle the registered interrupt.
> > + *
> >   * Returns:
> >   * Zero on success or a negative error code on failure.
> >   */
> > @@ -136,9 +156,9 @@ EXPORT_SYMBOL(drm_irq_install);
> >   * drm_irq_uninstall - uninstall the IRQ handler
> >   * @dev: DRM device
> >   *
> > - * Calls the driver's irq_uninstall() function and unregisters the IRQ handler.
> > - * This should only be called by drivers which used drm_irq_install() to set up
> > - * their interrupt handler. Other drivers must only reset
> > + * Calls the driver's &drm_driver.irq_uninstall function and
> > unregisters the IRQ
> > + * handler.  This should only be called by drivers which used drm_irq_install()
> > + * to set up their interrupt handler. Other drivers must only reset
> >   * &drm_device.irq_enabled to false.
> >   *
> >   * Note that for kernel modesetting drivers it is a bug if this function fails.
> > diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
> > index 630dc26379b7..463e4d81fb0d 100644
> > --- a/drivers/gpu/drm/drm_vblank.c
> > +++ b/drivers/gpu/drm/drm_vblank.c
> > @@ -363,6 +363,9 @@ static void vblank_disable_fn(unsigned long arg)
> >   * @dev: DRM device
> >   *
> >   * This function cleans up any resources allocated in drm_vblank_init.
> > + *
> > + * Drivers which don't use drm_irq_install() need to set
> > &drm_device.irq_enabled
> > + * themselves, to signal to the DRM core that vblank interrupts are enabled.
> >   */
> >  void drm_vblank_cleanup(struct drm_device *dev)
> >  {
> > diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> > index 2e0b76cceb97..39df16af7a4a 100644
> > --- a/include/drm/drmP.h
> > +++ b/include/drm/drmP.h
> > @@ -377,8 +377,13 @@ struct drm_device {
> >  	int last_context;		/**< Last current context */
> >  	/*@} */
> >  
> > -	/** \name VBLANK IRQ support */
> > -	/*@{ */
> > +	/**
> > +	 * @irq_enabled:
> > +	 *
> > +	 * Indicates that interrupt handling is enabled, specifically vblank
> > +	 * handling. Drivers which don't use drm_irq_install() need to set this
> > +	 * to true manually.
> > +	 */
> >  	bool irq_enabled;
> >  	int irq;
> >  
> > diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> > index ebb41688581b..f495eee01302 100644
> > --- a/include/drm/drm_drv.h
> > +++ b/include/drm/drm_drv.h
> > @@ -325,11 +325,40 @@ struct drm_driver {
> >  				     struct timeval *vblank_time,
> >  				     bool in_vblank_irq);
> >  
> > -	/* these have to be filled in */
> > -
> > +	/**
> > +	 * @irq_handler:
> > +	 *
> > +	 * Interrupt handler called when using drm_irq_install(). Not used by
> > +	 * drivers which implement their own interrupt handling.
> > +	 */
> >  	irqreturn_t(*irq_handler) (int irq, void *arg);
> > +
> > +	/**
> > +	 * @irq_preinstall:
> > +	 *
> > +	 * Optional callback used by drm_irq_install() which is called before
> > +	 * the interrupt handler is registered. This should be used to clear out
> > +	 * any pending interrupts (from e.g. firmware based drives) and reset
> > +	 * the interrupt handling registers.
> > +	 */
> >  	void (*irq_preinstall) (struct drm_device *dev);
> > +
> > +	/**
> > +	 * @irq_postinstall:
> > +	 *
> > +	 * Optional callback used by drm_irq_install() which is called after
> > +	 * the interrupt handler is registered. This should be used to enable
> > +	 * interrupt generation in the hardware.
> > +	 */
> >  	int (*irq_postinstall) (struct drm_device *dev);
> > +
> > +	/**
> > +	 * @irq_uninstall:
> > +	 *
> > +	 * Optional callback used by drm_irq_uninstall() which is called before
> > +	 * the interrupt handler is unregistered. This should be used to disable
> > +	 * interrupt generation in the hardware.
> > +	 */
> >  	void (*irq_uninstall) (struct drm_device *dev);
> >  
> >  	/**

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 07/37] drm: Extract drm_vblank.[hc]
  2017-05-24 14:51 ` [PATCH 07/37] drm: Extract drm_vblank.[hc] Daniel Vetter
@ 2017-05-29 19:36   ` Stefan Agner
  2017-05-31  8:04     ` Daniel Vetter
  2017-05-31  9:21   ` [PATCH] " Daniel Vetter
  1 sibling, 1 reply; 94+ messages in thread
From: Stefan Agner @ 2017-05-29 19:36 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On 2017-05-24 07:51, Daniel Vetter wrote:
> drm_irq.c contains both the irq helper library (optional) and the
> vblank support (optional, but part of the modeset uapi, and doesn't
> require the use of the irq helpers at all.
> 
> Split this up for more clarity of the scope of the individual bits.
> 
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  Documentation/gpu/drm-kms.rst |    4 +-
>  drivers/gpu/drm/drm_irq.c     | 1623 +---------------------------------------
>  drivers/gpu/drm/drm_vblank.c  | 1645 +++++++++++++++++++++++++++++++++++++++++
>  include/drm/drmP.h            |    5 +-
>  include/drm/drm_file.h        |    1 +
>  include/drm/drm_irq.h         |  158 +---
>  include/drm/drm_prime.h       |    2 +
>  include/drm/drm_vblank.h      |  181 +++++
>  8 files changed, 1857 insertions(+), 1762 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_vblank.c
>  create mode 100644 include/drm/drm_vblank.h
> 
> diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
> index bfecd21a8cdf..2d77c9580164 100644
> --- a/Documentation/gpu/drm-kms.rst
> +++ b/Documentation/gpu/drm-kms.rst
> @@ -612,8 +612,8 @@ operation handler.
>  Vertical Blanking and Interrupt Handling Functions Reference
>  ------------------------------------------------------------
>  
> -.. kernel-doc:: include/drm/drm_irq.h
> +.. kernel-doc:: include/drm/drm_vblank.h
>     :internal:
>  
> -.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> +.. kernel-doc:: drivers/gpu/drm/drm_vblank.c

Is that removing drm_irq from kernel-docs? Why? At least the C files
seems to contain valid kernel docs...

>     :export:
> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> index c7debaad67f8..28d736c3fcb4 100644
> --- a/drivers/gpu/drm/drm_irq.c
> +++ b/drivers/gpu/drm/drm_irq.c

<snip>  

>  /**
>   * drm_irq_install - install IRQ handler
> @@ -571,7 +176,7 @@ int drm_irq_uninstall(struct drm_device *dev)
>  
>  			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
>  
> -			vblank_disable_and_save(dev, i);
> +			drm_vblank_disable_and_save(dev, i);

This leads to:

drivers/gpu/drm/drm_irq.c: In function 'drm_irq_uninstall':           
drivers/gpu/drm/drm_irq.c:179:4: error: implicit declaration of function
'drm_vblank_disable_and_save' [-Werror=implicit-function-declaration]   
                             
    drm_vblank_disable_and_save(dev, i);                              
    ^

Since you moved the function to drm_vblank.c, I guess you have to add a
deceleration in drm_vblank.h?

--
Stefan
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 18/37] drm/exynos: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 18/37] drm/exynos: " Daniel Vetter
@ 2017-05-30  0:03   ` Inki Dae
  2017-05-31  8:45     ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Inki Dae @ 2017-05-30  0:03 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development
  Cc: Kyungmin Park, Intel Graphics Development, Seung-Woo Kim, Daniel Vetter

Hi Daniel,

2017년 05월 24일 23:51에 Daniel Vetter 이(가) 쓴 글:
> Only in the load failure path, where the hardware is quiet anyway.
> 
> Cc: Inki Dae <inki.dae@samsung.com>
> Cc: Joonyoung Shim <jy0922.shim@samsung.com>
> Cc: Seung-Woo Kim <sw0312.kim@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/exynos/exynos_drm_drv.c | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> index 50294a7bd29d..1c814b9342af 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> @@ -376,7 +376,7 @@ static int exynos_drm_bind(struct device *dev)
>  	/* Probe non kms sub drivers and virtual display driver. */
>  	ret = exynos_drm_device_subdrv_probe(drm);
>  	if (ret)
> -		goto err_cleanup_vblank;
> +		goto err_unbind_all;

With this change shouldn't you post the patch to remove drm_vblank_init and setup vblank stuff in drm_crtc_init together?
I couldn't find the relevant patch on your patch series[1].

As of now, I think resource leak would happen with this patch only.

Thanks,
Inki Dae

[1] http://www.spinics.net/lists/dri-devel/msg142387.html

>  
>  	drm_mode_config_reset(drm);
>  
> @@ -407,8 +407,6 @@ static int exynos_drm_bind(struct device *dev)
>  	exynos_drm_fbdev_fini(drm);
>  	drm_kms_helper_poll_fini(drm);
>  	exynos_drm_device_subdrv_remove(drm);
> -err_cleanup_vblank:
> -	drm_vblank_cleanup(drm);
>  err_unbind_all:
>  	component_unbind_all(drm->dev, drm);
>  err_mode_config_cleanup:
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 01/37] drm/doc: move printf helpers out of drmP.h
  2017-05-24 14:51 ` [PATCH 01/37] drm/doc: move printf helpers out of drmP.h Daniel Vetter
@ 2017-05-30  7:33   ` Neil Armstrong
  0 siblings, 0 replies; 94+ messages in thread
From: Neil Armstrong @ 2017-05-30  7:33 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development; +Cc: Daniel Vetter, Intel Graphics Development

On 05/24/2017 04:51 PM, Daniel Vetter wrote:
> And document them lightly. Unfortunately kernel-doc isn't the most
> awesome for documenting #defines that don't look like functions, it
> makes functions out of them :-/
> 
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  include/drm/drmP.h      | 17 -----------------
>  include/drm/drm_modes.h | 17 +++++++++++++++++
>  include/drm/drm_rect.h  | 27 +++++++++++++++++++++++++++
>  3 files changed, 44 insertions(+), 17 deletions(-)
> 
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index b9b5566acfe6..575b29b47811 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -292,23 +292,6 @@ struct pci_controller;
>  /* Format strings and argument splitters to simplify printing
>   * various "complex" objects
>   */
> -#define DRM_MODE_FMT    "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x"
> -#define DRM_MODE_ARG(m) \
> -	(m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \
> -	(m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \
> -	(m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \
> -	(m)->type, (m)->flags
> -
> -#define DRM_RECT_FMT    "%dx%d%+d%+d"
> -#define DRM_RECT_ARG(r) drm_rect_width(r), drm_rect_height(r), (r)->x1, (r)->y1
> -
> -/* for rect's in fixed-point format: */
> -#define DRM_RECT_FP_FMT "%d.%06ux%d.%06u%+d.%06u%+d.%06u"
> -#define DRM_RECT_FP_ARG(r) \
> -		drm_rect_width(r) >> 16, ((drm_rect_width(r) & 0xffff) * 15625) >> 10, \
> -		drm_rect_height(r) >> 16, ((drm_rect_height(r) & 0xffff) * 15625) >> 10, \
> -		(r)->x1 >> 16, (((r)->x1 & 0xffff) * 15625) >> 10, \
> -		(r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10
>  
>  /*@}*/
>  
> diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
> index 6dd34280e892..94ac771fe460 100644
> --- a/include/drm/drm_modes.h
> +++ b/include/drm/drm_modes.h
> @@ -197,6 +197,8 @@ enum drm_mode_status {
>   * there's the hardware timings, which are corrected for interlacing,
>   * double-clocking and similar things. They are provided as a convenience, and
>   * can be appropriately computed using drm_mode_set_crtcinfo().
> + *
> + * For printing you can use %DRM_MODE_FMT and DRM_MODE_ARG().
>   */
>  struct drm_display_mode {
>  	/**
> @@ -407,6 +409,21 @@ struct drm_display_mode {
>  	enum hdmi_picture_aspect picture_aspect_ratio;
>  };
>  
> +/**
> + * DRM_MODE_FMT - printf string for &struct drm_display_mode
> + */
> +#define DRM_MODE_FMT    "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x"
> +
> +/**
> + * DRM_MODE_ARG - printf arguments for &struct drm_display_mode
> + * @m: display mode
> + */
> +#define DRM_MODE_ARG(m) \
> +	(m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \
> +	(m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \
> +	(m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \
> +	(m)->type, (m)->flags
> +
>  #define obj_to_mode(x) container_of(x, struct drm_display_mode, base)
>  
>  /**
> diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h
> index 83bb156d4356..44bc122b9ee0 100644
> --- a/include/drm/drm_rect.h
> +++ b/include/drm/drm_rect.h
> @@ -43,6 +43,33 @@ struct drm_rect {
>  };
>  
>  /**
> + * DRM_RECT_FMT - printf string for &struct drm_rect
> + */
> +#define DRM_RECT_FMT    "%dx%d%+d%+d"
> +/**
> + * DRM_RECT_ARG - printf arguments for &struct drm_rect
> + * @r: rectangle struct
> + */
> +#define DRM_RECT_ARG(r) drm_rect_width(r), drm_rect_height(r), (r)->x1, (r)->y1
> +
> +/**
> + * DRM_RECT_FP_FMT - printf string for &struct drm_rect in 16.16 fixed point
> + */
> +#define DRM_RECT_FP_FMT "%d.%06ux%d.%06u%+d.%06u%+d.%06u"
> +/**
> + * DRM_RECT_FP_ARG - printf arguments for &struct drm_rect in 16.16 fixed point
> + * @r: rectangle struct
> + *
> + * This is useful for e.g. printing plane source rectangles, which are in 16.16
> + * fixed point.
> + */
> +#define DRM_RECT_FP_ARG(r) \
> +		drm_rect_width(r) >> 16, ((drm_rect_width(r) & 0xffff) * 15625) >> 10, \
> +		drm_rect_height(r) >> 16, ((drm_rect_height(r) & 0xffff) * 15625) >> 10, \
> +		(r)->x1 >> 16, (((r)->x1 & 0xffff) * 15625) >> 10, \
> +		(r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10
> +
> +/**
>   * drm_rect_adjust_size - adjust the size of the rectangle
>   * @r: rectangle to be adjusted
>   * @dw: horizontal adjustment
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/37] drm: Remove drm_device->virtdev
  2017-05-24 14:51 ` Daniel Vetter
                     ` (2 preceding siblings ...)
  2017-05-30  7:33   ` Neil Armstrong
@ 2017-05-30  7:33   ` Neil Armstrong
  3 siblings, 0 replies; 94+ messages in thread
From: Neil Armstrong @ 2017-05-30  7:33 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, virtualization

On 05/24/2017 04:51 PM, Daniel Vetter wrote:
> This is a leftover from the drm_bus days, where we've had a
> bus-specific device type for every bus type in drm_device. Except for
> pci (which we can't remove because dri1 drivers) this is all gone. And
> the virt driver also doesn't really need it, dev_to_virtio works
> perfectly fine.
> 
> Cc: David Airlie <airlied@linux.ie>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: virtualization@lists.linux-foundation.org
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 1 -
>  drivers/gpu/drm/virtio/virtgpu_kms.c     | 4 ++--
>  include/drm/drmP.h                       | 2 --
>  3 files changed, 2 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> index 43e1d5916c6c..7df8d0c9026a 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> @@ -56,7 +56,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
>  	dev = drm_dev_alloc(driver, &vdev->dev);
>  	if (IS_ERR(dev))
>  		return PTR_ERR(dev);
> -	dev->virtdev = vdev;
>  	vdev->priv = dev;
>  
>  	if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
> index 1e1c90b30d4a..6400506a06b0 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -138,7 +138,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  	u32 num_scanouts, num_capsets;
>  	int ret;
>  
> -	if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
> +	if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1))
>  		return -ENODEV;
>  
>  	vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
> @@ -147,7 +147,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  
>  	vgdev->ddev = dev;
>  	dev->dev_private = vgdev;
> -	vgdev->vdev = dev->virtdev;
> +	vgdev->vdev = dev_to_virtio(dev->dev);
>  	vgdev->dev = dev->dev;
>  
>  	spin_lock_init(&vgdev->display_info_lock);
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 575b29b47811..c363f2fdff31 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -412,8 +412,6 @@ struct drm_device {
>  	struct pci_controller *hose;
>  #endif
>  
> -	struct virtio_device *virtdev;
> -
>  	struct drm_sg_mem *sg;	/**< Scatter gather memory */
>  	unsigned int num_crtcs;                  /**< Number of CRTCs on this device */
>  
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/37] drm: Remove drm_device->virtdev
  2017-05-24 14:51 ` Daniel Vetter
  2017-05-29  6:52   ` Gerd Hoffmann
  2017-05-29  6:52   ` Gerd Hoffmann
@ 2017-05-30  7:33   ` Neil Armstrong
  2017-05-30  7:33   ` Neil Armstrong
  3 siblings, 0 replies; 94+ messages in thread
From: Neil Armstrong @ 2017-05-30  7:33 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Gerd Hoffmann, virtualization

On 05/24/2017 04:51 PM, Daniel Vetter wrote:
> This is a leftover from the drm_bus days, where we've had a
> bus-specific device type for every bus type in drm_device. Except for
> pci (which we can't remove because dri1 drivers) this is all gone. And
> the virt driver also doesn't really need it, dev_to_virtio works
> perfectly fine.
> 
> Cc: David Airlie <airlied@linux.ie>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: virtualization@lists.linux-foundation.org
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 1 -
>  drivers/gpu/drm/virtio/virtgpu_kms.c     | 4 ++--
>  include/drm/drmP.h                       | 2 --
>  3 files changed, 2 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> index 43e1d5916c6c..7df8d0c9026a 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> @@ -56,7 +56,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
>  	dev = drm_dev_alloc(driver, &vdev->dev);
>  	if (IS_ERR(dev))
>  		return PTR_ERR(dev);
> -	dev->virtdev = vdev;
>  	vdev->priv = dev;
>  
>  	if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
> index 1e1c90b30d4a..6400506a06b0 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -138,7 +138,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  	u32 num_scanouts, num_capsets;
>  	int ret;
>  
> -	if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
> +	if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1))
>  		return -ENODEV;
>  
>  	vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
> @@ -147,7 +147,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  
>  	vgdev->ddev = dev;
>  	dev->dev_private = vgdev;
> -	vgdev->vdev = dev->virtdev;
> +	vgdev->vdev = dev_to_virtio(dev->dev);
>  	vgdev->dev = dev->dev;
>  
>  	spin_lock_init(&vgdev->display_info_lock);
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 575b29b47811..c363f2fdff31 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -412,8 +412,6 @@ struct drm_device {
>  	struct pci_controller *hose;
>  #endif
>  
> -	struct virtio_device *virtdev;
> -
>  	struct drm_sg_mem *sg;	/**< Scatter gather memory */
>  	unsigned int num_crtcs;                  /**< Number of CRTCs on this device */
>  
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 13/37] drm: better document how to send out the crtc disable event
  2017-05-24 14:51 ` [PATCH 13/37] drm: better document how to send out the crtc disable event Daniel Vetter
  2017-05-24 14:54   ` Boris Brezillon
@ 2017-05-30  7:35   ` Neil Armstrong
  1 sibling, 0 replies; 94+ messages in thread
From: Neil Armstrong @ 2017-05-30  7:35 UTC (permalink / raw)
  To: Daniel Vetter, DRI Development; +Cc: Daniel Vetter, Intel Graphics Development

On 05/24/2017 04:51 PM, Daniel Vetter wrote:
> The kernel doc explained what needs to happen, but not how to most
> easily accomplish that using the functions. Fix that.
> 
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  include/drm/drm_crtc.h | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 5f5d53973ca5..3a911a64c257 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -214,7 +214,9 @@ struct drm_crtc_state {
>  	 *    atomic commit. In that case the event can be send out any time
>  	 *    after the hardware has stopped scanning out the current
>  	 *    framebuffers. It should contain the timestamp and counter for the
> -	 *    last vblank before the display pipeline was shut off.
> +	 *    last vblank before the display pipeline was shut off. The simplest
> +	 *    way to achieve that is calling drm_crtc_send_vblank_event()
> +	 *    somewhen after drm_crtc_vblank_off() has been called.
>  	 *
>  	 *  - For a CRTC which is enabled at the end of the commit (even when it
>  	 *    undergoes an full modeset) the vblank timestamp and counter must
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 19/37] drm/fsl: Drop drm_vblank_cleanup
  2017-05-26  7:00     ` Daniel Vetter
@ 2017-05-30 21:17       ` Stefan Agner
  2017-05-31  8:52         ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Stefan Agner @ 2017-05-30 21:17 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Intel Graphics Development, DRI Development

On 2017-05-26 00:00, Daniel Vetter wrote:
> On Thu, May 25, 2017 at 10:18 AM, Stefan Agner <stefan@agner.ch> wrote:
>> On 2017-05-24 07:51, Daniel Vetter wrote:
>>> Again cleanup before irq disabling doesn't really stop the races,
>>> so just drop it. Proper fix would be to put drm_atomic_helper_shutdown
>>> before everything gets cleaned up.
>>
>> Hm, I already use the non-atomic drm_crtc_force_disable_all before, I
>> think that fixed the races I saw.
>>
>> But I guess what you are saying instead of using
>> drm_crtc_force_disable_all I should use drm_atomic_helper_shutdown...?
> 
> Yes. I thought I audited all existing users of the legacy
> force_disable (it won't work correctly for atomic drivers), but
> somehow I missed the one in fsl. See
> 
> commit 18dddadc78c91a91b546acc48506c24f5f840c4f
> Author: Daniel Vetter <daniel.vetter@ffwll.ch>
> Date:   Tue Mar 21 17:41:49 2017 +0100
> 
>     drm/atomic: Introduce drm_atomic_helper_shutdown
> 

Ok, I see.

Since this leads to a change just a few lines above this patch, it has
potential for merge conflict. Can I take this change through my tree?

--
Stefan

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 07/37] drm: Extract drm_vblank.[hc]
  2017-05-29 19:36   ` Stefan Agner
@ 2017-05-31  8:04     ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  8:04 UTC (permalink / raw)
  To: Stefan Agner
  Cc: Daniel Vetter, Intel Graphics Development, DRI Development,
	Daniel Vetter

On Mon, May 29, 2017 at 12:36:54PM -0700, Stefan Agner wrote:
> On 2017-05-24 07:51, Daniel Vetter wrote:
> > drm_irq.c contains both the irq helper library (optional) and the
> > vblank support (optional, but part of the modeset uapi, and doesn't
> > require the use of the irq helpers at all.
> > 
> > Split this up for more clarity of the scope of the individual bits.
> > 
> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> > ---
> >  Documentation/gpu/drm-kms.rst |    4 +-
> >  drivers/gpu/drm/drm_irq.c     | 1623 +---------------------------------------
> >  drivers/gpu/drm/drm_vblank.c  | 1645 +++++++++++++++++++++++++++++++++++++++++
> >  include/drm/drmP.h            |    5 +-
> >  include/drm/drm_file.h        |    1 +
> >  include/drm/drm_irq.h         |  158 +---
> >  include/drm/drm_prime.h       |    2 +
> >  include/drm/drm_vblank.h      |  181 +++++
> >  8 files changed, 1857 insertions(+), 1762 deletions(-)
> >  create mode 100644 drivers/gpu/drm/drm_vblank.c
> >  create mode 100644 include/drm/drm_vblank.h
> > 
> > diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
> > index bfecd21a8cdf..2d77c9580164 100644
> > --- a/Documentation/gpu/drm-kms.rst
> > +++ b/Documentation/gpu/drm-kms.rst
> > @@ -612,8 +612,8 @@ operation handler.
> >  Vertical Blanking and Interrupt Handling Functions Reference
> >  ------------------------------------------------------------
> >  
> > -.. kernel-doc:: include/drm/drm_irq.h
> > +.. kernel-doc:: include/drm/drm_vblank.h
> >     :internal:
> >  
> > -.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> > +.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
> 
> Is that removing drm_irq from kernel-docs? Why? At least the C files
> seems to contain valid kernel docs...

Get's fixed in the next patch again, where I re-add them when rewriting
all the irq docs. Thats why I said these two patches depend upon each
another :-)

I can shuffle the include hunk of the rewrite into this patch if you think
it's better, but I figured renaming/splitting separate from rewriting the
docs is good.

> 
> >     :export:
> > diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> > index c7debaad67f8..28d736c3fcb4 100644
> > --- a/drivers/gpu/drm/drm_irq.c
> > +++ b/drivers/gpu/drm/drm_irq.c
> 
> <snip>  
> 
> >  /**
> >   * drm_irq_install - install IRQ handler
> > @@ -571,7 +176,7 @@ int drm_irq_uninstall(struct drm_device *dev)
> >  
> >  			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
> >  
> > -			vblank_disable_and_save(dev, i);
> > +			drm_vblank_disable_and_save(dev, i);
> 
> This leads to:
> 
> drivers/gpu/drm/drm_irq.c: In function 'drm_irq_uninstall':           
> drivers/gpu/drm/drm_irq.c:179:4: error: implicit declaration of function
> 'drm_vblank_disable_and_save' [-Werror=implicit-function-declaration]   
>                              
>     drm_vblank_disable_and_save(dev, i);                              
>     ^
> 
> Since you moved the function to drm_vblank.c, I guess you have to add a
> deceleration in drm_vblank.h?

Oops, misplaced hunk, a later patch adds it to drm_internal.h. I'll fix
that up.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 18/37] drm/exynos: Drop drm_vblank_cleanup
  2017-05-30  0:03   ` Inki Dae
@ 2017-05-31  8:45     ` Daniel Vetter
  2017-06-01  6:15       ` Inki Dae
  0 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  8:45 UTC (permalink / raw)
  To: Inki Dae
  Cc: Joonyoung Shim, Daniel Vetter, Intel Graphics Development,
	Seung-Woo Kim, DRI Development, Kyungmin Park, Daniel Vetter

On Tue, May 30, 2017 at 09:03:34AM +0900, Inki Dae wrote:
> Hi Daniel,
> 
> 2017년 05월 24일 23:51에 Daniel Vetter 이(가) 쓴 글:
> > Only in the load failure path, where the hardware is quiet anyway.
> > 
> > Cc: Inki Dae <inki.dae@samsung.com>
> > Cc: Joonyoung Shim <jy0922.shim@samsung.com>
> > Cc: Seung-Woo Kim <sw0312.kim@samsung.com>
> > Cc: Kyungmin Park <kyungmin.park@samsung.com>
> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> > ---
> >  drivers/gpu/drm/exynos/exynos_drm_drv.c | 4 +---
> >  1 file changed, 1 insertion(+), 3 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > index 50294a7bd29d..1c814b9342af 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > @@ -376,7 +376,7 @@ static int exynos_drm_bind(struct device *dev)
> >  	/* Probe non kms sub drivers and virtual display driver. */
> >  	ret = exynos_drm_device_subdrv_probe(drm);
> >  	if (ret)
> > -		goto err_cleanup_vblank;
> > +		goto err_unbind_all;
> 
> With this change shouldn't you post the patch to remove drm_vblank_init and setup vblank stuff in drm_crtc_init together?
> I couldn't find the relevant patch on your patch series[1].

No, drm_vblank_cleanup is already called in the core. The only reason to
call it in the driver is to fall back from kms to ums when irq setup
somehow failed, then you need to disable vblank support again.

The only driver which ever needed this was radeon, and radeon long ago
lost ums support. drm_vblank_cleanup is already called for you, and most
drivers don't even do this cleanup call. But somehow a lot of people
copied from radeon without understanding what it does.

Looking at the last patch in this series might help a bit in understanding
the history here. Can you pls reevaluate the patch?

Thanks, Daniel

> As of now, I think resource leak would happen with this patch only.
> 
> Thanks,
> Inki Dae
> 
> [1] http://www.spinics.net/lists/dri-devel/msg142387.html
> 
> >  
> >  	drm_mode_config_reset(drm);
> >  
> > @@ -407,8 +407,6 @@ static int exynos_drm_bind(struct device *dev)
> >  	exynos_drm_fbdev_fini(drm);
> >  	drm_kms_helper_poll_fini(drm);
> >  	exynos_drm_device_subdrv_remove(drm);
> > -err_cleanup_vblank:
> > -	drm_vblank_cleanup(drm);
> >  err_unbind_all:
> >  	component_unbind_all(drm->dev, drm);
> >  err_mode_config_cleanup:
> > 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 23/37] drm/imx: Drop drm_vblank_cleanup
  2017-05-29 11:07   ` Philipp Zabel
@ 2017-05-31  8:51     ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  8:51 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Daniel Vetter, Intel Graphics Development, DRI Development,
	Daniel Vetter

On Mon, May 29, 2017 at 01:07:40PM +0200, Philipp Zabel wrote:
> Hi Daniel,
> 
> On Wed, 2017-05-24 at 16:51 +0200, Daniel Vetter wrote:
> > It's only done in the driver load error path, where vblanks don't need
> > to be quiescent anyway. And that's all drm_vblank_cleanup does, since
> > the core will release the vblank allocations on its own already. So
> > drop it.
> 
> Thank you for cleaning this up and improving the docs.
> From the function name and kerneldoc comment, it was really not clear
> that this function is already called in the drm_device release path.
> 
> I think the comment is slightly misleading though, as drm_vblank_cleanup
> does call kfree(dev->vblank).

Yeah I got a bit sloppy with the commit message. Since this is error code
resetting ->num_crtcs doesn't do anything useful, and the kfree happens
anyway (because the core calls drm_vblank_cleanup for you). That only
leaves the timer shutdown, and the timer can't run yet here at this point.

Hence this does nothing useful (except move the kfree around).
> 
> > Cc: Philipp Zabel <p.zabel@pengutronix.de>
> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> >
> > ---
> >  drivers/gpu/drm/imx/imx-drm-core.c | 4 +---
> >  1 file changed, 1 insertion(+), 3 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
> > index 50add2f9e250..95e2181963d9 100644
> > --- a/drivers/gpu/drm/imx/imx-drm-core.c
> > +++ b/drivers/gpu/drm/imx/imx-drm-core.c
> > @@ -278,7 +278,7 @@ static int imx_drm_bind(struct device *dev)
> >  	/* Now try and bind all our sub-components */
> >  	ret = component_bind_all(dev, drm);
> >  	if (ret)
> > -		goto err_vblank;
> > +		goto err_kms;
> >  
> >  	drm_mode_config_reset(drm);
> >  
> > @@ -316,8 +316,6 @@ static int imx_drm_bind(struct device *dev)
> >  err_unbind:
> >  #endif
> >  	component_unbind_all(drm->dev, drm);
> > -err_vblank:
> > -	drm_vblank_cleanup(drm);
> >  err_kms:
> >  	drm_mode_config_cleanup(drm);
> >  err_unref:
> 
> As I understand, the drm_dev_unref(drm) that follows this causes
> drm_dev_release -> drm_dev_fini -> drm_vblank_cleanup to be called, so
> 
> Acked-by: Philipp Zabel <p.zabel@pengutronix.de>

Thanks, applied, as I did all the other patches which gathered an ack or
r-b already.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 19/37] drm/fsl: Drop drm_vblank_cleanup
  2017-05-30 21:17       ` Stefan Agner
@ 2017-05-31  8:52         ` Daniel Vetter
  2017-06-08 21:42           ` Stefan Agner
  0 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  8:52 UTC (permalink / raw)
  To: Stefan Agner; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Tue, May 30, 2017 at 02:17:04PM -0700, Stefan Agner wrote:
> On 2017-05-26 00:00, Daniel Vetter wrote:
> > On Thu, May 25, 2017 at 10:18 AM, Stefan Agner <stefan@agner.ch> wrote:
> >> On 2017-05-24 07:51, Daniel Vetter wrote:
> >>> Again cleanup before irq disabling doesn't really stop the races,
> >>> so just drop it. Proper fix would be to put drm_atomic_helper_shutdown
> >>> before everything gets cleaned up.
> >>
> >> Hm, I already use the non-atomic drm_crtc_force_disable_all before, I
> >> think that fixed the races I saw.
> >>
> >> But I guess what you are saying instead of using
> >> drm_crtc_force_disable_all I should use drm_atomic_helper_shutdown...?
> > 
> > Yes. I thought I audited all existing users of the legacy
> > force_disable (it won't work correctly for atomic drivers), but
> > somehow I missed the one in fsl. See
> > 
> > commit 18dddadc78c91a91b546acc48506c24f5f840c4f
> > Author: Daniel Vetter <daniel.vetter@ffwll.ch>
> > Date:   Tue Mar 21 17:41:49 2017 +0100
> > 
> >     drm/atomic: Introduce drm_atomic_helper_shutdown
> > 
> 
> Ok, I see.
> 
> Since this leads to a change just a few lines above this patch, it has
> potential for merge conflict. Can I take this change through my tree?

Yeah, it'll probably take until 4.14 anyway until I get them all in and
finally can remove drm_vblank_cleanup from the driver-exported set of
functions. Just make sure it does get into 4.13 pls.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH] drm/doc: Improve ioctl/fops docs a bit more
  2017-05-24 14:51 ` [PATCH 06/37] drm/doc: Improve ioctl/fops docs a bit more Daniel Vetter
@ 2017-05-31  9:20   ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  9:20 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

I spotted a markup issue, plus adding the descriptions in drm_driver.
Plus a few more links while at it.

I'm still mildly unhappy with the split between fops and ioctls, but I
still think having the ioctls in the uapi chapter makes more sense. Oh
well ...

v2: Rebase.

v3: Move misplace hunk to the right patch.

Cc: Stefan Agner <stefan@agner.ch>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-internals.rst |  2 ++
 Documentation/gpu/drm-uapi.rst      |  2 ++
 drivers/gpu/drm/drm_file.c          |  7 ++++++-
 drivers/gpu/drm/drm_ioctl.c         |  5 ++++-
 include/drm/drm_drv.h               | 18 ++++++++++++++++++
 5 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index babfb6143bd9..d218dd29221a 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -243,6 +243,8 @@ drivers.
 Open/Close, File Operations and IOCTLs
 ======================================
 
+.. _drm_driver_fops:
+
 File Operations
 ---------------
 
diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst
index 858457567d3d..679373b4a03f 100644
--- a/Documentation/gpu/drm-uapi.rst
+++ b/Documentation/gpu/drm-uapi.rst
@@ -160,6 +160,8 @@ other hand, a driver requires shared state between clients which is
 visible to user-space and accessible beyond open-file boundaries, they
 cannot support render nodes.
 
+.. _drm_driver_ioctl:
+
 IOCTL Support on Device Nodes
 =============================
 
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index caad93dab54b..6631f61b66ca 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -75,7 +75,7 @@ DEFINE_MUTEX(drm_global_mutex);
  * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap().
  *
  * No other file operations are supported by the DRM userspace API. Overall the
- * following is an example #file_operations structure::
+ * following is an example &file_operations structure::
  *
  *     static const example_drm_fops = {
  *             .owner = THIS_MODULE,
@@ -92,6 +92,11 @@ DEFINE_MUTEX(drm_global_mutex);
  * For plain GEM based drivers there is the DEFINE_DRM_GEM_FOPS() macro, and for
  * CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make this
  * simpler.
+ *
+ * The driver's &file_operations must be stored in &drm_driver.fops.
+ *
+ * For driver-private IOCTL handling see the more detailed discussion in
+ * :ref:`IOCTL support in the userland interfaces chapter<drm_driver_ioctl>`.
  */
 
 static int drm_open_helper(struct file *filp, struct drm_minor *minor);
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index a423bf60e230..3690706f051d 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -683,7 +683,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
  * 
  * DRM driver private IOCTL must be in the range from DRM_COMMAND_BASE to
  * DRM_COMMAND_END. Finally you need an array of &struct drm_ioctl_desc to wire
- * up the handlers and set the access rights:
+ * up the handlers and set the access rights::
  *
  *     static const struct drm_ioctl_desc my_driver_ioctls[] = {
  *         DRM_IOCTL_DEF_DRV(MY_DRIVER_OPERATION, my_driver_operation,
@@ -692,6 +692,9 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
  *
  * And then assign this to the &drm_driver.ioctls field in your driver
  * structure.
+ *
+ * See the separate chapter on :ref:`file operations<drm_driver_fops>` for how
+ * the driver-specific IOCTLs are wired up.
  */
 
 /**
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 5d06c68bb00b..ebb41688581b 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -486,8 +486,26 @@ struct drm_driver {
 	char *date;
 
 	u32 driver_features;
+
+	/**
+	 * @ioctls:
+	 *
+	 * Array of driver-private IOCTL description entries. See the chapter on
+	 * :ref:`IOCTL support in the userland interfaces
+	 * chapter<drm_driver_ioctl>` for the full details.
+	 */
+
 	const struct drm_ioctl_desc *ioctls;
+	/** @num_ioctls: Number of entries in @ioctls. */
 	int num_ioctls;
+
+	/**
+	 * @fops:
+	 *
+	 * File operations for the DRM device node. See the discussion in
+	 * :ref:`file operations<drm_driver_fops>` for in-depth coverage and
+	 * some examples.
+	 */
 	const struct file_operations *fops;
 
 	/* Everything below here is for legacy driver, never use! */
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH] drm: Extract drm_vblank.[hc]
  2017-05-24 14:51 ` [PATCH 07/37] drm: Extract drm_vblank.[hc] Daniel Vetter
  2017-05-29 19:36   ` Stefan Agner
@ 2017-05-31  9:21   ` Daniel Vetter
  2017-05-31 17:51     ` Stefan Agner
  2017-05-31 18:22     ` kbuild test robot
  1 sibling, 2 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  9:21 UTC (permalink / raw)
  To: DRI Development; +Cc: Daniel Vetter, Intel Graphics Development, Daniel Vetter

drm_irq.c contains both the irq helper library (optional) and the
vblank support (optional, but part of the modeset uapi, and doesn't
require the use of the irq helpers at all.

Split this up for more clarity of the scope of the individual bits.

v2: Move misplaced hunks to this patch (Stefan).

Cc: Stefan Agner <stefan@agner.ch>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-internals.rst |    9 +
 Documentation/gpu/drm-kms.rst       |    4 +-
 drivers/gpu/drm/Makefile            |    2 +-
 drivers/gpu/drm/drm_internal.h      |    3 +-
 drivers/gpu/drm/drm_irq.c           | 1623 +---------------------------------
 drivers/gpu/drm/drm_vblank.c        | 1645 +++++++++++++++++++++++++++++++++++
 include/drm/drmP.h                  |    5 +-
 include/drm/drm_file.h              |    1 +
 include/drm/drm_irq.h               |  158 +---
 include/drm/drm_prime.h             |    2 +
 include/drm/drm_vblank.h            |  181 ++++
 11 files changed, 1869 insertions(+), 1764 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_vblank.c
 create mode 100644 include/drm/drm_vblank.h

diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index d218dd29221a..dd28e39cf966 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -204,6 +204,15 @@ drm_device <drm_device>` irq_enabled field to 1 upon
 registration of the IRQs, and clear it to 0 after unregistering the
 IRQs.
 
+IRQ Helper Library
+~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: drivers/gpu/drm/drm_irq.c
+   :doc: irq helpers
+
+.. kernel-doc:: drivers/gpu/drm/drm_irq.c
+   :export:
+
 Memory Manager Initialization
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index bfecd21a8cdf..2d77c9580164 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -612,8 +612,8 @@ operation handler.
 Vertical Blanking and Interrupt Handling Functions Reference
 ------------------------------------------------------------
 
-.. kernel-doc:: include/drm/drm_irq.h
+.. kernel-doc:: include/drm/drm_vblank.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_irq.c
+.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
    :export:
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c156fecfb362..acc88942c2e5 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -16,7 +16,7 @@ drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
 		drm_framebuffer.o drm_connector.o drm_blend.o \
 		drm_encoder.o drm_mode_object.o drm_property.o \
 		drm_plane.o drm_color_mgmt.o drm_print.o \
-		drm_dumb_buffers.o drm_mode_config.o
+		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o
 
 drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
 drm-$(CONFIG_DRM_VM) += drm_vm.o
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index bca2c66c5d28..6a0cbcc84534 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -54,8 +54,9 @@ int drm_name_info(struct seq_file *m, void *data);
 int drm_clients_info(struct seq_file *m, void* data);
 int drm_gem_name_info(struct seq_file *m, void *data);
 
-/* drm_irq.c */
+/* drm_vblank.c */
 extern unsigned int drm_timestamp_monotonic;
+void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
 
 /* IOCTLS */
 int drm_wait_vblank(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index c7debaad67f8..28d736c3fcb4 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -3,6 +3,25 @@
  *
  * \author Rickard E. (Rik) Faith <faith@valinux.com>
  * \author Gareth Hughes <gareth@valinux.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  */
 
 /*
@@ -32,429 +51,15 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <drm/drm_irq.h>
 #include <drm/drmP.h>
-#include "drm_trace.h"
-#include "drm_internal.h"
 
 #include <linux/interrupt.h>	/* For task queue support */
-#include <linux/slab.h>
 
 #include <linux/vgaarb.h>
 #include <linux/export.h>
 
-/* Retry timestamp calculation up to 3 times to satisfy
- * drm_timestamp_precision before giving up.
- */
-#define DRM_TIMESTAMP_MAXRETRIES 3
-
-/* Threshold in nanoseconds for detection of redundant
- * vblank irq in drm_handle_vblank(). 1 msec should be ok.
- */
-#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
-
-static bool
-drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-			  struct timeval *tvblank, bool in_vblank_irq);
-
-static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
-
-/*
- * Default to use monotonic timestamps for wait-for-vblank and page-flip
- * complete events.
- */
-unsigned int drm_timestamp_monotonic = 1;
-
-static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
-
-module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
-module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
-module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
-MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
-MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
-MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
-
-static void store_vblank(struct drm_device *dev, unsigned int pipe,
-			 u32 vblank_count_inc,
-			 struct timeval *t_vblank, u32 last)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	assert_spin_locked(&dev->vblank_time_lock);
-
-	vblank->last = last;
-
-	write_seqlock(&vblank->seqlock);
-	vblank->time = *t_vblank;
-	vblank->count += vblank_count_inc;
-	write_sequnlock(&vblank->seqlock);
-}
-
-/*
- * "No hw counter" fallback implementation of .get_vblank_counter() hook,
- * if there is no useable hardware frame counter available.
- */
-static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
-{
-	WARN_ON_ONCE(dev->max_vblank_count != 0);
-	return 0;
-}
-
-static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
-{
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
-
-		if (crtc->funcs->get_vblank_counter)
-			return crtc->funcs->get_vblank_counter(crtc);
-	}
-
-	if (dev->driver->get_vblank_counter)
-		return dev->driver->get_vblank_counter(dev, pipe);
-
-	return drm_vblank_no_hw_counter(dev, pipe);
-}
-
-/*
- * Reset the stored timestamp for the current vblank count to correspond
- * to the last vblank occurred.
- *
- * Only to be called from drm_crtc_vblank_on().
- *
- * Note: caller must hold &drm_device.vbl_lock since this reads & writes
- * device vblank fields.
- */
-static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe)
-{
-	u32 cur_vblank;
-	bool rc;
-	struct timeval t_vblank;
-	int count = DRM_TIMESTAMP_MAXRETRIES;
-
-	spin_lock(&dev->vblank_time_lock);
-
-	/*
-	 * sample the current counter to avoid random jumps
-	 * when drm_vblank_enable() applies the diff
-	 */
-	do {
-		cur_vblank = __get_vblank_counter(dev, pipe);
-		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
-	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
-
-	/*
-	 * Only reinitialize corresponding vblank timestamp if high-precision query
-	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
-	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
-	 */
-	if (!rc)
-		t_vblank = (struct timeval) {0, 0};
-
-	/*
-	 * +1 to make sure user will never see the same
-	 * vblank counter value before and after a modeset
-	 */
-	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
-
-	spin_unlock(&dev->vblank_time_lock);
-}
-
-/*
- * Call back into the driver to update the appropriate vblank counter
- * (specified by @pipe).  Deal with wraparound, if it occurred, and
- * update the last read value so we can deal with wraparound on the next
- * call if necessary.
- *
- * Only necessary when going from off->on, to account for frames we
- * didn't get an interrupt for.
- *
- * Note: caller must hold &drm_device.vbl_lock since this reads & writes
- * device vblank fields.
- */
-static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
-				    bool in_vblank_irq)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	u32 cur_vblank, diff;
-	bool rc;
-	struct timeval t_vblank;
-	int count = DRM_TIMESTAMP_MAXRETRIES;
-	int framedur_ns = vblank->framedur_ns;
-
-	/*
-	 * Interrupts were disabled prior to this call, so deal with counter
-	 * wrap if needed.
-	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
-	 * here if the register is small or we had vblank interrupts off for
-	 * a long time.
-	 *
-	 * We repeat the hardware vblank counter & timestamp query until
-	 * we get consistent results. This to prevent races between gpu
-	 * updating its hardware counter while we are retrieving the
-	 * corresponding vblank timestamp.
-	 */
-	do {
-		cur_vblank = __get_vblank_counter(dev, pipe);
-		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
-	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
-
-	if (dev->max_vblank_count != 0) {
-		/* trust the hw counter when it's around */
-		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
-	} else if (rc && framedur_ns) {
-		const struct timeval *t_old;
-		u64 diff_ns;
-
-		t_old = &vblank->time;
-		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
-
-		/*
-		 * Figure out how many vblanks we've missed based
-		 * on the difference in the timestamps and the
-		 * frame/field duration.
-		 */
-		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
-
-		if (diff == 0 && in_vblank_irq)
-			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
-				      " diff_ns = %lld, framedur_ns = %d)\n",
-				      pipe, (long long) diff_ns, framedur_ns);
-	} else {
-		/* some kind of default for drivers w/o accurate vbl timestamping */
-		diff = in_vblank_irq ? 1 : 0;
-	}
-
-	/*
-	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
-	 * interval? If so then vblank irqs keep running and it will likely
-	 * happen that the hardware vblank counter is not trustworthy as it
-	 * might reset at some point in that interval and vblank timestamps
-	 * are not trustworthy either in that interval. Iow. this can result
-	 * in a bogus diff >> 1 which must be avoided as it would cause
-	 * random large forward jumps of the software vblank counter.
-	 */
-	if (diff > 1 && (vblank->inmodeset & 0x2)) {
-		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
-			      " due to pre-modeset.\n", pipe, diff);
-		diff = 1;
-	}
-
-	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
-		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
-		      pipe, vblank->count, diff, cur_vblank, vblank->last);
-
-	if (diff == 0) {
-		WARN_ON_ONCE(cur_vblank != vblank->last);
-		return;
-	}
-
-	/*
-	 * Only reinitialize corresponding vblank timestamp if high-precision query
-	 * available and didn't fail, or we were called from the vblank interrupt.
-	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
-	 * for now, to mark the vblanktimestamp as invalid.
-	 */
-	if (!rc && in_vblank_irq)
-		t_vblank = (struct timeval) {0, 0};
-
-	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
-}
-
-static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return 0;
-
-	return vblank->count;
-}
-
-/**
- * drm_accurate_vblank_count - retrieve the master vblank counter
- * @crtc: which counter to retrieve
- *
- * This function is similar to @drm_crtc_vblank_count but this
- * function interpolates to handle a race with vblank irq's.
- *
- * This is mostly useful for hardware that can obtain the scanout
- * position, but doesn't have a frame counter.
- */
-u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	u32 vblank;
-	unsigned long flags;
-
-	WARN(!dev->driver->get_vblank_timestamp,
-	     "This function requires support for accurate vblank timestamps.");
-
-	spin_lock_irqsave(&dev->vblank_time_lock, flags);
-
-	drm_update_vblank_count(dev, pipe, false);
-	vblank = drm_vblank_count(dev, pipe);
-
-	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
-
-	return vblank;
-}
-EXPORT_SYMBOL(drm_accurate_vblank_count);
-
-static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
-
-		if (crtc->funcs->disable_vblank) {
-			crtc->funcs->disable_vblank(crtc);
-			return;
-		}
-	}
-
-	dev->driver->disable_vblank(dev, pipe);
-}
-
-/*
- * Disable vblank irq's on crtc, make sure that last vblank count
- * of hardware and corresponding consistent software vblank counter
- * are preserved, even if there are any spurious vblank irq's after
- * disable.
- */
-static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-
-	assert_spin_locked(&dev->vbl_lock);
-
-	/* Prevent vblank irq processing while disabling vblank irqs,
-	 * so no updates of timestamps or count can happen after we've
-	 * disabled. Needed to prevent races in case of delayed irq's.
-	 */
-	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
-
-	/*
-	 * Only disable vblank interrupts if they're enabled. This avoids
-	 * calling the ->disable_vblank() operation in atomic context with the
-	 * hardware potentially runtime suspended.
-	 */
-	if (vblank->enabled) {
-		__disable_vblank(dev, pipe);
-		vblank->enabled = false;
-	}
-
-	/*
-	 * Always update the count and timestamp to maintain the
-	 * appearance that the counter has been ticking all along until
-	 * this time. This makes the count account for the entire time
-	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
-	 */
-	drm_update_vblank_count(dev, pipe, false);
-
-	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
-}
-
-static void vblank_disable_fn(unsigned long arg)
-{
-	struct drm_vblank_crtc *vblank = (void *)arg;
-	struct drm_device *dev = vblank->dev;
-	unsigned int pipe = vblank->pipe;
-	unsigned long irqflags;
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
-		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
-		vblank_disable_and_save(dev, pipe);
-	}
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-}
-
-/**
- * drm_vblank_cleanup - cleanup vblank support
- * @dev: DRM device
- *
- * This function cleans up any resources allocated in drm_vblank_init.
- */
-void drm_vblank_cleanup(struct drm_device *dev)
-{
-	unsigned int pipe;
-
-	/* Bail if the driver didn't call drm_vblank_init() */
-	if (dev->num_crtcs == 0)
-		return;
-
-	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
-		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-		WARN_ON(READ_ONCE(vblank->enabled) &&
-			drm_core_check_feature(dev, DRIVER_MODESET));
-
-		del_timer_sync(&vblank->disable_timer);
-	}
-
-	kfree(dev->vblank);
-
-	dev->num_crtcs = 0;
-}
-EXPORT_SYMBOL(drm_vblank_cleanup);
-
-/**
- * drm_vblank_init - initialize vblank support
- * @dev: DRM device
- * @num_crtcs: number of CRTCs supported by @dev
- *
- * This function initializes vblank support for @num_crtcs display pipelines.
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
-{
-	int ret = -ENOMEM;
-	unsigned int i;
-
-	spin_lock_init(&dev->vbl_lock);
-	spin_lock_init(&dev->vblank_time_lock);
-
-	dev->num_crtcs = num_crtcs;
-
-	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
-	if (!dev->vblank)
-		goto err;
-
-	for (i = 0; i < num_crtcs; i++) {
-		struct drm_vblank_crtc *vblank = &dev->vblank[i];
-
-		vblank->dev = dev;
-		vblank->pipe = i;
-		init_waitqueue_head(&vblank->queue);
-		setup_timer(&vblank->disable_timer, vblank_disable_fn,
-			    (unsigned long)vblank);
-		seqlock_init(&vblank->seqlock);
-	}
-
-	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
-
-	/* Driver specific high-precision vblank timestamping supported? */
-	if (dev->driver->get_vblank_timestamp)
-		DRM_INFO("Driver supports precise vblank timestamp query.\n");
-	else
-		DRM_INFO("No driver support for vblank timestamp query.\n");
-
-	/* Must have precise timestamping for reliable vblank instant disable */
-	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
-		dev->vblank_disable_immediate = false;
-		DRM_INFO("Setting vblank_disable_immediate to false because "
-			 "get_vblank_timestamp == NULL\n");
-	}
-
-	return 0;
-
-err:
-	dev->num_crtcs = 0;
-	return ret;
-}
-EXPORT_SYMBOL(drm_vblank_init);
+#include "drm_internal.h"
 
 /**
  * drm_irq_install - install IRQ handler
@@ -571,7 +176,7 @@ int drm_irq_uninstall(struct drm_device *dev)
 
 			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
 
-			vblank_disable_and_save(dev, i);
+			drm_vblank_disable_and_save(dev, i);
 			wake_up(&vblank->queue);
 		}
 		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -634,1187 +239,3 @@ int drm_legacy_irq_control(struct drm_device *dev, void *data,
 		return -EINVAL;
 	}
 }
-
-/**
- * drm_calc_timestamping_constants - calculate vblank timestamp constants
- * @crtc: drm_crtc whose timestamp constants should be updated.
- * @mode: display mode containing the scanout timings
- *
- * Calculate and store various constants which are later
- * needed by vblank and swap-completion timestamping, e.g,
- * by drm_calc_vbltimestamp_from_scanoutpos(). They are
- * derived from CRTC's true scanout timing, so they take
- * things like panel scaling or other adjustments into account.
- */
-void drm_calc_timestamping_constants(struct drm_crtc *crtc,
-				     const struct drm_display_mode *mode)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int linedur_ns = 0, framedur_ns = 0;
-	int dotclock = mode->crtc_clock;
-
-	if (!dev->num_crtcs)
-		return;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	/* Valid dotclock? */
-	if (dotclock > 0) {
-		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
-
-		/*
-		 * Convert scanline length in pixels and video
-		 * dot clock to line duration and frame duration
-		 * in nanoseconds:
-		 */
-		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
-		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
-
-		/*
-		 * Fields of interlaced scanout modes are only half a frame duration.
-		 */
-		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-			framedur_ns /= 2;
-	} else
-		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
-			  crtc->base.id);
-
-	vblank->linedur_ns  = linedur_ns;
-	vblank->framedur_ns = framedur_ns;
-	vblank->hwmode = *mode;
-
-	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
-		  crtc->base.id, mode->crtc_htotal,
-		  mode->crtc_vtotal, mode->crtc_vdisplay);
-	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
-		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
-}
-EXPORT_SYMBOL(drm_calc_timestamping_constants);
-
-/**
- * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
- * @dev: DRM device
- * @pipe: index of CRTC whose vblank timestamp to retrieve
- * @max_error: Desired maximum allowable error in timestamps (nanosecs)
- *             On return contains true maximum error of timestamp
- * @vblank_time: Pointer to struct timeval which should receive the timestamp
- * @in_vblank_irq:
- *     True when called from drm_crtc_handle_vblank().  Some drivers
- *     need to apply some workarounds for gpu-specific vblank irq quirks
- *     if flag is set.
- *
- * Implements calculation of exact vblank timestamps from given drm_display_mode
- * timings and current video scanout position of a CRTC. This can be called from
- * within get_vblank_timestamp() implementation of a kms driver to implement the
- * actual timestamping.
- *
- * Should return timestamps conforming to the OML_sync_control OpenML
- * extension specification. The timestamp corresponds to the end of
- * the vblank interval, aka start of scanout of topmost-leftmost display
- * pixel in the following video frame.
- *
- * Requires support for optional dev->driver->get_scanout_position()
- * in kms driver, plus a bit of setup code to provide a drm_display_mode
- * that corresponds to the true scanout timing.
- *
- * The current implementation only handles standard video modes. It
- * returns as no operation if a doublescan or interlaced video mode is
- * active. Higher level code is expected to handle this.
- *
- * This function can be used to implement the &drm_driver.get_vblank_timestamp
- * directly, if the driver implements the &drm_driver.get_scanout_position hook.
- *
- * Note that atomic drivers must call drm_calc_timestamping_constants() before
- * enabling a CRTC. The atomic helpers already take care of that in
- * drm_atomic_helper_update_legacy_modeset_state().
- *
- * Returns:
- *
- * Returns true on success, and false on failure, i.e. when no accurate
- * timestamp could be acquired.
- */
-bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-					   unsigned int pipe,
-					   int *max_error,
-					   struct timeval *vblank_time,
-					   bool in_vblank_irq)
-{
-	struct timeval tv_etime;
-	ktime_t stime, etime;
-	bool vbl_status;
-	struct drm_crtc *crtc;
-	const struct drm_display_mode *mode;
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int vpos, hpos, i;
-	int delta_ns, duration_ns;
-
-	if (!drm_core_check_feature(dev, DRIVER_MODESET))
-		return false;
-
-	crtc = drm_crtc_from_index(dev, pipe);
-
-	if (pipe >= dev->num_crtcs || !crtc) {
-		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return false;
-	}
-
-	/* Scanout position query not supported? Should not happen. */
-	if (!dev->driver->get_scanout_position) {
-		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
-		return false;
-	}
-
-	if (drm_drv_uses_atomic_modeset(dev))
-		mode = &vblank->hwmode;
-	else
-		mode = &crtc->hwmode;
-
-	/* If mode timing undefined, just return as no-op:
-	 * Happens during initial modesetting of a crtc.
-	 */
-	if (mode->crtc_clock == 0) {
-		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
-		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
-
-		return false;
-	}
-
-	/* Get current scanout position with system timestamp.
-	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
-	 * if single query takes longer than max_error nanoseconds.
-	 *
-	 * This guarantees a tight bound on maximum error if
-	 * code gets preempted or delayed for some reason.
-	 */
-	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
-		/*
-		 * Get vertical and horizontal scanout position vpos, hpos,
-		 * and bounding timestamps stime, etime, pre/post query.
-		 */
-		vbl_status = dev->driver->get_scanout_position(dev, pipe,
-							       in_vblank_irq,
-							       &vpos, &hpos,
-							       &stime, &etime,
-							       mode);
-
-		/* Return as no-op if scanout query unsupported or failed. */
-		if (!vbl_status) {
-			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
-				  pipe);
-			return false;
-		}
-
-		/* Compute uncertainty in timestamp of scanout position query. */
-		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
-
-		/* Accept result with <  max_error nsecs timing uncertainty. */
-		if (duration_ns <= *max_error)
-			break;
-	}
-
-	/* Noisy system timing? */
-	if (i == DRM_TIMESTAMP_MAXRETRIES) {
-		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
-			  pipe, duration_ns/1000, *max_error/1000, i);
-	}
-
-	/* Return upper bound of timestamp precision error. */
-	*max_error = duration_ns;
-
-	/* Convert scanout position into elapsed time at raw_time query
-	 * since start of scanout at first display scanline. delta_ns
-	 * can be negative if start of scanout hasn't happened yet.
-	 */
-	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
-			   mode->crtc_clock);
-
-	if (!drm_timestamp_monotonic)
-		etime = ktime_mono_to_real(etime);
-
-	/* save this only for debugging purposes */
-	tv_etime = ktime_to_timeval(etime);
-	/* Subtract time delta from raw timestamp to get final
-	 * vblank_time timestamp for end of vblank.
-	 */
-	etime = ktime_sub_ns(etime, delta_ns);
-	*vblank_time = ktime_to_timeval(etime);
-
-	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
-		      pipe, hpos, vpos,
-		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
-		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
-		      duration_ns/1000, i);
-
-	return true;
-}
-EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
-
-static struct timeval get_drm_timestamp(void)
-{
-	ktime_t now;
-
-	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
-	return ktime_to_timeval(now);
-}
-
-/**
- * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
- *                             vblank interval
- * @dev: DRM device
- * @pipe: index of CRTC whose vblank timestamp to retrieve
- * @tvblank: Pointer to target struct timeval which should receive the timestamp
- * @in_vblank_irq:
- *     True when called from drm_crtc_handle_vblank().  Some drivers
- *     need to apply some workarounds for gpu-specific vblank irq quirks
- *     if flag is set.
- *
- * Fetches the system timestamp corresponding to the time of the most recent
- * vblank interval on specified CRTC. May call into kms-driver to
- * compute the timestamp with a high-precision GPU specific method.
- *
- * Returns zero if timestamp originates from uncorrected do_gettimeofday()
- * call, i.e., it isn't very precisely locked to the true vblank.
- *
- * Returns:
- * True if timestamp is considered to be very precise, false otherwise.
- */
-static bool
-drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-			  struct timeval *tvblank, bool in_vblank_irq)
-{
-	bool ret = false;
-
-	/* Define requested maximum error on timestamps (nanoseconds). */
-	int max_error = (int) drm_timestamp_precision * 1000;
-
-	/* Query driver if possible and precision timestamping enabled. */
-	if (dev->driver->get_vblank_timestamp && (max_error > 0))
-		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
-							tvblank, in_vblank_irq);
-
-	/* GPU high precision timestamp query unsupported or failed.
-	 * Return current monotonic/gettimeofday timestamp as best estimate.
-	 */
-	if (!ret)
-		*tvblank = get_drm_timestamp();
-
-	return ret;
-}
-
-/**
- * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
- * @crtc: which counter to retrieve
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity.
- *
- * Returns:
- * The software vblank counter.
- */
-u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
-{
-	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_count);
-
-/**
- * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
- *     system timestamp corresponding to that vblank counter value.
- * @dev: DRM device
- * @pipe: index of CRTC whose counter to retrieve
- * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current vblank counter value.
- *
- * This is the legacy version of drm_crtc_vblank_count_and_time().
- */
-static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
-				     struct timeval *vblanktime)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	u32 vblank_count;
-	unsigned int seq;
-
-	if (WARN_ON(pipe >= dev->num_crtcs)) {
-		*vblanktime = (struct timeval) { 0 };
-		return 0;
-	}
-
-	do {
-		seq = read_seqbegin(&vblank->seqlock);
-		vblank_count = vblank->count;
-		*vblanktime = vblank->time;
-	} while (read_seqretry(&vblank->seqlock, seq));
-
-	return vblank_count;
-}
-
-/**
- * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
- *     and the system timestamp corresponding to that vblank counter value
- * @crtc: which counter to retrieve
- * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
- *
- * Fetches the "cooked" vblank count value that represents the number of
- * vblank events since the system was booted, including lost events due to
- * modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current vblank counter value.
- */
-u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
-				   struct timeval *vblanktime)
-{
-	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
-					 vblanktime);
-}
-EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
-
-static void send_vblank_event(struct drm_device *dev,
-		struct drm_pending_vblank_event *e,
-		unsigned long seq, struct timeval *now)
-{
-	e->event.sequence = seq;
-	e->event.tv_sec = now->tv_sec;
-	e->event.tv_usec = now->tv_usec;
-
-	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
-					 e->event.sequence);
-
-	drm_send_event_locked(dev, &e->base);
-}
-
-/**
- * drm_crtc_arm_vblank_event - arm vblank event after pageflip
- * @crtc: the source CRTC of the vblank event
- * @e: the event to send
- *
- * A lot of drivers need to generate vblank events for the very next vblank
- * interrupt. For example when the page flip interrupt happens when the page
- * flip gets armed, but not when it actually executes within the next vblank
- * period. This helper function implements exactly the required vblank arming
- * behaviour.
- *
- * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
- * atomic commit must ensure that the next vblank happens at exactly the same
- * time as the atomic commit is committed to the hardware. This function itself
- * does **not** protect again the next vblank interrupt racing with either this
- * function call or the atomic commit operation. A possible sequence could be:
- *
- * 1. Driver commits new hardware state into vblank-synchronized registers.
- * 2. A vblank happens, committing the hardware state. Also the corresponding
- *    vblank interrupt is fired off and fully processed by the interrupt
- *    handler.
- * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
- * 4. The event is only send out for the next vblank, which is wrong.
- *
- * An equivalent race can happen when the driver calls
- * drm_crtc_arm_vblank_event() before writing out the new hardware state.
- *
- * The only way to make this work safely is to prevent the vblank from firing
- * (and the hardware from committing anything else) until the entire atomic
- * commit sequence has run to completion. If the hardware does not have such a
- * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
- * Instead drivers need to manually send out the event from their interrupt
- * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
- * possible race with the hardware committing the atomic update.
- *
- * Caller must hold event lock. Caller must also hold a vblank reference for
- * the event @e, which will be dropped when the next vblank arrives.
- */
-void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
-			       struct drm_pending_vblank_event *e)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-
-	assert_spin_locked(&dev->event_lock);
-
-	e->pipe = pipe;
-	e->event.sequence = drm_vblank_count(dev, pipe);
-	e->event.crtc_id = crtc->base.id;
-	list_add_tail(&e->base.link, &dev->vblank_event_list);
-}
-EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
-
-/**
- * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
- * @crtc: the source CRTC of the vblank event
- * @e: the event to send
- *
- * Updates sequence # and timestamp on event for the most recently processed
- * vblank, and sends it to userspace.  Caller must hold event lock.
- *
- * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
- * situation, especially to send out events for atomic commit operations.
- */
-void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
-				struct drm_pending_vblank_event *e)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int seq, pipe = drm_crtc_index(crtc);
-	struct timeval now;
-
-	if (dev->num_crtcs > 0) {
-		seq = drm_vblank_count_and_time(dev, pipe, &now);
-	} else {
-		seq = 0;
-
-		now = get_drm_timestamp();
-	}
-	e->pipe = pipe;
-	e->event.crtc_id = crtc->base.id;
-	send_vblank_event(dev, e, seq, &now);
-}
-EXPORT_SYMBOL(drm_crtc_send_vblank_event);
-
-static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
-
-		if (crtc->funcs->enable_vblank)
-			return crtc->funcs->enable_vblank(crtc);
-	}
-
-	return dev->driver->enable_vblank(dev, pipe);
-}
-
-/**
- * drm_vblank_enable - enable the vblank interrupt on a CRTC
- * @dev: DRM device
- * @pipe: CRTC index
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int ret = 0;
-
-	assert_spin_locked(&dev->vbl_lock);
-
-	spin_lock(&dev->vblank_time_lock);
-
-	if (!vblank->enabled) {
-		/*
-		 * Enable vblank irqs under vblank_time_lock protection.
-		 * All vblank count & timestamp updates are held off
-		 * until we are done reinitializing master counter and
-		 * timestamps. Filtercode in drm_handle_vblank() will
-		 * prevent double-accounting of same vblank interval.
-		 */
-		ret = __enable_vblank(dev, pipe);
-		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
-		if (ret) {
-			atomic_dec(&vblank->refcount);
-		} else {
-			drm_update_vblank_count(dev, pipe, 0);
-			/* drm_update_vblank_count() includes a wmb so we just
-			 * need to ensure that the compiler emits the write
-			 * to mark the vblank as enabled after the call
-			 * to drm_update_vblank_count().
-			 */
-			WRITE_ONCE(vblank->enabled, true);
-		}
-	}
-
-	spin_unlock(&dev->vblank_time_lock);
-
-	return ret;
-}
-
-/**
- * drm_vblank_get - get a reference count on vblank events
- * @dev: DRM device
- * @pipe: index of CRTC to own
- *
- * Acquire a reference count on vblank events to avoid having them disabled
- * while in use.
- *
- * This is the legacy version of drm_crtc_vblank_get().
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-	int ret = 0;
-
-	if (!dev->num_crtcs)
-		return -EINVAL;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return -EINVAL;
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	/* Going from 0->1 means we have to enable interrupts again */
-	if (atomic_add_return(1, &vblank->refcount) == 1) {
-		ret = drm_vblank_enable(dev, pipe);
-	} else {
-		if (!vblank->enabled) {
-			atomic_dec(&vblank->refcount);
-			ret = -EINVAL;
-		}
-	}
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-	return ret;
-}
-
-/**
- * drm_crtc_vblank_get - get a reference count on vblank events
- * @crtc: which CRTC to own
- *
- * Acquire a reference count on vblank events to avoid having them disabled
- * while in use.
- *
- * Returns:
- * Zero on success or a negative error code on failure.
- */
-int drm_crtc_vblank_get(struct drm_crtc *crtc)
-{
-	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_get);
-
-/**
- * drm_vblank_put - release ownership of vblank events
- * @dev: DRM device
- * @pipe: index of CRTC to release
- *
- * Release ownership of a given vblank counter, turning off interrupts
- * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
- *
- * This is the legacy version of drm_crtc_vblank_put().
- */
-static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
-		return;
-
-	/* Last user schedules interrupt disable */
-	if (atomic_dec_and_test(&vblank->refcount)) {
-		if (drm_vblank_offdelay == 0)
-			return;
-		else if (drm_vblank_offdelay < 0)
-			vblank_disable_fn((unsigned long)vblank);
-		else if (!dev->vblank_disable_immediate)
-			mod_timer(&vblank->disable_timer,
-				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
-	}
-}
-
-/**
- * drm_crtc_vblank_put - give up ownership of vblank events
- * @crtc: which counter to give up
- *
- * Release ownership of a given vblank counter, turning off interrupts
- * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
- */
-void drm_crtc_vblank_put(struct drm_crtc *crtc)
-{
-	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_put);
-
-/**
- * drm_wait_one_vblank - wait for one vblank
- * @dev: DRM device
- * @pipe: CRTC index
- *
- * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
- * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
- * due to lack of driver support or because the crtc is off.
- */
-void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	int ret;
-	u32 last;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	ret = drm_vblank_get(dev, pipe);
-	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
-		return;
-
-	last = drm_vblank_count(dev, pipe);
-
-	ret = wait_event_timeout(vblank->queue,
-				 last != drm_vblank_count(dev, pipe),
-				 msecs_to_jiffies(100));
-
-	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
-
-	drm_vblank_put(dev, pipe);
-}
-EXPORT_SYMBOL(drm_wait_one_vblank);
-
-/**
- * drm_crtc_wait_one_vblank - wait for one vblank
- * @crtc: DRM crtc
- *
- * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
- * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
- * due to lack of driver support or because the crtc is off.
- */
-void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
-{
-	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
-
-/**
- * drm_crtc_vblank_off - disable vblank events on a CRTC
- * @crtc: CRTC in question
- *
- * Drivers can use this function to shut down the vblank interrupt handling when
- * disabling a crtc. This function ensures that the latest vblank frame count is
- * stored so that drm_vblank_on can restore it again.
- *
- * Drivers must use this function when the hardware vblank counter can get
- * reset, e.g. when suspending.
- */
-void drm_crtc_vblank_off(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	struct drm_pending_vblank_event *e, *t;
-	struct timeval now;
-	unsigned long irqflags;
-	unsigned int seq;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	spin_lock_irqsave(&dev->event_lock, irqflags);
-
-	spin_lock(&dev->vbl_lock);
-	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
-		      pipe, vblank->enabled, vblank->inmodeset);
-
-	/* Avoid redundant vblank disables without previous
-	 * drm_crtc_vblank_on(). */
-	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
-		vblank_disable_and_save(dev, pipe);
-
-	wake_up(&vblank->queue);
-
-	/*
-	 * Prevent subsequent drm_vblank_get() from re-enabling
-	 * the vblank interrupt by bumping the refcount.
-	 */
-	if (!vblank->inmodeset) {
-		atomic_inc(&vblank->refcount);
-		vblank->inmodeset = 1;
-	}
-	spin_unlock(&dev->vbl_lock);
-
-	/* Send any queued vblank events, lest the natives grow disquiet */
-	seq = drm_vblank_count_and_time(dev, pipe, &now);
-
-	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
-		if (e->pipe != pipe)
-			continue;
-		DRM_DEBUG("Sending premature vblank event on disable: "
-			  "wanted %u, current %u\n",
-			  e->event.sequence, seq);
-		list_del(&e->base.link);
-		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
-	}
-	spin_unlock_irqrestore(&dev->event_lock, irqflags);
-
-	/* Will be reset by the modeset helpers when re-enabling the crtc by
-	 * calling drm_calc_timestamping_constants(). */
-	vblank->hwmode.crtc_clock = 0;
-}
-EXPORT_SYMBOL(drm_crtc_vblank_off);
-
-/**
- * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
- * @crtc: CRTC in question
- *
- * Drivers can use this function to reset the vblank state to off at load time.
- * Drivers should use this together with the drm_crtc_vblank_off() and
- * drm_crtc_vblank_on() functions. The difference compared to
- * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
- * and hence doesn't need to call any driver hooks.
- */
-void drm_crtc_vblank_reset(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned long irqflags;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	/*
-	 * Prevent subsequent drm_vblank_get() from enabling the vblank
-	 * interrupt by bumping the refcount.
-	 */
-	if (!vblank->inmodeset) {
-		atomic_inc(&vblank->refcount);
-		vblank->inmodeset = 1;
-	}
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-	WARN_ON(!list_empty(&dev->vblank_event_list));
-}
-EXPORT_SYMBOL(drm_crtc_vblank_reset);
-
-/**
- * drm_crtc_vblank_on - enable vblank events on a CRTC
- * @crtc: CRTC in question
- *
- * This functions restores the vblank interrupt state captured with
- * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
- * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called
- * in driver load code to reflect the current hardware state of the crtc.
- */
-void drm_crtc_vblank_on(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
-		      pipe, vblank->enabled, vblank->inmodeset);
-
-	/* Drop our private "prevent drm_vblank_get" refcount */
-	if (vblank->inmodeset) {
-		atomic_dec(&vblank->refcount);
-		vblank->inmodeset = 0;
-	}
-
-	drm_reset_vblank_timestamp(dev, pipe);
-
-	/*
-	 * re-enable interrupts if there are users left, or the
-	 * user wishes vblank interrupts to be enabled all the time.
-	 */
-	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
-		WARN_ON(drm_vblank_enable(dev, pipe));
-	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-}
-EXPORT_SYMBOL(drm_crtc_vblank_on);
-
-static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
-					  unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-
-	/* vblank is not initialized (IRQ not installed ?), or has been freed */
-	if (!dev->num_crtcs)
-		return;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	/*
-	 * To avoid all the problems that might happen if interrupts
-	 * were enabled/disabled around or between these calls, we just
-	 * have the kernel take a reference on the CRTC (just once though
-	 * to avoid corrupting the count if multiple, mismatch calls occur),
-	 * so that interrupts remain enabled in the interim.
-	 */
-	if (!vblank->inmodeset) {
-		vblank->inmodeset = 0x1;
-		if (drm_vblank_get(dev, pipe) == 0)
-			vblank->inmodeset |= 0x2;
-	}
-}
-
-static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
-					   unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-
-	/* vblank is not initialized (IRQ not installed ?), or has been freed */
-	if (!dev->num_crtcs)
-		return;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return;
-
-	if (vblank->inmodeset) {
-		spin_lock_irqsave(&dev->vbl_lock, irqflags);
-		drm_reset_vblank_timestamp(dev, pipe);
-		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-		if (vblank->inmodeset & 0x2)
-			drm_vblank_put(dev, pipe);
-
-		vblank->inmodeset = 0;
-	}
-}
-
-int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
-			   struct drm_file *file_priv)
-{
-	struct drm_modeset_ctl *modeset = data;
-	unsigned int pipe;
-
-	/* If drm_vblank_init() hasn't been called yet, just no-op */
-	if (!dev->num_crtcs)
-		return 0;
-
-	/* KMS drivers handle this internally */
-	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
-		return 0;
-
-	pipe = modeset->crtc;
-	if (pipe >= dev->num_crtcs)
-		return -EINVAL;
-
-	switch (modeset->cmd) {
-	case _DRM_PRE_MODESET:
-		drm_legacy_vblank_pre_modeset(dev, pipe);
-		break;
-	case _DRM_POST_MODESET:
-		drm_legacy_vblank_post_modeset(dev, pipe);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static inline bool vblank_passed(u32 seq, u32 ref)
-{
-	return (seq - ref) <= (1 << 23);
-}
-
-static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
-				  union drm_wait_vblank *vblwait,
-				  struct drm_file *file_priv)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	struct drm_pending_vblank_event *e;
-	struct timeval now;
-	unsigned long flags;
-	unsigned int seq;
-	int ret;
-
-	e = kzalloc(sizeof(*e), GFP_KERNEL);
-	if (e == NULL) {
-		ret = -ENOMEM;
-		goto err_put;
-	}
-
-	e->pipe = pipe;
-	e->event.base.type = DRM_EVENT_VBLANK;
-	e->event.base.length = sizeof(e->event);
-	e->event.user_data = vblwait->request.signal;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-
-	/*
-	 * drm_crtc_vblank_off() might have been called after we called
-	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
-	 * vblank disable, so no need for further locking.  The reference from
-	 * drm_vblank_get() protects against vblank disable from another source.
-	 */
-	if (!READ_ONCE(vblank->enabled)) {
-		ret = -EINVAL;
-		goto err_unlock;
-	}
-
-	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
-					    &e->event.base);
-
-	if (ret)
-		goto err_unlock;
-
-	seq = drm_vblank_count_and_time(dev, pipe, &now);
-
-	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
-		  vblwait->request.sequence, seq, pipe);
-
-	trace_drm_vblank_event_queued(file_priv, pipe,
-				      vblwait->request.sequence);
-
-	e->event.sequence = vblwait->request.sequence;
-	if (vblank_passed(seq, vblwait->request.sequence)) {
-		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
-		vblwait->reply.sequence = seq;
-	} else {
-		/* drm_handle_vblank_events will call drm_vblank_put */
-		list_add_tail(&e->base.link, &dev->vblank_event_list);
-		vblwait->reply.sequence = vblwait->request.sequence;
-	}
-
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-
-	return 0;
-
-err_unlock:
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-	kfree(e);
-err_put:
-	drm_vblank_put(dev, pipe);
-	return ret;
-}
-
-static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
-{
-	if (vblwait->request.sequence)
-		return false;
-
-	return _DRM_VBLANK_RELATIVE ==
-		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
-					  _DRM_VBLANK_EVENT |
-					  _DRM_VBLANK_NEXTONMISS));
-}
-
-/*
- * Wait for VBLANK.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param data user argument, pointing to a drm_wait_vblank structure.
- * \return zero on success or a negative number on failure.
- *
- * This function enables the vblank interrupt on the pipe requested, then
- * sleeps waiting for the requested sequence number to occur, and drops
- * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
- * after a timeout with no further vblank waits scheduled).
- */
-int drm_wait_vblank(struct drm_device *dev, void *data,
-		    struct drm_file *file_priv)
-{
-	struct drm_vblank_crtc *vblank;
-	union drm_wait_vblank *vblwait = data;
-	int ret;
-	unsigned int flags, seq, pipe, high_pipe;
-
-	if (!dev->irq_enabled)
-		return -EINVAL;
-
-	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
-		return -EINVAL;
-
-	if (vblwait->request.type &
-	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
-	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
-		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
-			  vblwait->request.type,
-			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
-			   _DRM_VBLANK_HIGH_CRTC_MASK));
-		return -EINVAL;
-	}
-
-	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
-	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
-	if (high_pipe)
-		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
-	else
-		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
-	if (pipe >= dev->num_crtcs)
-		return -EINVAL;
-
-	vblank = &dev->vblank[pipe];
-
-	/* If the counter is currently enabled and accurate, short-circuit
-	 * queries to return the cached timestamp of the last vblank.
-	 */
-	if (dev->vblank_disable_immediate &&
-	    drm_wait_vblank_is_query(vblwait) &&
-	    READ_ONCE(vblank->enabled)) {
-		struct timeval now;
-
-		vblwait->reply.sequence =
-			drm_vblank_count_and_time(dev, pipe, &now);
-		vblwait->reply.tval_sec = now.tv_sec;
-		vblwait->reply.tval_usec = now.tv_usec;
-		return 0;
-	}
-
-	ret = drm_vblank_get(dev, pipe);
-	if (ret) {
-		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
-		return ret;
-	}
-	seq = drm_vblank_count(dev, pipe);
-
-	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
-	case _DRM_VBLANK_RELATIVE:
-		vblwait->request.sequence += seq;
-		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
-	case _DRM_VBLANK_ABSOLUTE:
-		break;
-	default:
-		ret = -EINVAL;
-		goto done;
-	}
-
-	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
-	    vblank_passed(seq, vblwait->request.sequence))
-		vblwait->request.sequence = seq + 1;
-
-	if (flags & _DRM_VBLANK_EVENT) {
-		/* must hold on to the vblank ref until the event fires
-		 * drm_vblank_put will be called asynchronously
-		 */
-		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
-	}
-
-	if (vblwait->request.sequence != seq) {
-		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
-			  vblwait->request.sequence, pipe);
-		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
-			    vblank_passed(drm_vblank_count(dev, pipe),
-					  vblwait->request.sequence) ||
-			    !READ_ONCE(vblank->enabled));
-	}
-
-	if (ret != -EINTR) {
-		struct timeval now;
-
-		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
-		vblwait->reply.tval_sec = now.tv_sec;
-		vblwait->reply.tval_usec = now.tv_usec;
-
-		DRM_DEBUG("crtc %d returning %u to client\n",
-			  pipe, vblwait->reply.sequence);
-	} else {
-		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
-	}
-
-done:
-	drm_vblank_put(dev, pipe);
-	return ret;
-}
-
-static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_pending_vblank_event *e, *t;
-	struct timeval now;
-	unsigned int seq;
-
-	assert_spin_locked(&dev->event_lock);
-
-	seq = drm_vblank_count_and_time(dev, pipe, &now);
-
-	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
-		if (e->pipe != pipe)
-			continue;
-		if (!vblank_passed(seq, e->event.sequence))
-			continue;
-
-		DRM_DEBUG("vblank event on %u, current %u\n",
-			  e->event.sequence, seq);
-
-		list_del(&e->base.link);
-		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
-	}
-
-	trace_drm_vblank_event(pipe, seq);
-}
-
-/**
- * drm_handle_vblank - handle a vblank event
- * @dev: DRM device
- * @pipe: index of CRTC where this event occurred
- *
- * Drivers should call this routine in their vblank interrupt handlers to
- * update the vblank counter and send any signals that may be pending.
- *
- * This is the legacy version of drm_crtc_handle_vblank().
- */
-bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
-{
-	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	unsigned long irqflags;
-	bool disable_irq;
-
-	if (WARN_ON_ONCE(!dev->num_crtcs))
-		return false;
-
-	if (WARN_ON(pipe >= dev->num_crtcs))
-		return false;
-
-	spin_lock_irqsave(&dev->event_lock, irqflags);
-
-	/* Need timestamp lock to prevent concurrent execution with
-	 * vblank enable/disable, as this would cause inconsistent
-	 * or corrupted timestamps and vblank counts.
-	 */
-	spin_lock(&dev->vblank_time_lock);
-
-	/* Vblank irq handling disabled. Nothing to do. */
-	if (!vblank->enabled) {
-		spin_unlock(&dev->vblank_time_lock);
-		spin_unlock_irqrestore(&dev->event_lock, irqflags);
-		return false;
-	}
-
-	drm_update_vblank_count(dev, pipe, true);
-
-	spin_unlock(&dev->vblank_time_lock);
-
-	wake_up(&vblank->queue);
-
-	/* With instant-off, we defer disabling the interrupt until after
-	 * we finish processing the following vblank after all events have
-	 * been signaled. The disable has to be last (after
-	 * drm_handle_vblank_events) so that the timestamp is always accurate.
-	 */
-	disable_irq = (dev->vblank_disable_immediate &&
-		       drm_vblank_offdelay > 0 &&
-		       !atomic_read(&vblank->refcount));
-
-	drm_handle_vblank_events(dev, pipe);
-
-	spin_unlock_irqrestore(&dev->event_lock, irqflags);
-
-	if (disable_irq)
-		vblank_disable_fn((unsigned long)vblank);
-
-	return true;
-}
-EXPORT_SYMBOL(drm_handle_vblank);
-
-/**
- * drm_crtc_handle_vblank - handle a vblank event
- * @crtc: where this event occurred
- *
- * Drivers should call this routine in their vblank interrupt handlers to
- * update the vblank counter and send any signals that may be pending.
- *
- * This is the native KMS version of drm_handle_vblank().
- *
- * Returns:
- * True if the event was successfully handled, false on failure.
- */
-bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
-{
-	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
-}
-EXPORT_SYMBOL(drm_crtc_handle_vblank);
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
new file mode 100644
index 000000000000..630dc26379b7
--- /dev/null
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -0,0 +1,1645 @@
+/*
+ * drm_irq.c IRQ and vblank support
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drm_vblank.h>
+#include <drm/drmP.h>
+#include <linux/export.h>
+
+#include "drm_trace.h"
+#include "drm_internal.h"
+
+/* Retry timestamp calculation up to 3 times to satisfy
+ * drm_timestamp_precision before giving up.
+ */
+#define DRM_TIMESTAMP_MAXRETRIES 3
+
+/* Threshold in nanoseconds for detection of redundant
+ * vblank irq in drm_handle_vblank(). 1 msec should be ok.
+ */
+#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
+
+static bool
+drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
+			  struct timeval *tvblank, bool in_vblank_irq);
+
+static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
+
+/*
+ * Default to use monotonic timestamps for wait-for-vblank and page-flip
+ * complete events.
+ */
+unsigned int drm_timestamp_monotonic = 1;
+
+static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
+
+module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
+module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
+module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
+MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
+MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
+MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
+
+static void store_vblank(struct drm_device *dev, unsigned int pipe,
+			 u32 vblank_count_inc,
+			 struct timeval *t_vblank, u32 last)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	assert_spin_locked(&dev->vblank_time_lock);
+
+	vblank->last = last;
+
+	write_seqlock(&vblank->seqlock);
+	vblank->time = *t_vblank;
+	vblank->count += vblank_count_inc;
+	write_sequnlock(&vblank->seqlock);
+}
+
+/*
+ * "No hw counter" fallback implementation of .get_vblank_counter() hook,
+ * if there is no useable hardware frame counter available.
+ */
+static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
+{
+	WARN_ON_ONCE(dev->max_vblank_count != 0);
+	return 0;
+}
+
+static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
+
+		if (crtc->funcs->get_vblank_counter)
+			return crtc->funcs->get_vblank_counter(crtc);
+	}
+
+	if (dev->driver->get_vblank_counter)
+		return dev->driver->get_vblank_counter(dev, pipe);
+
+	return drm_vblank_no_hw_counter(dev, pipe);
+}
+
+/*
+ * Reset the stored timestamp for the current vblank count to correspond
+ * to the last vblank occurred.
+ *
+ * Only to be called from drm_crtc_vblank_on().
+ *
+ * Note: caller must hold &drm_device.vbl_lock since this reads & writes
+ * device vblank fields.
+ */
+static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe)
+{
+	u32 cur_vblank;
+	bool rc;
+	struct timeval t_vblank;
+	int count = DRM_TIMESTAMP_MAXRETRIES;
+
+	spin_lock(&dev->vblank_time_lock);
+
+	/*
+	 * sample the current counter to avoid random jumps
+	 * when drm_vblank_enable() applies the diff
+	 */
+	do {
+		cur_vblank = __get_vblank_counter(dev, pipe);
+		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
+	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+
+	/*
+	 * Only reinitialize corresponding vblank timestamp if high-precision query
+	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
+	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
+	 */
+	if (!rc)
+		t_vblank = (struct timeval) {0, 0};
+
+	/*
+	 * +1 to make sure user will never see the same
+	 * vblank counter value before and after a modeset
+	 */
+	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
+
+	spin_unlock(&dev->vblank_time_lock);
+}
+
+/*
+ * Call back into the driver to update the appropriate vblank counter
+ * (specified by @pipe).  Deal with wraparound, if it occurred, and
+ * update the last read value so we can deal with wraparound on the next
+ * call if necessary.
+ *
+ * Only necessary when going from off->on, to account for frames we
+ * didn't get an interrupt for.
+ *
+ * Note: caller must hold &drm_device.vbl_lock since this reads & writes
+ * device vblank fields.
+ */
+static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
+				    bool in_vblank_irq)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	u32 cur_vblank, diff;
+	bool rc;
+	struct timeval t_vblank;
+	int count = DRM_TIMESTAMP_MAXRETRIES;
+	int framedur_ns = vblank->framedur_ns;
+
+	/*
+	 * Interrupts were disabled prior to this call, so deal with counter
+	 * wrap if needed.
+	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
+	 * here if the register is small or we had vblank interrupts off for
+	 * a long time.
+	 *
+	 * We repeat the hardware vblank counter & timestamp query until
+	 * we get consistent results. This to prevent races between gpu
+	 * updating its hardware counter while we are retrieving the
+	 * corresponding vblank timestamp.
+	 */
+	do {
+		cur_vblank = __get_vblank_counter(dev, pipe);
+		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
+	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+
+	if (dev->max_vblank_count != 0) {
+		/* trust the hw counter when it's around */
+		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
+	} else if (rc && framedur_ns) {
+		const struct timeval *t_old;
+		u64 diff_ns;
+
+		t_old = &vblank->time;
+		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
+
+		/*
+		 * Figure out how many vblanks we've missed based
+		 * on the difference in the timestamps and the
+		 * frame/field duration.
+		 */
+		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
+
+		if (diff == 0 && in_vblank_irq)
+			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
+				      " diff_ns = %lld, framedur_ns = %d)\n",
+				      pipe, (long long) diff_ns, framedur_ns);
+	} else {
+		/* some kind of default for drivers w/o accurate vbl timestamping */
+		diff = in_vblank_irq ? 1 : 0;
+	}
+
+	/*
+	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
+	 * interval? If so then vblank irqs keep running and it will likely
+	 * happen that the hardware vblank counter is not trustworthy as it
+	 * might reset at some point in that interval and vblank timestamps
+	 * are not trustworthy either in that interval. Iow. this can result
+	 * in a bogus diff >> 1 which must be avoided as it would cause
+	 * random large forward jumps of the software vblank counter.
+	 */
+	if (diff > 1 && (vblank->inmodeset & 0x2)) {
+		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
+			      " due to pre-modeset.\n", pipe, diff);
+		diff = 1;
+	}
+
+	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
+		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
+		      pipe, vblank->count, diff, cur_vblank, vblank->last);
+
+	if (diff == 0) {
+		WARN_ON_ONCE(cur_vblank != vblank->last);
+		return;
+	}
+
+	/*
+	 * Only reinitialize corresponding vblank timestamp if high-precision query
+	 * available and didn't fail, or we were called from the vblank interrupt.
+	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
+	 * for now, to mark the vblanktimestamp as invalid.
+	 */
+	if (!rc && in_vblank_irq)
+		t_vblank = (struct timeval) {0, 0};
+
+	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
+}
+
+static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return 0;
+
+	return vblank->count;
+}
+
+/**
+ * drm_accurate_vblank_count - retrieve the master vblank counter
+ * @crtc: which counter to retrieve
+ *
+ * This function is similar to @drm_crtc_vblank_count but this
+ * function interpolates to handle a race with vblank irq's.
+ *
+ * This is mostly useful for hardware that can obtain the scanout
+ * position, but doesn't have a frame counter.
+ */
+u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	u32 vblank;
+	unsigned long flags;
+
+	WARN(!dev->driver->get_vblank_timestamp,
+	     "This function requires support for accurate vblank timestamps.");
+
+	spin_lock_irqsave(&dev->vblank_time_lock, flags);
+
+	drm_update_vblank_count(dev, pipe, false);
+	vblank = drm_vblank_count(dev, pipe);
+
+	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
+
+	return vblank;
+}
+EXPORT_SYMBOL(drm_accurate_vblank_count);
+
+static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
+
+		if (crtc->funcs->disable_vblank) {
+			crtc->funcs->disable_vblank(crtc);
+			return;
+		}
+	}
+
+	dev->driver->disable_vblank(dev, pipe);
+}
+
+/*
+ * Disable vblank irq's on crtc, make sure that last vblank count
+ * of hardware and corresponding consistent software vblank counter
+ * are preserved, even if there are any spurious vblank irq's after
+ * disable.
+ */
+void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+
+	assert_spin_locked(&dev->vbl_lock);
+
+	/* Prevent vblank irq processing while disabling vblank irqs,
+	 * so no updates of timestamps or count can happen after we've
+	 * disabled. Needed to prevent races in case of delayed irq's.
+	 */
+	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
+
+	/*
+	 * Only disable vblank interrupts if they're enabled. This avoids
+	 * calling the ->disable_vblank() operation in atomic context with the
+	 * hardware potentially runtime suspended.
+	 */
+	if (vblank->enabled) {
+		__disable_vblank(dev, pipe);
+		vblank->enabled = false;
+	}
+
+	/*
+	 * Always update the count and timestamp to maintain the
+	 * appearance that the counter has been ticking all along until
+	 * this time. This makes the count account for the entire time
+	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
+	 */
+	drm_update_vblank_count(dev, pipe, false);
+
+	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
+}
+
+static void vblank_disable_fn(unsigned long arg)
+{
+	struct drm_vblank_crtc *vblank = (void *)arg;
+	struct drm_device *dev = vblank->dev;
+	unsigned int pipe = vblank->pipe;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
+		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
+		drm_vblank_disable_and_save(dev, pipe);
+	}
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+
+/**
+ * drm_vblank_cleanup - cleanup vblank support
+ * @dev: DRM device
+ *
+ * This function cleans up any resources allocated in drm_vblank_init.
+ */
+void drm_vblank_cleanup(struct drm_device *dev)
+{
+	unsigned int pipe;
+
+	/* Bail if the driver didn't call drm_vblank_init() */
+	if (dev->num_crtcs == 0)
+		return;
+
+	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
+		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+		WARN_ON(READ_ONCE(vblank->enabled) &&
+			drm_core_check_feature(dev, DRIVER_MODESET));
+
+		del_timer_sync(&vblank->disable_timer);
+	}
+
+	kfree(dev->vblank);
+
+	dev->num_crtcs = 0;
+}
+EXPORT_SYMBOL(drm_vblank_cleanup);
+
+/**
+ * drm_vblank_init - initialize vblank support
+ * @dev: DRM device
+ * @num_crtcs: number of CRTCs supported by @dev
+ *
+ * This function initializes vblank support for @num_crtcs display pipelines.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
+{
+	int ret = -ENOMEM;
+	unsigned int i;
+
+	spin_lock_init(&dev->vbl_lock);
+	spin_lock_init(&dev->vblank_time_lock);
+
+	dev->num_crtcs = num_crtcs;
+
+	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
+	if (!dev->vblank)
+		goto err;
+
+	for (i = 0; i < num_crtcs; i++) {
+		struct drm_vblank_crtc *vblank = &dev->vblank[i];
+
+		vblank->dev = dev;
+		vblank->pipe = i;
+		init_waitqueue_head(&vblank->queue);
+		setup_timer(&vblank->disable_timer, vblank_disable_fn,
+			    (unsigned long)vblank);
+		seqlock_init(&vblank->seqlock);
+	}
+
+	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
+
+	/* Driver specific high-precision vblank timestamping supported? */
+	if (dev->driver->get_vblank_timestamp)
+		DRM_INFO("Driver supports precise vblank timestamp query.\n");
+	else
+		DRM_INFO("No driver support for vblank timestamp query.\n");
+
+	/* Must have precise timestamping for reliable vblank instant disable */
+	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
+		dev->vblank_disable_immediate = false;
+		DRM_INFO("Setting vblank_disable_immediate to false because "
+			 "get_vblank_timestamp == NULL\n");
+	}
+
+	return 0;
+
+err:
+	dev->num_crtcs = 0;
+	return ret;
+}
+EXPORT_SYMBOL(drm_vblank_init);
+
+/**
+ * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
+ * @crtc: which CRTC's vblank waitqueue to retrieve
+ *
+ * This function returns a pointer to the vblank waitqueue for the CRTC.
+ * Drivers can use this to implement vblank waits using wait_event() and related
+ * functions.
+ */
+wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
+{
+	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
+
+
+/**
+ * drm_calc_timestamping_constants - calculate vblank timestamp constants
+ * @crtc: drm_crtc whose timestamp constants should be updated.
+ * @mode: display mode containing the scanout timings
+ *
+ * Calculate and store various constants which are later
+ * needed by vblank and swap-completion timestamping, e.g,
+ * by drm_calc_vbltimestamp_from_scanoutpos(). They are
+ * derived from CRTC's true scanout timing, so they take
+ * things like panel scaling or other adjustments into account.
+ */
+void drm_calc_timestamping_constants(struct drm_crtc *crtc,
+				     const struct drm_display_mode *mode)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int linedur_ns = 0, framedur_ns = 0;
+	int dotclock = mode->crtc_clock;
+
+	if (!dev->num_crtcs)
+		return;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	/* Valid dotclock? */
+	if (dotclock > 0) {
+		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
+
+		/*
+		 * Convert scanline length in pixels and video
+		 * dot clock to line duration and frame duration
+		 * in nanoseconds:
+		 */
+		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
+		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
+
+		/*
+		 * Fields of interlaced scanout modes are only half a frame duration.
+		 */
+		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+			framedur_ns /= 2;
+	} else
+		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
+			  crtc->base.id);
+
+	vblank->linedur_ns  = linedur_ns;
+	vblank->framedur_ns = framedur_ns;
+	vblank->hwmode = *mode;
+
+	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
+		  crtc->base.id, mode->crtc_htotal,
+		  mode->crtc_vtotal, mode->crtc_vdisplay);
+	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
+		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
+}
+EXPORT_SYMBOL(drm_calc_timestamping_constants);
+
+/**
+ * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
+ * @dev: DRM device
+ * @pipe: index of CRTC whose vblank timestamp to retrieve
+ * @max_error: Desired maximum allowable error in timestamps (nanosecs)
+ *             On return contains true maximum error of timestamp
+ * @vblank_time: Pointer to struct timeval which should receive the timestamp
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
+ *
+ * Implements calculation of exact vblank timestamps from given drm_display_mode
+ * timings and current video scanout position of a CRTC. This can be called from
+ * within get_vblank_timestamp() implementation of a kms driver to implement the
+ * actual timestamping.
+ *
+ * Should return timestamps conforming to the OML_sync_control OpenML
+ * extension specification. The timestamp corresponds to the end of
+ * the vblank interval, aka start of scanout of topmost-leftmost display
+ * pixel in the following video frame.
+ *
+ * Requires support for optional dev->driver->get_scanout_position()
+ * in kms driver, plus a bit of setup code to provide a drm_display_mode
+ * that corresponds to the true scanout timing.
+ *
+ * The current implementation only handles standard video modes. It
+ * returns as no operation if a doublescan or interlaced video mode is
+ * active. Higher level code is expected to handle this.
+ *
+ * This function can be used to implement the &drm_driver.get_vblank_timestamp
+ * directly, if the driver implements the &drm_driver.get_scanout_position hook.
+ *
+ * Note that atomic drivers must call drm_calc_timestamping_constants() before
+ * enabling a CRTC. The atomic helpers already take care of that in
+ * drm_atomic_helper_update_legacy_modeset_state().
+ *
+ * Returns:
+ *
+ * Returns true on success, and false on failure, i.e. when no accurate
+ * timestamp could be acquired.
+ */
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+					   unsigned int pipe,
+					   int *max_error,
+					   struct timeval *vblank_time,
+					   bool in_vblank_irq)
+{
+	struct timeval tv_etime;
+	ktime_t stime, etime;
+	bool vbl_status;
+	struct drm_crtc *crtc;
+	const struct drm_display_mode *mode;
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int vpos, hpos, i;
+	int delta_ns, duration_ns;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return false;
+
+	crtc = drm_crtc_from_index(dev, pipe);
+
+	if (pipe >= dev->num_crtcs || !crtc) {
+		DRM_ERROR("Invalid crtc %u\n", pipe);
+		return false;
+	}
+
+	/* Scanout position query not supported? Should not happen. */
+	if (!dev->driver->get_scanout_position) {
+		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
+		return false;
+	}
+
+	if (drm_drv_uses_atomic_modeset(dev))
+		mode = &vblank->hwmode;
+	else
+		mode = &crtc->hwmode;
+
+	/* If mode timing undefined, just return as no-op:
+	 * Happens during initial modesetting of a crtc.
+	 */
+	if (mode->crtc_clock == 0) {
+		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
+		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
+
+		return false;
+	}
+
+	/* Get current scanout position with system timestamp.
+	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
+	 * if single query takes longer than max_error nanoseconds.
+	 *
+	 * This guarantees a tight bound on maximum error if
+	 * code gets preempted or delayed for some reason.
+	 */
+	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
+		/*
+		 * Get vertical and horizontal scanout position vpos, hpos,
+		 * and bounding timestamps stime, etime, pre/post query.
+		 */
+		vbl_status = dev->driver->get_scanout_position(dev, pipe,
+							       in_vblank_irq,
+							       &vpos, &hpos,
+							       &stime, &etime,
+							       mode);
+
+		/* Return as no-op if scanout query unsupported or failed. */
+		if (!vbl_status) {
+			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
+				  pipe);
+			return false;
+		}
+
+		/* Compute uncertainty in timestamp of scanout position query. */
+		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
+
+		/* Accept result with <  max_error nsecs timing uncertainty. */
+		if (duration_ns <= *max_error)
+			break;
+	}
+
+	/* Noisy system timing? */
+	if (i == DRM_TIMESTAMP_MAXRETRIES) {
+		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
+			  pipe, duration_ns/1000, *max_error/1000, i);
+	}
+
+	/* Return upper bound of timestamp precision error. */
+	*max_error = duration_ns;
+
+	/* Convert scanout position into elapsed time at raw_time query
+	 * since start of scanout at first display scanline. delta_ns
+	 * can be negative if start of scanout hasn't happened yet.
+	 */
+	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
+			   mode->crtc_clock);
+
+	if (!drm_timestamp_monotonic)
+		etime = ktime_mono_to_real(etime);
+
+	/* save this only for debugging purposes */
+	tv_etime = ktime_to_timeval(etime);
+	/* Subtract time delta from raw timestamp to get final
+	 * vblank_time timestamp for end of vblank.
+	 */
+	etime = ktime_sub_ns(etime, delta_ns);
+	*vblank_time = ktime_to_timeval(etime);
+
+	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+		      pipe, hpos, vpos,
+		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
+		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
+		      duration_ns/1000, i);
+
+	return true;
+}
+EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
+
+static struct timeval get_drm_timestamp(void)
+{
+	ktime_t now;
+
+	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
+	return ktime_to_timeval(now);
+}
+
+/**
+ * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
+ *                             vblank interval
+ * @dev: DRM device
+ * @pipe: index of CRTC whose vblank timestamp to retrieve
+ * @tvblank: Pointer to target struct timeval which should receive the timestamp
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
+ *
+ * Fetches the system timestamp corresponding to the time of the most recent
+ * vblank interval on specified CRTC. May call into kms-driver to
+ * compute the timestamp with a high-precision GPU specific method.
+ *
+ * Returns zero if timestamp originates from uncorrected do_gettimeofday()
+ * call, i.e., it isn't very precisely locked to the true vblank.
+ *
+ * Returns:
+ * True if timestamp is considered to be very precise, false otherwise.
+ */
+static bool
+drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
+			  struct timeval *tvblank, bool in_vblank_irq)
+{
+	bool ret = false;
+
+	/* Define requested maximum error on timestamps (nanoseconds). */
+	int max_error = (int) drm_timestamp_precision * 1000;
+
+	/* Query driver if possible and precision timestamping enabled. */
+	if (dev->driver->get_vblank_timestamp && (max_error > 0))
+		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
+							tvblank, in_vblank_irq);
+
+	/* GPU high precision timestamp query unsupported or failed.
+	 * Return current monotonic/gettimeofday timestamp as best estimate.
+	 */
+	if (!ret)
+		*tvblank = get_drm_timestamp();
+
+	return ret;
+}
+
+/**
+ * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
+ * @crtc: which counter to retrieve
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity.
+ *
+ * Returns:
+ * The software vblank counter.
+ */
+u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
+{
+	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_count);
+
+/**
+ * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
+ *     system timestamp corresponding to that vblank counter value.
+ * @dev: DRM device
+ * @pipe: index of CRTC whose counter to retrieve
+ * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity. Returns corresponding system timestamp of the time
+ * of the vblank interval that corresponds to the current vblank counter value.
+ *
+ * This is the legacy version of drm_crtc_vblank_count_and_time().
+ */
+static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
+				     struct timeval *vblanktime)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	u32 vblank_count;
+	unsigned int seq;
+
+	if (WARN_ON(pipe >= dev->num_crtcs)) {
+		*vblanktime = (struct timeval) { 0 };
+		return 0;
+	}
+
+	do {
+		seq = read_seqbegin(&vblank->seqlock);
+		vblank_count = vblank->count;
+		*vblanktime = vblank->time;
+	} while (read_seqretry(&vblank->seqlock, seq));
+
+	return vblank_count;
+}
+
+/**
+ * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
+ *     and the system timestamp corresponding to that vblank counter value
+ * @crtc: which counter to retrieve
+ * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity. Returns corresponding system timestamp of the time
+ * of the vblank interval that corresponds to the current vblank counter value.
+ */
+u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+				   struct timeval *vblanktime)
+{
+	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
+					 vblanktime);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
+
+static void send_vblank_event(struct drm_device *dev,
+		struct drm_pending_vblank_event *e,
+		unsigned long seq, struct timeval *now)
+{
+	e->event.sequence = seq;
+	e->event.tv_sec = now->tv_sec;
+	e->event.tv_usec = now->tv_usec;
+
+	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
+					 e->event.sequence);
+
+	drm_send_event_locked(dev, &e->base);
+}
+
+/**
+ * drm_crtc_arm_vblank_event - arm vblank event after pageflip
+ * @crtc: the source CRTC of the vblank event
+ * @e: the event to send
+ *
+ * A lot of drivers need to generate vblank events for the very next vblank
+ * interrupt. For example when the page flip interrupt happens when the page
+ * flip gets armed, but not when it actually executes within the next vblank
+ * period. This helper function implements exactly the required vblank arming
+ * behaviour.
+ *
+ * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
+ * atomic commit must ensure that the next vblank happens at exactly the same
+ * time as the atomic commit is committed to the hardware. This function itself
+ * does **not** protect again the next vblank interrupt racing with either this
+ * function call or the atomic commit operation. A possible sequence could be:
+ *
+ * 1. Driver commits new hardware state into vblank-synchronized registers.
+ * 2. A vblank happens, committing the hardware state. Also the corresponding
+ *    vblank interrupt is fired off and fully processed by the interrupt
+ *    handler.
+ * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
+ * 4. The event is only send out for the next vblank, which is wrong.
+ *
+ * An equivalent race can happen when the driver calls
+ * drm_crtc_arm_vblank_event() before writing out the new hardware state.
+ *
+ * The only way to make this work safely is to prevent the vblank from firing
+ * (and the hardware from committing anything else) until the entire atomic
+ * commit sequence has run to completion. If the hardware does not have such a
+ * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
+ * Instead drivers need to manually send out the event from their interrupt
+ * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
+ * possible race with the hardware committing the atomic update.
+ *
+ * Caller must hold event lock. Caller must also hold a vblank reference for
+ * the event @e, which will be dropped when the next vblank arrives.
+ */
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+			       struct drm_pending_vblank_event *e)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+
+	assert_spin_locked(&dev->event_lock);
+
+	e->pipe = pipe;
+	e->event.sequence = drm_vblank_count(dev, pipe);
+	e->event.crtc_id = crtc->base.id;
+	list_add_tail(&e->base.link, &dev->vblank_event_list);
+}
+EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
+
+/**
+ * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
+ * @crtc: the source CRTC of the vblank event
+ * @e: the event to send
+ *
+ * Updates sequence # and timestamp on event for the most recently processed
+ * vblank, and sends it to userspace.  Caller must hold event lock.
+ *
+ * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
+ * situation, especially to send out events for atomic commit operations.
+ */
+void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
+				struct drm_pending_vblank_event *e)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int seq, pipe = drm_crtc_index(crtc);
+	struct timeval now;
+
+	if (dev->num_crtcs > 0) {
+		seq = drm_vblank_count_and_time(dev, pipe, &now);
+	} else {
+		seq = 0;
+
+		now = get_drm_timestamp();
+	}
+	e->pipe = pipe;
+	e->event.crtc_id = crtc->base.id;
+	send_vblank_event(dev, e, seq, &now);
+}
+EXPORT_SYMBOL(drm_crtc_send_vblank_event);
+
+static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
+
+		if (crtc->funcs->enable_vblank)
+			return crtc->funcs->enable_vblank(crtc);
+	}
+
+	return dev->driver->enable_vblank(dev, pipe);
+}
+
+/**
+ * drm_vblank_enable - enable the vblank interrupt on a CRTC
+ * @dev: DRM device
+ * @pipe: CRTC index
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int ret = 0;
+
+	assert_spin_locked(&dev->vbl_lock);
+
+	spin_lock(&dev->vblank_time_lock);
+
+	if (!vblank->enabled) {
+		/*
+		 * Enable vblank irqs under vblank_time_lock protection.
+		 * All vblank count & timestamp updates are held off
+		 * until we are done reinitializing master counter and
+		 * timestamps. Filtercode in drm_handle_vblank() will
+		 * prevent double-accounting of same vblank interval.
+		 */
+		ret = __enable_vblank(dev, pipe);
+		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
+		if (ret) {
+			atomic_dec(&vblank->refcount);
+		} else {
+			drm_update_vblank_count(dev, pipe, 0);
+			/* drm_update_vblank_count() includes a wmb so we just
+			 * need to ensure that the compiler emits the write
+			 * to mark the vblank as enabled after the call
+			 * to drm_update_vblank_count().
+			 */
+			WRITE_ONCE(vblank->enabled, true);
+		}
+	}
+
+	spin_unlock(&dev->vblank_time_lock);
+
+	return ret;
+}
+
+/**
+ * drm_vblank_get - get a reference count on vblank events
+ * @dev: DRM device
+ * @pipe: index of CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * This is the legacy version of drm_crtc_vblank_get().
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+	int ret = 0;
+
+	if (!dev->num_crtcs)
+		return -EINVAL;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	/* Going from 0->1 means we have to enable interrupts again */
+	if (atomic_add_return(1, &vblank->refcount) == 1) {
+		ret = drm_vblank_enable(dev, pipe);
+	} else {
+		if (!vblank->enabled) {
+			atomic_dec(&vblank->refcount);
+			ret = -EINVAL;
+		}
+	}
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+	return ret;
+}
+
+/**
+ * drm_crtc_vblank_get - get a reference count on vblank events
+ * @crtc: which CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
+int drm_crtc_vblank_get(struct drm_crtc *crtc)
+{
+	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get);
+
+/**
+ * drm_vblank_put - release ownership of vblank events
+ * @dev: DRM device
+ * @pipe: index of CRTC to release
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the legacy version of drm_crtc_vblank_put().
+ */
+static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
+		return;
+
+	/* Last user schedules interrupt disable */
+	if (atomic_dec_and_test(&vblank->refcount)) {
+		if (drm_vblank_offdelay == 0)
+			return;
+		else if (drm_vblank_offdelay < 0)
+			vblank_disable_fn((unsigned long)vblank);
+		else if (!dev->vblank_disable_immediate)
+			mod_timer(&vblank->disable_timer,
+				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
+	}
+}
+
+/**
+ * drm_crtc_vblank_put - give up ownership of vblank events
+ * @crtc: which counter to give up
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ */
+void drm_crtc_vblank_put(struct drm_crtc *crtc)
+{
+	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_put);
+
+/**
+ * drm_wait_one_vblank - wait for one vblank
+ * @dev: DRM device
+ * @pipe: CRTC index
+ *
+ * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	int ret;
+	u32 last;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	ret = drm_vblank_get(dev, pipe);
+	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
+		return;
+
+	last = drm_vblank_count(dev, pipe);
+
+	ret = wait_event_timeout(vblank->queue,
+				 last != drm_vblank_count(dev, pipe),
+				 msecs_to_jiffies(100));
+
+	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
+
+	drm_vblank_put(dev, pipe);
+}
+EXPORT_SYMBOL(drm_wait_one_vblank);
+
+/**
+ * drm_crtc_wait_one_vblank - wait for one vblank
+ * @crtc: DRM crtc
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
+{
+	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
+
+/**
+ * drm_crtc_vblank_off - disable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ */
+void drm_crtc_vblank_off(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	struct drm_pending_vblank_event *e, *t;
+	struct timeval now;
+	unsigned long irqflags;
+	unsigned int seq;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	spin_lock_irqsave(&dev->event_lock, irqflags);
+
+	spin_lock(&dev->vbl_lock);
+	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
+		      pipe, vblank->enabled, vblank->inmodeset);
+
+	/* Avoid redundant vblank disables without previous
+	 * drm_crtc_vblank_on(). */
+	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
+		drm_vblank_disable_and_save(dev, pipe);
+
+	wake_up(&vblank->queue);
+
+	/*
+	 * Prevent subsequent drm_vblank_get() from re-enabling
+	 * the vblank interrupt by bumping the refcount.
+	 */
+	if (!vblank->inmodeset) {
+		atomic_inc(&vblank->refcount);
+		vblank->inmodeset = 1;
+	}
+	spin_unlock(&dev->vbl_lock);
+
+	/* Send any queued vblank events, lest the natives grow disquiet */
+	seq = drm_vblank_count_and_time(dev, pipe, &now);
+
+	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+		if (e->pipe != pipe)
+			continue;
+		DRM_DEBUG("Sending premature vblank event on disable: "
+			  "wanted %u, current %u\n",
+			  e->event.sequence, seq);
+		list_del(&e->base.link);
+		drm_vblank_put(dev, pipe);
+		send_vblank_event(dev, e, seq, &now);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+	/* Will be reset by the modeset helpers when re-enabling the crtc by
+	 * calling drm_calc_timestamping_constants(). */
+	vblank->hwmode.crtc_clock = 0;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_off);
+
+/**
+ * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to reset the vblank state to off at load time.
+ * Drivers should use this together with the drm_crtc_vblank_off() and
+ * drm_crtc_vblank_on() functions. The difference compared to
+ * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
+ * and hence doesn't need to call any driver hooks.
+ */
+void drm_crtc_vblank_reset(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned long irqflags;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	/*
+	 * Prevent subsequent drm_vblank_get() from enabling the vblank
+	 * interrupt by bumping the refcount.
+	 */
+	if (!vblank->inmodeset) {
+		atomic_inc(&vblank->refcount);
+		vblank->inmodeset = 1;
+	}
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+	WARN_ON(!list_empty(&dev->vblank_event_list));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_reset);
+
+/**
+ * drm_crtc_vblank_on - enable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
+ * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called
+ * in driver load code to reflect the current hardware state of the crtc.
+ */
+void drm_crtc_vblank_on(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
+		      pipe, vblank->enabled, vblank->inmodeset);
+
+	/* Drop our private "prevent drm_vblank_get" refcount */
+	if (vblank->inmodeset) {
+		atomic_dec(&vblank->refcount);
+		vblank->inmodeset = 0;
+	}
+
+	drm_reset_vblank_timestamp(dev, pipe);
+
+	/*
+	 * re-enable interrupts if there are users left, or the
+	 * user wishes vblank interrupts to be enabled all the time.
+	 */
+	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
+		WARN_ON(drm_vblank_enable(dev, pipe));
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_on);
+
+static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
+					  unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+
+	/* vblank is not initialized (IRQ not installed ?), or has been freed */
+	if (!dev->num_crtcs)
+		return;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	/*
+	 * To avoid all the problems that might happen if interrupts
+	 * were enabled/disabled around or between these calls, we just
+	 * have the kernel take a reference on the CRTC (just once though
+	 * to avoid corrupting the count if multiple, mismatch calls occur),
+	 * so that interrupts remain enabled in the interim.
+	 */
+	if (!vblank->inmodeset) {
+		vblank->inmodeset = 0x1;
+		if (drm_vblank_get(dev, pipe) == 0)
+			vblank->inmodeset |= 0x2;
+	}
+}
+
+static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
+					   unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+
+	/* vblank is not initialized (IRQ not installed ?), or has been freed */
+	if (!dev->num_crtcs)
+		return;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return;
+
+	if (vblank->inmodeset) {
+		spin_lock_irqsave(&dev->vbl_lock, irqflags);
+		drm_reset_vblank_timestamp(dev, pipe);
+		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+		if (vblank->inmodeset & 0x2)
+			drm_vblank_put(dev, pipe);
+
+		vblank->inmodeset = 0;
+	}
+}
+
+int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_modeset_ctl *modeset = data;
+	unsigned int pipe;
+
+	/* If drm_vblank_init() hasn't been called yet, just no-op */
+	if (!dev->num_crtcs)
+		return 0;
+
+	/* KMS drivers handle this internally */
+	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
+		return 0;
+
+	pipe = modeset->crtc;
+	if (pipe >= dev->num_crtcs)
+		return -EINVAL;
+
+	switch (modeset->cmd) {
+	case _DRM_PRE_MODESET:
+		drm_legacy_vblank_pre_modeset(dev, pipe);
+		break;
+	case _DRM_POST_MODESET:
+		drm_legacy_vblank_post_modeset(dev, pipe);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline bool vblank_passed(u32 seq, u32 ref)
+{
+	return (seq - ref) <= (1 << 23);
+}
+
+static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
+				  union drm_wait_vblank *vblwait,
+				  struct drm_file *file_priv)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+	unsigned int seq;
+	int ret;
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (e == NULL) {
+		ret = -ENOMEM;
+		goto err_put;
+	}
+
+	e->pipe = pipe;
+	e->event.base.type = DRM_EVENT_VBLANK;
+	e->event.base.length = sizeof(e->event);
+	e->event.user_data = vblwait->request.signal;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	/*
+	 * drm_crtc_vblank_off() might have been called after we called
+	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
+	 * vblank disable, so no need for further locking.  The reference from
+	 * drm_vblank_get() protects against vblank disable from another source.
+	 */
+	if (!READ_ONCE(vblank->enabled)) {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
+					    &e->event.base);
+
+	if (ret)
+		goto err_unlock;
+
+	seq = drm_vblank_count_and_time(dev, pipe, &now);
+
+	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
+		  vblwait->request.sequence, seq, pipe);
+
+	trace_drm_vblank_event_queued(file_priv, pipe,
+				      vblwait->request.sequence);
+
+	e->event.sequence = vblwait->request.sequence;
+	if (vblank_passed(seq, vblwait->request.sequence)) {
+		drm_vblank_put(dev, pipe);
+		send_vblank_event(dev, e, seq, &now);
+		vblwait->reply.sequence = seq;
+	} else {
+		/* drm_handle_vblank_events will call drm_vblank_put */
+		list_add_tail(&e->base.link, &dev->vblank_event_list);
+		vblwait->reply.sequence = vblwait->request.sequence;
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	return 0;
+
+err_unlock:
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+	kfree(e);
+err_put:
+	drm_vblank_put(dev, pipe);
+	return ret;
+}
+
+static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
+{
+	if (vblwait->request.sequence)
+		return false;
+
+	return _DRM_VBLANK_RELATIVE ==
+		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
+					  _DRM_VBLANK_EVENT |
+					  _DRM_VBLANK_NEXTONMISS));
+}
+
+/*
+ * Wait for VBLANK.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param data user argument, pointing to a drm_wait_vblank structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * This function enables the vblank interrupt on the pipe requested, then
+ * sleeps waiting for the requested sequence number to occur, and drops
+ * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
+ * after a timeout with no further vblank waits scheduled).
+ */
+int drm_wait_vblank(struct drm_device *dev, void *data,
+		    struct drm_file *file_priv)
+{
+	struct drm_vblank_crtc *vblank;
+	union drm_wait_vblank *vblwait = data;
+	int ret;
+	unsigned int flags, seq, pipe, high_pipe;
+
+	if (!dev->irq_enabled)
+		return -EINVAL;
+
+	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
+		return -EINVAL;
+
+	if (vblwait->request.type &
+	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
+	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
+		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
+			  vblwait->request.type,
+			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
+			   _DRM_VBLANK_HIGH_CRTC_MASK));
+		return -EINVAL;
+	}
+
+	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
+	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
+	if (high_pipe)
+		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else
+		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
+	if (pipe >= dev->num_crtcs)
+		return -EINVAL;
+
+	vblank = &dev->vblank[pipe];
+
+	/* If the counter is currently enabled and accurate, short-circuit
+	 * queries to return the cached timestamp of the last vblank.
+	 */
+	if (dev->vblank_disable_immediate &&
+	    drm_wait_vblank_is_query(vblwait) &&
+	    READ_ONCE(vblank->enabled)) {
+		struct timeval now;
+
+		vblwait->reply.sequence =
+			drm_vblank_count_and_time(dev, pipe, &now);
+		vblwait->reply.tval_sec = now.tv_sec;
+		vblwait->reply.tval_usec = now.tv_usec;
+		return 0;
+	}
+
+	ret = drm_vblank_get(dev, pipe);
+	if (ret) {
+		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
+		return ret;
+	}
+	seq = drm_vblank_count(dev, pipe);
+
+	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
+	case _DRM_VBLANK_RELATIVE:
+		vblwait->request.sequence += seq;
+		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
+	case _DRM_VBLANK_ABSOLUTE:
+		break;
+	default:
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
+	    vblank_passed(seq, vblwait->request.sequence))
+		vblwait->request.sequence = seq + 1;
+
+	if (flags & _DRM_VBLANK_EVENT) {
+		/* must hold on to the vblank ref until the event fires
+		 * drm_vblank_put will be called asynchronously
+		 */
+		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
+	}
+
+	if (vblwait->request.sequence != seq) {
+		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
+			  vblwait->request.sequence, pipe);
+		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
+			    vblank_passed(drm_vblank_count(dev, pipe),
+					  vblwait->request.sequence) ||
+			    !READ_ONCE(vblank->enabled));
+	}
+
+	if (ret != -EINTR) {
+		struct timeval now;
+
+		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
+		vblwait->reply.tval_sec = now.tv_sec;
+		vblwait->reply.tval_usec = now.tv_usec;
+
+		DRM_DEBUG("crtc %d returning %u to client\n",
+			  pipe, vblwait->reply.sequence);
+	} else {
+		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
+	}
+
+done:
+	drm_vblank_put(dev, pipe);
+	return ret;
+}
+
+static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_pending_vblank_event *e, *t;
+	struct timeval now;
+	unsigned int seq;
+
+	assert_spin_locked(&dev->event_lock);
+
+	seq = drm_vblank_count_and_time(dev, pipe, &now);
+
+	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+		if (e->pipe != pipe)
+			continue;
+		if (!vblank_passed(seq, e->event.sequence))
+			continue;
+
+		DRM_DEBUG("vblank event on %u, current %u\n",
+			  e->event.sequence, seq);
+
+		list_del(&e->base.link);
+		drm_vblank_put(dev, pipe);
+		send_vblank_event(dev, e, seq, &now);
+	}
+
+	trace_drm_vblank_event(pipe, seq);
+}
+
+/**
+ * drm_handle_vblank - handle a vblank event
+ * @dev: DRM device
+ * @pipe: index of CRTC where this event occurred
+ *
+ * Drivers should call this routine in their vblank interrupt handlers to
+ * update the vblank counter and send any signals that may be pending.
+ *
+ * This is the legacy version of drm_crtc_handle_vblank().
+ */
+bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned long irqflags;
+	bool disable_irq;
+
+	if (WARN_ON_ONCE(!dev->num_crtcs))
+		return false;
+
+	if (WARN_ON(pipe >= dev->num_crtcs))
+		return false;
+
+	spin_lock_irqsave(&dev->event_lock, irqflags);
+
+	/* Need timestamp lock to prevent concurrent execution with
+	 * vblank enable/disable, as this would cause inconsistent
+	 * or corrupted timestamps and vblank counts.
+	 */
+	spin_lock(&dev->vblank_time_lock);
+
+	/* Vblank irq handling disabled. Nothing to do. */
+	if (!vblank->enabled) {
+		spin_unlock(&dev->vblank_time_lock);
+		spin_unlock_irqrestore(&dev->event_lock, irqflags);
+		return false;
+	}
+
+	drm_update_vblank_count(dev, pipe, true);
+
+	spin_unlock(&dev->vblank_time_lock);
+
+	wake_up(&vblank->queue);
+
+	/* With instant-off, we defer disabling the interrupt until after
+	 * we finish processing the following vblank after all events have
+	 * been signaled. The disable has to be last (after
+	 * drm_handle_vblank_events) so that the timestamp is always accurate.
+	 */
+	disable_irq = (dev->vblank_disable_immediate &&
+		       drm_vblank_offdelay > 0 &&
+		       !atomic_read(&vblank->refcount));
+
+	drm_handle_vblank_events(dev, pipe);
+
+	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+	if (disable_irq)
+		vblank_disable_fn((unsigned long)vblank);
+
+	return true;
+}
+EXPORT_SYMBOL(drm_handle_vblank);
+
+/**
+ * drm_crtc_handle_vblank - handle a vblank event
+ * @crtc: where this event occurred
+ *
+ * Drivers should call this routine in their vblank interrupt handlers to
+ * update the vblank counter and send any signals that may be pending.
+ *
+ * This is the native KMS version of drm_handle_vblank().
+ *
+ * Returns:
+ * True if the event was successfully handled, false on failure.
+ */
+bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
+{
+	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_handle_vblank);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index c363f2fdff31..2e0b76cceb97 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -80,6 +80,9 @@
 #include <drm/drm_debugfs.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_sysfs.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_irq.h>
+
 
 struct module;
 
@@ -447,8 +450,6 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev)
 	return dev->mode_config.funcs->atomic_commit != NULL;
 }
 
-#include <drm/drm_irq.h>
-
 #define DRM_SWITCH_POWER_ON 0
 #define DRM_SWITCH_POWER_OFF 1
 #define DRM_SWITCH_POWER_CHANGING 2
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
index 5dd27ae5c47c..d66f7ee07fb5 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -40,6 +40,7 @@
 struct dma_fence;
 struct drm_file;
 struct drm_device;
+struct device;
 
 /*
  * FIXME: Not sure we want to have drm_minor here in the end, but to avoid
diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h
index 569ca86d4e1f..d77f6e65b1c6 100644
--- a/include/drm/drm_irq.h
+++ b/include/drm/drm_irq.h
@@ -24,165 +24,9 @@
 #ifndef _DRM_IRQ_H_
 #define _DRM_IRQ_H_
 
-#include <linux/seqlock.h>
-
-/**
- * struct drm_pending_vblank_event - pending vblank event tracking
- */
-struct drm_pending_vblank_event {
-	/**
-	 * @base: Base structure for tracking pending DRM events.
-	 */
-	struct drm_pending_event base;
-	/**
-	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
-	 */
-	unsigned int pipe;
-	/**
-	 * @event: Actual event which will be sent to userspace.
-	 */
-	struct drm_event_vblank event;
-};
-
-/**
- * struct drm_vblank_crtc - vblank tracking for a CRTC
- *
- * This structure tracks the vblank state for one CRTC.
- *
- * Note that for historical reasons - the vblank handling code is still shared
- * with legacy/non-kms drivers - this is a free-standing structure not directly
- * connected to &struct drm_crtc. But all public interface functions are taking
- * a &struct drm_crtc to hide this implementation detail.
- */
-struct drm_vblank_crtc {
-	/**
-	 * @dev: Pointer to the &drm_device.
-	 */
-	struct drm_device *dev;
-	/**
-	 * @queue: Wait queue for vblank waiters.
-	 */
-	wait_queue_head_t queue;	/**< VBLANK wait queue */
-	/**
-	 * @disable_timer: Disable timer for the delayed vblank disabling
-	 * hysteresis logic. Vblank disabling is controlled through the
-	 * drm_vblank_offdelay module option and the setting of the
-	 * &drm_device.max_vblank_count value.
-	 */
-	struct timer_list disable_timer;
-
-	/**
-	 * @seqlock: Protect vblank count and time.
-	 */
-	seqlock_t seqlock;		/* protects vblank count and time */
-
-	/**
-	 * @count: Current software vblank counter.
-	 */
-	u32 count;
-	/**
-	 * @time: Vblank timestamp corresponding to @count.
-	 */
-	struct timeval time;
-
-	/**
-	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
-	 * this refcount reaches 0 can the hardware interrupt be disabled using
-	 * @disable_timer.
-	 */
-	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
-	/**
-	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
-	 */
-	u32 last;
-	/**
-	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
-	 * For legacy driver bit 2 additionally tracks whether an additional
-	 * temporary vblank reference has been acquired to paper over the
-	 * hardware counter resetting/jumping. KMS drivers should instead just
-	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
-	 * save and restore the vblank count.
-	 */
-	unsigned int inmodeset;		/* Display driver is setting mode */
-	/**
-	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
-	 * structure.
-	 */
-	unsigned int pipe;
-	/**
-	 * @framedur_ns: Frame/Field duration in ns, used by
-	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
-	 * drm_calc_timestamping_constants().
-	 */
-	int framedur_ns;
-	/**
-	 * @linedur_ns: Line duration in ns, used by
-	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
-	 * drm_calc_timestamping_constants().
-	 */
-	int linedur_ns;
-
-	/**
-	 * @hwmode:
-	 *
-	 * Cache of the current hardware display mode. Only valid when @enabled
-	 * is set. This is used by helpers like
-	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
-	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
-	 * because that one is really hard to get from interrupt context.
-	 */
-	struct drm_display_mode hwmode;
-
-	/**
-	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
-	 * avoid double-disabling and hence corrupting saved state. Needed by
-	 * drivers not using atomic KMS, since those might go through their CRTC
-	 * disabling functions multiple times.
-	 */
-	bool enabled;
-};
+struct drm_device;
 
 int drm_irq_install(struct drm_device *dev, int irq);
 int drm_irq_uninstall(struct drm_device *dev);
 
-int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
-u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
-u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
-				   struct timeval *vblanktime);
-void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
-			       struct drm_pending_vblank_event *e);
-void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
-			      struct drm_pending_vblank_event *e);
-bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
-bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
-int drm_crtc_vblank_get(struct drm_crtc *crtc);
-void drm_crtc_vblank_put(struct drm_crtc *crtc);
-void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
-void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
-void drm_crtc_vblank_off(struct drm_crtc *crtc);
-void drm_crtc_vblank_reset(struct drm_crtc *crtc);
-void drm_crtc_vblank_on(struct drm_crtc *crtc);
-void drm_vblank_cleanup(struct drm_device *dev);
-u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
-
-bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-					   unsigned int pipe, int *max_error,
-					   struct timeval *vblank_time,
-					   bool in_vblank_irq);
-void drm_calc_timestamping_constants(struct drm_crtc *crtc,
-				     const struct drm_display_mode *mode);
-
-/**
- * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
- * @crtc: which CRTC's vblank waitqueue to retrieve
- *
- * This function returns a pointer to the vblank waitqueue for the CRTC.
- * Drivers can use this to implement vblank waits using wait_event() and related
- * functions.
- */
-static inline wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
-{
-	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
-}
-
 #endif
diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h
index 59ccab402e85..9cd9e36f77b5 100644
--- a/include/drm/drm_prime.h
+++ b/include/drm/drm_prime.h
@@ -59,6 +59,8 @@ struct drm_device;
 struct drm_gem_object;
 struct drm_file;
 
+struct device;
+
 struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
 				     struct drm_gem_object *obj,
 				     int flags);
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
new file mode 100644
index 000000000000..4cde47332dfa
--- /dev/null
+++ b/include/drm/drm_vblank.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2016 Intel Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DRM_VBLANK_H_
+#define _DRM_VBLANK_H_
+
+#include <linux/seqlock.h>
+#include <linux/idr.h>
+#include <linux/poll.h>
+
+#include <drm/drm_file.h>
+#include <drm/drm_modes.h>
+#include <uapi/drm/drm.h>
+
+struct drm_device;
+struct drm_crtc;
+
+/**
+ * struct drm_pending_vblank_event - pending vblank event tracking
+ */
+struct drm_pending_vblank_event {
+	/**
+	 * @base: Base structure for tracking pending DRM events.
+	 */
+	struct drm_pending_event base;
+	/**
+	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
+	 */
+	unsigned int pipe;
+	/**
+	 * @event: Actual event which will be sent to userspace.
+	 */
+	struct drm_event_vblank event;
+};
+
+/**
+ * struct drm_vblank_crtc - vblank tracking for a CRTC
+ *
+ * This structure tracks the vblank state for one CRTC.
+ *
+ * Note that for historical reasons - the vblank handling code is still shared
+ * with legacy/non-kms drivers - this is a free-standing structure not directly
+ * connected to &struct drm_crtc. But all public interface functions are taking
+ * a &struct drm_crtc to hide this implementation detail.
+ */
+struct drm_vblank_crtc {
+	/**
+	 * @dev: Pointer to the &drm_device.
+	 */
+	struct drm_device *dev;
+	/**
+	 * @queue: Wait queue for vblank waiters.
+	 */
+	wait_queue_head_t queue;	/**< VBLANK wait queue */
+	/**
+	 * @disable_timer: Disable timer for the delayed vblank disabling
+	 * hysteresis logic. Vblank disabling is controlled through the
+	 * drm_vblank_offdelay module option and the setting of the
+	 * &drm_device.max_vblank_count value.
+	 */
+	struct timer_list disable_timer;
+
+	/**
+	 * @seqlock: Protect vblank count and time.
+	 */
+	seqlock_t seqlock;		/* protects vblank count and time */
+
+	/**
+	 * @count: Current software vblank counter.
+	 */
+	u32 count;
+	/**
+	 * @time: Vblank timestamp corresponding to @count.
+	 */
+	struct timeval time;
+
+	/**
+	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
+	 * this refcount reaches 0 can the hardware interrupt be disabled using
+	 * @disable_timer.
+	 */
+	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
+	/**
+	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
+	 */
+	u32 last;
+	/**
+	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
+	 * For legacy driver bit 2 additionally tracks whether an additional
+	 * temporary vblank reference has been acquired to paper over the
+	 * hardware counter resetting/jumping. KMS drivers should instead just
+	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
+	 * save and restore the vblank count.
+	 */
+	unsigned int inmodeset;		/* Display driver is setting mode */
+	/**
+	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
+	 * structure.
+	 */
+	unsigned int pipe;
+	/**
+	 * @framedur_ns: Frame/Field duration in ns, used by
+	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
+	 * drm_calc_timestamping_constants().
+	 */
+	int framedur_ns;
+	/**
+	 * @linedur_ns: Line duration in ns, used by
+	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
+	 * drm_calc_timestamping_constants().
+	 */
+	int linedur_ns;
+
+	/**
+	 * @hwmode:
+	 *
+	 * Cache of the current hardware display mode. Only valid when @enabled
+	 * is set. This is used by helpers like
+	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
+	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
+	 * because that one is really hard to get from interrupt context.
+	 */
+	struct drm_display_mode hwmode;
+
+	/**
+	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
+	 * avoid double-disabling and hence corrupting saved state. Needed by
+	 * drivers not using atomic KMS, since those might go through their CRTC
+	 * disabling functions multiple times.
+	 */
+	bool enabled;
+};
+
+int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
+u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
+u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+				   struct timeval *vblanktime);
+void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
+			       struct drm_pending_vblank_event *e);
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+			      struct drm_pending_vblank_event *e);
+bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
+bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
+int drm_crtc_vblank_get(struct drm_crtc *crtc);
+void drm_crtc_vblank_put(struct drm_crtc *crtc);
+void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
+void drm_crtc_vblank_off(struct drm_crtc *crtc);
+void drm_crtc_vblank_reset(struct drm_crtc *crtc);
+void drm_crtc_vblank_on(struct drm_crtc *crtc);
+void drm_vblank_cleanup(struct drm_device *dev);
+u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
+
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+					   unsigned int pipe, int *max_error,
+					   struct timeval *vblank_time,
+					   bool in_vblank_irq);
+void drm_calc_timestamping_constants(struct drm_crtc *crtc,
+				     const struct drm_display_mode *mode);
+wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
+#endif
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH] drm/doc: Polish irq helper documentation
  2017-05-24 14:51 ` [PATCH 08/37] drm/doc: Polish irq helper documentation Daniel Vetter
  2017-05-25  7:46   ` Stefan Agner
@ 2017-05-31  9:22   ` Daniel Vetter
  1 sibling, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31  9:22 UTC (permalink / raw)
  To: DRI Development
  Cc: Daniel Vetter, Intel Graphics Development, Stefan Agner, Daniel Vetter

Pull a (much shorter) overview into drm_irq.c, and instead put the
callback documentation into in-line comments in drm_drv.h.

v2: Move the include stanzas back to the split-up patch (Stefan).

Cc: Stefan Agner <stefan@agner.ch>
Reviewed-by: Stefan Agner <stefan@agner.ch>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 Documentation/gpu/drm-internals.rst | 54 -------------------------------------
 drivers/gpu/drm/drm_irq.c           | 30 +++++++++++++++++----
 drivers/gpu/drm/drm_vblank.c        |  3 +++
 include/drm/drmP.h                  |  9 +++++--
 include/drm/drm_drv.h               | 33 +++++++++++++++++++++--
 5 files changed, 66 insertions(+), 63 deletions(-)

diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index dd28e39cf966..bece92258647 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -149,60 +149,6 @@ Device Instance and Driver Handling
 Driver Load
 -----------
 
-IRQ Registration
-~~~~~~~~~~~~~~~~
-
-The DRM core tries to facilitate IRQ handler registration and
-unregistration by providing :c:func:`drm_irq_install()` and
-:c:func:`drm_irq_uninstall()` functions. Those functions only
-support a single interrupt per device, devices that use more than one
-IRQs need to be handled manually.
-
-Managed IRQ Registration
-''''''''''''''''''''''''
-
-:c:func:`drm_irq_install()` starts by calling the irq_preinstall
-driver operation. The operation is optional and must make sure that the
-interrupt will not get fired by clearing all pending interrupt flags or
-disabling the interrupt.
-
-The passed-in IRQ will then be requested by a call to
-:c:func:`request_irq()`. If the DRIVER_IRQ_SHARED driver feature
-flag is set, a shared (IRQF_SHARED) IRQ handler will be requested.
-
-The IRQ handler function must be provided as the mandatory irq_handler
-driver operation. It will get passed directly to
-:c:func:`request_irq()` and thus has the same prototype as all IRQ
-handlers. It will get called with a pointer to the DRM device as the
-second argument.
-
-Finally the function calls the optional irq_postinstall driver
-operation. The operation usually enables interrupts (excluding the
-vblank interrupt, which is enabled separately), but drivers may choose
-to enable/disable interrupts at a different time.
-
-:c:func:`drm_irq_uninstall()` is similarly used to uninstall an
-IRQ handler. It starts by waking up all processes waiting on a vblank
-interrupt to make sure they don't hang, and then calls the optional
-irq_uninstall driver operation. The operation must disable all hardware
-interrupts. Finally the function frees the IRQ by calling
-:c:func:`free_irq()`.
-
-Manual IRQ Registration
-'''''''''''''''''''''''
-
-Drivers that require multiple interrupt handlers can't use the managed
-IRQ registration functions. In that case IRQs must be registered and
-unregistered manually (usually with the :c:func:`request_irq()` and
-:c:func:`free_irq()` functions, or their :c:func:`devm_request_irq()` and
-:c:func:`devm_free_irq()` equivalents).
-
-When manually registering IRQs, drivers must not set the
-DRIVER_HAVE_IRQ driver feature flag, and must not provide the
-irq_handler driver operation. They must set the :c:type:`struct
-drm_device <drm_device>` irq_enabled field to 1 upon
-registration of the IRQs, and clear it to 0 after unregistering the
-IRQs.
 
 IRQ Helper Library
 ~~~~~~~~~~~~~~~~~~
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 28d736c3fcb4..3b04c25100ae 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -62,19 +62,39 @@
 #include "drm_internal.h"
 
 /**
+ * DOC: irq helpers
+ *
+ * The DRM core provides very simple support helpers to enable IRQ handling on a
+ * device through the drm_irq_install() and drm_irq_uninstall() functions. This
+ * only supports devices with a single interrupt on the main device stored in
+ * &drm_device.dev and set as the device paramter in drm_dev_alloc().
+ *
+ * These IRQ helpers are strictly optional. Drivers which roll their own only
+ * need to set &drm_device.irq_enabled to signal the DRM core that vblank
+ * interrupts are working. Since these helpers don't automatically clean up the
+ * requested interrupt like e.g. devm_request_irq() they're not really
+ * recommended.
+ */
+
+/**
  * drm_irq_install - install IRQ handler
  * @dev: DRM device
  * @irq: IRQ number to install the handler for
  *
  * Initializes the IRQ related data. Installs the handler, calling the driver
- * irq_preinstall() and irq_postinstall() functions before and after the
- * installation.
+ * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
+ * and after the installation.
  *
  * This is the simplified helper interface provided for drivers with no special
  * needs. Drivers which need to install interrupt handlers for multiple
  * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
  * that vblank interrupts are available.
  *
+ * @irq must match the interrupt number that would be passed to request_irq(),
+ * if called directly instead of using this helper function.
+ *
+ * &drm_driver.irq_handler is called to handle the registered interrupt.
+ *
  * Returns:
  * Zero on success or a negative error code on failure.
  */
@@ -136,9 +156,9 @@ EXPORT_SYMBOL(drm_irq_install);
  * drm_irq_uninstall - uninstall the IRQ handler
  * @dev: DRM device
  *
- * Calls the driver's irq_uninstall() function and unregisters the IRQ handler.
- * This should only be called by drivers which used drm_irq_install() to set up
- * their interrupt handler. Other drivers must only reset
+ * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ
+ * handler.  This should only be called by drivers which used drm_irq_install()
+ * to set up their interrupt handler. Other drivers must only reset
  * &drm_device.irq_enabled to false.
  *
  * Note that for kernel modesetting drivers it is a bug if this function fails.
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 630dc26379b7..463e4d81fb0d 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -363,6 +363,9 @@ static void vblank_disable_fn(unsigned long arg)
  * @dev: DRM device
  *
  * This function cleans up any resources allocated in drm_vblank_init.
+ *
+ * Drivers which don't use drm_irq_install() need to set &drm_device.irq_enabled
+ * themselves, to signal to the DRM core that vblank interrupts are enabled.
  */
 void drm_vblank_cleanup(struct drm_device *dev)
 {
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 2e0b76cceb97..39df16af7a4a 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -377,8 +377,13 @@ struct drm_device {
 	int last_context;		/**< Last current context */
 	/*@} */
 
-	/** \name VBLANK IRQ support */
-	/*@{ */
+	/**
+	 * @irq_enabled:
+	 *
+	 * Indicates that interrupt handling is enabled, specifically vblank
+	 * handling. Drivers which don't use drm_irq_install() need to set this
+	 * to true manually.
+	 */
 	bool irq_enabled;
 	int irq;
 
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index ebb41688581b..f495eee01302 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -325,11 +325,40 @@ struct drm_driver {
 				     struct timeval *vblank_time,
 				     bool in_vblank_irq);
 
-	/* these have to be filled in */
-
+	/**
+	 * @irq_handler:
+	 *
+	 * Interrupt handler called when using drm_irq_install(). Not used by
+	 * drivers which implement their own interrupt handling.
+	 */
 	irqreturn_t(*irq_handler) (int irq, void *arg);
+
+	/**
+	 * @irq_preinstall:
+	 *
+	 * Optional callback used by drm_irq_install() which is called before
+	 * the interrupt handler is registered. This should be used to clear out
+	 * any pending interrupts (from e.g. firmware based drives) and reset
+	 * the interrupt handling registers.
+	 */
 	void (*irq_preinstall) (struct drm_device *dev);
+
+	/**
+	 * @irq_postinstall:
+	 *
+	 * Optional callback used by drm_irq_install() which is called after
+	 * the interrupt handler is registered. This should be used to enable
+	 * interrupt generation in the hardware.
+	 */
 	int (*irq_postinstall) (struct drm_device *dev);
+
+	/**
+	 * @irq_uninstall:
+	 *
+	 * Optional callback used by drm_irq_uninstall() which is called before
+	 * the interrupt handler is unregistered. This should be used to disable
+	 * interrupt generation in the hardware.
+	 */
 	void (*irq_uninstall) (struct drm_device *dev);
 
 	/**
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* ✓ Fi.CI.BAT: success for drm: more doc work&cleanup, mostly vblank related (rev4)
  2017-05-24 14:51 [PATCH 00/37] drm: more doc work&cleanup, mostly vblank related Daniel Vetter
                   ` (39 preceding siblings ...)
  2017-05-24 15:19 ` [PATCH 00/37] " Chris Wilson
@ 2017-05-31 10:05 ` Patchwork
  40 siblings, 0 replies; 94+ messages in thread
From: Patchwork @ 2017-05-31 10:05 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx

== Series Details ==

Series: drm: more doc work&cleanup, mostly vblank related (rev4)
URL   : https://patchwork.freedesktop.org/series/24877/
State : success

== Summary ==

Series 24877v4 drm: more doc work&cleanup, mostly vblank related
https://patchwork.freedesktop.org/api/1.0/series/24877/revisions/4/mbox/

Test gem_exec_flush:
        Subgroup basic-batch-kernel-default-uc:
                fail       -> PASS       (fi-snb-2600) fdo#100007

fdo#100007 https://bugs.freedesktop.org/show_bug.cgi?id=100007

fi-bdw-5557u     total:278  pass:267  dwarn:0   dfail:0   fail:0   skip:11  time:441s
fi-bdw-gvtdvm    total:278  pass:256  dwarn:8   dfail:0   fail:0   skip:14  time:437s
fi-bsw-n3050     total:278  pass:242  dwarn:0   dfail:0   fail:0   skip:36  time:582s
fi-bxt-j4205     total:278  pass:259  dwarn:0   dfail:0   fail:0   skip:19  time:513s
fi-byt-j1900     total:278  pass:254  dwarn:0   dfail:0   fail:0   skip:24  time:490s
fi-byt-n2820     total:278  pass:250  dwarn:0   dfail:0   fail:0   skip:28  time:487s
fi-hsw-4770      total:278  pass:262  dwarn:0   dfail:0   fail:0   skip:16  time:432s
fi-hsw-4770r     total:278  pass:262  dwarn:0   dfail:0   fail:0   skip:16  time:416s
fi-ilk-650       total:278  pass:228  dwarn:0   dfail:0   fail:0   skip:50  time:419s
fi-ivb-3520m     total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:498s
fi-ivb-3770      total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:462s
fi-kbl-7500u     total:278  pass:255  dwarn:5   dfail:0   fail:0   skip:18  time:463s
fi-kbl-7560u     total:278  pass:263  dwarn:5   dfail:0   fail:0   skip:10  time:570s
fi-skl-6260u     total:278  pass:268  dwarn:0   dfail:0   fail:0   skip:10  time:461s
fi-skl-6700hq    total:278  pass:239  dwarn:0   dfail:1   fail:17  skip:21  time:430s
fi-skl-6700k     total:278  pass:256  dwarn:4   dfail:0   fail:0   skip:18  time:465s
fi-skl-6770hq    total:278  pass:268  dwarn:0   dfail:0   fail:0   skip:10  time:502s
fi-skl-gvtdvm    total:278  pass:265  dwarn:0   dfail:0   fail:0   skip:13  time:436s
fi-snb-2520m     total:278  pass:250  dwarn:0   dfail:0   fail:0   skip:28  time:537s
fi-snb-2600      total:278  pass:249  dwarn:0   dfail:0   fail:0   skip:29  time:401s

4aa1d46d3b24bbe545cdf7cbd407c18f5cd5e43d drm-tip: 2017y-05m-31d-08h-58m-36s UTC integration manifest
5e6b8bd drm/doc: vblank cleanup
fe65d6d drm/doc: Drop empty include for drm_color_mgmt.h
2ba5557 drm/doc: Polish irq helper documentation
7c77f48 drm: Extract drm_vblank.[hc]
0664adb drm/doc: Improve ioctl/fops docs a bit more
9a401b4 drm/pci: Deprecate drm_pci_init/exit completely
8340653 drm: Remove drm_driver->set_busid hook
820600a drm/udl: Remove dummy busid callback

== Logs ==

For more details see: https://intel-gfx-ci.01.org/CI/Patchwork_4839/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 16/37] drm/hdlcd|mali: " Daniel Vetter
@ 2017-05-31 10:57   ` Liviu Dudau
  2017-05-31 11:03     ` Daniel Vetter
  2017-05-31 16:37   ` Liviu Dudau
  1 sibling, 1 reply; 94+ messages in thread
From: Liviu Dudau @ 2017-05-31 10:57 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

Hi Daniel,

On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
> IRQs are properly shut down, so it almost works as race-free shutdown.
> Except the irq is stopped after the vblank stuff, so boom anyway.
> Proper way would be to call drm_atomic_helper_shutdown before any of
> the kms things gets stopped. So no harm in removing the
> drm_vblank_cleanup here really.

Slightly confused on the implied message in the commit text: is "Proper way
would be to call drm_atomic_helper_shutdown" a hint? A promise of a future
patch? A message to the future us on how to fix things if they blow up?

If calling drm_atomic_helper_shutdown() is the proper thing to do, why does the
patch (and the series) avoids doing that? Lack of understanding of the driver's
internal workings? Then I want to help, if I can understand the new direction.

Best regards,
Liviu

> 
> Same story for both hdlcd and mali.
> 
> v2: Move misplaced malidp hunk to this patch (Liviu).
> 
> Cc: Liviu Dudau <liviu.dudau@arm.com>
> Cc: Brian Starkey <brian.starkey@arm.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  drivers/gpu/drm/arm/hdlcd_drv.c  | 2 --
>  drivers/gpu/drm/arm/malidp_drv.c | 2 --
>  2 files changed, 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
> index 0f49c4b12772..345c8357b273 100644
> --- a/drivers/gpu/drm/arm/hdlcd_drv.c
> +++ b/drivers/gpu/drm/arm/hdlcd_drv.c
> @@ -340,7 +340,6 @@ static int hdlcd_drm_bind(struct device *dev)
>  	}
>  err_fbdev:
>  	drm_kms_helper_poll_fini(drm);
> -	drm_vblank_cleanup(drm);
>  err_vblank:
>  	pm_runtime_disable(drm->dev);
>  err_pm_active:
> @@ -368,7 +367,6 @@ static void hdlcd_drm_unbind(struct device *dev)
>  	}
>  	drm_kms_helper_poll_fini(drm);
>  	component_unbind_all(dev, drm);
> -	drm_vblank_cleanup(drm);
>  	pm_runtime_get_sync(drm->dev);
>  	drm_irq_uninstall(drm);
>  	pm_runtime_put_sync(drm->dev);
> diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
> index 0d3eb537d08b..01b13d219917 100644
> --- a/drivers/gpu/drm/arm/malidp_drv.c
> +++ b/drivers/gpu/drm/arm/malidp_drv.c
> @@ -652,7 +652,6 @@ static int malidp_bind(struct device *dev)
>  	drm_kms_helper_poll_fini(drm);
>  fbdev_fail:
>  	pm_runtime_get_sync(dev);
> -	drm_vblank_cleanup(drm);
>  vblank_fail:
>  	malidp_se_irq_fini(drm);
>  	malidp_de_irq_fini(drm);
> @@ -692,7 +691,6 @@ static void malidp_unbind(struct device *dev)
>  	}
>  	drm_kms_helper_poll_fini(drm);
>  	pm_runtime_get_sync(dev);
> -	drm_vblank_cleanup(drm);
>  	malidp_se_irq_fini(drm);
>  	malidp_de_irq_fini(drm);
>  	component_unbind_all(dev, drm);
> -- 
> 2.11.0
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-31 10:57   ` Liviu Dudau
@ 2017-05-31 11:03     ` Daniel Vetter
  2017-05-31 11:22       ` Liviu Dudau
  0 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31 11:03 UTC (permalink / raw)
  To: Liviu Dudau; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, May 31, 2017 at 12:57 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
>> IRQs are properly shut down, so it almost works as race-free shutdown.
>> Except the irq is stopped after the vblank stuff, so boom anyway.
>> Proper way would be to call drm_atomic_helper_shutdown before any of
>> the kms things gets stopped. So no harm in removing the
>> drm_vblank_cleanup here really.
>
> Slightly confused on the implied message in the commit text: is "Proper way
> would be to call drm_atomic_helper_shutdown" a hint? A promise of a future
> patch? A message to the future us on how to fix things if they blow up?
>
> If calling drm_atomic_helper_shutdown() is the proper thing to do, why does the
> patch (and the series) avoids doing that? Lack of understanding of the driver's
> internal workings? Then I want to help, if I can understand the new direction.

Yes, I wanted to not make things worse. If you look at the overall
result (especially last patch) I'm also trying to better document
stuff in the vblank area, but summarized, if you want to make sure
that vblank processing has stopped, you need to call drm_vblank_off on
each active crtc. The simplest way to get that is by using
drm_atomic_helper_shutdown(). Calling drm_vblank_cleanup doesn't
really do anything useful (see the last patch for the only valid
usecase there ever was).
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-31 11:03     ` Daniel Vetter
@ 2017-05-31 11:22       ` Liviu Dudau
  2017-05-31 16:41         ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Liviu Dudau @ 2017-05-31 11:22 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, May 31, 2017 at 01:03:34PM +0200, Daniel Vetter wrote:
> On Wed, May 31, 2017 at 12:57 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> > On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
> >> IRQs are properly shut down, so it almost works as race-free shutdown.
> >> Except the irq is stopped after the vblank stuff, so boom anyway.
> >> Proper way would be to call drm_atomic_helper_shutdown before any of
> >> the kms things gets stopped. So no harm in removing the
> >> drm_vblank_cleanup here really.
> >
> > Slightly confused on the implied message in the commit text: is "Proper way
> > would be to call drm_atomic_helper_shutdown" a hint? A promise of a future
> > patch? A message to the future us on how to fix things if they blow up?
> >
> > If calling drm_atomic_helper_shutdown() is the proper thing to do, why does the
> > patch (and the series) avoids doing that? Lack of understanding of the driver's
> > internal workings? Then I want to help, if I can understand the new direction.
> 
> Yes, I wanted to not make things worse. If you look at the overall
> result (especially last patch) I'm also trying to better document
> stuff in the vblank area, but summarized, if you want to make sure
> that vblank processing has stopped, you need to call drm_vblank_off on
> each active crtc. The simplest way to get that is by using
> drm_atomic_helper_shutdown(). Calling drm_vblank_cleanup doesn't
> really do anything useful (see the last patch for the only valid
> usecase there ever was).

OK, so there should be a follow up patch adding drm_atomic_helper_shutdown(). I guess
I need to call dibs on it? :)

Best regards,
Liviu

> -Daniel
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-24 14:51 ` [PATCH 16/37] drm/hdlcd|mali: " Daniel Vetter
  2017-05-31 10:57   ` Liviu Dudau
@ 2017-05-31 16:37   ` Liviu Dudau
  2017-06-01  6:01     ` [Intel-gfx] " Daniel Vetter
  1 sibling, 1 reply; 94+ messages in thread
From: Liviu Dudau @ 2017-05-31 16:37 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
> IRQs are properly shut down, so it almost works as race-free shutdown.
> Except the irq is stopped after the vblank stuff, so boom anyway.
> Proper way would be to call drm_atomic_helper_shutdown before any of
> the kms things gets stopped. So no harm in removing the
> drm_vblank_cleanup here really.
> 
> Same story for both hdlcd and mali.
> 
> v2: Move misplaced malidp hunk to this patch (Liviu).
> 
> Cc: Liviu Dudau <liviu.dudau@arm.com>
> Cc: Brian Starkey <brian.starkey@arm.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>

On the assumption that a subsequent patch will fix the issue highlighted in
the commit message (doesn't have to be part of this series):

Acked-by: Liviu Dudau <liviu.dudau@arm.com>

I'm assuming that you are going to carry this patch through one of your trees,
so I will not pull it into mali-dp tree.

Thanks,
Liviu

> ---
>  drivers/gpu/drm/arm/hdlcd_drv.c  | 2 --
>  drivers/gpu/drm/arm/malidp_drv.c | 2 --
>  2 files changed, 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
> index 0f49c4b12772..345c8357b273 100644
> --- a/drivers/gpu/drm/arm/hdlcd_drv.c
> +++ b/drivers/gpu/drm/arm/hdlcd_drv.c
> @@ -340,7 +340,6 @@ static int hdlcd_drm_bind(struct device *dev)
>  	}
>  err_fbdev:
>  	drm_kms_helper_poll_fini(drm);
> -	drm_vblank_cleanup(drm);
>  err_vblank:
>  	pm_runtime_disable(drm->dev);
>  err_pm_active:
> @@ -368,7 +367,6 @@ static void hdlcd_drm_unbind(struct device *dev)
>  	}
>  	drm_kms_helper_poll_fini(drm);
>  	component_unbind_all(dev, drm);
> -	drm_vblank_cleanup(drm);
>  	pm_runtime_get_sync(drm->dev);
>  	drm_irq_uninstall(drm);
>  	pm_runtime_put_sync(drm->dev);
> diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
> index 0d3eb537d08b..01b13d219917 100644
> --- a/drivers/gpu/drm/arm/malidp_drv.c
> +++ b/drivers/gpu/drm/arm/malidp_drv.c
> @@ -652,7 +652,6 @@ static int malidp_bind(struct device *dev)
>  	drm_kms_helper_poll_fini(drm);
>  fbdev_fail:
>  	pm_runtime_get_sync(dev);
> -	drm_vblank_cleanup(drm);
>  vblank_fail:
>  	malidp_se_irq_fini(drm);
>  	malidp_de_irq_fini(drm);
> @@ -692,7 +691,6 @@ static void malidp_unbind(struct device *dev)
>  	}
>  	drm_kms_helper_poll_fini(drm);
>  	pm_runtime_get_sync(dev);
> -	drm_vblank_cleanup(drm);
>  	malidp_se_irq_fini(drm);
>  	malidp_de_irq_fini(drm);
>  	component_unbind_all(dev, drm);
> -- 
> 2.11.0
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-31 11:22       ` Liviu Dudau
@ 2017-05-31 16:41         ` Daniel Vetter
  2017-05-31 16:57           ` Liviu Dudau
  0 siblings, 1 reply; 94+ messages in thread
From: Daniel Vetter @ 2017-05-31 16:41 UTC (permalink / raw)
  To: Liviu Dudau; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, May 31, 2017 at 1:22 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> On Wed, May 31, 2017 at 01:03:34PM +0200, Daniel Vetter wrote:
>> On Wed, May 31, 2017 at 12:57 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
>> > On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
>> >> IRQs are properly shut down, so it almost works as race-free shutdown.
>> >> Except the irq is stopped after the vblank stuff, so boom anyway.
>> >> Proper way would be to call drm_atomic_helper_shutdown before any of
>> >> the kms things gets stopped. So no harm in removing the
>> >> drm_vblank_cleanup here really.
>> >
>> > Slightly confused on the implied message in the commit text: is "Proper way
>> > would be to call drm_atomic_helper_shutdown" a hint? A promise of a future
>> > patch? A message to the future us on how to fix things if they blow up?
>> >
>> > If calling drm_atomic_helper_shutdown() is the proper thing to do, why does the
>> > patch (and the series) avoids doing that? Lack of understanding of the driver's
>> > internal workings? Then I want to help, if I can understand the new direction.
>>
>> Yes, I wanted to not make things worse. If you look at the overall
>> result (especially last patch) I'm also trying to better document
>> stuff in the vblank area, but summarized, if you want to make sure
>> that vblank processing has stopped, you need to call drm_vblank_off on
>> each active crtc. The simplest way to get that is by using
>> drm_atomic_helper_shutdown(). Calling drm_vblank_cleanup doesn't
>> really do anything useful (see the last patch for the only valid
>> usecase there ever was).
>
> OK, so there should be a follow up patch adding drm_atomic_helper_shutdown(). I guess
> I need to call dibs on it? :)

I googled what "to call dibs on smth" means and still can't figure out
what you mean here. Can you pls explain?

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-31 16:41         ` Daniel Vetter
@ 2017-05-31 16:57           ` Liviu Dudau
  2017-06-01  5:55             ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Liviu Dudau @ 2017-05-31 16:57 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On Wed, May 31, 2017 at 06:41:05PM +0200, Daniel Vetter wrote:
> On Wed, May 31, 2017 at 1:22 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> > On Wed, May 31, 2017 at 01:03:34PM +0200, Daniel Vetter wrote:
> >> On Wed, May 31, 2017 at 12:57 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> >> > On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
> >> >> IRQs are properly shut down, so it almost works as race-free shutdown.
> >> >> Except the irq is stopped after the vblank stuff, so boom anyway.
> >> >> Proper way would be to call drm_atomic_helper_shutdown before any of
> >> >> the kms things gets stopped. So no harm in removing the
> >> >> drm_vblank_cleanup here really.
> >> >
> >> > Slightly confused on the implied message in the commit text: is "Proper way
> >> > would be to call drm_atomic_helper_shutdown" a hint? A promise of a future
> >> > patch? A message to the future us on how to fix things if they blow up?
> >> >
> >> > If calling drm_atomic_helper_shutdown() is the proper thing to do, why does the
> >> > patch (and the series) avoids doing that? Lack of understanding of the driver's
> >> > internal workings? Then I want to help, if I can understand the new direction.
> >>
> >> Yes, I wanted to not make things worse. If you look at the overall
> >> result (especially last patch) I'm also trying to better document
> >> stuff in the vblank area, but summarized, if you want to make sure
> >> that vblank processing has stopped, you need to call drm_vblank_off on
> >> each active crtc. The simplest way to get that is by using
> >> drm_atomic_helper_shutdown(). Calling drm_vblank_cleanup doesn't
> >> really do anything useful (see the last patch for the only valid
> >> usecase there ever was).
> >
> > OK, so there should be a follow up patch adding drm_atomic_helper_shutdown(). I guess
> > I need to call dibs on it? :)
> 
> I googled what "to call dibs on smth" means and still can't figure out
> what you mean here. Can you pls explain?

Means I put my name on it. Urban dictionary on "call dibs" gives it a very gender
specific usage, my intent was to convey the message that I will take it on my TODO list.

Best regards,
Liviu

> 
> Thanks, Daniel
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH] drm: Extract drm_vblank.[hc]
  2017-05-31  9:21   ` [PATCH] " Daniel Vetter
@ 2017-05-31 17:51     ` Stefan Agner
  2017-06-01  5:55       ` Daniel Vetter
  2017-05-31 18:22     ` kbuild test robot
  1 sibling, 1 reply; 94+ messages in thread
From: Stefan Agner @ 2017-05-31 17:51 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, Intel Graphics Development, DRI Development

On 2017-05-31 02:21, Daniel Vetter wrote:
> drm_irq.c contains both the irq helper library (optional) and the
> vblank support (optional, but part of the modeset uapi, and doesn't
> require the use of the irq helpers at all.
> 
> Split this up for more clarity of the scope of the individual bits.
> 
> v2: Move misplaced hunks to this patch (Stefan).
> 

Looks good to me:

Reviewed-by: Stefan Agner <stefan@agner.ch>

--
Stefan

> Cc: Stefan Agner <stefan@agner.ch>
> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> ---
>  Documentation/gpu/drm-internals.rst |    9 +
>  Documentation/gpu/drm-kms.rst       |    4 +-
>  drivers/gpu/drm/Makefile            |    2 +-
>  drivers/gpu/drm/drm_internal.h      |    3 +-
>  drivers/gpu/drm/drm_irq.c           | 1623 +---------------------------------
>  drivers/gpu/drm/drm_vblank.c        | 1645 +++++++++++++++++++++++++++++++++++
>  include/drm/drmP.h                  |    5 +-
>  include/drm/drm_file.h              |    1 +
>  include/drm/drm_irq.h               |  158 +---
>  include/drm/drm_prime.h             |    2 +
>  include/drm/drm_vblank.h            |  181 ++++
>  11 files changed, 1869 insertions(+), 1764 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_vblank.c
>  create mode 100644 include/drm/drm_vblank.h
> 
> diff --git a/Documentation/gpu/drm-internals.rst
> b/Documentation/gpu/drm-internals.rst
> index d218dd29221a..dd28e39cf966 100644
> --- a/Documentation/gpu/drm-internals.rst
> +++ b/Documentation/gpu/drm-internals.rst
> @@ -204,6 +204,15 @@ drm_device <drm_device>` irq_enabled field to 1 upon
>  registration of the IRQs, and clear it to 0 after unregistering the
>  IRQs.
>  
> +IRQ Helper Library
> +~~~~~~~~~~~~~~~~~~
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> +   :doc: irq helpers
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> +   :export:
> +
>  Memory Manager Initialization
>  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>  
> diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
> index bfecd21a8cdf..2d77c9580164 100644
> --- a/Documentation/gpu/drm-kms.rst
> +++ b/Documentation/gpu/drm-kms.rst
> @@ -612,8 +612,8 @@ operation handler.
>  Vertical Blanking and Interrupt Handling Functions Reference
>  ------------------------------------------------------------
>  
> -.. kernel-doc:: include/drm/drm_irq.h
> +.. kernel-doc:: include/drm/drm_vblank.h
>     :internal:
>  
> -.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> +.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
>     :export:
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index c156fecfb362..acc88942c2e5 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -16,7 +16,7 @@ drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
>  		drm_framebuffer.o drm_connector.o drm_blend.o \
>  		drm_encoder.o drm_mode_object.o drm_property.o \
>  		drm_plane.o drm_color_mgmt.o drm_print.o \
> -		drm_dumb_buffers.o drm_mode_config.o
> +		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o
>  
>  drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
>  drm-$(CONFIG_DRM_VM) += drm_vm.o
> diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> index bca2c66c5d28..6a0cbcc84534 100644
> --- a/drivers/gpu/drm/drm_internal.h
> +++ b/drivers/gpu/drm/drm_internal.h
> @@ -54,8 +54,9 @@ int drm_name_info(struct seq_file *m, void *data);
>  int drm_clients_info(struct seq_file *m, void* data);
>  int drm_gem_name_info(struct seq_file *m, void *data);
>  
> -/* drm_irq.c */
> +/* drm_vblank.c */
>  extern unsigned int drm_timestamp_monotonic;
> +void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
>  
>  /* IOCTLS */
>  int drm_wait_vblank(struct drm_device *dev, void *data,
> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> index c7debaad67f8..28d736c3fcb4 100644
> --- a/drivers/gpu/drm/drm_irq.c
> +++ b/drivers/gpu/drm/drm_irq.c
> @@ -3,6 +3,25 @@
>   *
>   * \author Rickard E. (Rik) Faith <faith@valinux.com>
>   * \author Gareth Hughes <gareth@valinux.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
>   */
>  
>  /*
> @@ -32,429 +51,15 @@
>   * OTHER DEALINGS IN THE SOFTWARE.
>   */
>  
> +#include <drm/drm_irq.h>
>  #include <drm/drmP.h>
> -#include "drm_trace.h"
> -#include "drm_internal.h"
>  
>  #include <linux/interrupt.h>	/* For task queue support */
> -#include <linux/slab.h>
>  
>  #include <linux/vgaarb.h>
>  #include <linux/export.h>
>  
> -/* Retry timestamp calculation up to 3 times to satisfy
> - * drm_timestamp_precision before giving up.
> - */
> -#define DRM_TIMESTAMP_MAXRETRIES 3
> -
> -/* Threshold in nanoseconds for detection of redundant
> - * vblank irq in drm_handle_vblank(). 1 msec should be ok.
> - */
> -#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
> -
> -static bool
> -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> -			  struct timeval *tvblank, bool in_vblank_irq);
> -
> -static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
> -
> -/*
> - * Default to use monotonic timestamps for wait-for-vblank and page-flip
> - * complete events.
> - */
> -unsigned int drm_timestamp_monotonic = 1;
> -
> -static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
> -
> -module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
> -module_param_named(timestamp_precision_usec, drm_timestamp_precision,
> int, 0600);
> -module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
> -MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable
> [msecs] (0: never disable, <0: disable immediately)");
> -MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
> -MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
> -
> -static void store_vblank(struct drm_device *dev, unsigned int pipe,
> -			 u32 vblank_count_inc,
> -			 struct timeval *t_vblank, u32 last)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -
> -	assert_spin_locked(&dev->vblank_time_lock);
> -
> -	vblank->last = last;
> -
> -	write_seqlock(&vblank->seqlock);
> -	vblank->time = *t_vblank;
> -	vblank->count += vblank_count_inc;
> -	write_sequnlock(&vblank->seqlock);
> -}
> -
> -/*
> - * "No hw counter" fallback implementation of .get_vblank_counter() hook,
> - * if there is no useable hardware frame counter available.
> - */
> -static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
> -{
> -	WARN_ON_ONCE(dev->max_vblank_count != 0);
> -	return 0;
> -}
> -
> -static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
> -{
> -	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> -		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> -
> -		if (crtc->funcs->get_vblank_counter)
> -			return crtc->funcs->get_vblank_counter(crtc);
> -	}
> -
> -	if (dev->driver->get_vblank_counter)
> -		return dev->driver->get_vblank_counter(dev, pipe);
> -
> -	return drm_vblank_no_hw_counter(dev, pipe);
> -}
> -
> -/*
> - * Reset the stored timestamp for the current vblank count to correspond
> - * to the last vblank occurred.
> - *
> - * Only to be called from drm_crtc_vblank_on().
> - *
> - * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> - * device vblank fields.
> - */
> -static void drm_reset_vblank_timestamp(struct drm_device *dev,
> unsigned int pipe)
> -{
> -	u32 cur_vblank;
> -	bool rc;
> -	struct timeval t_vblank;
> -	int count = DRM_TIMESTAMP_MAXRETRIES;
> -
> -	spin_lock(&dev->vblank_time_lock);
> -
> -	/*
> -	 * sample the current counter to avoid random jumps
> -	 * when drm_vblank_enable() applies the diff
> -	 */
> -	do {
> -		cur_vblank = __get_vblank_counter(dev, pipe);
> -		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
> -	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> -
> -	/*
> -	 * Only reinitialize corresponding vblank timestamp if high-precision query
> -	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
> -	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
> -	 */
> -	if (!rc)
> -		t_vblank = (struct timeval) {0, 0};
> -
> -	/*
> -	 * +1 to make sure user will never see the same
> -	 * vblank counter value before and after a modeset
> -	 */
> -	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
> -
> -	spin_unlock(&dev->vblank_time_lock);
> -}
> -
> -/*
> - * Call back into the driver to update the appropriate vblank counter
> - * (specified by @pipe).  Deal with wraparound, if it occurred, and
> - * update the last read value so we can deal with wraparound on the next
> - * call if necessary.
> - *
> - * Only necessary when going from off->on, to account for frames we
> - * didn't get an interrupt for.
> - *
> - * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> - * device vblank fields.
> - */
> -static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
> -				    bool in_vblank_irq)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	u32 cur_vblank, diff;
> -	bool rc;
> -	struct timeval t_vblank;
> -	int count = DRM_TIMESTAMP_MAXRETRIES;
> -	int framedur_ns = vblank->framedur_ns;
> -
> -	/*
> -	 * Interrupts were disabled prior to this call, so deal with counter
> -	 * wrap if needed.
> -	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
> -	 * here if the register is small or we had vblank interrupts off for
> -	 * a long time.
> -	 *
> -	 * We repeat the hardware vblank counter & timestamp query until
> -	 * we get consistent results. This to prevent races between gpu
> -	 * updating its hardware counter while we are retrieving the
> -	 * corresponding vblank timestamp.
> -	 */
> -	do {
> -		cur_vblank = __get_vblank_counter(dev, pipe);
> -		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
> -	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> -
> -	if (dev->max_vblank_count != 0) {
> -		/* trust the hw counter when it's around */
> -		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
> -	} else if (rc && framedur_ns) {
> -		const struct timeval *t_old;
> -		u64 diff_ns;
> -
> -		t_old = &vblank->time;
> -		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
> -
> -		/*
> -		 * Figure out how many vblanks we've missed based
> -		 * on the difference in the timestamps and the
> -		 * frame/field duration.
> -		 */
> -		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
> -
> -		if (diff == 0 && in_vblank_irq)
> -			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
> -				      " diff_ns = %lld, framedur_ns = %d)\n",
> -				      pipe, (long long) diff_ns, framedur_ns);
> -	} else {
> -		/* some kind of default for drivers w/o accurate vbl timestamping */
> -		diff = in_vblank_irq ? 1 : 0;
> -	}
> -
> -	/*
> -	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
> -	 * interval? If so then vblank irqs keep running and it will likely
> -	 * happen that the hardware vblank counter is not trustworthy as it
> -	 * might reset at some point in that interval and vblank timestamps
> -	 * are not trustworthy either in that interval. Iow. this can result
> -	 * in a bogus diff >> 1 which must be avoided as it would cause
> -	 * random large forward jumps of the software vblank counter.
> -	 */
> -	if (diff > 1 && (vblank->inmodeset & 0x2)) {
> -		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
> -			      " due to pre-modeset.\n", pipe, diff);
> -		diff = 1;
> -	}
> -
> -	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
> -		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
> -		      pipe, vblank->count, diff, cur_vblank, vblank->last);
> -
> -	if (diff == 0) {
> -		WARN_ON_ONCE(cur_vblank != vblank->last);
> -		return;
> -	}
> -
> -	/*
> -	 * Only reinitialize corresponding vblank timestamp if high-precision query
> -	 * available and didn't fail, or we were called from the vblank interrupt.
> -	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
> -	 * for now, to mark the vblanktimestamp as invalid.
> -	 */
> -	if (!rc && in_vblank_irq)
> -		t_vblank = (struct timeval) {0, 0};
> -
> -	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
> -}
> -
> -static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return 0;
> -
> -	return vblank->count;
> -}
> -
> -/**
> - * drm_accurate_vblank_count - retrieve the master vblank counter
> - * @crtc: which counter to retrieve
> - *
> - * This function is similar to @drm_crtc_vblank_count but this
> - * function interpolates to handle a race with vblank irq's.
> - *
> - * This is mostly useful for hardware that can obtain the scanout
> - * position, but doesn't have a frame counter.
> - */
> -u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned int pipe = drm_crtc_index(crtc);
> -	u32 vblank;
> -	unsigned long flags;
> -
> -	WARN(!dev->driver->get_vblank_timestamp,
> -	     "This function requires support for accurate vblank timestamps.");
> -
> -	spin_lock_irqsave(&dev->vblank_time_lock, flags);
> -
> -	drm_update_vblank_count(dev, pipe, false);
> -	vblank = drm_vblank_count(dev, pipe);
> -
> -	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
> -
> -	return vblank;
> -}
> -EXPORT_SYMBOL(drm_accurate_vblank_count);
> -
> -static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
> -{
> -	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> -		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> -
> -		if (crtc->funcs->disable_vblank) {
> -			crtc->funcs->disable_vblank(crtc);
> -			return;
> -		}
> -	}
> -
> -	dev->driver->disable_vblank(dev, pipe);
> -}
> -
> -/*
> - * Disable vblank irq's on crtc, make sure that last vblank count
> - * of hardware and corresponding consistent software vblank counter
> - * are preserved, even if there are any spurious vblank irq's after
> - * disable.
> - */
> -static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	unsigned long irqflags;
> -
> -	assert_spin_locked(&dev->vbl_lock);
> -
> -	/* Prevent vblank irq processing while disabling vblank irqs,
> -	 * so no updates of timestamps or count can happen after we've
> -	 * disabled. Needed to prevent races in case of delayed irq's.
> -	 */
> -	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
> -
> -	/*
> -	 * Only disable vblank interrupts if they're enabled. This avoids
> -	 * calling the ->disable_vblank() operation in atomic context with the
> -	 * hardware potentially runtime suspended.
> -	 */
> -	if (vblank->enabled) {
> -		__disable_vblank(dev, pipe);
> -		vblank->enabled = false;
> -	}
> -
> -	/*
> -	 * Always update the count and timestamp to maintain the
> -	 * appearance that the counter has been ticking all along until
> -	 * this time. This makes the count account for the entire time
> -	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
> -	 */
> -	drm_update_vblank_count(dev, pipe, false);
> -
> -	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
> -}
> -
> -static void vblank_disable_fn(unsigned long arg)
> -{
> -	struct drm_vblank_crtc *vblank = (void *)arg;
> -	struct drm_device *dev = vblank->dev;
> -	unsigned int pipe = vblank->pipe;
> -	unsigned long irqflags;
> -
> -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> -	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
> -		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
> -		vblank_disable_and_save(dev, pipe);
> -	}
> -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> -}
> -
> -/**
> - * drm_vblank_cleanup - cleanup vblank support
> - * @dev: DRM device
> - *
> - * This function cleans up any resources allocated in drm_vblank_init.
> - */
> -void drm_vblank_cleanup(struct drm_device *dev)
> -{
> -	unsigned int pipe;
> -
> -	/* Bail if the driver didn't call drm_vblank_init() */
> -	if (dev->num_crtcs == 0)
> -		return;
> -
> -	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
> -		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -
> -		WARN_ON(READ_ONCE(vblank->enabled) &&
> -			drm_core_check_feature(dev, DRIVER_MODESET));
> -
> -		del_timer_sync(&vblank->disable_timer);
> -	}
> -
> -	kfree(dev->vblank);
> -
> -	dev->num_crtcs = 0;
> -}
> -EXPORT_SYMBOL(drm_vblank_cleanup);
> -
> -/**
> - * drm_vblank_init - initialize vblank support
> - * @dev: DRM device
> - * @num_crtcs: number of CRTCs supported by @dev
> - *
> - * This function initializes vblank support for @num_crtcs display pipelines.
> - *
> - * Returns:
> - * Zero on success or a negative error code on failure.
> - */
> -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
> -{
> -	int ret = -ENOMEM;
> -	unsigned int i;
> -
> -	spin_lock_init(&dev->vbl_lock);
> -	spin_lock_init(&dev->vblank_time_lock);
> -
> -	dev->num_crtcs = num_crtcs;
> -
> -	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
> -	if (!dev->vblank)
> -		goto err;
> -
> -	for (i = 0; i < num_crtcs; i++) {
> -		struct drm_vblank_crtc *vblank = &dev->vblank[i];
> -
> -		vblank->dev = dev;
> -		vblank->pipe = i;
> -		init_waitqueue_head(&vblank->queue);
> -		setup_timer(&vblank->disable_timer, vblank_disable_fn,
> -			    (unsigned long)vblank);
> -		seqlock_init(&vblank->seqlock);
> -	}
> -
> -	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
> -
> -	/* Driver specific high-precision vblank timestamping supported? */
> -	if (dev->driver->get_vblank_timestamp)
> -		DRM_INFO("Driver supports precise vblank timestamp query.\n");
> -	else
> -		DRM_INFO("No driver support for vblank timestamp query.\n");
> -
> -	/* Must have precise timestamping for reliable vblank instant disable */
> -	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
> -		dev->vblank_disable_immediate = false;
> -		DRM_INFO("Setting vblank_disable_immediate to false because "
> -			 "get_vblank_timestamp == NULL\n");
> -	}
> -
> -	return 0;
> -
> -err:
> -	dev->num_crtcs = 0;
> -	return ret;
> -}
> -EXPORT_SYMBOL(drm_vblank_init);
> +#include "drm_internal.h"
>  
>  /**
>   * drm_irq_install - install IRQ handler
> @@ -571,7 +176,7 @@ int drm_irq_uninstall(struct drm_device *dev)
>  
>  			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
>  
> -			vblank_disable_and_save(dev, i);
> +			drm_vblank_disable_and_save(dev, i);
>  			wake_up(&vblank->queue);
>  		}
>  		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> @@ -634,1187 +239,3 @@ int drm_legacy_irq_control(struct drm_device
> *dev, void *data,
>  		return -EINVAL;
>  	}
>  }
> -
> -/**
> - * drm_calc_timestamping_constants - calculate vblank timestamp constants
> - * @crtc: drm_crtc whose timestamp constants should be updated.
> - * @mode: display mode containing the scanout timings
> - *
> - * Calculate and store various constants which are later
> - * needed by vblank and swap-completion timestamping, e.g,
> - * by drm_calc_vbltimestamp_from_scanoutpos(). They are
> - * derived from CRTC's true scanout timing, so they take
> - * things like panel scaling or other adjustments into account.
> - */
> -void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> -				     const struct drm_display_mode *mode)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned int pipe = drm_crtc_index(crtc);
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	int linedur_ns = 0, framedur_ns = 0;
> -	int dotclock = mode->crtc_clock;
> -
> -	if (!dev->num_crtcs)
> -		return;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	/* Valid dotclock? */
> -	if (dotclock > 0) {
> -		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
> -
> -		/*
> -		 * Convert scanline length in pixels and video
> -		 * dot clock to line duration and frame duration
> -		 * in nanoseconds:
> -		 */
> -		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
> -		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
> -
> -		/*
> -		 * Fields of interlaced scanout modes are only half a frame duration.
> -		 */
> -		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> -			framedur_ns /= 2;
> -	} else
> -		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
> -			  crtc->base.id);
> -
> -	vblank->linedur_ns  = linedur_ns;
> -	vblank->framedur_ns = framedur_ns;
> -	vblank->hwmode = *mode;
> -
> -	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
> -		  crtc->base.id, mode->crtc_htotal,
> -		  mode->crtc_vtotal, mode->crtc_vdisplay);
> -	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
> -		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
> -}
> -EXPORT_SYMBOL(drm_calc_timestamping_constants);
> -
> -/**
> - * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
> - * @dev: DRM device
> - * @pipe: index of CRTC whose vblank timestamp to retrieve
> - * @max_error: Desired maximum allowable error in timestamps (nanosecs)
> - *             On return contains true maximum error of timestamp
> - * @vblank_time: Pointer to struct timeval which should receive the timestamp
> - * @in_vblank_irq:
> - *     True when called from drm_crtc_handle_vblank().  Some drivers
> - *     need to apply some workarounds for gpu-specific vblank irq quirks
> - *     if flag is set.
> - *
> - * Implements calculation of exact vblank timestamps from given
> drm_display_mode
> - * timings and current video scanout position of a CRTC. This can be
> called from
> - * within get_vblank_timestamp() implementation of a kms driver to
> implement the
> - * actual timestamping.
> - *
> - * Should return timestamps conforming to the OML_sync_control OpenML
> - * extension specification. The timestamp corresponds to the end of
> - * the vblank interval, aka start of scanout of topmost-leftmost display
> - * pixel in the following video frame.
> - *
> - * Requires support for optional dev->driver->get_scanout_position()
> - * in kms driver, plus a bit of setup code to provide a drm_display_mode
> - * that corresponds to the true scanout timing.
> - *
> - * The current implementation only handles standard video modes. It
> - * returns as no operation if a doublescan or interlaced video mode is
> - * active. Higher level code is expected to handle this.
> - *
> - * This function can be used to implement the &drm_driver.get_vblank_timestamp
> - * directly, if the driver implements the
> &drm_driver.get_scanout_position hook.
> - *
> - * Note that atomic drivers must call drm_calc_timestamping_constants() before
> - * enabling a CRTC. The atomic helpers already take care of that in
> - * drm_atomic_helper_update_legacy_modeset_state().
> - *
> - * Returns:
> - *
> - * Returns true on success, and false on failure, i.e. when no accurate
> - * timestamp could be acquired.
> - */
> -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> -					   unsigned int pipe,
> -					   int *max_error,
> -					   struct timeval *vblank_time,
> -					   bool in_vblank_irq)
> -{
> -	struct timeval tv_etime;
> -	ktime_t stime, etime;
> -	bool vbl_status;
> -	struct drm_crtc *crtc;
> -	const struct drm_display_mode *mode;
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	int vpos, hpos, i;
> -	int delta_ns, duration_ns;
> -
> -	if (!drm_core_check_feature(dev, DRIVER_MODESET))
> -		return false;
> -
> -	crtc = drm_crtc_from_index(dev, pipe);
> -
> -	if (pipe >= dev->num_crtcs || !crtc) {
> -		DRM_ERROR("Invalid crtc %u\n", pipe);
> -		return false;
> -	}
> -
> -	/* Scanout position query not supported? Should not happen. */
> -	if (!dev->driver->get_scanout_position) {
> -		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
> -		return false;
> -	}
> -
> -	if (drm_drv_uses_atomic_modeset(dev))
> -		mode = &vblank->hwmode;
> -	else
> -		mode = &crtc->hwmode;
> -
> -	/* If mode timing undefined, just return as no-op:
> -	 * Happens during initial modesetting of a crtc.
> -	 */
> -	if (mode->crtc_clock == 0) {
> -		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
> -		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
> -
> -		return false;
> -	}
> -
> -	/* Get current scanout position with system timestamp.
> -	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
> -	 * if single query takes longer than max_error nanoseconds.
> -	 *
> -	 * This guarantees a tight bound on maximum error if
> -	 * code gets preempted or delayed for some reason.
> -	 */
> -	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
> -		/*
> -		 * Get vertical and horizontal scanout position vpos, hpos,
> -		 * and bounding timestamps stime, etime, pre/post query.
> -		 */
> -		vbl_status = dev->driver->get_scanout_position(dev, pipe,
> -							       in_vblank_irq,
> -							       &vpos, &hpos,
> -							       &stime, &etime,
> -							       mode);
> -
> -		/* Return as no-op if scanout query unsupported or failed. */
> -		if (!vbl_status) {
> -			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
> -				  pipe);
> -			return false;
> -		}
> -
> -		/* Compute uncertainty in timestamp of scanout position query. */
> -		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
> -
> -		/* Accept result with <  max_error nsecs timing uncertainty. */
> -		if (duration_ns <= *max_error)
> -			break;
> -	}
> -
> -	/* Noisy system timing? */
> -	if (i == DRM_TIMESTAMP_MAXRETRIES) {
> -		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
> -			  pipe, duration_ns/1000, *max_error/1000, i);
> -	}
> -
> -	/* Return upper bound of timestamp precision error. */
> -	*max_error = duration_ns;
> -
> -	/* Convert scanout position into elapsed time at raw_time query
> -	 * since start of scanout at first display scanline. delta_ns
> -	 * can be negative if start of scanout hasn't happened yet.
> -	 */
> -	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
> -			   mode->crtc_clock);
> -
> -	if (!drm_timestamp_monotonic)
> -		etime = ktime_mono_to_real(etime);
> -
> -	/* save this only for debugging purposes */
> -	tv_etime = ktime_to_timeval(etime);
> -	/* Subtract time delta from raw timestamp to get final
> -	 * vblank_time timestamp for end of vblank.
> -	 */
> -	etime = ktime_sub_ns(etime, delta_ns);
> -	*vblank_time = ktime_to_timeval(etime);
> -
> -	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
> -		      pipe, hpos, vpos,
> -		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
> -		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
> -		      duration_ns/1000, i);
> -
> -	return true;
> -}
> -EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
> -
> -static struct timeval get_drm_timestamp(void)
> -{
> -	ktime_t now;
> -
> -	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
> -	return ktime_to_timeval(now);
> -}
> -
> -/**
> - * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
> - *                             vblank interval
> - * @dev: DRM device
> - * @pipe: index of CRTC whose vblank timestamp to retrieve
> - * @tvblank: Pointer to target struct timeval which should receive
> the timestamp
> - * @in_vblank_irq:
> - *     True when called from drm_crtc_handle_vblank().  Some drivers
> - *     need to apply some workarounds for gpu-specific vblank irq quirks
> - *     if flag is set.
> - *
> - * Fetches the system timestamp corresponding to the time of the most recent
> - * vblank interval on specified CRTC. May call into kms-driver to
> - * compute the timestamp with a high-precision GPU specific method.
> - *
> - * Returns zero if timestamp originates from uncorrected do_gettimeofday()
> - * call, i.e., it isn't very precisely locked to the true vblank.
> - *
> - * Returns:
> - * True if timestamp is considered to be very precise, false otherwise.
> - */
> -static bool
> -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> -			  struct timeval *tvblank, bool in_vblank_irq)
> -{
> -	bool ret = false;
> -
> -	/* Define requested maximum error on timestamps (nanoseconds). */
> -	int max_error = (int) drm_timestamp_precision * 1000;
> -
> -	/* Query driver if possible and precision timestamping enabled. */
> -	if (dev->driver->get_vblank_timestamp && (max_error > 0))
> -		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
> -							tvblank, in_vblank_irq);
> -
> -	/* GPU high precision timestamp query unsupported or failed.
> -	 * Return current monotonic/gettimeofday timestamp as best estimate.
> -	 */
> -	if (!ret)
> -		*tvblank = get_drm_timestamp();
> -
> -	return ret;
> -}
> -
> -/**
> - * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
> - * @crtc: which counter to retrieve
> - *
> - * Fetches the "cooked" vblank count value that represents the number of
> - * vblank events since the system was booted, including lost events due to
> - * modesetting activity.
> - *
> - * Returns:
> - * The software vblank counter.
> - */
> -u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
> -{
> -	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_count);
> -
> -/**
> - * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
> - *     system timestamp corresponding to that vblank counter value.
> - * @dev: DRM device
> - * @pipe: index of CRTC whose counter to retrieve
> - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> - *
> - * Fetches the "cooked" vblank count value that represents the number of
> - * vblank events since the system was booted, including lost events due to
> - * modesetting activity. Returns corresponding system timestamp of the time
> - * of the vblank interval that corresponds to the current vblank counter value.
> - *
> - * This is the legacy version of drm_crtc_vblank_count_and_time().
> - */
> -static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
> -				     struct timeval *vblanktime)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	u32 vblank_count;
> -	unsigned int seq;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs)) {
> -		*vblanktime = (struct timeval) { 0 };
> -		return 0;
> -	}
> -
> -	do {
> -		seq = read_seqbegin(&vblank->seqlock);
> -		vblank_count = vblank->count;
> -		*vblanktime = vblank->time;
> -	} while (read_seqretry(&vblank->seqlock, seq));
> -
> -	return vblank_count;
> -}
> -
> -/**
> - * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
> - *     and the system timestamp corresponding to that vblank counter value
> - * @crtc: which counter to retrieve
> - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> - *
> - * Fetches the "cooked" vblank count value that represents the number of
> - * vblank events since the system was booted, including lost events due to
> - * modesetting activity. Returns corresponding system timestamp of the time
> - * of the vblank interval that corresponds to the current vblank counter value.
> - */
> -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> -				   struct timeval *vblanktime)
> -{
> -	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
> -					 vblanktime);
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
> -
> -static void send_vblank_event(struct drm_device *dev,
> -		struct drm_pending_vblank_event *e,
> -		unsigned long seq, struct timeval *now)
> -{
> -	e->event.sequence = seq;
> -	e->event.tv_sec = now->tv_sec;
> -	e->event.tv_usec = now->tv_usec;
> -
> -	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
> -					 e->event.sequence);
> -
> -	drm_send_event_locked(dev, &e->base);
> -}
> -
> -/**
> - * drm_crtc_arm_vblank_event - arm vblank event after pageflip
> - * @crtc: the source CRTC of the vblank event
> - * @e: the event to send
> - *
> - * A lot of drivers need to generate vblank events for the very next vblank
> - * interrupt. For example when the page flip interrupt happens when the page
> - * flip gets armed, but not when it actually executes within the next vblank
> - * period. This helper function implements exactly the required vblank arming
> - * behaviour.
> - *
> - * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
> - * atomic commit must ensure that the next vblank happens at exactly the same
> - * time as the atomic commit is committed to the hardware. This function itself
> - * does **not** protect again the next vblank interrupt racing with either this
> - * function call or the atomic commit operation. A possible sequence could be:
> - *
> - * 1. Driver commits new hardware state into vblank-synchronized registers.
> - * 2. A vblank happens, committing the hardware state. Also the corresponding
> - *    vblank interrupt is fired off and fully processed by the interrupt
> - *    handler.
> - * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
> - * 4. The event is only send out for the next vblank, which is wrong.
> - *
> - * An equivalent race can happen when the driver calls
> - * drm_crtc_arm_vblank_event() before writing out the new hardware state.
> - *
> - * The only way to make this work safely is to prevent the vblank from firing
> - * (and the hardware from committing anything else) until the entire atomic
> - * commit sequence has run to completion. If the hardware does not have such a
> - * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
> - * Instead drivers need to manually send out the event from their interrupt
> - * handler by calling drm_crtc_send_vblank_event() and make sure that
> there's no
> - * possible race with the hardware committing the atomic update.
> - *
> - * Caller must hold event lock. Caller must also hold a vblank reference for
> - * the event @e, which will be dropped when the next vblank arrives.
> - */
> -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> -			       struct drm_pending_vblank_event *e)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned int pipe = drm_crtc_index(crtc);
> -
> -	assert_spin_locked(&dev->event_lock);
> -
> -	e->pipe = pipe;
> -	e->event.sequence = drm_vblank_count(dev, pipe);
> -	e->event.crtc_id = crtc->base.id;
> -	list_add_tail(&e->base.link, &dev->vblank_event_list);
> -}
> -EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
> -
> -/**
> - * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
> - * @crtc: the source CRTC of the vblank event
> - * @e: the event to send
> - *
> - * Updates sequence # and timestamp on event for the most recently processed
> - * vblank, and sends it to userspace.  Caller must hold event lock.
> - *
> - * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
> - * situation, especially to send out events for atomic commit operations.
> - */
> -void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> -				struct drm_pending_vblank_event *e)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned int seq, pipe = drm_crtc_index(crtc);
> -	struct timeval now;
> -
> -	if (dev->num_crtcs > 0) {
> -		seq = drm_vblank_count_and_time(dev, pipe, &now);
> -	} else {
> -		seq = 0;
> -
> -		now = get_drm_timestamp();
> -	}
> -	e->pipe = pipe;
> -	e->event.crtc_id = crtc->base.id;
> -	send_vblank_event(dev, e, seq, &now);
> -}
> -EXPORT_SYMBOL(drm_crtc_send_vblank_event);
> -
> -static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
> -{
> -	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> -		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> -
> -		if (crtc->funcs->enable_vblank)
> -			return crtc->funcs->enable_vblank(crtc);
> -	}
> -
> -	return dev->driver->enable_vblank(dev, pipe);
> -}
> -
> -/**
> - * drm_vblank_enable - enable the vblank interrupt on a CRTC
> - * @dev: DRM device
> - * @pipe: CRTC index
> - *
> - * Returns:
> - * Zero on success or a negative error code on failure.
> - */
> -static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	int ret = 0;
> -
> -	assert_spin_locked(&dev->vbl_lock);
> -
> -	spin_lock(&dev->vblank_time_lock);
> -
> -	if (!vblank->enabled) {
> -		/*
> -		 * Enable vblank irqs under vblank_time_lock protection.
> -		 * All vblank count & timestamp updates are held off
> -		 * until we are done reinitializing master counter and
> -		 * timestamps. Filtercode in drm_handle_vblank() will
> -		 * prevent double-accounting of same vblank interval.
> -		 */
> -		ret = __enable_vblank(dev, pipe);
> -		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
> -		if (ret) {
> -			atomic_dec(&vblank->refcount);
> -		} else {
> -			drm_update_vblank_count(dev, pipe, 0);
> -			/* drm_update_vblank_count() includes a wmb so we just
> -			 * need to ensure that the compiler emits the write
> -			 * to mark the vblank as enabled after the call
> -			 * to drm_update_vblank_count().
> -			 */
> -			WRITE_ONCE(vblank->enabled, true);
> -		}
> -	}
> -
> -	spin_unlock(&dev->vblank_time_lock);
> -
> -	return ret;
> -}
> -
> -/**
> - * drm_vblank_get - get a reference count on vblank events
> - * @dev: DRM device
> - * @pipe: index of CRTC to own
> - *
> - * Acquire a reference count on vblank events to avoid having them disabled
> - * while in use.
> - *
> - * This is the legacy version of drm_crtc_vblank_get().
> - *
> - * Returns:
> - * Zero on success or a negative error code on failure.
> - */
> -static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	unsigned long irqflags;
> -	int ret = 0;
> -
> -	if (!dev->num_crtcs)
> -		return -EINVAL;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return -EINVAL;
> -
> -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> -	/* Going from 0->1 means we have to enable interrupts again */
> -	if (atomic_add_return(1, &vblank->refcount) == 1) {
> -		ret = drm_vblank_enable(dev, pipe);
> -	} else {
> -		if (!vblank->enabled) {
> -			atomic_dec(&vblank->refcount);
> -			ret = -EINVAL;
> -		}
> -	}
> -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> -
> -	return ret;
> -}
> -
> -/**
> - * drm_crtc_vblank_get - get a reference count on vblank events
> - * @crtc: which CRTC to own
> - *
> - * Acquire a reference count on vblank events to avoid having them disabled
> - * while in use.
> - *
> - * Returns:
> - * Zero on success or a negative error code on failure.
> - */
> -int drm_crtc_vblank_get(struct drm_crtc *crtc)
> -{
> -	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_get);
> -
> -/**
> - * drm_vblank_put - release ownership of vblank events
> - * @dev: DRM device
> - * @pipe: index of CRTC to release
> - *
> - * Release ownership of a given vblank counter, turning off interrupts
> - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> - *
> - * This is the legacy version of drm_crtc_vblank_put().
> - */
> -static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
> -		return;
> -
> -	/* Last user schedules interrupt disable */
> -	if (atomic_dec_and_test(&vblank->refcount)) {
> -		if (drm_vblank_offdelay == 0)
> -			return;
> -		else if (drm_vblank_offdelay < 0)
> -			vblank_disable_fn((unsigned long)vblank);
> -		else if (!dev->vblank_disable_immediate)
> -			mod_timer(&vblank->disable_timer,
> -				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
> -	}
> -}
> -
> -/**
> - * drm_crtc_vblank_put - give up ownership of vblank events
> - * @crtc: which counter to give up
> - *
> - * Release ownership of a given vblank counter, turning off interrupts
> - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> - */
> -void drm_crtc_vblank_put(struct drm_crtc *crtc)
> -{
> -	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_put);
> -
> -/**
> - * drm_wait_one_vblank - wait for one vblank
> - * @dev: DRM device
> - * @pipe: CRTC index
> - *
> - * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
> - * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
> - * due to lack of driver support or because the crtc is off.
> - */
> -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	int ret;
> -	u32 last;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	ret = drm_vblank_get(dev, pipe);
> -	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
> -		return;
> -
> -	last = drm_vblank_count(dev, pipe);
> -
> -	ret = wait_event_timeout(vblank->queue,
> -				 last != drm_vblank_count(dev, pipe),
> -				 msecs_to_jiffies(100));
> -
> -	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
> -
> -	drm_vblank_put(dev, pipe);
> -}
> -EXPORT_SYMBOL(drm_wait_one_vblank);
> -
> -/**
> - * drm_crtc_wait_one_vblank - wait for one vblank
> - * @crtc: DRM crtc
> - *
> - * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> - * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
> - * due to lack of driver support or because the crtc is off.
> - */
> -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> -{
> -	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
> -}
> -EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
> -
> -/**
> - * drm_crtc_vblank_off - disable vblank events on a CRTC
> - * @crtc: CRTC in question
> - *
> - * Drivers can use this function to shut down the vblank interrupt
> handling when
> - * disabling a crtc. This function ensures that the latest vblank
> frame count is
> - * stored so that drm_vblank_on can restore it again.
> - *
> - * Drivers must use this function when the hardware vblank counter can get
> - * reset, e.g. when suspending.
> - */
> -void drm_crtc_vblank_off(struct drm_crtc *crtc)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned int pipe = drm_crtc_index(crtc);
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	struct drm_pending_vblank_event *e, *t;
> -	struct timeval now;
> -	unsigned long irqflags;
> -	unsigned int seq;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	spin_lock_irqsave(&dev->event_lock, irqflags);
> -
> -	spin_lock(&dev->vbl_lock);
> -	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> -		      pipe, vblank->enabled, vblank->inmodeset);
> -
> -	/* Avoid redundant vblank disables without previous
> -	 * drm_crtc_vblank_on(). */
> -	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
> -		vblank_disable_and_save(dev, pipe);
> -
> -	wake_up(&vblank->queue);
> -
> -	/*
> -	 * Prevent subsequent drm_vblank_get() from re-enabling
> -	 * the vblank interrupt by bumping the refcount.
> -	 */
> -	if (!vblank->inmodeset) {
> -		atomic_inc(&vblank->refcount);
> -		vblank->inmodeset = 1;
> -	}
> -	spin_unlock(&dev->vbl_lock);
> -
> -	/* Send any queued vblank events, lest the natives grow disquiet */
> -	seq = drm_vblank_count_and_time(dev, pipe, &now);
> -
> -	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> -		if (e->pipe != pipe)
> -			continue;
> -		DRM_DEBUG("Sending premature vblank event on disable: "
> -			  "wanted %u, current %u\n",
> -			  e->event.sequence, seq);
> -		list_del(&e->base.link);
> -		drm_vblank_put(dev, pipe);
> -		send_vblank_event(dev, e, seq, &now);
> -	}
> -	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> -
> -	/* Will be reset by the modeset helpers when re-enabling the crtc by
> -	 * calling drm_calc_timestamping_constants(). */
> -	vblank->hwmode.crtc_clock = 0;
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_off);
> -
> -/**
> - * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
> - * @crtc: CRTC in question
> - *
> - * Drivers can use this function to reset the vblank state to off at load time.
> - * Drivers should use this together with the drm_crtc_vblank_off() and
> - * drm_crtc_vblank_on() functions. The difference compared to
> - * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
> - * and hence doesn't need to call any driver hooks.
> - */
> -void drm_crtc_vblank_reset(struct drm_crtc *crtc)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned long irqflags;
> -	unsigned int pipe = drm_crtc_index(crtc);
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -
> -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> -	/*
> -	 * Prevent subsequent drm_vblank_get() from enabling the vblank
> -	 * interrupt by bumping the refcount.
> -	 */
> -	if (!vblank->inmodeset) {
> -		atomic_inc(&vblank->refcount);
> -		vblank->inmodeset = 1;
> -	}
> -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> -
> -	WARN_ON(!list_empty(&dev->vblank_event_list));
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_reset);
> -
> -/**
> - * drm_crtc_vblank_on - enable vblank events on a CRTC
> - * @crtc: CRTC in question
> - *
> - * This functions restores the vblank interrupt state captured with
> - * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
> - * drm_crtc_vblank_off() can be unbalanced and so can also be
> unconditionally called
> - * in driver load code to reflect the current hardware state of the crtc.
> - */
> -void drm_crtc_vblank_on(struct drm_crtc *crtc)
> -{
> -	struct drm_device *dev = crtc->dev;
> -	unsigned int pipe = drm_crtc_index(crtc);
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	unsigned long irqflags;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> -	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> -		      pipe, vblank->enabled, vblank->inmodeset);
> -
> -	/* Drop our private "prevent drm_vblank_get" refcount */
> -	if (vblank->inmodeset) {
> -		atomic_dec(&vblank->refcount);
> -		vblank->inmodeset = 0;
> -	}
> -
> -	drm_reset_vblank_timestamp(dev, pipe);
> -
> -	/*
> -	 * re-enable interrupts if there are users left, or the
> -	 * user wishes vblank interrupts to be enabled all the time.
> -	 */
> -	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
> -		WARN_ON(drm_vblank_enable(dev, pipe));
> -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> -}
> -EXPORT_SYMBOL(drm_crtc_vblank_on);
> -
> -static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
> -					  unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -
> -	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> -	if (!dev->num_crtcs)
> -		return;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	/*
> -	 * To avoid all the problems that might happen if interrupts
> -	 * were enabled/disabled around or between these calls, we just
> -	 * have the kernel take a reference on the CRTC (just once though
> -	 * to avoid corrupting the count if multiple, mismatch calls occur),
> -	 * so that interrupts remain enabled in the interim.
> -	 */
> -	if (!vblank->inmodeset) {
> -		vblank->inmodeset = 0x1;
> -		if (drm_vblank_get(dev, pipe) == 0)
> -			vblank->inmodeset |= 0x2;
> -	}
> -}
> -
> -static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
> -					   unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	unsigned long irqflags;
> -
> -	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> -	if (!dev->num_crtcs)
> -		return;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return;
> -
> -	if (vblank->inmodeset) {
> -		spin_lock_irqsave(&dev->vbl_lock, irqflags);
> -		drm_reset_vblank_timestamp(dev, pipe);
> -		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> -
> -		if (vblank->inmodeset & 0x2)
> -			drm_vblank_put(dev, pipe);
> -
> -		vblank->inmodeset = 0;
> -	}
> -}
> -
> -int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
> -			   struct drm_file *file_priv)
> -{
> -	struct drm_modeset_ctl *modeset = data;
> -	unsigned int pipe;
> -
> -	/* If drm_vblank_init() hasn't been called yet, just no-op */
> -	if (!dev->num_crtcs)
> -		return 0;
> -
> -	/* KMS drivers handle this internally */
> -	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
> -		return 0;
> -
> -	pipe = modeset->crtc;
> -	if (pipe >= dev->num_crtcs)
> -		return -EINVAL;
> -
> -	switch (modeset->cmd) {
> -	case _DRM_PRE_MODESET:
> -		drm_legacy_vblank_pre_modeset(dev, pipe);
> -		break;
> -	case _DRM_POST_MODESET:
> -		drm_legacy_vblank_post_modeset(dev, pipe);
> -		break;
> -	default:
> -		return -EINVAL;
> -	}
> -
> -	return 0;
> -}
> -
> -static inline bool vblank_passed(u32 seq, u32 ref)
> -{
> -	return (seq - ref) <= (1 << 23);
> -}
> -
> -static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
> -				  union drm_wait_vblank *vblwait,
> -				  struct drm_file *file_priv)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	struct drm_pending_vblank_event *e;
> -	struct timeval now;
> -	unsigned long flags;
> -	unsigned int seq;
> -	int ret;
> -
> -	e = kzalloc(sizeof(*e), GFP_KERNEL);
> -	if (e == NULL) {
> -		ret = -ENOMEM;
> -		goto err_put;
> -	}
> -
> -	e->pipe = pipe;
> -	e->event.base.type = DRM_EVENT_VBLANK;
> -	e->event.base.length = sizeof(e->event);
> -	e->event.user_data = vblwait->request.signal;
> -
> -	spin_lock_irqsave(&dev->event_lock, flags);
> -
> -	/*
> -	 * drm_crtc_vblank_off() might have been called after we called
> -	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
> -	 * vblank disable, so no need for further locking.  The reference from
> -	 * drm_vblank_get() protects against vblank disable from another source.
> -	 */
> -	if (!READ_ONCE(vblank->enabled)) {
> -		ret = -EINVAL;
> -		goto err_unlock;
> -	}
> -
> -	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
> -					    &e->event.base);
> -
> -	if (ret)
> -		goto err_unlock;
> -
> -	seq = drm_vblank_count_and_time(dev, pipe, &now);
> -
> -	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
> -		  vblwait->request.sequence, seq, pipe);
> -
> -	trace_drm_vblank_event_queued(file_priv, pipe,
> -				      vblwait->request.sequence);
> -
> -	e->event.sequence = vblwait->request.sequence;
> -	if (vblank_passed(seq, vblwait->request.sequence)) {
> -		drm_vblank_put(dev, pipe);
> -		send_vblank_event(dev, e, seq, &now);
> -		vblwait->reply.sequence = seq;
> -	} else {
> -		/* drm_handle_vblank_events will call drm_vblank_put */
> -		list_add_tail(&e->base.link, &dev->vblank_event_list);
> -		vblwait->reply.sequence = vblwait->request.sequence;
> -	}
> -
> -	spin_unlock_irqrestore(&dev->event_lock, flags);
> -
> -	return 0;
> -
> -err_unlock:
> -	spin_unlock_irqrestore(&dev->event_lock, flags);
> -	kfree(e);
> -err_put:
> -	drm_vblank_put(dev, pipe);
> -	return ret;
> -}
> -
> -static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
> -{
> -	if (vblwait->request.sequence)
> -		return false;
> -
> -	return _DRM_VBLANK_RELATIVE ==
> -		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
> -					  _DRM_VBLANK_EVENT |
> -					  _DRM_VBLANK_NEXTONMISS));
> -}
> -
> -/*
> - * Wait for VBLANK.
> - *
> - * \param inode device inode.
> - * \param file_priv DRM file private.
> - * \param cmd command.
> - * \param data user argument, pointing to a drm_wait_vblank structure.
> - * \return zero on success or a negative number on failure.
> - *
> - * This function enables the vblank interrupt on the pipe requested, then
> - * sleeps waiting for the requested sequence number to occur, and drops
> - * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
> - * after a timeout with no further vblank waits scheduled).
> - */
> -int drm_wait_vblank(struct drm_device *dev, void *data,
> -		    struct drm_file *file_priv)
> -{
> -	struct drm_vblank_crtc *vblank;
> -	union drm_wait_vblank *vblwait = data;
> -	int ret;
> -	unsigned int flags, seq, pipe, high_pipe;
> -
> -	if (!dev->irq_enabled)
> -		return -EINVAL;
> -
> -	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
> -		return -EINVAL;
> -
> -	if (vblwait->request.type &
> -	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> -	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
> -		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
> -			  vblwait->request.type,
> -			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> -			   _DRM_VBLANK_HIGH_CRTC_MASK));
> -		return -EINVAL;
> -	}
> -
> -	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
> -	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
> -	if (high_pipe)
> -		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
> -	else
> -		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
> -	if (pipe >= dev->num_crtcs)
> -		return -EINVAL;
> -
> -	vblank = &dev->vblank[pipe];
> -
> -	/* If the counter is currently enabled and accurate, short-circuit
> -	 * queries to return the cached timestamp of the last vblank.
> -	 */
> -	if (dev->vblank_disable_immediate &&
> -	    drm_wait_vblank_is_query(vblwait) &&
> -	    READ_ONCE(vblank->enabled)) {
> -		struct timeval now;
> -
> -		vblwait->reply.sequence =
> -			drm_vblank_count_and_time(dev, pipe, &now);
> -		vblwait->reply.tval_sec = now.tv_sec;
> -		vblwait->reply.tval_usec = now.tv_usec;
> -		return 0;
> -	}
> -
> -	ret = drm_vblank_get(dev, pipe);
> -	if (ret) {
> -		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
> -		return ret;
> -	}
> -	seq = drm_vblank_count(dev, pipe);
> -
> -	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
> -	case _DRM_VBLANK_RELATIVE:
> -		vblwait->request.sequence += seq;
> -		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
> -	case _DRM_VBLANK_ABSOLUTE:
> -		break;
> -	default:
> -		ret = -EINVAL;
> -		goto done;
> -	}
> -
> -	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
> -	    vblank_passed(seq, vblwait->request.sequence))
> -		vblwait->request.sequence = seq + 1;
> -
> -	if (flags & _DRM_VBLANK_EVENT) {
> -		/* must hold on to the vblank ref until the event fires
> -		 * drm_vblank_put will be called asynchronously
> -		 */
> -		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
> -	}
> -
> -	if (vblwait->request.sequence != seq) {
> -		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
> -			  vblwait->request.sequence, pipe);
> -		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
> -			    vblank_passed(drm_vblank_count(dev, pipe),
> -					  vblwait->request.sequence) ||
> -			    !READ_ONCE(vblank->enabled));
> -	}
> -
> -	if (ret != -EINTR) {
> -		struct timeval now;
> -
> -		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
> -		vblwait->reply.tval_sec = now.tv_sec;
> -		vblwait->reply.tval_usec = now.tv_usec;
> -
> -		DRM_DEBUG("crtc %d returning %u to client\n",
> -			  pipe, vblwait->reply.sequence);
> -	} else {
> -		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
> -	}
> -
> -done:
> -	drm_vblank_put(dev, pipe);
> -	return ret;
> -}
> -
> -static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_pending_vblank_event *e, *t;
> -	struct timeval now;
> -	unsigned int seq;
> -
> -	assert_spin_locked(&dev->event_lock);
> -
> -	seq = drm_vblank_count_and_time(dev, pipe, &now);
> -
> -	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> -		if (e->pipe != pipe)
> -			continue;
> -		if (!vblank_passed(seq, e->event.sequence))
> -			continue;
> -
> -		DRM_DEBUG("vblank event on %u, current %u\n",
> -			  e->event.sequence, seq);
> -
> -		list_del(&e->base.link);
> -		drm_vblank_put(dev, pipe);
> -		send_vblank_event(dev, e, seq, &now);
> -	}
> -
> -	trace_drm_vblank_event(pipe, seq);
> -}
> -
> -/**
> - * drm_handle_vblank - handle a vblank event
> - * @dev: DRM device
> - * @pipe: index of CRTC where this event occurred
> - *
> - * Drivers should call this routine in their vblank interrupt handlers to
> - * update the vblank counter and send any signals that may be pending.
> - *
> - * This is the legacy version of drm_crtc_handle_vblank().
> - */
> -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
> -{
> -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> -	unsigned long irqflags;
> -	bool disable_irq;
> -
> -	if (WARN_ON_ONCE(!dev->num_crtcs))
> -		return false;
> -
> -	if (WARN_ON(pipe >= dev->num_crtcs))
> -		return false;
> -
> -	spin_lock_irqsave(&dev->event_lock, irqflags);
> -
> -	/* Need timestamp lock to prevent concurrent execution with
> -	 * vblank enable/disable, as this would cause inconsistent
> -	 * or corrupted timestamps and vblank counts.
> -	 */
> -	spin_lock(&dev->vblank_time_lock);
> -
> -	/* Vblank irq handling disabled. Nothing to do. */
> -	if (!vblank->enabled) {
> -		spin_unlock(&dev->vblank_time_lock);
> -		spin_unlock_irqrestore(&dev->event_lock, irqflags);
> -		return false;
> -	}
> -
> -	drm_update_vblank_count(dev, pipe, true);
> -
> -	spin_unlock(&dev->vblank_time_lock);
> -
> -	wake_up(&vblank->queue);
> -
> -	/* With instant-off, we defer disabling the interrupt until after
> -	 * we finish processing the following vblank after all events have
> -	 * been signaled. The disable has to be last (after
> -	 * drm_handle_vblank_events) so that the timestamp is always accurate.
> -	 */
> -	disable_irq = (dev->vblank_disable_immediate &&
> -		       drm_vblank_offdelay > 0 &&
> -		       !atomic_read(&vblank->refcount));
> -
> -	drm_handle_vblank_events(dev, pipe);
> -
> -	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> -
> -	if (disable_irq)
> -		vblank_disable_fn((unsigned long)vblank);
> -
> -	return true;
> -}
> -EXPORT_SYMBOL(drm_handle_vblank);
> -
> -/**
> - * drm_crtc_handle_vblank - handle a vblank event
> - * @crtc: where this event occurred
> - *
> - * Drivers should call this routine in their vblank interrupt handlers to
> - * update the vblank counter and send any signals that may be pending.
> - *
> - * This is the native KMS version of drm_handle_vblank().
> - *
> - * Returns:
> - * True if the event was successfully handled, false on failure.
> - */
> -bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
> -{
> -	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
> -}
> -EXPORT_SYMBOL(drm_crtc_handle_vblank);
> diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
> new file mode 100644
> index 000000000000..630dc26379b7
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_vblank.c
> @@ -0,0 +1,1645 @@
> +/*
> + * drm_irq.c IRQ and vblank support
> + *
> + * \author Rickard E. (Rik) Faith <faith@valinux.com>
> + * \author Gareth Hughes <gareth@valinux.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <drm/drm_vblank.h>
> +#include <drm/drmP.h>
> +#include <linux/export.h>
> +
> +#include "drm_trace.h"
> +#include "drm_internal.h"
> +
> +/* Retry timestamp calculation up to 3 times to satisfy
> + * drm_timestamp_precision before giving up.
> + */
> +#define DRM_TIMESTAMP_MAXRETRIES 3
> +
> +/* Threshold in nanoseconds for detection of redundant
> + * vblank irq in drm_handle_vblank(). 1 msec should be ok.
> + */
> +#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
> +
> +static bool
> +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> +			  struct timeval *tvblank, bool in_vblank_irq);
> +
> +static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
> +
> +/*
> + * Default to use monotonic timestamps for wait-for-vblank and page-flip
> + * complete events.
> + */
> +unsigned int drm_timestamp_monotonic = 1;
> +
> +static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
> +
> +module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
> +module_param_named(timestamp_precision_usec, drm_timestamp_precision,
> int, 0600);
> +module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
> +MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable
> [msecs] (0: never disable, <0: disable immediately)");
> +MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
> +MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
> +
> +static void store_vblank(struct drm_device *dev, unsigned int pipe,
> +			 u32 vblank_count_inc,
> +			 struct timeval *t_vblank, u32 last)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +
> +	assert_spin_locked(&dev->vblank_time_lock);
> +
> +	vblank->last = last;
> +
> +	write_seqlock(&vblank->seqlock);
> +	vblank->time = *t_vblank;
> +	vblank->count += vblank_count_inc;
> +	write_sequnlock(&vblank->seqlock);
> +}
> +
> +/*
> + * "No hw counter" fallback implementation of .get_vblank_counter() hook,
> + * if there is no useable hardware frame counter available.
> + */
> +static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
> +{
> +	WARN_ON_ONCE(dev->max_vblank_count != 0);
> +	return 0;
> +}
> +
> +static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
> +{
> +	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> +		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> +
> +		if (crtc->funcs->get_vblank_counter)
> +			return crtc->funcs->get_vblank_counter(crtc);
> +	}
> +
> +	if (dev->driver->get_vblank_counter)
> +		return dev->driver->get_vblank_counter(dev, pipe);
> +
> +	return drm_vblank_no_hw_counter(dev, pipe);
> +}
> +
> +/*
> + * Reset the stored timestamp for the current vblank count to correspond
> + * to the last vblank occurred.
> + *
> + * Only to be called from drm_crtc_vblank_on().
> + *
> + * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> + * device vblank fields.
> + */
> +static void drm_reset_vblank_timestamp(struct drm_device *dev,
> unsigned int pipe)
> +{
> +	u32 cur_vblank;
> +	bool rc;
> +	struct timeval t_vblank;
> +	int count = DRM_TIMESTAMP_MAXRETRIES;
> +
> +	spin_lock(&dev->vblank_time_lock);
> +
> +	/*
> +	 * sample the current counter to avoid random jumps
> +	 * when drm_vblank_enable() applies the diff
> +	 */
> +	do {
> +		cur_vblank = __get_vblank_counter(dev, pipe);
> +		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
> +	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> +
> +	/*
> +	 * Only reinitialize corresponding vblank timestamp if high-precision query
> +	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
> +	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
> +	 */
> +	if (!rc)
> +		t_vblank = (struct timeval) {0, 0};
> +
> +	/*
> +	 * +1 to make sure user will never see the same
> +	 * vblank counter value before and after a modeset
> +	 */
> +	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
> +
> +	spin_unlock(&dev->vblank_time_lock);
> +}
> +
> +/*
> + * Call back into the driver to update the appropriate vblank counter
> + * (specified by @pipe).  Deal with wraparound, if it occurred, and
> + * update the last read value so we can deal with wraparound on the next
> + * call if necessary.
> + *
> + * Only necessary when going from off->on, to account for frames we
> + * didn't get an interrupt for.
> + *
> + * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> + * device vblank fields.
> + */
> +static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
> +				    bool in_vblank_irq)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	u32 cur_vblank, diff;
> +	bool rc;
> +	struct timeval t_vblank;
> +	int count = DRM_TIMESTAMP_MAXRETRIES;
> +	int framedur_ns = vblank->framedur_ns;
> +
> +	/*
> +	 * Interrupts were disabled prior to this call, so deal with counter
> +	 * wrap if needed.
> +	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
> +	 * here if the register is small or we had vblank interrupts off for
> +	 * a long time.
> +	 *
> +	 * We repeat the hardware vblank counter & timestamp query until
> +	 * we get consistent results. This to prevent races between gpu
> +	 * updating its hardware counter while we are retrieving the
> +	 * corresponding vblank timestamp.
> +	 */
> +	do {
> +		cur_vblank = __get_vblank_counter(dev, pipe);
> +		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
> +	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> +
> +	if (dev->max_vblank_count != 0) {
> +		/* trust the hw counter when it's around */
> +		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
> +	} else if (rc && framedur_ns) {
> +		const struct timeval *t_old;
> +		u64 diff_ns;
> +
> +		t_old = &vblank->time;
> +		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
> +
> +		/*
> +		 * Figure out how many vblanks we've missed based
> +		 * on the difference in the timestamps and the
> +		 * frame/field duration.
> +		 */
> +		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
> +
> +		if (diff == 0 && in_vblank_irq)
> +			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
> +				      " diff_ns = %lld, framedur_ns = %d)\n",
> +				      pipe, (long long) diff_ns, framedur_ns);
> +	} else {
> +		/* some kind of default for drivers w/o accurate vbl timestamping */
> +		diff = in_vblank_irq ? 1 : 0;
> +	}
> +
> +	/*
> +	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
> +	 * interval? If so then vblank irqs keep running and it will likely
> +	 * happen that the hardware vblank counter is not trustworthy as it
> +	 * might reset at some point in that interval and vblank timestamps
> +	 * are not trustworthy either in that interval. Iow. this can result
> +	 * in a bogus diff >> 1 which must be avoided as it would cause
> +	 * random large forward jumps of the software vblank counter.
> +	 */
> +	if (diff > 1 && (vblank->inmodeset & 0x2)) {
> +		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
> +			      " due to pre-modeset.\n", pipe, diff);
> +		diff = 1;
> +	}
> +
> +	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
> +		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
> +		      pipe, vblank->count, diff, cur_vblank, vblank->last);
> +
> +	if (diff == 0) {
> +		WARN_ON_ONCE(cur_vblank != vblank->last);
> +		return;
> +	}
> +
> +	/*
> +	 * Only reinitialize corresponding vblank timestamp if high-precision query
> +	 * available and didn't fail, or we were called from the vblank interrupt.
> +	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
> +	 * for now, to mark the vblanktimestamp as invalid.
> +	 */
> +	if (!rc && in_vblank_irq)
> +		t_vblank = (struct timeval) {0, 0};
> +
> +	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
> +}
> +
> +static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return 0;
> +
> +	return vblank->count;
> +}
> +
> +/**
> + * drm_accurate_vblank_count - retrieve the master vblank counter
> + * @crtc: which counter to retrieve
> + *
> + * This function is similar to @drm_crtc_vblank_count but this
> + * function interpolates to handle a race with vblank irq's.
> + *
> + * This is mostly useful for hardware that can obtain the scanout
> + * position, but doesn't have a frame counter.
> + */
> +u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned int pipe = drm_crtc_index(crtc);
> +	u32 vblank;
> +	unsigned long flags;
> +
> +	WARN(!dev->driver->get_vblank_timestamp,
> +	     "This function requires support for accurate vblank timestamps.");
> +
> +	spin_lock_irqsave(&dev->vblank_time_lock, flags);
> +
> +	drm_update_vblank_count(dev, pipe, false);
> +	vblank = drm_vblank_count(dev, pipe);
> +
> +	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
> +
> +	return vblank;
> +}
> +EXPORT_SYMBOL(drm_accurate_vblank_count);
> +
> +static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
> +{
> +	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> +		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> +
> +		if (crtc->funcs->disable_vblank) {
> +			crtc->funcs->disable_vblank(crtc);
> +			return;
> +		}
> +	}
> +
> +	dev->driver->disable_vblank(dev, pipe);
> +}
> +
> +/*
> + * Disable vblank irq's on crtc, make sure that last vblank count
> + * of hardware and corresponding consistent software vblank counter
> + * are preserved, even if there are any spurious vblank irq's after
> + * disable.
> + */
> +void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	unsigned long irqflags;
> +
> +	assert_spin_locked(&dev->vbl_lock);
> +
> +	/* Prevent vblank irq processing while disabling vblank irqs,
> +	 * so no updates of timestamps or count can happen after we've
> +	 * disabled. Needed to prevent races in case of delayed irq's.
> +	 */
> +	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
> +
> +	/*
> +	 * Only disable vblank interrupts if they're enabled. This avoids
> +	 * calling the ->disable_vblank() operation in atomic context with the
> +	 * hardware potentially runtime suspended.
> +	 */
> +	if (vblank->enabled) {
> +		__disable_vblank(dev, pipe);
> +		vblank->enabled = false;
> +	}
> +
> +	/*
> +	 * Always update the count and timestamp to maintain the
> +	 * appearance that the counter has been ticking all along until
> +	 * this time. This makes the count account for the entire time
> +	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
> +	 */
> +	drm_update_vblank_count(dev, pipe, false);
> +
> +	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
> +}
> +
> +static void vblank_disable_fn(unsigned long arg)
> +{
> +	struct drm_vblank_crtc *vblank = (void *)arg;
> +	struct drm_device *dev = vblank->dev;
> +	unsigned int pipe = vblank->pipe;
> +	unsigned long irqflags;
> +
> +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> +	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
> +		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
> +		drm_vblank_disable_and_save(dev, pipe);
> +	}
> +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> +}
> +
> +/**
> + * drm_vblank_cleanup - cleanup vblank support
> + * @dev: DRM device
> + *
> + * This function cleans up any resources allocated in drm_vblank_init.
> + */
> +void drm_vblank_cleanup(struct drm_device *dev)
> +{
> +	unsigned int pipe;
> +
> +	/* Bail if the driver didn't call drm_vblank_init() */
> +	if (dev->num_crtcs == 0)
> +		return;
> +
> +	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
> +		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +
> +		WARN_ON(READ_ONCE(vblank->enabled) &&
> +			drm_core_check_feature(dev, DRIVER_MODESET));
> +
> +		del_timer_sync(&vblank->disable_timer);
> +	}
> +
> +	kfree(dev->vblank);
> +
> +	dev->num_crtcs = 0;
> +}
> +EXPORT_SYMBOL(drm_vblank_cleanup);
> +
> +/**
> + * drm_vblank_init - initialize vblank support
> + * @dev: DRM device
> + * @num_crtcs: number of CRTCs supported by @dev
> + *
> + * This function initializes vblank support for @num_crtcs display pipelines.
> + *
> + * Returns:
> + * Zero on success or a negative error code on failure.
> + */
> +int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
> +{
> +	int ret = -ENOMEM;
> +	unsigned int i;
> +
> +	spin_lock_init(&dev->vbl_lock);
> +	spin_lock_init(&dev->vblank_time_lock);
> +
> +	dev->num_crtcs = num_crtcs;
> +
> +	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
> +	if (!dev->vblank)
> +		goto err;
> +
> +	for (i = 0; i < num_crtcs; i++) {
> +		struct drm_vblank_crtc *vblank = &dev->vblank[i];
> +
> +		vblank->dev = dev;
> +		vblank->pipe = i;
> +		init_waitqueue_head(&vblank->queue);
> +		setup_timer(&vblank->disable_timer, vblank_disable_fn,
> +			    (unsigned long)vblank);
> +		seqlock_init(&vblank->seqlock);
> +	}
> +
> +	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
> +
> +	/* Driver specific high-precision vblank timestamping supported? */
> +	if (dev->driver->get_vblank_timestamp)
> +		DRM_INFO("Driver supports precise vblank timestamp query.\n");
> +	else
> +		DRM_INFO("No driver support for vblank timestamp query.\n");
> +
> +	/* Must have precise timestamping for reliable vblank instant disable */
> +	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
> +		dev->vblank_disable_immediate = false;
> +		DRM_INFO("Setting vblank_disable_immediate to false because "
> +			 "get_vblank_timestamp == NULL\n");
> +	}
> +
> +	return 0;
> +
> +err:
> +	dev->num_crtcs = 0;
> +	return ret;
> +}
> +EXPORT_SYMBOL(drm_vblank_init);
> +
> +/**
> + * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
> + * @crtc: which CRTC's vblank waitqueue to retrieve
> + *
> + * This function returns a pointer to the vblank waitqueue for the CRTC.
> + * Drivers can use this to implement vblank waits using wait_event()
> and related
> + * functions.
> + */
> +wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
> +{
> +	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
> +
> +
> +/**
> + * drm_calc_timestamping_constants - calculate vblank timestamp constants
> + * @crtc: drm_crtc whose timestamp constants should be updated.
> + * @mode: display mode containing the scanout timings
> + *
> + * Calculate and store various constants which are later
> + * needed by vblank and swap-completion timestamping, e.g,
> + * by drm_calc_vbltimestamp_from_scanoutpos(). They are
> + * derived from CRTC's true scanout timing, so they take
> + * things like panel scaling or other adjustments into account.
> + */
> +void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> +				     const struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned int pipe = drm_crtc_index(crtc);
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	int linedur_ns = 0, framedur_ns = 0;
> +	int dotclock = mode->crtc_clock;
> +
> +	if (!dev->num_crtcs)
> +		return;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	/* Valid dotclock? */
> +	if (dotclock > 0) {
> +		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
> +
> +		/*
> +		 * Convert scanline length in pixels and video
> +		 * dot clock to line duration and frame duration
> +		 * in nanoseconds:
> +		 */
> +		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
> +		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
> +
> +		/*
> +		 * Fields of interlaced scanout modes are only half a frame duration.
> +		 */
> +		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> +			framedur_ns /= 2;
> +	} else
> +		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
> +			  crtc->base.id);
> +
> +	vblank->linedur_ns  = linedur_ns;
> +	vblank->framedur_ns = framedur_ns;
> +	vblank->hwmode = *mode;
> +
> +	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
> +		  crtc->base.id, mode->crtc_htotal,
> +		  mode->crtc_vtotal, mode->crtc_vdisplay);
> +	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
> +		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
> +}
> +EXPORT_SYMBOL(drm_calc_timestamping_constants);
> +
> +/**
> + * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
> + * @dev: DRM device
> + * @pipe: index of CRTC whose vblank timestamp to retrieve
> + * @max_error: Desired maximum allowable error in timestamps (nanosecs)
> + *             On return contains true maximum error of timestamp
> + * @vblank_time: Pointer to struct timeval which should receive the timestamp
> + * @in_vblank_irq:
> + *     True when called from drm_crtc_handle_vblank().  Some drivers
> + *     need to apply some workarounds for gpu-specific vblank irq quirks
> + *     if flag is set.
> + *
> + * Implements calculation of exact vblank timestamps from given
> drm_display_mode
> + * timings and current video scanout position of a CRTC. This can be
> called from
> + * within get_vblank_timestamp() implementation of a kms driver to
> implement the
> + * actual timestamping.
> + *
> + * Should return timestamps conforming to the OML_sync_control OpenML
> + * extension specification. The timestamp corresponds to the end of
> + * the vblank interval, aka start of scanout of topmost-leftmost display
> + * pixel in the following video frame.
> + *
> + * Requires support for optional dev->driver->get_scanout_position()
> + * in kms driver, plus a bit of setup code to provide a drm_display_mode
> + * that corresponds to the true scanout timing.
> + *
> + * The current implementation only handles standard video modes. It
> + * returns as no operation if a doublescan or interlaced video mode is
> + * active. Higher level code is expected to handle this.
> + *
> + * This function can be used to implement the &drm_driver.get_vblank_timestamp
> + * directly, if the driver implements the
> &drm_driver.get_scanout_position hook.
> + *
> + * Note that atomic drivers must call drm_calc_timestamping_constants() before
> + * enabling a CRTC. The atomic helpers already take care of that in
> + * drm_atomic_helper_update_legacy_modeset_state().
> + *
> + * Returns:
> + *
> + * Returns true on success, and false on failure, i.e. when no accurate
> + * timestamp could be acquired.
> + */
> +bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> +					   unsigned int pipe,
> +					   int *max_error,
> +					   struct timeval *vblank_time,
> +					   bool in_vblank_irq)
> +{
> +	struct timeval tv_etime;
> +	ktime_t stime, etime;
> +	bool vbl_status;
> +	struct drm_crtc *crtc;
> +	const struct drm_display_mode *mode;
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	int vpos, hpos, i;
> +	int delta_ns, duration_ns;
> +
> +	if (!drm_core_check_feature(dev, DRIVER_MODESET))
> +		return false;
> +
> +	crtc = drm_crtc_from_index(dev, pipe);
> +
> +	if (pipe >= dev->num_crtcs || !crtc) {
> +		DRM_ERROR("Invalid crtc %u\n", pipe);
> +		return false;
> +	}
> +
> +	/* Scanout position query not supported? Should not happen. */
> +	if (!dev->driver->get_scanout_position) {
> +		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
> +		return false;
> +	}
> +
> +	if (drm_drv_uses_atomic_modeset(dev))
> +		mode = &vblank->hwmode;
> +	else
> +		mode = &crtc->hwmode;
> +
> +	/* If mode timing undefined, just return as no-op:
> +	 * Happens during initial modesetting of a crtc.
> +	 */
> +	if (mode->crtc_clock == 0) {
> +		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
> +		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
> +
> +		return false;
> +	}
> +
> +	/* Get current scanout position with system timestamp.
> +	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
> +	 * if single query takes longer than max_error nanoseconds.
> +	 *
> +	 * This guarantees a tight bound on maximum error if
> +	 * code gets preempted or delayed for some reason.
> +	 */
> +	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
> +		/*
> +		 * Get vertical and horizontal scanout position vpos, hpos,
> +		 * and bounding timestamps stime, etime, pre/post query.
> +		 */
> +		vbl_status = dev->driver->get_scanout_position(dev, pipe,
> +							       in_vblank_irq,
> +							       &vpos, &hpos,
> +							       &stime, &etime,
> +							       mode);
> +
> +		/* Return as no-op if scanout query unsupported or failed. */
> +		if (!vbl_status) {
> +			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
> +				  pipe);
> +			return false;
> +		}
> +
> +		/* Compute uncertainty in timestamp of scanout position query. */
> +		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
> +
> +		/* Accept result with <  max_error nsecs timing uncertainty. */
> +		if (duration_ns <= *max_error)
> +			break;
> +	}
> +
> +	/* Noisy system timing? */
> +	if (i == DRM_TIMESTAMP_MAXRETRIES) {
> +		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
> +			  pipe, duration_ns/1000, *max_error/1000, i);
> +	}
> +
> +	/* Return upper bound of timestamp precision error. */
> +	*max_error = duration_ns;
> +
> +	/* Convert scanout position into elapsed time at raw_time query
> +	 * since start of scanout at first display scanline. delta_ns
> +	 * can be negative if start of scanout hasn't happened yet.
> +	 */
> +	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
> +			   mode->crtc_clock);
> +
> +	if (!drm_timestamp_monotonic)
> +		etime = ktime_mono_to_real(etime);
> +
> +	/* save this only for debugging purposes */
> +	tv_etime = ktime_to_timeval(etime);
> +	/* Subtract time delta from raw timestamp to get final
> +	 * vblank_time timestamp for end of vblank.
> +	 */
> +	etime = ktime_sub_ns(etime, delta_ns);
> +	*vblank_time = ktime_to_timeval(etime);
> +
> +	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
> +		      pipe, hpos, vpos,
> +		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
> +		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
> +		      duration_ns/1000, i);
> +
> +	return true;
> +}
> +EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
> +
> +static struct timeval get_drm_timestamp(void)
> +{
> +	ktime_t now;
> +
> +	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
> +	return ktime_to_timeval(now);
> +}
> +
> +/**
> + * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
> + *                             vblank interval
> + * @dev: DRM device
> + * @pipe: index of CRTC whose vblank timestamp to retrieve
> + * @tvblank: Pointer to target struct timeval which should receive
> the timestamp
> + * @in_vblank_irq:
> + *     True when called from drm_crtc_handle_vblank().  Some drivers
> + *     need to apply some workarounds for gpu-specific vblank irq quirks
> + *     if flag is set.
> + *
> + * Fetches the system timestamp corresponding to the time of the most recent
> + * vblank interval on specified CRTC. May call into kms-driver to
> + * compute the timestamp with a high-precision GPU specific method.
> + *
> + * Returns zero if timestamp originates from uncorrected do_gettimeofday()
> + * call, i.e., it isn't very precisely locked to the true vblank.
> + *
> + * Returns:
> + * True if timestamp is considered to be very precise, false otherwise.
> + */
> +static bool
> +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> +			  struct timeval *tvblank, bool in_vblank_irq)
> +{
> +	bool ret = false;
> +
> +	/* Define requested maximum error on timestamps (nanoseconds). */
> +	int max_error = (int) drm_timestamp_precision * 1000;
> +
> +	/* Query driver if possible and precision timestamping enabled. */
> +	if (dev->driver->get_vblank_timestamp && (max_error > 0))
> +		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
> +							tvblank, in_vblank_irq);
> +
> +	/* GPU high precision timestamp query unsupported or failed.
> +	 * Return current monotonic/gettimeofday timestamp as best estimate.
> +	 */
> +	if (!ret)
> +		*tvblank = get_drm_timestamp();
> +
> +	return ret;
> +}
> +
> +/**
> + * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
> + * @crtc: which counter to retrieve
> + *
> + * Fetches the "cooked" vblank count value that represents the number of
> + * vblank events since the system was booted, including lost events due to
> + * modesetting activity.
> + *
> + * Returns:
> + * The software vblank counter.
> + */
> +u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
> +{
> +	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_count);
> +
> +/**
> + * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
> + *     system timestamp corresponding to that vblank counter value.
> + * @dev: DRM device
> + * @pipe: index of CRTC whose counter to retrieve
> + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> + *
> + * Fetches the "cooked" vblank count value that represents the number of
> + * vblank events since the system was booted, including lost events due to
> + * modesetting activity. Returns corresponding system timestamp of the time
> + * of the vblank interval that corresponds to the current vblank counter value.
> + *
> + * This is the legacy version of drm_crtc_vblank_count_and_time().
> + */
> +static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
> +				     struct timeval *vblanktime)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	u32 vblank_count;
> +	unsigned int seq;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs)) {
> +		*vblanktime = (struct timeval) { 0 };
> +		return 0;
> +	}
> +
> +	do {
> +		seq = read_seqbegin(&vblank->seqlock);
> +		vblank_count = vblank->count;
> +		*vblanktime = vblank->time;
> +	} while (read_seqretry(&vblank->seqlock, seq));
> +
> +	return vblank_count;
> +}
> +
> +/**
> + * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
> + *     and the system timestamp corresponding to that vblank counter value
> + * @crtc: which counter to retrieve
> + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> + *
> + * Fetches the "cooked" vblank count value that represents the number of
> + * vblank events since the system was booted, including lost events due to
> + * modesetting activity. Returns corresponding system timestamp of the time
> + * of the vblank interval that corresponds to the current vblank counter value.
> + */
> +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> +				   struct timeval *vblanktime)
> +{
> +	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
> +					 vblanktime);
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
> +
> +static void send_vblank_event(struct drm_device *dev,
> +		struct drm_pending_vblank_event *e,
> +		unsigned long seq, struct timeval *now)
> +{
> +	e->event.sequence = seq;
> +	e->event.tv_sec = now->tv_sec;
> +	e->event.tv_usec = now->tv_usec;
> +
> +	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
> +					 e->event.sequence);
> +
> +	drm_send_event_locked(dev, &e->base);
> +}
> +
> +/**
> + * drm_crtc_arm_vblank_event - arm vblank event after pageflip
> + * @crtc: the source CRTC of the vblank event
> + * @e: the event to send
> + *
> + * A lot of drivers need to generate vblank events for the very next vblank
> + * interrupt. For example when the page flip interrupt happens when the page
> + * flip gets armed, but not when it actually executes within the next vblank
> + * period. This helper function implements exactly the required vblank arming
> + * behaviour.
> + *
> + * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
> + * atomic commit must ensure that the next vblank happens at exactly the same
> + * time as the atomic commit is committed to the hardware. This function itself
> + * does **not** protect again the next vblank interrupt racing with either this
> + * function call or the atomic commit operation. A possible sequence could be:
> + *
> + * 1. Driver commits new hardware state into vblank-synchronized registers.
> + * 2. A vblank happens, committing the hardware state. Also the corresponding
> + *    vblank interrupt is fired off and fully processed by the interrupt
> + *    handler.
> + * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
> + * 4. The event is only send out for the next vblank, which is wrong.
> + *
> + * An equivalent race can happen when the driver calls
> + * drm_crtc_arm_vblank_event() before writing out the new hardware state.
> + *
> + * The only way to make this work safely is to prevent the vblank from firing
> + * (and the hardware from committing anything else) until the entire atomic
> + * commit sequence has run to completion. If the hardware does not have such a
> + * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
> + * Instead drivers need to manually send out the event from their interrupt
> + * handler by calling drm_crtc_send_vblank_event() and make sure that
> there's no
> + * possible race with the hardware committing the atomic update.
> + *
> + * Caller must hold event lock. Caller must also hold a vblank reference for
> + * the event @e, which will be dropped when the next vblank arrives.
> + */
> +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> +			       struct drm_pending_vblank_event *e)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned int pipe = drm_crtc_index(crtc);
> +
> +	assert_spin_locked(&dev->event_lock);
> +
> +	e->pipe = pipe;
> +	e->event.sequence = drm_vblank_count(dev, pipe);
> +	e->event.crtc_id = crtc->base.id;
> +	list_add_tail(&e->base.link, &dev->vblank_event_list);
> +}
> +EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
> +
> +/**
> + * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
> + * @crtc: the source CRTC of the vblank event
> + * @e: the event to send
> + *
> + * Updates sequence # and timestamp on event for the most recently processed
> + * vblank, and sends it to userspace.  Caller must hold event lock.
> + *
> + * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
> + * situation, especially to send out events for atomic commit operations.
> + */
> +void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> +				struct drm_pending_vblank_event *e)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned int seq, pipe = drm_crtc_index(crtc);
> +	struct timeval now;
> +
> +	if (dev->num_crtcs > 0) {
> +		seq = drm_vblank_count_and_time(dev, pipe, &now);
> +	} else {
> +		seq = 0;
> +
> +		now = get_drm_timestamp();
> +	}
> +	e->pipe = pipe;
> +	e->event.crtc_id = crtc->base.id;
> +	send_vblank_event(dev, e, seq, &now);
> +}
> +EXPORT_SYMBOL(drm_crtc_send_vblank_event);
> +
> +static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
> +{
> +	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> +		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> +
> +		if (crtc->funcs->enable_vblank)
> +			return crtc->funcs->enable_vblank(crtc);
> +	}
> +
> +	return dev->driver->enable_vblank(dev, pipe);
> +}
> +
> +/**
> + * drm_vblank_enable - enable the vblank interrupt on a CRTC
> + * @dev: DRM device
> + * @pipe: CRTC index
> + *
> + * Returns:
> + * Zero on success or a negative error code on failure.
> + */
> +static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	int ret = 0;
> +
> +	assert_spin_locked(&dev->vbl_lock);
> +
> +	spin_lock(&dev->vblank_time_lock);
> +
> +	if (!vblank->enabled) {
> +		/*
> +		 * Enable vblank irqs under vblank_time_lock protection.
> +		 * All vblank count & timestamp updates are held off
> +		 * until we are done reinitializing master counter and
> +		 * timestamps. Filtercode in drm_handle_vblank() will
> +		 * prevent double-accounting of same vblank interval.
> +		 */
> +		ret = __enable_vblank(dev, pipe);
> +		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
> +		if (ret) {
> +			atomic_dec(&vblank->refcount);
> +		} else {
> +			drm_update_vblank_count(dev, pipe, 0);
> +			/* drm_update_vblank_count() includes a wmb so we just
> +			 * need to ensure that the compiler emits the write
> +			 * to mark the vblank as enabled after the call
> +			 * to drm_update_vblank_count().
> +			 */
> +			WRITE_ONCE(vblank->enabled, true);
> +		}
> +	}
> +
> +	spin_unlock(&dev->vblank_time_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * drm_vblank_get - get a reference count on vblank events
> + * @dev: DRM device
> + * @pipe: index of CRTC to own
> + *
> + * Acquire a reference count on vblank events to avoid having them disabled
> + * while in use.
> + *
> + * This is the legacy version of drm_crtc_vblank_get().
> + *
> + * Returns:
> + * Zero on success or a negative error code on failure.
> + */
> +static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	unsigned long irqflags;
> +	int ret = 0;
> +
> +	if (!dev->num_crtcs)
> +		return -EINVAL;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> +	/* Going from 0->1 means we have to enable interrupts again */
> +	if (atomic_add_return(1, &vblank->refcount) == 1) {
> +		ret = drm_vblank_enable(dev, pipe);
> +	} else {
> +		if (!vblank->enabled) {
> +			atomic_dec(&vblank->refcount);
> +			ret = -EINVAL;
> +		}
> +	}
> +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> +
> +	return ret;
> +}
> +
> +/**
> + * drm_crtc_vblank_get - get a reference count on vblank events
> + * @crtc: which CRTC to own
> + *
> + * Acquire a reference count on vblank events to avoid having them disabled
> + * while in use.
> + *
> + * Returns:
> + * Zero on success or a negative error code on failure.
> + */
> +int drm_crtc_vblank_get(struct drm_crtc *crtc)
> +{
> +	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_get);
> +
> +/**
> + * drm_vblank_put - release ownership of vblank events
> + * @dev: DRM device
> + * @pipe: index of CRTC to release
> + *
> + * Release ownership of a given vblank counter, turning off interrupts
> + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> + *
> + * This is the legacy version of drm_crtc_vblank_put().
> + */
> +static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
> +		return;
> +
> +	/* Last user schedules interrupt disable */
> +	if (atomic_dec_and_test(&vblank->refcount)) {
> +		if (drm_vblank_offdelay == 0)
> +			return;
> +		else if (drm_vblank_offdelay < 0)
> +			vblank_disable_fn((unsigned long)vblank);
> +		else if (!dev->vblank_disable_immediate)
> +			mod_timer(&vblank->disable_timer,
> +				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
> +	}
> +}
> +
> +/**
> + * drm_crtc_vblank_put - give up ownership of vblank events
> + * @crtc: which counter to give up
> + *
> + * Release ownership of a given vblank counter, turning off interrupts
> + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> + */
> +void drm_crtc_vblank_put(struct drm_crtc *crtc)
> +{
> +	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_put);
> +
> +/**
> + * drm_wait_one_vblank - wait for one vblank
> + * @dev: DRM device
> + * @pipe: CRTC index
> + *
> + * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
> + * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
> + * due to lack of driver support or because the crtc is off.
> + */
> +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	int ret;
> +	u32 last;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	ret = drm_vblank_get(dev, pipe);
> +	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
> +		return;
> +
> +	last = drm_vblank_count(dev, pipe);
> +
> +	ret = wait_event_timeout(vblank->queue,
> +				 last != drm_vblank_count(dev, pipe),
> +				 msecs_to_jiffies(100));
> +
> +	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
> +
> +	drm_vblank_put(dev, pipe);
> +}
> +EXPORT_SYMBOL(drm_wait_one_vblank);
> +
> +/**
> + * drm_crtc_wait_one_vblank - wait for one vblank
> + * @crtc: DRM crtc
> + *
> + * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
> + * due to lack of driver support or because the crtc is off.
> + */
> +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> +{
> +	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
> +}
> +EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
> +
> +/**
> + * drm_crtc_vblank_off - disable vblank events on a CRTC
> + * @crtc: CRTC in question
> + *
> + * Drivers can use this function to shut down the vblank interrupt
> handling when
> + * disabling a crtc. This function ensures that the latest vblank
> frame count is
> + * stored so that drm_vblank_on can restore it again.
> + *
> + * Drivers must use this function when the hardware vblank counter can get
> + * reset, e.g. when suspending.
> + */
> +void drm_crtc_vblank_off(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned int pipe = drm_crtc_index(crtc);
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	struct drm_pending_vblank_event *e, *t;
> +	struct timeval now;
> +	unsigned long irqflags;
> +	unsigned int seq;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	spin_lock_irqsave(&dev->event_lock, irqflags);
> +
> +	spin_lock(&dev->vbl_lock);
> +	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> +		      pipe, vblank->enabled, vblank->inmodeset);
> +
> +	/* Avoid redundant vblank disables without previous
> +	 * drm_crtc_vblank_on(). */
> +	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
> +		drm_vblank_disable_and_save(dev, pipe);
> +
> +	wake_up(&vblank->queue);
> +
> +	/*
> +	 * Prevent subsequent drm_vblank_get() from re-enabling
> +	 * the vblank interrupt by bumping the refcount.
> +	 */
> +	if (!vblank->inmodeset) {
> +		atomic_inc(&vblank->refcount);
> +		vblank->inmodeset = 1;
> +	}
> +	spin_unlock(&dev->vbl_lock);
> +
> +	/* Send any queued vblank events, lest the natives grow disquiet */
> +	seq = drm_vblank_count_and_time(dev, pipe, &now);
> +
> +	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> +		if (e->pipe != pipe)
> +			continue;
> +		DRM_DEBUG("Sending premature vblank event on disable: "
> +			  "wanted %u, current %u\n",
> +			  e->event.sequence, seq);
> +		list_del(&e->base.link);
> +		drm_vblank_put(dev, pipe);
> +		send_vblank_event(dev, e, seq, &now);
> +	}
> +	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> +
> +	/* Will be reset by the modeset helpers when re-enabling the crtc by
> +	 * calling drm_calc_timestamping_constants(). */
> +	vblank->hwmode.crtc_clock = 0;
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_off);
> +
> +/**
> + * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
> + * @crtc: CRTC in question
> + *
> + * Drivers can use this function to reset the vblank state to off at load time.
> + * Drivers should use this together with the drm_crtc_vblank_off() and
> + * drm_crtc_vblank_on() functions. The difference compared to
> + * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
> + * and hence doesn't need to call any driver hooks.
> + */
> +void drm_crtc_vblank_reset(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned long irqflags;
> +	unsigned int pipe = drm_crtc_index(crtc);
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +
> +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> +	/*
> +	 * Prevent subsequent drm_vblank_get() from enabling the vblank
> +	 * interrupt by bumping the refcount.
> +	 */
> +	if (!vblank->inmodeset) {
> +		atomic_inc(&vblank->refcount);
> +		vblank->inmodeset = 1;
> +	}
> +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> +
> +	WARN_ON(!list_empty(&dev->vblank_event_list));
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_reset);
> +
> +/**
> + * drm_crtc_vblank_on - enable vblank events on a CRTC
> + * @crtc: CRTC in question
> + *
> + * This functions restores the vblank interrupt state captured with
> + * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
> + * drm_crtc_vblank_off() can be unbalanced and so can also be
> unconditionally called
> + * in driver load code to reflect the current hardware state of the crtc.
> + */
> +void drm_crtc_vblank_on(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	unsigned int pipe = drm_crtc_index(crtc);
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> +	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> +		      pipe, vblank->enabled, vblank->inmodeset);
> +
> +	/* Drop our private "prevent drm_vblank_get" refcount */
> +	if (vblank->inmodeset) {
> +		atomic_dec(&vblank->refcount);
> +		vblank->inmodeset = 0;
> +	}
> +
> +	drm_reset_vblank_timestamp(dev, pipe);
> +
> +	/*
> +	 * re-enable interrupts if there are users left, or the
> +	 * user wishes vblank interrupts to be enabled all the time.
> +	 */
> +	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
> +		WARN_ON(drm_vblank_enable(dev, pipe));
> +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> +}
> +EXPORT_SYMBOL(drm_crtc_vblank_on);
> +
> +static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
> +					  unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +
> +	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> +	if (!dev->num_crtcs)
> +		return;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	/*
> +	 * To avoid all the problems that might happen if interrupts
> +	 * were enabled/disabled around or between these calls, we just
> +	 * have the kernel take a reference on the CRTC (just once though
> +	 * to avoid corrupting the count if multiple, mismatch calls occur),
> +	 * so that interrupts remain enabled in the interim.
> +	 */
> +	if (!vblank->inmodeset) {
> +		vblank->inmodeset = 0x1;
> +		if (drm_vblank_get(dev, pipe) == 0)
> +			vblank->inmodeset |= 0x2;
> +	}
> +}
> +
> +static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
> +					   unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	unsigned long irqflags;
> +
> +	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> +	if (!dev->num_crtcs)
> +		return;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return;
> +
> +	if (vblank->inmodeset) {
> +		spin_lock_irqsave(&dev->vbl_lock, irqflags);
> +		drm_reset_vblank_timestamp(dev, pipe);
> +		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> +
> +		if (vblank->inmodeset & 0x2)
> +			drm_vblank_put(dev, pipe);
> +
> +		vblank->inmodeset = 0;
> +	}
> +}
> +
> +int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
> +			   struct drm_file *file_priv)
> +{
> +	struct drm_modeset_ctl *modeset = data;
> +	unsigned int pipe;
> +
> +	/* If drm_vblank_init() hasn't been called yet, just no-op */
> +	if (!dev->num_crtcs)
> +		return 0;
> +
> +	/* KMS drivers handle this internally */
> +	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
> +		return 0;
> +
> +	pipe = modeset->crtc;
> +	if (pipe >= dev->num_crtcs)
> +		return -EINVAL;
> +
> +	switch (modeset->cmd) {
> +	case _DRM_PRE_MODESET:
> +		drm_legacy_vblank_pre_modeset(dev, pipe);
> +		break;
> +	case _DRM_POST_MODESET:
> +		drm_legacy_vblank_post_modeset(dev, pipe);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline bool vblank_passed(u32 seq, u32 ref)
> +{
> +	return (seq - ref) <= (1 << 23);
> +}
> +
> +static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
> +				  union drm_wait_vblank *vblwait,
> +				  struct drm_file *file_priv)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	struct drm_pending_vblank_event *e;
> +	struct timeval now;
> +	unsigned long flags;
> +	unsigned int seq;
> +	int ret;
> +
> +	e = kzalloc(sizeof(*e), GFP_KERNEL);
> +	if (e == NULL) {
> +		ret = -ENOMEM;
> +		goto err_put;
> +	}
> +
> +	e->pipe = pipe;
> +	e->event.base.type = DRM_EVENT_VBLANK;
> +	e->event.base.length = sizeof(e->event);
> +	e->event.user_data = vblwait->request.signal;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +
> +	/*
> +	 * drm_crtc_vblank_off() might have been called after we called
> +	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
> +	 * vblank disable, so no need for further locking.  The reference from
> +	 * drm_vblank_get() protects against vblank disable from another source.
> +	 */
> +	if (!READ_ONCE(vblank->enabled)) {
> +		ret = -EINVAL;
> +		goto err_unlock;
> +	}
> +
> +	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
> +					    &e->event.base);
> +
> +	if (ret)
> +		goto err_unlock;
> +
> +	seq = drm_vblank_count_and_time(dev, pipe, &now);
> +
> +	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
> +		  vblwait->request.sequence, seq, pipe);
> +
> +	trace_drm_vblank_event_queued(file_priv, pipe,
> +				      vblwait->request.sequence);
> +
> +	e->event.sequence = vblwait->request.sequence;
> +	if (vblank_passed(seq, vblwait->request.sequence)) {
> +		drm_vblank_put(dev, pipe);
> +		send_vblank_event(dev, e, seq, &now);
> +		vblwait->reply.sequence = seq;
> +	} else {
> +		/* drm_handle_vblank_events will call drm_vblank_put */
> +		list_add_tail(&e->base.link, &dev->vblank_event_list);
> +		vblwait->reply.sequence = vblwait->request.sequence;
> +	}
> +
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +	return 0;
> +
> +err_unlock:
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +	kfree(e);
> +err_put:
> +	drm_vblank_put(dev, pipe);
> +	return ret;
> +}
> +
> +static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
> +{
> +	if (vblwait->request.sequence)
> +		return false;
> +
> +	return _DRM_VBLANK_RELATIVE ==
> +		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
> +					  _DRM_VBLANK_EVENT |
> +					  _DRM_VBLANK_NEXTONMISS));
> +}
> +
> +/*
> + * Wait for VBLANK.
> + *
> + * \param inode device inode.
> + * \param file_priv DRM file private.
> + * \param cmd command.
> + * \param data user argument, pointing to a drm_wait_vblank structure.
> + * \return zero on success or a negative number on failure.
> + *
> + * This function enables the vblank interrupt on the pipe requested, then
> + * sleeps waiting for the requested sequence number to occur, and drops
> + * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
> + * after a timeout with no further vblank waits scheduled).
> + */
> +int drm_wait_vblank(struct drm_device *dev, void *data,
> +		    struct drm_file *file_priv)
> +{
> +	struct drm_vblank_crtc *vblank;
> +	union drm_wait_vblank *vblwait = data;
> +	int ret;
> +	unsigned int flags, seq, pipe, high_pipe;
> +
> +	if (!dev->irq_enabled)
> +		return -EINVAL;
> +
> +	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
> +		return -EINVAL;
> +
> +	if (vblwait->request.type &
> +	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> +	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
> +		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
> +			  vblwait->request.type,
> +			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> +			   _DRM_VBLANK_HIGH_CRTC_MASK));
> +		return -EINVAL;
> +	}
> +
> +	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
> +	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
> +	if (high_pipe)
> +		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
> +	else
> +		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
> +	if (pipe >= dev->num_crtcs)
> +		return -EINVAL;
> +
> +	vblank = &dev->vblank[pipe];
> +
> +	/* If the counter is currently enabled and accurate, short-circuit
> +	 * queries to return the cached timestamp of the last vblank.
> +	 */
> +	if (dev->vblank_disable_immediate &&
> +	    drm_wait_vblank_is_query(vblwait) &&
> +	    READ_ONCE(vblank->enabled)) {
> +		struct timeval now;
> +
> +		vblwait->reply.sequence =
> +			drm_vblank_count_and_time(dev, pipe, &now);
> +		vblwait->reply.tval_sec = now.tv_sec;
> +		vblwait->reply.tval_usec = now.tv_usec;
> +		return 0;
> +	}
> +
> +	ret = drm_vblank_get(dev, pipe);
> +	if (ret) {
> +		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
> +		return ret;
> +	}
> +	seq = drm_vblank_count(dev, pipe);
> +
> +	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
> +	case _DRM_VBLANK_RELATIVE:
> +		vblwait->request.sequence += seq;
> +		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
> +	case _DRM_VBLANK_ABSOLUTE:
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
> +	    vblank_passed(seq, vblwait->request.sequence))
> +		vblwait->request.sequence = seq + 1;
> +
> +	if (flags & _DRM_VBLANK_EVENT) {
> +		/* must hold on to the vblank ref until the event fires
> +		 * drm_vblank_put will be called asynchronously
> +		 */
> +		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
> +	}
> +
> +	if (vblwait->request.sequence != seq) {
> +		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
> +			  vblwait->request.sequence, pipe);
> +		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
> +			    vblank_passed(drm_vblank_count(dev, pipe),
> +					  vblwait->request.sequence) ||
> +			    !READ_ONCE(vblank->enabled));
> +	}
> +
> +	if (ret != -EINTR) {
> +		struct timeval now;
> +
> +		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
> +		vblwait->reply.tval_sec = now.tv_sec;
> +		vblwait->reply.tval_usec = now.tv_usec;
> +
> +		DRM_DEBUG("crtc %d returning %u to client\n",
> +			  pipe, vblwait->reply.sequence);
> +	} else {
> +		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
> +	}
> +
> +done:
> +	drm_vblank_put(dev, pipe);
> +	return ret;
> +}
> +
> +static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_pending_vblank_event *e, *t;
> +	struct timeval now;
> +	unsigned int seq;
> +
> +	assert_spin_locked(&dev->event_lock);
> +
> +	seq = drm_vblank_count_and_time(dev, pipe, &now);
> +
> +	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> +		if (e->pipe != pipe)
> +			continue;
> +		if (!vblank_passed(seq, e->event.sequence))
> +			continue;
> +
> +		DRM_DEBUG("vblank event on %u, current %u\n",
> +			  e->event.sequence, seq);
> +
> +		list_del(&e->base.link);
> +		drm_vblank_put(dev, pipe);
> +		send_vblank_event(dev, e, seq, &now);
> +	}
> +
> +	trace_drm_vblank_event(pipe, seq);
> +}
> +
> +/**
> + * drm_handle_vblank - handle a vblank event
> + * @dev: DRM device
> + * @pipe: index of CRTC where this event occurred
> + *
> + * Drivers should call this routine in their vblank interrupt handlers to
> + * update the vblank counter and send any signals that may be pending.
> + *
> + * This is the legacy version of drm_crtc_handle_vblank().
> + */
> +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
> +{
> +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> +	unsigned long irqflags;
> +	bool disable_irq;
> +
> +	if (WARN_ON_ONCE(!dev->num_crtcs))
> +		return false;
> +
> +	if (WARN_ON(pipe >= dev->num_crtcs))
> +		return false;
> +
> +	spin_lock_irqsave(&dev->event_lock, irqflags);
> +
> +	/* Need timestamp lock to prevent concurrent execution with
> +	 * vblank enable/disable, as this would cause inconsistent
> +	 * or corrupted timestamps and vblank counts.
> +	 */
> +	spin_lock(&dev->vblank_time_lock);
> +
> +	/* Vblank irq handling disabled. Nothing to do. */
> +	if (!vblank->enabled) {
> +		spin_unlock(&dev->vblank_time_lock);
> +		spin_unlock_irqrestore(&dev->event_lock, irqflags);
> +		return false;
> +	}
> +
> +	drm_update_vblank_count(dev, pipe, true);
> +
> +	spin_unlock(&dev->vblank_time_lock);
> +
> +	wake_up(&vblank->queue);
> +
> +	/* With instant-off, we defer disabling the interrupt until after
> +	 * we finish processing the following vblank after all events have
> +	 * been signaled. The disable has to be last (after
> +	 * drm_handle_vblank_events) so that the timestamp is always accurate.
> +	 */
> +	disable_irq = (dev->vblank_disable_immediate &&
> +		       drm_vblank_offdelay > 0 &&
> +		       !atomic_read(&vblank->refcount));
> +
> +	drm_handle_vblank_events(dev, pipe);
> +
> +	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> +
> +	if (disable_irq)
> +		vblank_disable_fn((unsigned long)vblank);
> +
> +	return true;
> +}
> +EXPORT_SYMBOL(drm_handle_vblank);
> +
> +/**
> + * drm_crtc_handle_vblank - handle a vblank event
> + * @crtc: where this event occurred
> + *
> + * Drivers should call this routine in their vblank interrupt handlers to
> + * update the vblank counter and send any signals that may be pending.
> + *
> + * This is the native KMS version of drm_handle_vblank().
> + *
> + * Returns:
> + * True if the event was successfully handled, false on failure.
> + */
> +bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
> +{
> +	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
> +}
> +EXPORT_SYMBOL(drm_crtc_handle_vblank);
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index c363f2fdff31..2e0b76cceb97 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -80,6 +80,9 @@
>  #include <drm/drm_debugfs.h>
>  #include <drm/drm_ioctl.h>
>  #include <drm/drm_sysfs.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/drm_irq.h>
> +
>  
>  struct module;
>  
> @@ -447,8 +450,6 @@ static inline bool
> drm_drv_uses_atomic_modeset(struct drm_device *dev)
>  	return dev->mode_config.funcs->atomic_commit != NULL;
>  }
>  
> -#include <drm/drm_irq.h>
> -
>  #define DRM_SWITCH_POWER_ON 0
>  #define DRM_SWITCH_POWER_OFF 1
>  #define DRM_SWITCH_POWER_CHANGING 2
> diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
> index 5dd27ae5c47c..d66f7ee07fb5 100644
> --- a/include/drm/drm_file.h
> +++ b/include/drm/drm_file.h
> @@ -40,6 +40,7 @@
>  struct dma_fence;
>  struct drm_file;
>  struct drm_device;
> +struct device;
>  
>  /*
>   * FIXME: Not sure we want to have drm_minor here in the end, but to avoid
> diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h
> index 569ca86d4e1f..d77f6e65b1c6 100644
> --- a/include/drm/drm_irq.h
> +++ b/include/drm/drm_irq.h
> @@ -24,165 +24,9 @@
>  #ifndef _DRM_IRQ_H_
>  #define _DRM_IRQ_H_
>  
> -#include <linux/seqlock.h>
> -
> -/**
> - * struct drm_pending_vblank_event - pending vblank event tracking
> - */
> -struct drm_pending_vblank_event {
> -	/**
> -	 * @base: Base structure for tracking pending DRM events.
> -	 */
> -	struct drm_pending_event base;
> -	/**
> -	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
> -	 */
> -	unsigned int pipe;
> -	/**
> -	 * @event: Actual event which will be sent to userspace.
> -	 */
> -	struct drm_event_vblank event;
> -};
> -
> -/**
> - * struct drm_vblank_crtc - vblank tracking for a CRTC
> - *
> - * This structure tracks the vblank state for one CRTC.
> - *
> - * Note that for historical reasons - the vblank handling code is still shared
> - * with legacy/non-kms drivers - this is a free-standing structure not directly
> - * connected to &struct drm_crtc. But all public interface functions are taking
> - * a &struct drm_crtc to hide this implementation detail.
> - */
> -struct drm_vblank_crtc {
> -	/**
> -	 * @dev: Pointer to the &drm_device.
> -	 */
> -	struct drm_device *dev;
> -	/**
> -	 * @queue: Wait queue for vblank waiters.
> -	 */
> -	wait_queue_head_t queue;	/**< VBLANK wait queue */
> -	/**
> -	 * @disable_timer: Disable timer for the delayed vblank disabling
> -	 * hysteresis logic. Vblank disabling is controlled through the
> -	 * drm_vblank_offdelay module option and the setting of the
> -	 * &drm_device.max_vblank_count value.
> -	 */
> -	struct timer_list disable_timer;
> -
> -	/**
> -	 * @seqlock: Protect vblank count and time.
> -	 */
> -	seqlock_t seqlock;		/* protects vblank count and time */
> -
> -	/**
> -	 * @count: Current software vblank counter.
> -	 */
> -	u32 count;
> -	/**
> -	 * @time: Vblank timestamp corresponding to @count.
> -	 */
> -	struct timeval time;
> -
> -	/**
> -	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
> -	 * this refcount reaches 0 can the hardware interrupt be disabled using
> -	 * @disable_timer.
> -	 */
> -	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
> -	/**
> -	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
> -	 */
> -	u32 last;
> -	/**
> -	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
> -	 * For legacy driver bit 2 additionally tracks whether an additional
> -	 * temporary vblank reference has been acquired to paper over the
> -	 * hardware counter resetting/jumping. KMS drivers should instead just
> -	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
> -	 * save and restore the vblank count.
> -	 */
> -	unsigned int inmodeset;		/* Display driver is setting mode */
> -	/**
> -	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
> -	 * structure.
> -	 */
> -	unsigned int pipe;
> -	/**
> -	 * @framedur_ns: Frame/Field duration in ns, used by
> -	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> -	 * drm_calc_timestamping_constants().
> -	 */
> -	int framedur_ns;
> -	/**
> -	 * @linedur_ns: Line duration in ns, used by
> -	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> -	 * drm_calc_timestamping_constants().
> -	 */
> -	int linedur_ns;
> -
> -	/**
> -	 * @hwmode:
> -	 *
> -	 * Cache of the current hardware display mode. Only valid when @enabled
> -	 * is set. This is used by helpers like
> -	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
> -	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
> -	 * because that one is really hard to get from interrupt context.
> -	 */
> -	struct drm_display_mode hwmode;
> -
> -	/**
> -	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
> -	 * avoid double-disabling and hence corrupting saved state. Needed by
> -	 * drivers not using atomic KMS, since those might go through their CRTC
> -	 * disabling functions multiple times.
> -	 */
> -	bool enabled;
> -};
> +struct drm_device;
>  
>  int drm_irq_install(struct drm_device *dev, int irq);
>  int drm_irq_uninstall(struct drm_device *dev);
>  
> -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
> -u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
> -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> -				   struct timeval *vblanktime);
> -void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> -			       struct drm_pending_vblank_event *e);
> -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> -			      struct drm_pending_vblank_event *e);
> -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
> -bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
> -int drm_crtc_vblank_get(struct drm_crtc *crtc);
> -void drm_crtc_vblank_put(struct drm_crtc *crtc);
> -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
> -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
> -void drm_crtc_vblank_off(struct drm_crtc *crtc);
> -void drm_crtc_vblank_reset(struct drm_crtc *crtc);
> -void drm_crtc_vblank_on(struct drm_crtc *crtc);
> -void drm_vblank_cleanup(struct drm_device *dev);
> -u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
> -
> -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> -					   unsigned int pipe, int *max_error,
> -					   struct timeval *vblank_time,
> -					   bool in_vblank_irq);
> -void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> -				     const struct drm_display_mode *mode);
> -
> -/**
> - * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
> - * @crtc: which CRTC's vblank waitqueue to retrieve
> - *
> - * This function returns a pointer to the vblank waitqueue for the CRTC.
> - * Drivers can use this to implement vblank waits using wait_event()
> and related
> - * functions.
> - */
> -static inline wait_queue_head_t *drm_crtc_vblank_waitqueue(struct
> drm_crtc *crtc)
> -{
> -	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
> -}
> -
>  #endif
> diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h
> index 59ccab402e85..9cd9e36f77b5 100644
> --- a/include/drm/drm_prime.h
> +++ b/include/drm/drm_prime.h
> @@ -59,6 +59,8 @@ struct drm_device;
>  struct drm_gem_object;
>  struct drm_file;
>  
> +struct device;
> +
>  struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
>  				     struct drm_gem_object *obj,
>  				     int flags);
> diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
> new file mode 100644
> index 000000000000..4cde47332dfa
> --- /dev/null
> +++ b/include/drm/drm_vblank.h
> @@ -0,0 +1,181 @@
> +/*
> + * Copyright 2016 Intel Corp.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#ifndef _DRM_VBLANK_H_
> +#define _DRM_VBLANK_H_
> +
> +#include <linux/seqlock.h>
> +#include <linux/idr.h>
> +#include <linux/poll.h>
> +
> +#include <drm/drm_file.h>
> +#include <drm/drm_modes.h>
> +#include <uapi/drm/drm.h>
> +
> +struct drm_device;
> +struct drm_crtc;
> +
> +/**
> + * struct drm_pending_vblank_event - pending vblank event tracking
> + */
> +struct drm_pending_vblank_event {
> +	/**
> +	 * @base: Base structure for tracking pending DRM events.
> +	 */
> +	struct drm_pending_event base;
> +	/**
> +	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
> +	 */
> +	unsigned int pipe;
> +	/**
> +	 * @event: Actual event which will be sent to userspace.
> +	 */
> +	struct drm_event_vblank event;
> +};
> +
> +/**
> + * struct drm_vblank_crtc - vblank tracking for a CRTC
> + *
> + * This structure tracks the vblank state for one CRTC.
> + *
> + * Note that for historical reasons - the vblank handling code is still shared
> + * with legacy/non-kms drivers - this is a free-standing structure not directly
> + * connected to &struct drm_crtc. But all public interface functions are taking
> + * a &struct drm_crtc to hide this implementation detail.
> + */
> +struct drm_vblank_crtc {
> +	/**
> +	 * @dev: Pointer to the &drm_device.
> +	 */
> +	struct drm_device *dev;
> +	/**
> +	 * @queue: Wait queue for vblank waiters.
> +	 */
> +	wait_queue_head_t queue;	/**< VBLANK wait queue */
> +	/**
> +	 * @disable_timer: Disable timer for the delayed vblank disabling
> +	 * hysteresis logic. Vblank disabling is controlled through the
> +	 * drm_vblank_offdelay module option and the setting of the
> +	 * &drm_device.max_vblank_count value.
> +	 */
> +	struct timer_list disable_timer;
> +
> +	/**
> +	 * @seqlock: Protect vblank count and time.
> +	 */
> +	seqlock_t seqlock;		/* protects vblank count and time */
> +
> +	/**
> +	 * @count: Current software vblank counter.
> +	 */
> +	u32 count;
> +	/**
> +	 * @time: Vblank timestamp corresponding to @count.
> +	 */
> +	struct timeval time;
> +
> +	/**
> +	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
> +	 * this refcount reaches 0 can the hardware interrupt be disabled using
> +	 * @disable_timer.
> +	 */
> +	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
> +	/**
> +	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
> +	 */
> +	u32 last;
> +	/**
> +	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
> +	 * For legacy driver bit 2 additionally tracks whether an additional
> +	 * temporary vblank reference has been acquired to paper over the
> +	 * hardware counter resetting/jumping. KMS drivers should instead just
> +	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
> +	 * save and restore the vblank count.
> +	 */
> +	unsigned int inmodeset;		/* Display driver is setting mode */
> +	/**
> +	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
> +	 * structure.
> +	 */
> +	unsigned int pipe;
> +	/**
> +	 * @framedur_ns: Frame/Field duration in ns, used by
> +	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> +	 * drm_calc_timestamping_constants().
> +	 */
> +	int framedur_ns;
> +	/**
> +	 * @linedur_ns: Line duration in ns, used by
> +	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> +	 * drm_calc_timestamping_constants().
> +	 */
> +	int linedur_ns;
> +
> +	/**
> +	 * @hwmode:
> +	 *
> +	 * Cache of the current hardware display mode. Only valid when @enabled
> +	 * is set. This is used by helpers like
> +	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
> +	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
> +	 * because that one is really hard to get from interrupt context.
> +	 */
> +	struct drm_display_mode hwmode;
> +
> +	/**
> +	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
> +	 * avoid double-disabling and hence corrupting saved state. Needed by
> +	 * drivers not using atomic KMS, since those might go through their CRTC
> +	 * disabling functions multiple times.
> +	 */
> +	bool enabled;
> +};
> +
> +int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
> +u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
> +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> +				   struct timeval *vblanktime);
> +void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> +			       struct drm_pending_vblank_event *e);
> +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> +			      struct drm_pending_vblank_event *e);
> +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
> +bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
> +int drm_crtc_vblank_get(struct drm_crtc *crtc);
> +void drm_crtc_vblank_put(struct drm_crtc *crtc);
> +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
> +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
> +void drm_crtc_vblank_off(struct drm_crtc *crtc);
> +void drm_crtc_vblank_reset(struct drm_crtc *crtc);
> +void drm_crtc_vblank_on(struct drm_crtc *crtc);
> +void drm_vblank_cleanup(struct drm_device *dev);
> +u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
> +
> +bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> +					   unsigned int pipe, int *max_error,
> +					   struct timeval *vblank_time,
> +					   bool in_vblank_irq);
> +void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> +				     const struct drm_display_mode *mode);
> +wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
> +#endif
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH] drm: Extract drm_vblank.[hc]
  2017-05-31  9:21   ` [PATCH] " Daniel Vetter
  2017-05-31 17:51     ` Stefan Agner
@ 2017-05-31 18:22     ` kbuild test robot
  1 sibling, 0 replies; 94+ messages in thread
From: kbuild test robot @ 2017-05-31 18:22 UTC (permalink / raw)
  Cc: Stefan Agner, Daniel Vetter, Intel Graphics Development,
	DRI Development, kbuild-all, Daniel Vetter

[-- Attachment #1: Type: text/plain, Size: 19192 bytes --]

Hi Daniel,

[auto build test WARNING on drm/drm-next]
[also build test WARNING on next-20170531]
[cannot apply to v4.12-rc3]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Daniel-Vetter/drm-Extract-drm_vblank-hc/20170531-223258
base:   git://people.freedesktop.org/~airlied/linux.git drm-next
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

   WARNING: convert(1) not found, for SVG to PDF conversion install ImageMagick (https://www.imagemagick.org)
   arch/x86/include/asm/uaccess_32.h:1: warning: no structured comments found
   include/linux/init.h:1: warning: no structured comments found
   include/linux/mod_devicetable.h:686: warning: Excess struct/union/enum/typedef member 'ver_major' description in 'fsl_mc_device_id'
   include/linux/mod_devicetable.h:686: warning: Excess struct/union/enum/typedef member 'ver_minor' description in 'fsl_mc_device_id'
   kernel/sched/core.c:2088: warning: No description found for parameter 'rf'
   kernel/sched/core.c:2088: warning: Excess function parameter 'cookie' description in 'try_to_wake_up_local'
   include/linux/kthread.h:26: warning: Excess function parameter '...' description in 'kthread_create'
   kernel/sys.c:1: warning: no structured comments found
   include/linux/device.h:969: warning: No description found for parameter 'dma_ops'
   drivers/dma-buf/seqno-fence.c:1: warning: no structured comments found
   include/linux/iio/iio.h:597: warning: No description found for parameter 'trig_readonly'
   include/linux/iio/trigger.h:151: warning: No description found for parameter 'indio_dev'
   include/linux/iio/trigger.h:151: warning: No description found for parameter 'trig'
   include/linux/device.h:970: warning: No description found for parameter 'dma_ops'
   include/linux/usb/gadget.h:230: warning: No description found for parameter 'claimed'
   include/linux/usb/gadget.h:230: warning: No description found for parameter 'enabled'
   include/linux/usb/gadget.h:408: warning: No description found for parameter 'quirk_altset_not_supp'
   include/linux/usb/gadget.h:408: warning: No description found for parameter 'quirk_stall_not_supp'
   include/linux/usb/gadget.h:408: warning: No description found for parameter 'quirk_zlp_not_supp'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'set_busid'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'irq_handler'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'irq_preinstall'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'irq_postinstall'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'irq_uninstall'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'debugfs_init'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_open_object'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_close_object'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'prime_handle_to_fd'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'prime_fd_to_handle'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_export'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_import'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_pin'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_unpin'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_res_obj'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_get_sg_table'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_import_sg_table'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_vmap'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_vunmap'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_prime_mmap'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'gem_vm_ops'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'major'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'minor'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'patchlevel'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'name'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'desc'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'date'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'driver_features'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'ioctls'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'num_ioctls'
   include/drm/drm_drv.h:507: warning: No description found for parameter 'fops'
>> drivers/gpu/drm/drm_irq.c:1: warning: no structured comments found
   include/drm/drm_color_mgmt.h:1: warning: no structured comments found
   Documentation/core-api/assoc_array.rst:13: WARNING: Enumerated list ends without a blank line; unexpected unindent.
   Documentation/doc-guide/sphinx.rst:126: ERROR: Unknown target name: "sphinx c domain".
   kernel/sched/fair.c:7650: WARNING: Inline emphasis start-string without end-string.
   kernel/time/timer.c:1200: ERROR: Unexpected indentation.
   kernel/time/timer.c:1202: ERROR: Unexpected indentation.
   kernel/time/timer.c:1203: WARNING: Block quote ends without a blank line; unexpected unindent.
   include/linux/wait.h:122: WARNING: Block quote ends without a blank line; unexpected unindent.
   include/linux/wait.h:125: ERROR: Unexpected indentation.
   include/linux/wait.h:127: WARNING: Block quote ends without a blank line; unexpected unindent.
   kernel/time/hrtimer.c:990: WARNING: Block quote ends without a blank line; unexpected unindent.
   kernel/signal.c:322: WARNING: Inline literal start-string without end-string.
   include/linux/iio/iio.h:219: ERROR: Unexpected indentation.
   include/linux/iio/iio.h:220: WARNING: Block quote ends without a blank line; unexpected unindent.
   include/linux/iio/iio.h:226: WARNING: Definition list ends without a blank line; unexpected unindent.
   drivers/iio/industrialio-core.c:638: ERROR: Unknown target name: "iio_val".
   drivers/iio/industrialio-core.c:645: ERROR: Unknown target name: "iio_val".
   drivers/message/fusion/mptbase.c:5051: WARNING: Definition list ends without a blank line; unexpected unindent.
   drivers/tty/serial/serial_core.c:1898: WARNING: Definition list ends without a blank line; unexpected unindent.
   drivers/pci/pci.c:3457: ERROR: Unexpected indentation.
   include/linux/regulator/driver.h:271: ERROR: Unknown target name: "regulator_regmap_x_voltage".
   include/linux/spi/spi.h:370: ERROR: Unexpected indentation.
   drivers/gpu/drm/drm_scdc_helper.c:203: ERROR: Unexpected indentation.
   drivers/gpu/drm/drm_scdc_helper.c:204: WARNING: Block quote ends without a blank line; unexpected unindent.
   drivers/gpu/drm/drm_ioctl.c:690: WARNING: Definition list ends without a blank line; unexpected unindent.
   Documentation/gpu/todo.rst:111: ERROR: Unknown target name: "drm_fb".
   sound/soc/soc-core.c:2670: ERROR: Unknown target name: "snd_soc_daifmt".
   sound/core/jack.c:312: ERROR: Unknown target name: "snd_jack_btn".
   Documentation/userspace-api/unshare.rst:108: WARNING: Inline emphasis start-string without end-string.
   Documentation/usb/typec.rst:: WARNING: document isn't included in any toctree
   Documentation/usb/usb3-debug-port.rst:: WARNING: document isn't included in any toctree
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 193: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 193: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 193: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 193: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 193: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "~/.fonts.conf", line 193: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 43: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 56: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 69: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 82: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 96: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 109: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 122: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 133: Having multiple values in <test> isn't supported and may not work as expected
   Fontconfig warning: "/home/kbuild/.config/fontconfig/fonts.conf", line 164: Having multiple values in <test> isn't supported and may not work as expected

vim +1 drivers/gpu/drm/drm_irq.c

f5752b38 drivers/gpu/drm/drm_irq.c  Daniel Vetter  2014-05-08 @1  /*
f5752b38 drivers/gpu/drm/drm_irq.c  Daniel Vetter  2014-05-08  2   * drm_irq.c IRQ and vblank support
^1da177e drivers/char/drm/drm_irq.c Linus Torvalds 2005-04-16  3   *
^1da177e drivers/char/drm/drm_irq.c Linus Torvalds 2005-04-16  4   * \author Rickard E. (Rik) Faith <faith@valinux.com>
^1da177e drivers/char/drm/drm_irq.c Linus Torvalds 2005-04-16  5   * \author Gareth Hughes <gareth@valinux.com>
0d564670 drivers/gpu/drm/drm_irq.c  Daniel Vetter  2017-05-31  6   *
0d564670 drivers/gpu/drm/drm_irq.c  Daniel Vetter  2017-05-31  7   * Permission is hereby granted, free of charge, to any person obtaining a
0d564670 drivers/gpu/drm/drm_irq.c  Daniel Vetter  2017-05-31  8   * copy of this software and associated documentation files (the "Software"),
0d564670 drivers/gpu/drm/drm_irq.c  Daniel Vetter  2017-05-31  9   * to deal in the Software without restriction, including without limitation

:::::: The code at line 1 was first introduced by commit
:::::: f5752b38970990fa1495839751aed64bd178b9e0 drm/irq: kerneldoc polish

:::::: TO: Daniel Vetter <daniel.vetter@ffwll.ch>
:::::: CC: Daniel Vetter <daniel.vetter@ffwll.ch>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6634 bytes --]

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH] drm: Extract drm_vblank.[hc]
  2017-05-31 17:51     ` Stefan Agner
@ 2017-06-01  5:55       ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-06-01  5:55 UTC (permalink / raw)
  To: Stefan Agner
  Cc: Daniel Vetter, Intel Graphics Development, DRI Development,
	Daniel Vetter

On Wed, May 31, 2017 at 10:51:11AM -0700, Stefan Agner wrote:
> On 2017-05-31 02:21, Daniel Vetter wrote:
> > drm_irq.c contains both the irq helper library (optional) and the
> > vblank support (optional, but part of the modeset uapi, and doesn't
> > require the use of the irq helpers at all.
> > 
> > Split this up for more clarity of the scope of the individual bits.
> > 
> > v2: Move misplaced hunks to this patch (Stefan).
> > 
> 
> Looks good to me:
> 
> Reviewed-by: Stefan Agner <stefan@agner.ch>

Thanks for your review, I applied the these two patches.
-Daniel

> 
> --
> Stefan
> 
> > Cc: Stefan Agner <stefan@agner.ch>
> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> > ---
> >  Documentation/gpu/drm-internals.rst |    9 +
> >  Documentation/gpu/drm-kms.rst       |    4 +-
> >  drivers/gpu/drm/Makefile            |    2 +-
> >  drivers/gpu/drm/drm_internal.h      |    3 +-
> >  drivers/gpu/drm/drm_irq.c           | 1623 +---------------------------------
> >  drivers/gpu/drm/drm_vblank.c        | 1645 +++++++++++++++++++++++++++++++++++
> >  include/drm/drmP.h                  |    5 +-
> >  include/drm/drm_file.h              |    1 +
> >  include/drm/drm_irq.h               |  158 +---
> >  include/drm/drm_prime.h             |    2 +
> >  include/drm/drm_vblank.h            |  181 ++++
> >  11 files changed, 1869 insertions(+), 1764 deletions(-)
> >  create mode 100644 drivers/gpu/drm/drm_vblank.c
> >  create mode 100644 include/drm/drm_vblank.h
> > 
> > diff --git a/Documentation/gpu/drm-internals.rst
> > b/Documentation/gpu/drm-internals.rst
> > index d218dd29221a..dd28e39cf966 100644
> > --- a/Documentation/gpu/drm-internals.rst
> > +++ b/Documentation/gpu/drm-internals.rst
> > @@ -204,6 +204,15 @@ drm_device <drm_device>` irq_enabled field to 1 upon
> >  registration of the IRQs, and clear it to 0 after unregistering the
> >  IRQs.
> >  
> > +IRQ Helper Library
> > +~~~~~~~~~~~~~~~~~~
> > +
> > +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> > +   :doc: irq helpers
> > +
> > +.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> > +   :export:
> > +
> >  Memory Manager Initialization
> >  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >  
> > diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
> > index bfecd21a8cdf..2d77c9580164 100644
> > --- a/Documentation/gpu/drm-kms.rst
> > +++ b/Documentation/gpu/drm-kms.rst
> > @@ -612,8 +612,8 @@ operation handler.
> >  Vertical Blanking and Interrupt Handling Functions Reference
> >  ------------------------------------------------------------
> >  
> > -.. kernel-doc:: include/drm/drm_irq.h
> > +.. kernel-doc:: include/drm/drm_vblank.h
> >     :internal:
> >  
> > -.. kernel-doc:: drivers/gpu/drm/drm_irq.c
> > +.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
> >     :export:
> > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> > index c156fecfb362..acc88942c2e5 100644
> > --- a/drivers/gpu/drm/Makefile
> > +++ b/drivers/gpu/drm/Makefile
> > @@ -16,7 +16,7 @@ drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
> >  		drm_framebuffer.o drm_connector.o drm_blend.o \
> >  		drm_encoder.o drm_mode_object.o drm_property.o \
> >  		drm_plane.o drm_color_mgmt.o drm_print.o \
> > -		drm_dumb_buffers.o drm_mode_config.o
> > +		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o
> >  
> >  drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
> >  drm-$(CONFIG_DRM_VM) += drm_vm.o
> > diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> > index bca2c66c5d28..6a0cbcc84534 100644
> > --- a/drivers/gpu/drm/drm_internal.h
> > +++ b/drivers/gpu/drm/drm_internal.h
> > @@ -54,8 +54,9 @@ int drm_name_info(struct seq_file *m, void *data);
> >  int drm_clients_info(struct seq_file *m, void* data);
> >  int drm_gem_name_info(struct seq_file *m, void *data);
> >  
> > -/* drm_irq.c */
> > +/* drm_vblank.c */
> >  extern unsigned int drm_timestamp_monotonic;
> > +void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
> >  
> >  /* IOCTLS */
> >  int drm_wait_vblank(struct drm_device *dev, void *data,
> > diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> > index c7debaad67f8..28d736c3fcb4 100644
> > --- a/drivers/gpu/drm/drm_irq.c
> > +++ b/drivers/gpu/drm/drm_irq.c
> > @@ -3,6 +3,25 @@
> >   *
> >   * \author Rickard E. (Rik) Faith <faith@valinux.com>
> >   * \author Gareth Hughes <gareth@valinux.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> > + * OTHER DEALINGS IN THE SOFTWARE.
> >   */
> >  
> >  /*
> > @@ -32,429 +51,15 @@
> >   * OTHER DEALINGS IN THE SOFTWARE.
> >   */
> >  
> > +#include <drm/drm_irq.h>
> >  #include <drm/drmP.h>
> > -#include "drm_trace.h"
> > -#include "drm_internal.h"
> >  
> >  #include <linux/interrupt.h>	/* For task queue support */
> > -#include <linux/slab.h>
> >  
> >  #include <linux/vgaarb.h>
> >  #include <linux/export.h>
> >  
> > -/* Retry timestamp calculation up to 3 times to satisfy
> > - * drm_timestamp_precision before giving up.
> > - */
> > -#define DRM_TIMESTAMP_MAXRETRIES 3
> > -
> > -/* Threshold in nanoseconds for detection of redundant
> > - * vblank irq in drm_handle_vblank(). 1 msec should be ok.
> > - */
> > -#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
> > -
> > -static bool
> > -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> > -			  struct timeval *tvblank, bool in_vblank_irq);
> > -
> > -static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
> > -
> > -/*
> > - * Default to use monotonic timestamps for wait-for-vblank and page-flip
> > - * complete events.
> > - */
> > -unsigned int drm_timestamp_monotonic = 1;
> > -
> > -static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
> > -
> > -module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
> > -module_param_named(timestamp_precision_usec, drm_timestamp_precision,
> > int, 0600);
> > -module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
> > -MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable
> > [msecs] (0: never disable, <0: disable immediately)");
> > -MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
> > -MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
> > -
> > -static void store_vblank(struct drm_device *dev, unsigned int pipe,
> > -			 u32 vblank_count_inc,
> > -			 struct timeval *t_vblank, u32 last)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -
> > -	assert_spin_locked(&dev->vblank_time_lock);
> > -
> > -	vblank->last = last;
> > -
> > -	write_seqlock(&vblank->seqlock);
> > -	vblank->time = *t_vblank;
> > -	vblank->count += vblank_count_inc;
> > -	write_sequnlock(&vblank->seqlock);
> > -}
> > -
> > -/*
> > - * "No hw counter" fallback implementation of .get_vblank_counter() hook,
> > - * if there is no useable hardware frame counter available.
> > - */
> > -static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	WARN_ON_ONCE(dev->max_vblank_count != 0);
> > -	return 0;
> > -}
> > -
> > -static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> > -		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> > -
> > -		if (crtc->funcs->get_vblank_counter)
> > -			return crtc->funcs->get_vblank_counter(crtc);
> > -	}
> > -
> > -	if (dev->driver->get_vblank_counter)
> > -		return dev->driver->get_vblank_counter(dev, pipe);
> > -
> > -	return drm_vblank_no_hw_counter(dev, pipe);
> > -}
> > -
> > -/*
> > - * Reset the stored timestamp for the current vblank count to correspond
> > - * to the last vblank occurred.
> > - *
> > - * Only to be called from drm_crtc_vblank_on().
> > - *
> > - * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> > - * device vblank fields.
> > - */
> > -static void drm_reset_vblank_timestamp(struct drm_device *dev,
> > unsigned int pipe)
> > -{
> > -	u32 cur_vblank;
> > -	bool rc;
> > -	struct timeval t_vblank;
> > -	int count = DRM_TIMESTAMP_MAXRETRIES;
> > -
> > -	spin_lock(&dev->vblank_time_lock);
> > -
> > -	/*
> > -	 * sample the current counter to avoid random jumps
> > -	 * when drm_vblank_enable() applies the diff
> > -	 */
> > -	do {
> > -		cur_vblank = __get_vblank_counter(dev, pipe);
> > -		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
> > -	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> > -
> > -	/*
> > -	 * Only reinitialize corresponding vblank timestamp if high-precision query
> > -	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
> > -	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
> > -	 */
> > -	if (!rc)
> > -		t_vblank = (struct timeval) {0, 0};
> > -
> > -	/*
> > -	 * +1 to make sure user will never see the same
> > -	 * vblank counter value before and after a modeset
> > -	 */
> > -	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
> > -
> > -	spin_unlock(&dev->vblank_time_lock);
> > -}
> > -
> > -/*
> > - * Call back into the driver to update the appropriate vblank counter
> > - * (specified by @pipe).  Deal with wraparound, if it occurred, and
> > - * update the last read value so we can deal with wraparound on the next
> > - * call if necessary.
> > - *
> > - * Only necessary when going from off->on, to account for frames we
> > - * didn't get an interrupt for.
> > - *
> > - * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> > - * device vblank fields.
> > - */
> > -static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
> > -				    bool in_vblank_irq)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	u32 cur_vblank, diff;
> > -	bool rc;
> > -	struct timeval t_vblank;
> > -	int count = DRM_TIMESTAMP_MAXRETRIES;
> > -	int framedur_ns = vblank->framedur_ns;
> > -
> > -	/*
> > -	 * Interrupts were disabled prior to this call, so deal with counter
> > -	 * wrap if needed.
> > -	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
> > -	 * here if the register is small or we had vblank interrupts off for
> > -	 * a long time.
> > -	 *
> > -	 * We repeat the hardware vblank counter & timestamp query until
> > -	 * we get consistent results. This to prevent races between gpu
> > -	 * updating its hardware counter while we are retrieving the
> > -	 * corresponding vblank timestamp.
> > -	 */
> > -	do {
> > -		cur_vblank = __get_vblank_counter(dev, pipe);
> > -		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
> > -	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> > -
> > -	if (dev->max_vblank_count != 0) {
> > -		/* trust the hw counter when it's around */
> > -		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
> > -	} else if (rc && framedur_ns) {
> > -		const struct timeval *t_old;
> > -		u64 diff_ns;
> > -
> > -		t_old = &vblank->time;
> > -		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
> > -
> > -		/*
> > -		 * Figure out how many vblanks we've missed based
> > -		 * on the difference in the timestamps and the
> > -		 * frame/field duration.
> > -		 */
> > -		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
> > -
> > -		if (diff == 0 && in_vblank_irq)
> > -			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
> > -				      " diff_ns = %lld, framedur_ns = %d)\n",
> > -				      pipe, (long long) diff_ns, framedur_ns);
> > -	} else {
> > -		/* some kind of default for drivers w/o accurate vbl timestamping */
> > -		diff = in_vblank_irq ? 1 : 0;
> > -	}
> > -
> > -	/*
> > -	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
> > -	 * interval? If so then vblank irqs keep running and it will likely
> > -	 * happen that the hardware vblank counter is not trustworthy as it
> > -	 * might reset at some point in that interval and vblank timestamps
> > -	 * are not trustworthy either in that interval. Iow. this can result
> > -	 * in a bogus diff >> 1 which must be avoided as it would cause
> > -	 * random large forward jumps of the software vblank counter.
> > -	 */
> > -	if (diff > 1 && (vblank->inmodeset & 0x2)) {
> > -		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
> > -			      " due to pre-modeset.\n", pipe, diff);
> > -		diff = 1;
> > -	}
> > -
> > -	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
> > -		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
> > -		      pipe, vblank->count, diff, cur_vblank, vblank->last);
> > -
> > -	if (diff == 0) {
> > -		WARN_ON_ONCE(cur_vblank != vblank->last);
> > -		return;
> > -	}
> > -
> > -	/*
> > -	 * Only reinitialize corresponding vblank timestamp if high-precision query
> > -	 * available and didn't fail, or we were called from the vblank interrupt.
> > -	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
> > -	 * for now, to mark the vblanktimestamp as invalid.
> > -	 */
> > -	if (!rc && in_vblank_irq)
> > -		t_vblank = (struct timeval) {0, 0};
> > -
> > -	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
> > -}
> > -
> > -static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return 0;
> > -
> > -	return vblank->count;
> > -}
> > -
> > -/**
> > - * drm_accurate_vblank_count - retrieve the master vblank counter
> > - * @crtc: which counter to retrieve
> > - *
> > - * This function is similar to @drm_crtc_vblank_count but this
> > - * function interpolates to handle a race with vblank irq's.
> > - *
> > - * This is mostly useful for hardware that can obtain the scanout
> > - * position, but doesn't have a frame counter.
> > - */
> > -u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned int pipe = drm_crtc_index(crtc);
> > -	u32 vblank;
> > -	unsigned long flags;
> > -
> > -	WARN(!dev->driver->get_vblank_timestamp,
> > -	     "This function requires support for accurate vblank timestamps.");
> > -
> > -	spin_lock_irqsave(&dev->vblank_time_lock, flags);
> > -
> > -	drm_update_vblank_count(dev, pipe, false);
> > -	vblank = drm_vblank_count(dev, pipe);
> > -
> > -	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
> > -
> > -	return vblank;
> > -}
> > -EXPORT_SYMBOL(drm_accurate_vblank_count);
> > -
> > -static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> > -		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> > -
> > -		if (crtc->funcs->disable_vblank) {
> > -			crtc->funcs->disable_vblank(crtc);
> > -			return;
> > -		}
> > -	}
> > -
> > -	dev->driver->disable_vblank(dev, pipe);
> > -}
> > -
> > -/*
> > - * Disable vblank irq's on crtc, make sure that last vblank count
> > - * of hardware and corresponding consistent software vblank counter
> > - * are preserved, even if there are any spurious vblank irq's after
> > - * disable.
> > - */
> > -static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	unsigned long irqflags;
> > -
> > -	assert_spin_locked(&dev->vbl_lock);
> > -
> > -	/* Prevent vblank irq processing while disabling vblank irqs,
> > -	 * so no updates of timestamps or count can happen after we've
> > -	 * disabled. Needed to prevent races in case of delayed irq's.
> > -	 */
> > -	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
> > -
> > -	/*
> > -	 * Only disable vblank interrupts if they're enabled. This avoids
> > -	 * calling the ->disable_vblank() operation in atomic context with the
> > -	 * hardware potentially runtime suspended.
> > -	 */
> > -	if (vblank->enabled) {
> > -		__disable_vblank(dev, pipe);
> > -		vblank->enabled = false;
> > -	}
> > -
> > -	/*
> > -	 * Always update the count and timestamp to maintain the
> > -	 * appearance that the counter has been ticking all along until
> > -	 * this time. This makes the count account for the entire time
> > -	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
> > -	 */
> > -	drm_update_vblank_count(dev, pipe, false);
> > -
> > -	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
> > -}
> > -
> > -static void vblank_disable_fn(unsigned long arg)
> > -{
> > -	struct drm_vblank_crtc *vblank = (void *)arg;
> > -	struct drm_device *dev = vblank->dev;
> > -	unsigned int pipe = vblank->pipe;
> > -	unsigned long irqflags;
> > -
> > -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > -	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
> > -		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
> > -		vblank_disable_and_save(dev, pipe);
> > -	}
> > -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > -}
> > -
> > -/**
> > - * drm_vblank_cleanup - cleanup vblank support
> > - * @dev: DRM device
> > - *
> > - * This function cleans up any resources allocated in drm_vblank_init.
> > - */
> > -void drm_vblank_cleanup(struct drm_device *dev)
> > -{
> > -	unsigned int pipe;
> > -
> > -	/* Bail if the driver didn't call drm_vblank_init() */
> > -	if (dev->num_crtcs == 0)
> > -		return;
> > -
> > -	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
> > -		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -
> > -		WARN_ON(READ_ONCE(vblank->enabled) &&
> > -			drm_core_check_feature(dev, DRIVER_MODESET));
> > -
> > -		del_timer_sync(&vblank->disable_timer);
> > -	}
> > -
> > -	kfree(dev->vblank);
> > -
> > -	dev->num_crtcs = 0;
> > -}
> > -EXPORT_SYMBOL(drm_vblank_cleanup);
> > -
> > -/**
> > - * drm_vblank_init - initialize vblank support
> > - * @dev: DRM device
> > - * @num_crtcs: number of CRTCs supported by @dev
> > - *
> > - * This function initializes vblank support for @num_crtcs display pipelines.
> > - *
> > - * Returns:
> > - * Zero on success or a negative error code on failure.
> > - */
> > -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
> > -{
> > -	int ret = -ENOMEM;
> > -	unsigned int i;
> > -
> > -	spin_lock_init(&dev->vbl_lock);
> > -	spin_lock_init(&dev->vblank_time_lock);
> > -
> > -	dev->num_crtcs = num_crtcs;
> > -
> > -	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
> > -	if (!dev->vblank)
> > -		goto err;
> > -
> > -	for (i = 0; i < num_crtcs; i++) {
> > -		struct drm_vblank_crtc *vblank = &dev->vblank[i];
> > -
> > -		vblank->dev = dev;
> > -		vblank->pipe = i;
> > -		init_waitqueue_head(&vblank->queue);
> > -		setup_timer(&vblank->disable_timer, vblank_disable_fn,
> > -			    (unsigned long)vblank);
> > -		seqlock_init(&vblank->seqlock);
> > -	}
> > -
> > -	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
> > -
> > -	/* Driver specific high-precision vblank timestamping supported? */
> > -	if (dev->driver->get_vblank_timestamp)
> > -		DRM_INFO("Driver supports precise vblank timestamp query.\n");
> > -	else
> > -		DRM_INFO("No driver support for vblank timestamp query.\n");
> > -
> > -	/* Must have precise timestamping for reliable vblank instant disable */
> > -	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
> > -		dev->vblank_disable_immediate = false;
> > -		DRM_INFO("Setting vblank_disable_immediate to false because "
> > -			 "get_vblank_timestamp == NULL\n");
> > -	}
> > -
> > -	return 0;
> > -
> > -err:
> > -	dev->num_crtcs = 0;
> > -	return ret;
> > -}
> > -EXPORT_SYMBOL(drm_vblank_init);
> > +#include "drm_internal.h"
> >  
> >  /**
> >   * drm_irq_install - install IRQ handler
> > @@ -571,7 +176,7 @@ int drm_irq_uninstall(struct drm_device *dev)
> >  
> >  			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
> >  
> > -			vblank_disable_and_save(dev, i);
> > +			drm_vblank_disable_and_save(dev, i);
> >  			wake_up(&vblank->queue);
> >  		}
> >  		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > @@ -634,1187 +239,3 @@ int drm_legacy_irq_control(struct drm_device
> > *dev, void *data,
> >  		return -EINVAL;
> >  	}
> >  }
> > -
> > -/**
> > - * drm_calc_timestamping_constants - calculate vblank timestamp constants
> > - * @crtc: drm_crtc whose timestamp constants should be updated.
> > - * @mode: display mode containing the scanout timings
> > - *
> > - * Calculate and store various constants which are later
> > - * needed by vblank and swap-completion timestamping, e.g,
> > - * by drm_calc_vbltimestamp_from_scanoutpos(). They are
> > - * derived from CRTC's true scanout timing, so they take
> > - * things like panel scaling or other adjustments into account.
> > - */
> > -void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> > -				     const struct drm_display_mode *mode)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned int pipe = drm_crtc_index(crtc);
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	int linedur_ns = 0, framedur_ns = 0;
> > -	int dotclock = mode->crtc_clock;
> > -
> > -	if (!dev->num_crtcs)
> > -		return;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	/* Valid dotclock? */
> > -	if (dotclock > 0) {
> > -		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
> > -
> > -		/*
> > -		 * Convert scanline length in pixels and video
> > -		 * dot clock to line duration and frame duration
> > -		 * in nanoseconds:
> > -		 */
> > -		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
> > -		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
> > -
> > -		/*
> > -		 * Fields of interlaced scanout modes are only half a frame duration.
> > -		 */
> > -		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> > -			framedur_ns /= 2;
> > -	} else
> > -		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
> > -			  crtc->base.id);
> > -
> > -	vblank->linedur_ns  = linedur_ns;
> > -	vblank->framedur_ns = framedur_ns;
> > -	vblank->hwmode = *mode;
> > -
> > -	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
> > -		  crtc->base.id, mode->crtc_htotal,
> > -		  mode->crtc_vtotal, mode->crtc_vdisplay);
> > -	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
> > -		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
> > -}
> > -EXPORT_SYMBOL(drm_calc_timestamping_constants);
> > -
> > -/**
> > - * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
> > - * @dev: DRM device
> > - * @pipe: index of CRTC whose vblank timestamp to retrieve
> > - * @max_error: Desired maximum allowable error in timestamps (nanosecs)
> > - *             On return contains true maximum error of timestamp
> > - * @vblank_time: Pointer to struct timeval which should receive the timestamp
> > - * @in_vblank_irq:
> > - *     True when called from drm_crtc_handle_vblank().  Some drivers
> > - *     need to apply some workarounds for gpu-specific vblank irq quirks
> > - *     if flag is set.
> > - *
> > - * Implements calculation of exact vblank timestamps from given
> > drm_display_mode
> > - * timings and current video scanout position of a CRTC. This can be
> > called from
> > - * within get_vblank_timestamp() implementation of a kms driver to
> > implement the
> > - * actual timestamping.
> > - *
> > - * Should return timestamps conforming to the OML_sync_control OpenML
> > - * extension specification. The timestamp corresponds to the end of
> > - * the vblank interval, aka start of scanout of topmost-leftmost display
> > - * pixel in the following video frame.
> > - *
> > - * Requires support for optional dev->driver->get_scanout_position()
> > - * in kms driver, plus a bit of setup code to provide a drm_display_mode
> > - * that corresponds to the true scanout timing.
> > - *
> > - * The current implementation only handles standard video modes. It
> > - * returns as no operation if a doublescan or interlaced video mode is
> > - * active. Higher level code is expected to handle this.
> > - *
> > - * This function can be used to implement the &drm_driver.get_vblank_timestamp
> > - * directly, if the driver implements the
> > &drm_driver.get_scanout_position hook.
> > - *
> > - * Note that atomic drivers must call drm_calc_timestamping_constants() before
> > - * enabling a CRTC. The atomic helpers already take care of that in
> > - * drm_atomic_helper_update_legacy_modeset_state().
> > - *
> > - * Returns:
> > - *
> > - * Returns true on success, and false on failure, i.e. when no accurate
> > - * timestamp could be acquired.
> > - */
> > -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> > -					   unsigned int pipe,
> > -					   int *max_error,
> > -					   struct timeval *vblank_time,
> > -					   bool in_vblank_irq)
> > -{
> > -	struct timeval tv_etime;
> > -	ktime_t stime, etime;
> > -	bool vbl_status;
> > -	struct drm_crtc *crtc;
> > -	const struct drm_display_mode *mode;
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	int vpos, hpos, i;
> > -	int delta_ns, duration_ns;
> > -
> > -	if (!drm_core_check_feature(dev, DRIVER_MODESET))
> > -		return false;
> > -
> > -	crtc = drm_crtc_from_index(dev, pipe);
> > -
> > -	if (pipe >= dev->num_crtcs || !crtc) {
> > -		DRM_ERROR("Invalid crtc %u\n", pipe);
> > -		return false;
> > -	}
> > -
> > -	/* Scanout position query not supported? Should not happen. */
> > -	if (!dev->driver->get_scanout_position) {
> > -		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
> > -		return false;
> > -	}
> > -
> > -	if (drm_drv_uses_atomic_modeset(dev))
> > -		mode = &vblank->hwmode;
> > -	else
> > -		mode = &crtc->hwmode;
> > -
> > -	/* If mode timing undefined, just return as no-op:
> > -	 * Happens during initial modesetting of a crtc.
> > -	 */
> > -	if (mode->crtc_clock == 0) {
> > -		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
> > -		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
> > -
> > -		return false;
> > -	}
> > -
> > -	/* Get current scanout position with system timestamp.
> > -	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
> > -	 * if single query takes longer than max_error nanoseconds.
> > -	 *
> > -	 * This guarantees a tight bound on maximum error if
> > -	 * code gets preempted or delayed for some reason.
> > -	 */
> > -	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
> > -		/*
> > -		 * Get vertical and horizontal scanout position vpos, hpos,
> > -		 * and bounding timestamps stime, etime, pre/post query.
> > -		 */
> > -		vbl_status = dev->driver->get_scanout_position(dev, pipe,
> > -							       in_vblank_irq,
> > -							       &vpos, &hpos,
> > -							       &stime, &etime,
> > -							       mode);
> > -
> > -		/* Return as no-op if scanout query unsupported or failed. */
> > -		if (!vbl_status) {
> > -			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
> > -				  pipe);
> > -			return false;
> > -		}
> > -
> > -		/* Compute uncertainty in timestamp of scanout position query. */
> > -		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
> > -
> > -		/* Accept result with <  max_error nsecs timing uncertainty. */
> > -		if (duration_ns <= *max_error)
> > -			break;
> > -	}
> > -
> > -	/* Noisy system timing? */
> > -	if (i == DRM_TIMESTAMP_MAXRETRIES) {
> > -		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
> > -			  pipe, duration_ns/1000, *max_error/1000, i);
> > -	}
> > -
> > -	/* Return upper bound of timestamp precision error. */
> > -	*max_error = duration_ns;
> > -
> > -	/* Convert scanout position into elapsed time at raw_time query
> > -	 * since start of scanout at first display scanline. delta_ns
> > -	 * can be negative if start of scanout hasn't happened yet.
> > -	 */
> > -	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
> > -			   mode->crtc_clock);
> > -
> > -	if (!drm_timestamp_monotonic)
> > -		etime = ktime_mono_to_real(etime);
> > -
> > -	/* save this only for debugging purposes */
> > -	tv_etime = ktime_to_timeval(etime);
> > -	/* Subtract time delta from raw timestamp to get final
> > -	 * vblank_time timestamp for end of vblank.
> > -	 */
> > -	etime = ktime_sub_ns(etime, delta_ns);
> > -	*vblank_time = ktime_to_timeval(etime);
> > -
> > -	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
> > -		      pipe, hpos, vpos,
> > -		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
> > -		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
> > -		      duration_ns/1000, i);
> > -
> > -	return true;
> > -}
> > -EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
> > -
> > -static struct timeval get_drm_timestamp(void)
> > -{
> > -	ktime_t now;
> > -
> > -	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
> > -	return ktime_to_timeval(now);
> > -}
> > -
> > -/**
> > - * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
> > - *                             vblank interval
> > - * @dev: DRM device
> > - * @pipe: index of CRTC whose vblank timestamp to retrieve
> > - * @tvblank: Pointer to target struct timeval which should receive
> > the timestamp
> > - * @in_vblank_irq:
> > - *     True when called from drm_crtc_handle_vblank().  Some drivers
> > - *     need to apply some workarounds for gpu-specific vblank irq quirks
> > - *     if flag is set.
> > - *
> > - * Fetches the system timestamp corresponding to the time of the most recent
> > - * vblank interval on specified CRTC. May call into kms-driver to
> > - * compute the timestamp with a high-precision GPU specific method.
> > - *
> > - * Returns zero if timestamp originates from uncorrected do_gettimeofday()
> > - * call, i.e., it isn't very precisely locked to the true vblank.
> > - *
> > - * Returns:
> > - * True if timestamp is considered to be very precise, false otherwise.
> > - */
> > -static bool
> > -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> > -			  struct timeval *tvblank, bool in_vblank_irq)
> > -{
> > -	bool ret = false;
> > -
> > -	/* Define requested maximum error on timestamps (nanoseconds). */
> > -	int max_error = (int) drm_timestamp_precision * 1000;
> > -
> > -	/* Query driver if possible and precision timestamping enabled. */
> > -	if (dev->driver->get_vblank_timestamp && (max_error > 0))
> > -		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
> > -							tvblank, in_vblank_irq);
> > -
> > -	/* GPU high precision timestamp query unsupported or failed.
> > -	 * Return current monotonic/gettimeofday timestamp as best estimate.
> > -	 */
> > -	if (!ret)
> > -		*tvblank = get_drm_timestamp();
> > -
> > -	return ret;
> > -}
> > -
> > -/**
> > - * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
> > - * @crtc: which counter to retrieve
> > - *
> > - * Fetches the "cooked" vblank count value that represents the number of
> > - * vblank events since the system was booted, including lost events due to
> > - * modesetting activity.
> > - *
> > - * Returns:
> > - * The software vblank counter.
> > - */
> > -u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
> > -{
> > -	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_count);
> > -
> > -/**
> > - * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
> > - *     system timestamp corresponding to that vblank counter value.
> > - * @dev: DRM device
> > - * @pipe: index of CRTC whose counter to retrieve
> > - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> > - *
> > - * Fetches the "cooked" vblank count value that represents the number of
> > - * vblank events since the system was booted, including lost events due to
> > - * modesetting activity. Returns corresponding system timestamp of the time
> > - * of the vblank interval that corresponds to the current vblank counter value.
> > - *
> > - * This is the legacy version of drm_crtc_vblank_count_and_time().
> > - */
> > -static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
> > -				     struct timeval *vblanktime)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	u32 vblank_count;
> > -	unsigned int seq;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs)) {
> > -		*vblanktime = (struct timeval) { 0 };
> > -		return 0;
> > -	}
> > -
> > -	do {
> > -		seq = read_seqbegin(&vblank->seqlock);
> > -		vblank_count = vblank->count;
> > -		*vblanktime = vblank->time;
> > -	} while (read_seqretry(&vblank->seqlock, seq));
> > -
> > -	return vblank_count;
> > -}
> > -
> > -/**
> > - * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
> > - *     and the system timestamp corresponding to that vblank counter value
> > - * @crtc: which counter to retrieve
> > - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> > - *
> > - * Fetches the "cooked" vblank count value that represents the number of
> > - * vblank events since the system was booted, including lost events due to
> > - * modesetting activity. Returns corresponding system timestamp of the time
> > - * of the vblank interval that corresponds to the current vblank counter value.
> > - */
> > -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> > -				   struct timeval *vblanktime)
> > -{
> > -	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
> > -					 vblanktime);
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
> > -
> > -static void send_vblank_event(struct drm_device *dev,
> > -		struct drm_pending_vblank_event *e,
> > -		unsigned long seq, struct timeval *now)
> > -{
> > -	e->event.sequence = seq;
> > -	e->event.tv_sec = now->tv_sec;
> > -	e->event.tv_usec = now->tv_usec;
> > -
> > -	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
> > -					 e->event.sequence);
> > -
> > -	drm_send_event_locked(dev, &e->base);
> > -}
> > -
> > -/**
> > - * drm_crtc_arm_vblank_event - arm vblank event after pageflip
> > - * @crtc: the source CRTC of the vblank event
> > - * @e: the event to send
> > - *
> > - * A lot of drivers need to generate vblank events for the very next vblank
> > - * interrupt. For example when the page flip interrupt happens when the page
> > - * flip gets armed, but not when it actually executes within the next vblank
> > - * period. This helper function implements exactly the required vblank arming
> > - * behaviour.
> > - *
> > - * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
> > - * atomic commit must ensure that the next vblank happens at exactly the same
> > - * time as the atomic commit is committed to the hardware. This function itself
> > - * does **not** protect again the next vblank interrupt racing with either this
> > - * function call or the atomic commit operation. A possible sequence could be:
> > - *
> > - * 1. Driver commits new hardware state into vblank-synchronized registers.
> > - * 2. A vblank happens, committing the hardware state. Also the corresponding
> > - *    vblank interrupt is fired off and fully processed by the interrupt
> > - *    handler.
> > - * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
> > - * 4. The event is only send out for the next vblank, which is wrong.
> > - *
> > - * An equivalent race can happen when the driver calls
> > - * drm_crtc_arm_vblank_event() before writing out the new hardware state.
> > - *
> > - * The only way to make this work safely is to prevent the vblank from firing
> > - * (and the hardware from committing anything else) until the entire atomic
> > - * commit sequence has run to completion. If the hardware does not have such a
> > - * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
> > - * Instead drivers need to manually send out the event from their interrupt
> > - * handler by calling drm_crtc_send_vblank_event() and make sure that
> > there's no
> > - * possible race with the hardware committing the atomic update.
> > - *
> > - * Caller must hold event lock. Caller must also hold a vblank reference for
> > - * the event @e, which will be dropped when the next vblank arrives.
> > - */
> > -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> > -			       struct drm_pending_vblank_event *e)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned int pipe = drm_crtc_index(crtc);
> > -
> > -	assert_spin_locked(&dev->event_lock);
> > -
> > -	e->pipe = pipe;
> > -	e->event.sequence = drm_vblank_count(dev, pipe);
> > -	e->event.crtc_id = crtc->base.id;
> > -	list_add_tail(&e->base.link, &dev->vblank_event_list);
> > -}
> > -EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
> > -
> > -/**
> > - * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
> > - * @crtc: the source CRTC of the vblank event
> > - * @e: the event to send
> > - *
> > - * Updates sequence # and timestamp on event for the most recently processed
> > - * vblank, and sends it to userspace.  Caller must hold event lock.
> > - *
> > - * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
> > - * situation, especially to send out events for atomic commit operations.
> > - */
> > -void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> > -				struct drm_pending_vblank_event *e)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned int seq, pipe = drm_crtc_index(crtc);
> > -	struct timeval now;
> > -
> > -	if (dev->num_crtcs > 0) {
> > -		seq = drm_vblank_count_and_time(dev, pipe, &now);
> > -	} else {
> > -		seq = 0;
> > -
> > -		now = get_drm_timestamp();
> > -	}
> > -	e->pipe = pipe;
> > -	e->event.crtc_id = crtc->base.id;
> > -	send_vblank_event(dev, e, seq, &now);
> > -}
> > -EXPORT_SYMBOL(drm_crtc_send_vblank_event);
> > -
> > -static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> > -		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> > -
> > -		if (crtc->funcs->enable_vblank)
> > -			return crtc->funcs->enable_vblank(crtc);
> > -	}
> > -
> > -	return dev->driver->enable_vblank(dev, pipe);
> > -}
> > -
> > -/**
> > - * drm_vblank_enable - enable the vblank interrupt on a CRTC
> > - * @dev: DRM device
> > - * @pipe: CRTC index
> > - *
> > - * Returns:
> > - * Zero on success or a negative error code on failure.
> > - */
> > -static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	int ret = 0;
> > -
> > -	assert_spin_locked(&dev->vbl_lock);
> > -
> > -	spin_lock(&dev->vblank_time_lock);
> > -
> > -	if (!vblank->enabled) {
> > -		/*
> > -		 * Enable vblank irqs under vblank_time_lock protection.
> > -		 * All vblank count & timestamp updates are held off
> > -		 * until we are done reinitializing master counter and
> > -		 * timestamps. Filtercode in drm_handle_vblank() will
> > -		 * prevent double-accounting of same vblank interval.
> > -		 */
> > -		ret = __enable_vblank(dev, pipe);
> > -		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
> > -		if (ret) {
> > -			atomic_dec(&vblank->refcount);
> > -		} else {
> > -			drm_update_vblank_count(dev, pipe, 0);
> > -			/* drm_update_vblank_count() includes a wmb so we just
> > -			 * need to ensure that the compiler emits the write
> > -			 * to mark the vblank as enabled after the call
> > -			 * to drm_update_vblank_count().
> > -			 */
> > -			WRITE_ONCE(vblank->enabled, true);
> > -		}
> > -	}
> > -
> > -	spin_unlock(&dev->vblank_time_lock);
> > -
> > -	return ret;
> > -}
> > -
> > -/**
> > - * drm_vblank_get - get a reference count on vblank events
> > - * @dev: DRM device
> > - * @pipe: index of CRTC to own
> > - *
> > - * Acquire a reference count on vblank events to avoid having them disabled
> > - * while in use.
> > - *
> > - * This is the legacy version of drm_crtc_vblank_get().
> > - *
> > - * Returns:
> > - * Zero on success or a negative error code on failure.
> > - */
> > -static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	unsigned long irqflags;
> > -	int ret = 0;
> > -
> > -	if (!dev->num_crtcs)
> > -		return -EINVAL;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return -EINVAL;
> > -
> > -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > -	/* Going from 0->1 means we have to enable interrupts again */
> > -	if (atomic_add_return(1, &vblank->refcount) == 1) {
> > -		ret = drm_vblank_enable(dev, pipe);
> > -	} else {
> > -		if (!vblank->enabled) {
> > -			atomic_dec(&vblank->refcount);
> > -			ret = -EINVAL;
> > -		}
> > -	}
> > -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > -
> > -	return ret;
> > -}
> > -
> > -/**
> > - * drm_crtc_vblank_get - get a reference count on vblank events
> > - * @crtc: which CRTC to own
> > - *
> > - * Acquire a reference count on vblank events to avoid having them disabled
> > - * while in use.
> > - *
> > - * Returns:
> > - * Zero on success or a negative error code on failure.
> > - */
> > -int drm_crtc_vblank_get(struct drm_crtc *crtc)
> > -{
> > -	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_get);
> > -
> > -/**
> > - * drm_vblank_put - release ownership of vblank events
> > - * @dev: DRM device
> > - * @pipe: index of CRTC to release
> > - *
> > - * Release ownership of a given vblank counter, turning off interrupts
> > - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> > - *
> > - * This is the legacy version of drm_crtc_vblank_put().
> > - */
> > -static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
> > -		return;
> > -
> > -	/* Last user schedules interrupt disable */
> > -	if (atomic_dec_and_test(&vblank->refcount)) {
> > -		if (drm_vblank_offdelay == 0)
> > -			return;
> > -		else if (drm_vblank_offdelay < 0)
> > -			vblank_disable_fn((unsigned long)vblank);
> > -		else if (!dev->vblank_disable_immediate)
> > -			mod_timer(&vblank->disable_timer,
> > -				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
> > -	}
> > -}
> > -
> > -/**
> > - * drm_crtc_vblank_put - give up ownership of vblank events
> > - * @crtc: which counter to give up
> > - *
> > - * Release ownership of a given vblank counter, turning off interrupts
> > - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> > - */
> > -void drm_crtc_vblank_put(struct drm_crtc *crtc)
> > -{
> > -	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_put);
> > -
> > -/**
> > - * drm_wait_one_vblank - wait for one vblank
> > - * @dev: DRM device
> > - * @pipe: CRTC index
> > - *
> > - * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
> > - * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
> > - * due to lack of driver support or because the crtc is off.
> > - */
> > -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	int ret;
> > -	u32 last;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	ret = drm_vblank_get(dev, pipe);
> > -	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
> > -		return;
> > -
> > -	last = drm_vblank_count(dev, pipe);
> > -
> > -	ret = wait_event_timeout(vblank->queue,
> > -				 last != drm_vblank_count(dev, pipe),
> > -				 msecs_to_jiffies(100));
> > -
> > -	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
> > -
> > -	drm_vblank_put(dev, pipe);
> > -}
> > -EXPORT_SYMBOL(drm_wait_one_vblank);
> > -
> > -/**
> > - * drm_crtc_wait_one_vblank - wait for one vblank
> > - * @crtc: DRM crtc
> > - *
> > - * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> > - * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
> > - * due to lack of driver support or because the crtc is off.
> > - */
> > -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> > -{
> > -	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
> > -}
> > -EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
> > -
> > -/**
> > - * drm_crtc_vblank_off - disable vblank events on a CRTC
> > - * @crtc: CRTC in question
> > - *
> > - * Drivers can use this function to shut down the vblank interrupt
> > handling when
> > - * disabling a crtc. This function ensures that the latest vblank
> > frame count is
> > - * stored so that drm_vblank_on can restore it again.
> > - *
> > - * Drivers must use this function when the hardware vblank counter can get
> > - * reset, e.g. when suspending.
> > - */
> > -void drm_crtc_vblank_off(struct drm_crtc *crtc)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned int pipe = drm_crtc_index(crtc);
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	struct drm_pending_vblank_event *e, *t;
> > -	struct timeval now;
> > -	unsigned long irqflags;
> > -	unsigned int seq;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	spin_lock_irqsave(&dev->event_lock, irqflags);
> > -
> > -	spin_lock(&dev->vbl_lock);
> > -	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> > -		      pipe, vblank->enabled, vblank->inmodeset);
> > -
> > -	/* Avoid redundant vblank disables without previous
> > -	 * drm_crtc_vblank_on(). */
> > -	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
> > -		vblank_disable_and_save(dev, pipe);
> > -
> > -	wake_up(&vblank->queue);
> > -
> > -	/*
> > -	 * Prevent subsequent drm_vblank_get() from re-enabling
> > -	 * the vblank interrupt by bumping the refcount.
> > -	 */
> > -	if (!vblank->inmodeset) {
> > -		atomic_inc(&vblank->refcount);
> > -		vblank->inmodeset = 1;
> > -	}
> > -	spin_unlock(&dev->vbl_lock);
> > -
> > -	/* Send any queued vblank events, lest the natives grow disquiet */
> > -	seq = drm_vblank_count_and_time(dev, pipe, &now);
> > -
> > -	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> > -		if (e->pipe != pipe)
> > -			continue;
> > -		DRM_DEBUG("Sending premature vblank event on disable: "
> > -			  "wanted %u, current %u\n",
> > -			  e->event.sequence, seq);
> > -		list_del(&e->base.link);
> > -		drm_vblank_put(dev, pipe);
> > -		send_vblank_event(dev, e, seq, &now);
> > -	}
> > -	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> > -
> > -	/* Will be reset by the modeset helpers when re-enabling the crtc by
> > -	 * calling drm_calc_timestamping_constants(). */
> > -	vblank->hwmode.crtc_clock = 0;
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_off);
> > -
> > -/**
> > - * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
> > - * @crtc: CRTC in question
> > - *
> > - * Drivers can use this function to reset the vblank state to off at load time.
> > - * Drivers should use this together with the drm_crtc_vblank_off() and
> > - * drm_crtc_vblank_on() functions. The difference compared to
> > - * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
> > - * and hence doesn't need to call any driver hooks.
> > - */
> > -void drm_crtc_vblank_reset(struct drm_crtc *crtc)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned long irqflags;
> > -	unsigned int pipe = drm_crtc_index(crtc);
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -
> > -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > -	/*
> > -	 * Prevent subsequent drm_vblank_get() from enabling the vblank
> > -	 * interrupt by bumping the refcount.
> > -	 */
> > -	if (!vblank->inmodeset) {
> > -		atomic_inc(&vblank->refcount);
> > -		vblank->inmodeset = 1;
> > -	}
> > -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > -
> > -	WARN_ON(!list_empty(&dev->vblank_event_list));
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_reset);
> > -
> > -/**
> > - * drm_crtc_vblank_on - enable vblank events on a CRTC
> > - * @crtc: CRTC in question
> > - *
> > - * This functions restores the vblank interrupt state captured with
> > - * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
> > - * drm_crtc_vblank_off() can be unbalanced and so can also be
> > unconditionally called
> > - * in driver load code to reflect the current hardware state of the crtc.
> > - */
> > -void drm_crtc_vblank_on(struct drm_crtc *crtc)
> > -{
> > -	struct drm_device *dev = crtc->dev;
> > -	unsigned int pipe = drm_crtc_index(crtc);
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	unsigned long irqflags;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > -	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> > -		      pipe, vblank->enabled, vblank->inmodeset);
> > -
> > -	/* Drop our private "prevent drm_vblank_get" refcount */
> > -	if (vblank->inmodeset) {
> > -		atomic_dec(&vblank->refcount);
> > -		vblank->inmodeset = 0;
> > -	}
> > -
> > -	drm_reset_vblank_timestamp(dev, pipe);
> > -
> > -	/*
> > -	 * re-enable interrupts if there are users left, or the
> > -	 * user wishes vblank interrupts to be enabled all the time.
> > -	 */
> > -	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
> > -		WARN_ON(drm_vblank_enable(dev, pipe));
> > -	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > -}
> > -EXPORT_SYMBOL(drm_crtc_vblank_on);
> > -
> > -static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
> > -					  unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -
> > -	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> > -	if (!dev->num_crtcs)
> > -		return;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	/*
> > -	 * To avoid all the problems that might happen if interrupts
> > -	 * were enabled/disabled around or between these calls, we just
> > -	 * have the kernel take a reference on the CRTC (just once though
> > -	 * to avoid corrupting the count if multiple, mismatch calls occur),
> > -	 * so that interrupts remain enabled in the interim.
> > -	 */
> > -	if (!vblank->inmodeset) {
> > -		vblank->inmodeset = 0x1;
> > -		if (drm_vblank_get(dev, pipe) == 0)
> > -			vblank->inmodeset |= 0x2;
> > -	}
> > -}
> > -
> > -static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
> > -					   unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	unsigned long irqflags;
> > -
> > -	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> > -	if (!dev->num_crtcs)
> > -		return;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return;
> > -
> > -	if (vblank->inmodeset) {
> > -		spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > -		drm_reset_vblank_timestamp(dev, pipe);
> > -		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > -
> > -		if (vblank->inmodeset & 0x2)
> > -			drm_vblank_put(dev, pipe);
> > -
> > -		vblank->inmodeset = 0;
> > -	}
> > -}
> > -
> > -int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
> > -			   struct drm_file *file_priv)
> > -{
> > -	struct drm_modeset_ctl *modeset = data;
> > -	unsigned int pipe;
> > -
> > -	/* If drm_vblank_init() hasn't been called yet, just no-op */
> > -	if (!dev->num_crtcs)
> > -		return 0;
> > -
> > -	/* KMS drivers handle this internally */
> > -	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
> > -		return 0;
> > -
> > -	pipe = modeset->crtc;
> > -	if (pipe >= dev->num_crtcs)
> > -		return -EINVAL;
> > -
> > -	switch (modeset->cmd) {
> > -	case _DRM_PRE_MODESET:
> > -		drm_legacy_vblank_pre_modeset(dev, pipe);
> > -		break;
> > -	case _DRM_POST_MODESET:
> > -		drm_legacy_vblank_post_modeset(dev, pipe);
> > -		break;
> > -	default:
> > -		return -EINVAL;
> > -	}
> > -
> > -	return 0;
> > -}
> > -
> > -static inline bool vblank_passed(u32 seq, u32 ref)
> > -{
> > -	return (seq - ref) <= (1 << 23);
> > -}
> > -
> > -static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
> > -				  union drm_wait_vblank *vblwait,
> > -				  struct drm_file *file_priv)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	struct drm_pending_vblank_event *e;
> > -	struct timeval now;
> > -	unsigned long flags;
> > -	unsigned int seq;
> > -	int ret;
> > -
> > -	e = kzalloc(sizeof(*e), GFP_KERNEL);
> > -	if (e == NULL) {
> > -		ret = -ENOMEM;
> > -		goto err_put;
> > -	}
> > -
> > -	e->pipe = pipe;
> > -	e->event.base.type = DRM_EVENT_VBLANK;
> > -	e->event.base.length = sizeof(e->event);
> > -	e->event.user_data = vblwait->request.signal;
> > -
> > -	spin_lock_irqsave(&dev->event_lock, flags);
> > -
> > -	/*
> > -	 * drm_crtc_vblank_off() might have been called after we called
> > -	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
> > -	 * vblank disable, so no need for further locking.  The reference from
> > -	 * drm_vblank_get() protects against vblank disable from another source.
> > -	 */
> > -	if (!READ_ONCE(vblank->enabled)) {
> > -		ret = -EINVAL;
> > -		goto err_unlock;
> > -	}
> > -
> > -	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
> > -					    &e->event.base);
> > -
> > -	if (ret)
> > -		goto err_unlock;
> > -
> > -	seq = drm_vblank_count_and_time(dev, pipe, &now);
> > -
> > -	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
> > -		  vblwait->request.sequence, seq, pipe);
> > -
> > -	trace_drm_vblank_event_queued(file_priv, pipe,
> > -				      vblwait->request.sequence);
> > -
> > -	e->event.sequence = vblwait->request.sequence;
> > -	if (vblank_passed(seq, vblwait->request.sequence)) {
> > -		drm_vblank_put(dev, pipe);
> > -		send_vblank_event(dev, e, seq, &now);
> > -		vblwait->reply.sequence = seq;
> > -	} else {
> > -		/* drm_handle_vblank_events will call drm_vblank_put */
> > -		list_add_tail(&e->base.link, &dev->vblank_event_list);
> > -		vblwait->reply.sequence = vblwait->request.sequence;
> > -	}
> > -
> > -	spin_unlock_irqrestore(&dev->event_lock, flags);
> > -
> > -	return 0;
> > -
> > -err_unlock:
> > -	spin_unlock_irqrestore(&dev->event_lock, flags);
> > -	kfree(e);
> > -err_put:
> > -	drm_vblank_put(dev, pipe);
> > -	return ret;
> > -}
> > -
> > -static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
> > -{
> > -	if (vblwait->request.sequence)
> > -		return false;
> > -
> > -	return _DRM_VBLANK_RELATIVE ==
> > -		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
> > -					  _DRM_VBLANK_EVENT |
> > -					  _DRM_VBLANK_NEXTONMISS));
> > -}
> > -
> > -/*
> > - * Wait for VBLANK.
> > - *
> > - * \param inode device inode.
> > - * \param file_priv DRM file private.
> > - * \param cmd command.
> > - * \param data user argument, pointing to a drm_wait_vblank structure.
> > - * \return zero on success or a negative number on failure.
> > - *
> > - * This function enables the vblank interrupt on the pipe requested, then
> > - * sleeps waiting for the requested sequence number to occur, and drops
> > - * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
> > - * after a timeout with no further vblank waits scheduled).
> > - */
> > -int drm_wait_vblank(struct drm_device *dev, void *data,
> > -		    struct drm_file *file_priv)
> > -{
> > -	struct drm_vblank_crtc *vblank;
> > -	union drm_wait_vblank *vblwait = data;
> > -	int ret;
> > -	unsigned int flags, seq, pipe, high_pipe;
> > -
> > -	if (!dev->irq_enabled)
> > -		return -EINVAL;
> > -
> > -	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
> > -		return -EINVAL;
> > -
> > -	if (vblwait->request.type &
> > -	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> > -	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
> > -		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
> > -			  vblwait->request.type,
> > -			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> > -			   _DRM_VBLANK_HIGH_CRTC_MASK));
> > -		return -EINVAL;
> > -	}
> > -
> > -	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
> > -	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
> > -	if (high_pipe)
> > -		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
> > -	else
> > -		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
> > -	if (pipe >= dev->num_crtcs)
> > -		return -EINVAL;
> > -
> > -	vblank = &dev->vblank[pipe];
> > -
> > -	/* If the counter is currently enabled and accurate, short-circuit
> > -	 * queries to return the cached timestamp of the last vblank.
> > -	 */
> > -	if (dev->vblank_disable_immediate &&
> > -	    drm_wait_vblank_is_query(vblwait) &&
> > -	    READ_ONCE(vblank->enabled)) {
> > -		struct timeval now;
> > -
> > -		vblwait->reply.sequence =
> > -			drm_vblank_count_and_time(dev, pipe, &now);
> > -		vblwait->reply.tval_sec = now.tv_sec;
> > -		vblwait->reply.tval_usec = now.tv_usec;
> > -		return 0;
> > -	}
> > -
> > -	ret = drm_vblank_get(dev, pipe);
> > -	if (ret) {
> > -		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
> > -		return ret;
> > -	}
> > -	seq = drm_vblank_count(dev, pipe);
> > -
> > -	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
> > -	case _DRM_VBLANK_RELATIVE:
> > -		vblwait->request.sequence += seq;
> > -		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
> > -	case _DRM_VBLANK_ABSOLUTE:
> > -		break;
> > -	default:
> > -		ret = -EINVAL;
> > -		goto done;
> > -	}
> > -
> > -	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
> > -	    vblank_passed(seq, vblwait->request.sequence))
> > -		vblwait->request.sequence = seq + 1;
> > -
> > -	if (flags & _DRM_VBLANK_EVENT) {
> > -		/* must hold on to the vblank ref until the event fires
> > -		 * drm_vblank_put will be called asynchronously
> > -		 */
> > -		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
> > -	}
> > -
> > -	if (vblwait->request.sequence != seq) {
> > -		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
> > -			  vblwait->request.sequence, pipe);
> > -		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
> > -			    vblank_passed(drm_vblank_count(dev, pipe),
> > -					  vblwait->request.sequence) ||
> > -			    !READ_ONCE(vblank->enabled));
> > -	}
> > -
> > -	if (ret != -EINTR) {
> > -		struct timeval now;
> > -
> > -		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
> > -		vblwait->reply.tval_sec = now.tv_sec;
> > -		vblwait->reply.tval_usec = now.tv_usec;
> > -
> > -		DRM_DEBUG("crtc %d returning %u to client\n",
> > -			  pipe, vblwait->reply.sequence);
> > -	} else {
> > -		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
> > -	}
> > -
> > -done:
> > -	drm_vblank_put(dev, pipe);
> > -	return ret;
> > -}
> > -
> > -static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_pending_vblank_event *e, *t;
> > -	struct timeval now;
> > -	unsigned int seq;
> > -
> > -	assert_spin_locked(&dev->event_lock);
> > -
> > -	seq = drm_vblank_count_and_time(dev, pipe, &now);
> > -
> > -	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> > -		if (e->pipe != pipe)
> > -			continue;
> > -		if (!vblank_passed(seq, e->event.sequence))
> > -			continue;
> > -
> > -		DRM_DEBUG("vblank event on %u, current %u\n",
> > -			  e->event.sequence, seq);
> > -
> > -		list_del(&e->base.link);
> > -		drm_vblank_put(dev, pipe);
> > -		send_vblank_event(dev, e, seq, &now);
> > -	}
> > -
> > -	trace_drm_vblank_event(pipe, seq);
> > -}
> > -
> > -/**
> > - * drm_handle_vblank - handle a vblank event
> > - * @dev: DRM device
> > - * @pipe: index of CRTC where this event occurred
> > - *
> > - * Drivers should call this routine in their vblank interrupt handlers to
> > - * update the vblank counter and send any signals that may be pending.
> > - *
> > - * This is the legacy version of drm_crtc_handle_vblank().
> > - */
> > -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
> > -{
> > -	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > -	unsigned long irqflags;
> > -	bool disable_irq;
> > -
> > -	if (WARN_ON_ONCE(!dev->num_crtcs))
> > -		return false;
> > -
> > -	if (WARN_ON(pipe >= dev->num_crtcs))
> > -		return false;
> > -
> > -	spin_lock_irqsave(&dev->event_lock, irqflags);
> > -
> > -	/* Need timestamp lock to prevent concurrent execution with
> > -	 * vblank enable/disable, as this would cause inconsistent
> > -	 * or corrupted timestamps and vblank counts.
> > -	 */
> > -	spin_lock(&dev->vblank_time_lock);
> > -
> > -	/* Vblank irq handling disabled. Nothing to do. */
> > -	if (!vblank->enabled) {
> > -		spin_unlock(&dev->vblank_time_lock);
> > -		spin_unlock_irqrestore(&dev->event_lock, irqflags);
> > -		return false;
> > -	}
> > -
> > -	drm_update_vblank_count(dev, pipe, true);
> > -
> > -	spin_unlock(&dev->vblank_time_lock);
> > -
> > -	wake_up(&vblank->queue);
> > -
> > -	/* With instant-off, we defer disabling the interrupt until after
> > -	 * we finish processing the following vblank after all events have
> > -	 * been signaled. The disable has to be last (after
> > -	 * drm_handle_vblank_events) so that the timestamp is always accurate.
> > -	 */
> > -	disable_irq = (dev->vblank_disable_immediate &&
> > -		       drm_vblank_offdelay > 0 &&
> > -		       !atomic_read(&vblank->refcount));
> > -
> > -	drm_handle_vblank_events(dev, pipe);
> > -
> > -	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> > -
> > -	if (disable_irq)
> > -		vblank_disable_fn((unsigned long)vblank);
> > -
> > -	return true;
> > -}
> > -EXPORT_SYMBOL(drm_handle_vblank);
> > -
> > -/**
> > - * drm_crtc_handle_vblank - handle a vblank event
> > - * @crtc: where this event occurred
> > - *
> > - * Drivers should call this routine in their vblank interrupt handlers to
> > - * update the vblank counter and send any signals that may be pending.
> > - *
> > - * This is the native KMS version of drm_handle_vblank().
> > - *
> > - * Returns:
> > - * True if the event was successfully handled, false on failure.
> > - */
> > -bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
> > -{
> > -	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
> > -}
> > -EXPORT_SYMBOL(drm_crtc_handle_vblank);
> > diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
> > new file mode 100644
> > index 000000000000..630dc26379b7
> > --- /dev/null
> > +++ b/drivers/gpu/drm/drm_vblank.c
> > @@ -0,0 +1,1645 @@
> > +/*
> > + * drm_irq.c IRQ and vblank support
> > + *
> > + * \author Rickard E. (Rik) Faith <faith@valinux.com>
> > + * \author Gareth Hughes <gareth@valinux.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> > + * OTHER DEALINGS IN THE SOFTWARE.
> > + */
> > +
> > +#include <drm/drm_vblank.h>
> > +#include <drm/drmP.h>
> > +#include <linux/export.h>
> > +
> > +#include "drm_trace.h"
> > +#include "drm_internal.h"
> > +
> > +/* Retry timestamp calculation up to 3 times to satisfy
> > + * drm_timestamp_precision before giving up.
> > + */
> > +#define DRM_TIMESTAMP_MAXRETRIES 3
> > +
> > +/* Threshold in nanoseconds for detection of redundant
> > + * vblank irq in drm_handle_vblank(). 1 msec should be ok.
> > + */
> > +#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
> > +
> > +static bool
> > +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> > +			  struct timeval *tvblank, bool in_vblank_irq);
> > +
> > +static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
> > +
> > +/*
> > + * Default to use monotonic timestamps for wait-for-vblank and page-flip
> > + * complete events.
> > + */
> > +unsigned int drm_timestamp_monotonic = 1;
> > +
> > +static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
> > +
> > +module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
> > +module_param_named(timestamp_precision_usec, drm_timestamp_precision,
> > int, 0600);
> > +module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
> > +MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable
> > [msecs] (0: never disable, <0: disable immediately)");
> > +MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
> > +MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
> > +
> > +static void store_vblank(struct drm_device *dev, unsigned int pipe,
> > +			 u32 vblank_count_inc,
> > +			 struct timeval *t_vblank, u32 last)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +
> > +	assert_spin_locked(&dev->vblank_time_lock);
> > +
> > +	vblank->last = last;
> > +
> > +	write_seqlock(&vblank->seqlock);
> > +	vblank->time = *t_vblank;
> > +	vblank->count += vblank_count_inc;
> > +	write_sequnlock(&vblank->seqlock);
> > +}
> > +
> > +/*
> > + * "No hw counter" fallback implementation of .get_vblank_counter() hook,
> > + * if there is no useable hardware frame counter available.
> > + */
> > +static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	WARN_ON_ONCE(dev->max_vblank_count != 0);
> > +	return 0;
> > +}
> > +
> > +static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> > +		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> > +
> > +		if (crtc->funcs->get_vblank_counter)
> > +			return crtc->funcs->get_vblank_counter(crtc);
> > +	}
> > +
> > +	if (dev->driver->get_vblank_counter)
> > +		return dev->driver->get_vblank_counter(dev, pipe);
> > +
> > +	return drm_vblank_no_hw_counter(dev, pipe);
> > +}
> > +
> > +/*
> > + * Reset the stored timestamp for the current vblank count to correspond
> > + * to the last vblank occurred.
> > + *
> > + * Only to be called from drm_crtc_vblank_on().
> > + *
> > + * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> > + * device vblank fields.
> > + */
> > +static void drm_reset_vblank_timestamp(struct drm_device *dev,
> > unsigned int pipe)
> > +{
> > +	u32 cur_vblank;
> > +	bool rc;
> > +	struct timeval t_vblank;
> > +	int count = DRM_TIMESTAMP_MAXRETRIES;
> > +
> > +	spin_lock(&dev->vblank_time_lock);
> > +
> > +	/*
> > +	 * sample the current counter to avoid random jumps
> > +	 * when drm_vblank_enable() applies the diff
> > +	 */
> > +	do {
> > +		cur_vblank = __get_vblank_counter(dev, pipe);
> > +		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
> > +	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> > +
> > +	/*
> > +	 * Only reinitialize corresponding vblank timestamp if high-precision query
> > +	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
> > +	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
> > +	 */
> > +	if (!rc)
> > +		t_vblank = (struct timeval) {0, 0};
> > +
> > +	/*
> > +	 * +1 to make sure user will never see the same
> > +	 * vblank counter value before and after a modeset
> > +	 */
> > +	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
> > +
> > +	spin_unlock(&dev->vblank_time_lock);
> > +}
> > +
> > +/*
> > + * Call back into the driver to update the appropriate vblank counter
> > + * (specified by @pipe).  Deal with wraparound, if it occurred, and
> > + * update the last read value so we can deal with wraparound on the next
> > + * call if necessary.
> > + *
> > + * Only necessary when going from off->on, to account for frames we
> > + * didn't get an interrupt for.
> > + *
> > + * Note: caller must hold &drm_device.vbl_lock since this reads & writes
> > + * device vblank fields.
> > + */
> > +static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
> > +				    bool in_vblank_irq)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	u32 cur_vblank, diff;
> > +	bool rc;
> > +	struct timeval t_vblank;
> > +	int count = DRM_TIMESTAMP_MAXRETRIES;
> > +	int framedur_ns = vblank->framedur_ns;
> > +
> > +	/*
> > +	 * Interrupts were disabled prior to this call, so deal with counter
> > +	 * wrap if needed.
> > +	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
> > +	 * here if the register is small or we had vblank interrupts off for
> > +	 * a long time.
> > +	 *
> > +	 * We repeat the hardware vblank counter & timestamp query until
> > +	 * we get consistent results. This to prevent races between gpu
> > +	 * updating its hardware counter while we are retrieving the
> > +	 * corresponding vblank timestamp.
> > +	 */
> > +	do {
> > +		cur_vblank = __get_vblank_counter(dev, pipe);
> > +		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
> > +	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
> > +
> > +	if (dev->max_vblank_count != 0) {
> > +		/* trust the hw counter when it's around */
> > +		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
> > +	} else if (rc && framedur_ns) {
> > +		const struct timeval *t_old;
> > +		u64 diff_ns;
> > +
> > +		t_old = &vblank->time;
> > +		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
> > +
> > +		/*
> > +		 * Figure out how many vblanks we've missed based
> > +		 * on the difference in the timestamps and the
> > +		 * frame/field duration.
> > +		 */
> > +		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
> > +
> > +		if (diff == 0 && in_vblank_irq)
> > +			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
> > +				      " diff_ns = %lld, framedur_ns = %d)\n",
> > +				      pipe, (long long) diff_ns, framedur_ns);
> > +	} else {
> > +		/* some kind of default for drivers w/o accurate vbl timestamping */
> > +		diff = in_vblank_irq ? 1 : 0;
> > +	}
> > +
> > +	/*
> > +	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
> > +	 * interval? If so then vblank irqs keep running and it will likely
> > +	 * happen that the hardware vblank counter is not trustworthy as it
> > +	 * might reset at some point in that interval and vblank timestamps
> > +	 * are not trustworthy either in that interval. Iow. this can result
> > +	 * in a bogus diff >> 1 which must be avoided as it would cause
> > +	 * random large forward jumps of the software vblank counter.
> > +	 */
> > +	if (diff > 1 && (vblank->inmodeset & 0x2)) {
> > +		DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u"
> > +			      " due to pre-modeset.\n", pipe, diff);
> > +		diff = 1;
> > +	}
> > +
> > +	DRM_DEBUG_VBL("updating vblank count on crtc %u:"
> > +		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
> > +		      pipe, vblank->count, diff, cur_vblank, vblank->last);
> > +
> > +	if (diff == 0) {
> > +		WARN_ON_ONCE(cur_vblank != vblank->last);
> > +		return;
> > +	}
> > +
> > +	/*
> > +	 * Only reinitialize corresponding vblank timestamp if high-precision query
> > +	 * available and didn't fail, or we were called from the vblank interrupt.
> > +	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
> > +	 * for now, to mark the vblanktimestamp as invalid.
> > +	 */
> > +	if (!rc && in_vblank_irq)
> > +		t_vblank = (struct timeval) {0, 0};
> > +
> > +	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
> > +}
> > +
> > +static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return 0;
> > +
> > +	return vblank->count;
> > +}
> > +
> > +/**
> > + * drm_accurate_vblank_count - retrieve the master vblank counter
> > + * @crtc: which counter to retrieve
> > + *
> > + * This function is similar to @drm_crtc_vblank_count but this
> > + * function interpolates to handle a race with vblank irq's.
> > + *
> > + * This is mostly useful for hardware that can obtain the scanout
> > + * position, but doesn't have a frame counter.
> > + */
> > +u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned int pipe = drm_crtc_index(crtc);
> > +	u32 vblank;
> > +	unsigned long flags;
> > +
> > +	WARN(!dev->driver->get_vblank_timestamp,
> > +	     "This function requires support for accurate vblank timestamps.");
> > +
> > +	spin_lock_irqsave(&dev->vblank_time_lock, flags);
> > +
> > +	drm_update_vblank_count(dev, pipe, false);
> > +	vblank = drm_vblank_count(dev, pipe);
> > +
> > +	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
> > +
> > +	return vblank;
> > +}
> > +EXPORT_SYMBOL(drm_accurate_vblank_count);
> > +
> > +static void __disable_vblank(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> > +		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> > +
> > +		if (crtc->funcs->disable_vblank) {
> > +			crtc->funcs->disable_vblank(crtc);
> > +			return;
> > +		}
> > +	}
> > +
> > +	dev->driver->disable_vblank(dev, pipe);
> > +}
> > +
> > +/*
> > + * Disable vblank irq's on crtc, make sure that last vblank count
> > + * of hardware and corresponding consistent software vblank counter
> > + * are preserved, even if there are any spurious vblank irq's after
> > + * disable.
> > + */
> > +void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	unsigned long irqflags;
> > +
> > +	assert_spin_locked(&dev->vbl_lock);
> > +
> > +	/* Prevent vblank irq processing while disabling vblank irqs,
> > +	 * so no updates of timestamps or count can happen after we've
> > +	 * disabled. Needed to prevent races in case of delayed irq's.
> > +	 */
> > +	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
> > +
> > +	/*
> > +	 * Only disable vblank interrupts if they're enabled. This avoids
> > +	 * calling the ->disable_vblank() operation in atomic context with the
> > +	 * hardware potentially runtime suspended.
> > +	 */
> > +	if (vblank->enabled) {
> > +		__disable_vblank(dev, pipe);
> > +		vblank->enabled = false;
> > +	}
> > +
> > +	/*
> > +	 * Always update the count and timestamp to maintain the
> > +	 * appearance that the counter has been ticking all along until
> > +	 * this time. This makes the count account for the entire time
> > +	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
> > +	 */
> > +	drm_update_vblank_count(dev, pipe, false);
> > +
> > +	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
> > +}
> > +
> > +static void vblank_disable_fn(unsigned long arg)
> > +{
> > +	struct drm_vblank_crtc *vblank = (void *)arg;
> > +	struct drm_device *dev = vblank->dev;
> > +	unsigned int pipe = vblank->pipe;
> > +	unsigned long irqflags;
> > +
> > +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > +	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
> > +		DRM_DEBUG("disabling vblank on crtc %u\n", pipe);
> > +		drm_vblank_disable_and_save(dev, pipe);
> > +	}
> > +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > +}
> > +
> > +/**
> > + * drm_vblank_cleanup - cleanup vblank support
> > + * @dev: DRM device
> > + *
> > + * This function cleans up any resources allocated in drm_vblank_init.
> > + */
> > +void drm_vblank_cleanup(struct drm_device *dev)
> > +{
> > +	unsigned int pipe;
> > +
> > +	/* Bail if the driver didn't call drm_vblank_init() */
> > +	if (dev->num_crtcs == 0)
> > +		return;
> > +
> > +	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
> > +		struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +
> > +		WARN_ON(READ_ONCE(vblank->enabled) &&
> > +			drm_core_check_feature(dev, DRIVER_MODESET));
> > +
> > +		del_timer_sync(&vblank->disable_timer);
> > +	}
> > +
> > +	kfree(dev->vblank);
> > +
> > +	dev->num_crtcs = 0;
> > +}
> > +EXPORT_SYMBOL(drm_vblank_cleanup);
> > +
> > +/**
> > + * drm_vblank_init - initialize vblank support
> > + * @dev: DRM device
> > + * @num_crtcs: number of CRTCs supported by @dev
> > + *
> > + * This function initializes vblank support for @num_crtcs display pipelines.
> > + *
> > + * Returns:
> > + * Zero on success or a negative error code on failure.
> > + */
> > +int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
> > +{
> > +	int ret = -ENOMEM;
> > +	unsigned int i;
> > +
> > +	spin_lock_init(&dev->vbl_lock);
> > +	spin_lock_init(&dev->vblank_time_lock);
> > +
> > +	dev->num_crtcs = num_crtcs;
> > +
> > +	dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
> > +	if (!dev->vblank)
> > +		goto err;
> > +
> > +	for (i = 0; i < num_crtcs; i++) {
> > +		struct drm_vblank_crtc *vblank = &dev->vblank[i];
> > +
> > +		vblank->dev = dev;
> > +		vblank->pipe = i;
> > +		init_waitqueue_head(&vblank->queue);
> > +		setup_timer(&vblank->disable_timer, vblank_disable_fn,
> > +			    (unsigned long)vblank);
> > +		seqlock_init(&vblank->seqlock);
> > +	}
> > +
> > +	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
> > +
> > +	/* Driver specific high-precision vblank timestamping supported? */
> > +	if (dev->driver->get_vblank_timestamp)
> > +		DRM_INFO("Driver supports precise vblank timestamp query.\n");
> > +	else
> > +		DRM_INFO("No driver support for vblank timestamp query.\n");
> > +
> > +	/* Must have precise timestamping for reliable vblank instant disable */
> > +	if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
> > +		dev->vblank_disable_immediate = false;
> > +		DRM_INFO("Setting vblank_disable_immediate to false because "
> > +			 "get_vblank_timestamp == NULL\n");
> > +	}
> > +
> > +	return 0;
> > +
> > +err:
> > +	dev->num_crtcs = 0;
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(drm_vblank_init);
> > +
> > +/**
> > + * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
> > + * @crtc: which CRTC's vblank waitqueue to retrieve
> > + *
> > + * This function returns a pointer to the vblank waitqueue for the CRTC.
> > + * Drivers can use this to implement vblank waits using wait_event()
> > and related
> > + * functions.
> > + */
> > +wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
> > +{
> > +	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
> > +
> > +
> > +/**
> > + * drm_calc_timestamping_constants - calculate vblank timestamp constants
> > + * @crtc: drm_crtc whose timestamp constants should be updated.
> > + * @mode: display mode containing the scanout timings
> > + *
> > + * Calculate and store various constants which are later
> > + * needed by vblank and swap-completion timestamping, e.g,
> > + * by drm_calc_vbltimestamp_from_scanoutpos(). They are
> > + * derived from CRTC's true scanout timing, so they take
> > + * things like panel scaling or other adjustments into account.
> > + */
> > +void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> > +				     const struct drm_display_mode *mode)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned int pipe = drm_crtc_index(crtc);
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	int linedur_ns = 0, framedur_ns = 0;
> > +	int dotclock = mode->crtc_clock;
> > +
> > +	if (!dev->num_crtcs)
> > +		return;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	/* Valid dotclock? */
> > +	if (dotclock > 0) {
> > +		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
> > +
> > +		/*
> > +		 * Convert scanline length in pixels and video
> > +		 * dot clock to line duration and frame duration
> > +		 * in nanoseconds:
> > +		 */
> > +		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
> > +		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
> > +
> > +		/*
> > +		 * Fields of interlaced scanout modes are only half a frame duration.
> > +		 */
> > +		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> > +			framedur_ns /= 2;
> > +	} else
> > +		DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
> > +			  crtc->base.id);
> > +
> > +	vblank->linedur_ns  = linedur_ns;
> > +	vblank->framedur_ns = framedur_ns;
> > +	vblank->hwmode = *mode;
> > +
> > +	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
> > +		  crtc->base.id, mode->crtc_htotal,
> > +		  mode->crtc_vtotal, mode->crtc_vdisplay);
> > +	DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
> > +		  crtc->base.id, dotclock, framedur_ns, linedur_ns);
> > +}
> > +EXPORT_SYMBOL(drm_calc_timestamping_constants);
> > +
> > +/**
> > + * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
> > + * @dev: DRM device
> > + * @pipe: index of CRTC whose vblank timestamp to retrieve
> > + * @max_error: Desired maximum allowable error in timestamps (nanosecs)
> > + *             On return contains true maximum error of timestamp
> > + * @vblank_time: Pointer to struct timeval which should receive the timestamp
> > + * @in_vblank_irq:
> > + *     True when called from drm_crtc_handle_vblank().  Some drivers
> > + *     need to apply some workarounds for gpu-specific vblank irq quirks
> > + *     if flag is set.
> > + *
> > + * Implements calculation of exact vblank timestamps from given
> > drm_display_mode
> > + * timings and current video scanout position of a CRTC. This can be
> > called from
> > + * within get_vblank_timestamp() implementation of a kms driver to
> > implement the
> > + * actual timestamping.
> > + *
> > + * Should return timestamps conforming to the OML_sync_control OpenML
> > + * extension specification. The timestamp corresponds to the end of
> > + * the vblank interval, aka start of scanout of topmost-leftmost display
> > + * pixel in the following video frame.
> > + *
> > + * Requires support for optional dev->driver->get_scanout_position()
> > + * in kms driver, plus a bit of setup code to provide a drm_display_mode
> > + * that corresponds to the true scanout timing.
> > + *
> > + * The current implementation only handles standard video modes. It
> > + * returns as no operation if a doublescan or interlaced video mode is
> > + * active. Higher level code is expected to handle this.
> > + *
> > + * This function can be used to implement the &drm_driver.get_vblank_timestamp
> > + * directly, if the driver implements the
> > &drm_driver.get_scanout_position hook.
> > + *
> > + * Note that atomic drivers must call drm_calc_timestamping_constants() before
> > + * enabling a CRTC. The atomic helpers already take care of that in
> > + * drm_atomic_helper_update_legacy_modeset_state().
> > + *
> > + * Returns:
> > + *
> > + * Returns true on success, and false on failure, i.e. when no accurate
> > + * timestamp could be acquired.
> > + */
> > +bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> > +					   unsigned int pipe,
> > +					   int *max_error,
> > +					   struct timeval *vblank_time,
> > +					   bool in_vblank_irq)
> > +{
> > +	struct timeval tv_etime;
> > +	ktime_t stime, etime;
> > +	bool vbl_status;
> > +	struct drm_crtc *crtc;
> > +	const struct drm_display_mode *mode;
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	int vpos, hpos, i;
> > +	int delta_ns, duration_ns;
> > +
> > +	if (!drm_core_check_feature(dev, DRIVER_MODESET))
> > +		return false;
> > +
> > +	crtc = drm_crtc_from_index(dev, pipe);
> > +
> > +	if (pipe >= dev->num_crtcs || !crtc) {
> > +		DRM_ERROR("Invalid crtc %u\n", pipe);
> > +		return false;
> > +	}
> > +
> > +	/* Scanout position query not supported? Should not happen. */
> > +	if (!dev->driver->get_scanout_position) {
> > +		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
> > +		return false;
> > +	}
> > +
> > +	if (drm_drv_uses_atomic_modeset(dev))
> > +		mode = &vblank->hwmode;
> > +	else
> > +		mode = &crtc->hwmode;
> > +
> > +	/* If mode timing undefined, just return as no-op:
> > +	 * Happens during initial modesetting of a crtc.
> > +	 */
> > +	if (mode->crtc_clock == 0) {
> > +		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
> > +		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
> > +
> > +		return false;
> > +	}
> > +
> > +	/* Get current scanout position with system timestamp.
> > +	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
> > +	 * if single query takes longer than max_error nanoseconds.
> > +	 *
> > +	 * This guarantees a tight bound on maximum error if
> > +	 * code gets preempted or delayed for some reason.
> > +	 */
> > +	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
> > +		/*
> > +		 * Get vertical and horizontal scanout position vpos, hpos,
> > +		 * and bounding timestamps stime, etime, pre/post query.
> > +		 */
> > +		vbl_status = dev->driver->get_scanout_position(dev, pipe,
> > +							       in_vblank_irq,
> > +							       &vpos, &hpos,
> > +							       &stime, &etime,
> > +							       mode);
> > +
> > +		/* Return as no-op if scanout query unsupported or failed. */
> > +		if (!vbl_status) {
> > +			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
> > +				  pipe);
> > +			return false;
> > +		}
> > +
> > +		/* Compute uncertainty in timestamp of scanout position query. */
> > +		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
> > +
> > +		/* Accept result with <  max_error nsecs timing uncertainty. */
> > +		if (duration_ns <= *max_error)
> > +			break;
> > +	}
> > +
> > +	/* Noisy system timing? */
> > +	if (i == DRM_TIMESTAMP_MAXRETRIES) {
> > +		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
> > +			  pipe, duration_ns/1000, *max_error/1000, i);
> > +	}
> > +
> > +	/* Return upper bound of timestamp precision error. */
> > +	*max_error = duration_ns;
> > +
> > +	/* Convert scanout position into elapsed time at raw_time query
> > +	 * since start of scanout at first display scanline. delta_ns
> > +	 * can be negative if start of scanout hasn't happened yet.
> > +	 */
> > +	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
> > +			   mode->crtc_clock);
> > +
> > +	if (!drm_timestamp_monotonic)
> > +		etime = ktime_mono_to_real(etime);
> > +
> > +	/* save this only for debugging purposes */
> > +	tv_etime = ktime_to_timeval(etime);
> > +	/* Subtract time delta from raw timestamp to get final
> > +	 * vblank_time timestamp for end of vblank.
> > +	 */
> > +	etime = ktime_sub_ns(etime, delta_ns);
> > +	*vblank_time = ktime_to_timeval(etime);
> > +
> > +	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
> > +		      pipe, hpos, vpos,
> > +		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
> > +		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
> > +		      duration_ns/1000, i);
> > +
> > +	return true;
> > +}
> > +EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
> > +
> > +static struct timeval get_drm_timestamp(void)
> > +{
> > +	ktime_t now;
> > +
> > +	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
> > +	return ktime_to_timeval(now);
> > +}
> > +
> > +/**
> > + * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
> > + *                             vblank interval
> > + * @dev: DRM device
> > + * @pipe: index of CRTC whose vblank timestamp to retrieve
> > + * @tvblank: Pointer to target struct timeval which should receive
> > the timestamp
> > + * @in_vblank_irq:
> > + *     True when called from drm_crtc_handle_vblank().  Some drivers
> > + *     need to apply some workarounds for gpu-specific vblank irq quirks
> > + *     if flag is set.
> > + *
> > + * Fetches the system timestamp corresponding to the time of the most recent
> > + * vblank interval on specified CRTC. May call into kms-driver to
> > + * compute the timestamp with a high-precision GPU specific method.
> > + *
> > + * Returns zero if timestamp originates from uncorrected do_gettimeofday()
> > + * call, i.e., it isn't very precisely locked to the true vblank.
> > + *
> > + * Returns:
> > + * True if timestamp is considered to be very precise, false otherwise.
> > + */
> > +static bool
> > +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> > +			  struct timeval *tvblank, bool in_vblank_irq)
> > +{
> > +	bool ret = false;
> > +
> > +	/* Define requested maximum error on timestamps (nanoseconds). */
> > +	int max_error = (int) drm_timestamp_precision * 1000;
> > +
> > +	/* Query driver if possible and precision timestamping enabled. */
> > +	if (dev->driver->get_vblank_timestamp && (max_error > 0))
> > +		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
> > +							tvblank, in_vblank_irq);
> > +
> > +	/* GPU high precision timestamp query unsupported or failed.
> > +	 * Return current monotonic/gettimeofday timestamp as best estimate.
> > +	 */
> > +	if (!ret)
> > +		*tvblank = get_drm_timestamp();
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
> > + * @crtc: which counter to retrieve
> > + *
> > + * Fetches the "cooked" vblank count value that represents the number of
> > + * vblank events since the system was booted, including lost events due to
> > + * modesetting activity.
> > + *
> > + * Returns:
> > + * The software vblank counter.
> > + */
> > +u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
> > +{
> > +	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_count);
> > +
> > +/**
> > + * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
> > + *     system timestamp corresponding to that vblank counter value.
> > + * @dev: DRM device
> > + * @pipe: index of CRTC whose counter to retrieve
> > + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> > + *
> > + * Fetches the "cooked" vblank count value that represents the number of
> > + * vblank events since the system was booted, including lost events due to
> > + * modesetting activity. Returns corresponding system timestamp of the time
> > + * of the vblank interval that corresponds to the current vblank counter value.
> > + *
> > + * This is the legacy version of drm_crtc_vblank_count_and_time().
> > + */
> > +static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
> > +				     struct timeval *vblanktime)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	u32 vblank_count;
> > +	unsigned int seq;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs)) {
> > +		*vblanktime = (struct timeval) { 0 };
> > +		return 0;
> > +	}
> > +
> > +	do {
> > +		seq = read_seqbegin(&vblank->seqlock);
> > +		vblank_count = vblank->count;
> > +		*vblanktime = vblank->time;
> > +	} while (read_seqretry(&vblank->seqlock, seq));
> > +
> > +	return vblank_count;
> > +}
> > +
> > +/**
> > + * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
> > + *     and the system timestamp corresponding to that vblank counter value
> > + * @crtc: which counter to retrieve
> > + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> > + *
> > + * Fetches the "cooked" vblank count value that represents the number of
> > + * vblank events since the system was booted, including lost events due to
> > + * modesetting activity. Returns corresponding system timestamp of the time
> > + * of the vblank interval that corresponds to the current vblank counter value.
> > + */
> > +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> > +				   struct timeval *vblanktime)
> > +{
> > +	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
> > +					 vblanktime);
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
> > +
> > +static void send_vblank_event(struct drm_device *dev,
> > +		struct drm_pending_vblank_event *e,
> > +		unsigned long seq, struct timeval *now)
> > +{
> > +	e->event.sequence = seq;
> > +	e->event.tv_sec = now->tv_sec;
> > +	e->event.tv_usec = now->tv_usec;
> > +
> > +	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
> > +					 e->event.sequence);
> > +
> > +	drm_send_event_locked(dev, &e->base);
> > +}
> > +
> > +/**
> > + * drm_crtc_arm_vblank_event - arm vblank event after pageflip
> > + * @crtc: the source CRTC of the vblank event
> > + * @e: the event to send
> > + *
> > + * A lot of drivers need to generate vblank events for the very next vblank
> > + * interrupt. For example when the page flip interrupt happens when the page
> > + * flip gets armed, but not when it actually executes within the next vblank
> > + * period. This helper function implements exactly the required vblank arming
> > + * behaviour.
> > + *
> > + * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
> > + * atomic commit must ensure that the next vblank happens at exactly the same
> > + * time as the atomic commit is committed to the hardware. This function itself
> > + * does **not** protect again the next vblank interrupt racing with either this
> > + * function call or the atomic commit operation. A possible sequence could be:
> > + *
> > + * 1. Driver commits new hardware state into vblank-synchronized registers.
> > + * 2. A vblank happens, committing the hardware state. Also the corresponding
> > + *    vblank interrupt is fired off and fully processed by the interrupt
> > + *    handler.
> > + * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
> > + * 4. The event is only send out for the next vblank, which is wrong.
> > + *
> > + * An equivalent race can happen when the driver calls
> > + * drm_crtc_arm_vblank_event() before writing out the new hardware state.
> > + *
> > + * The only way to make this work safely is to prevent the vblank from firing
> > + * (and the hardware from committing anything else) until the entire atomic
> > + * commit sequence has run to completion. If the hardware does not have such a
> > + * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
> > + * Instead drivers need to manually send out the event from their interrupt
> > + * handler by calling drm_crtc_send_vblank_event() and make sure that
> > there's no
> > + * possible race with the hardware committing the atomic update.
> > + *
> > + * Caller must hold event lock. Caller must also hold a vblank reference for
> > + * the event @e, which will be dropped when the next vblank arrives.
> > + */
> > +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> > +			       struct drm_pending_vblank_event *e)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned int pipe = drm_crtc_index(crtc);
> > +
> > +	assert_spin_locked(&dev->event_lock);
> > +
> > +	e->pipe = pipe;
> > +	e->event.sequence = drm_vblank_count(dev, pipe);
> > +	e->event.crtc_id = crtc->base.id;
> > +	list_add_tail(&e->base.link, &dev->vblank_event_list);
> > +}
> > +EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
> > +
> > +/**
> > + * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
> > + * @crtc: the source CRTC of the vblank event
> > + * @e: the event to send
> > + *
> > + * Updates sequence # and timestamp on event for the most recently processed
> > + * vblank, and sends it to userspace.  Caller must hold event lock.
> > + *
> > + * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
> > + * situation, especially to send out events for atomic commit operations.
> > + */
> > +void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> > +				struct drm_pending_vblank_event *e)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned int seq, pipe = drm_crtc_index(crtc);
> > +	struct timeval now;
> > +
> > +	if (dev->num_crtcs > 0) {
> > +		seq = drm_vblank_count_and_time(dev, pipe, &now);
> > +	} else {
> > +		seq = 0;
> > +
> > +		now = get_drm_timestamp();
> > +	}
> > +	e->pipe = pipe;
> > +	e->event.crtc_id = crtc->base.id;
> > +	send_vblank_event(dev, e, seq, &now);
> > +}
> > +EXPORT_SYMBOL(drm_crtc_send_vblank_event);
> > +
> > +static int __enable_vblank(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
> > +		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
> > +
> > +		if (crtc->funcs->enable_vblank)
> > +			return crtc->funcs->enable_vblank(crtc);
> > +	}
> > +
> > +	return dev->driver->enable_vblank(dev, pipe);
> > +}
> > +
> > +/**
> > + * drm_vblank_enable - enable the vblank interrupt on a CRTC
> > + * @dev: DRM device
> > + * @pipe: CRTC index
> > + *
> > + * Returns:
> > + * Zero on success or a negative error code on failure.
> > + */
> > +static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	int ret = 0;
> > +
> > +	assert_spin_locked(&dev->vbl_lock);
> > +
> > +	spin_lock(&dev->vblank_time_lock);
> > +
> > +	if (!vblank->enabled) {
> > +		/*
> > +		 * Enable vblank irqs under vblank_time_lock protection.
> > +		 * All vblank count & timestamp updates are held off
> > +		 * until we are done reinitializing master counter and
> > +		 * timestamps. Filtercode in drm_handle_vblank() will
> > +		 * prevent double-accounting of same vblank interval.
> > +		 */
> > +		ret = __enable_vblank(dev, pipe);
> > +		DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret);
> > +		if (ret) {
> > +			atomic_dec(&vblank->refcount);
> > +		} else {
> > +			drm_update_vblank_count(dev, pipe, 0);
> > +			/* drm_update_vblank_count() includes a wmb so we just
> > +			 * need to ensure that the compiler emits the write
> > +			 * to mark the vblank as enabled after the call
> > +			 * to drm_update_vblank_count().
> > +			 */
> > +			WRITE_ONCE(vblank->enabled, true);
> > +		}
> > +	}
> > +
> > +	spin_unlock(&dev->vblank_time_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * drm_vblank_get - get a reference count on vblank events
> > + * @dev: DRM device
> > + * @pipe: index of CRTC to own
> > + *
> > + * Acquire a reference count on vblank events to avoid having them disabled
> > + * while in use.
> > + *
> > + * This is the legacy version of drm_crtc_vblank_get().
> > + *
> > + * Returns:
> > + * Zero on success or a negative error code on failure.
> > + */
> > +static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	unsigned long irqflags;
> > +	int ret = 0;
> > +
> > +	if (!dev->num_crtcs)
> > +		return -EINVAL;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return -EINVAL;
> > +
> > +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > +	/* Going from 0->1 means we have to enable interrupts again */
> > +	if (atomic_add_return(1, &vblank->refcount) == 1) {
> > +		ret = drm_vblank_enable(dev, pipe);
> > +	} else {
> > +		if (!vblank->enabled) {
> > +			atomic_dec(&vblank->refcount);
> > +			ret = -EINVAL;
> > +		}
> > +	}
> > +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * drm_crtc_vblank_get - get a reference count on vblank events
> > + * @crtc: which CRTC to own
> > + *
> > + * Acquire a reference count on vblank events to avoid having them disabled
> > + * while in use.
> > + *
> > + * Returns:
> > + * Zero on success or a negative error code on failure.
> > + */
> > +int drm_crtc_vblank_get(struct drm_crtc *crtc)
> > +{
> > +	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_get);
> > +
> > +/**
> > + * drm_vblank_put - release ownership of vblank events
> > + * @dev: DRM device
> > + * @pipe: index of CRTC to release
> > + *
> > + * Release ownership of a given vblank counter, turning off interrupts
> > + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> > + *
> > + * This is the legacy version of drm_crtc_vblank_put().
> > + */
> > +static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	if (WARN_ON(atomic_read(&vblank->refcount) == 0))
> > +		return;
> > +
> > +	/* Last user schedules interrupt disable */
> > +	if (atomic_dec_and_test(&vblank->refcount)) {
> > +		if (drm_vblank_offdelay == 0)
> > +			return;
> > +		else if (drm_vblank_offdelay < 0)
> > +			vblank_disable_fn((unsigned long)vblank);
> > +		else if (!dev->vblank_disable_immediate)
> > +			mod_timer(&vblank->disable_timer,
> > +				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
> > +	}
> > +}
> > +
> > +/**
> > + * drm_crtc_vblank_put - give up ownership of vblank events
> > + * @crtc: which counter to give up
> > + *
> > + * Release ownership of a given vblank counter, turning off interrupts
> > + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
> > + */
> > +void drm_crtc_vblank_put(struct drm_crtc *crtc)
> > +{
> > +	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_put);
> > +
> > +/**
> > + * drm_wait_one_vblank - wait for one vblank
> > + * @dev: DRM device
> > + * @pipe: CRTC index
> > + *
> > + * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
> > + * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
> > + * due to lack of driver support or because the crtc is off.
> > + */
> > +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	int ret;
> > +	u32 last;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	ret = drm_vblank_get(dev, pipe);
> > +	if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret))
> > +		return;
> > +
> > +	last = drm_vblank_count(dev, pipe);
> > +
> > +	ret = wait_event_timeout(vblank->queue,
> > +				 last != drm_vblank_count(dev, pipe),
> > +				 msecs_to_jiffies(100));
> > +
> > +	WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe);
> > +
> > +	drm_vblank_put(dev, pipe);
> > +}
> > +EXPORT_SYMBOL(drm_wait_one_vblank);
> > +
> > +/**
> > + * drm_crtc_wait_one_vblank - wait for one vblank
> > + * @crtc: DRM crtc
> > + *
> > + * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> > + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
> > + * due to lack of driver support or because the crtc is off.
> > + */
> > +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> > +{
> > +	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
> > +}
> > +EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
> > +
> > +/**
> > + * drm_crtc_vblank_off - disable vblank events on a CRTC
> > + * @crtc: CRTC in question
> > + *
> > + * Drivers can use this function to shut down the vblank interrupt
> > handling when
> > + * disabling a crtc. This function ensures that the latest vblank
> > frame count is
> > + * stored so that drm_vblank_on can restore it again.
> > + *
> > + * Drivers must use this function when the hardware vblank counter can get
> > + * reset, e.g. when suspending.
> > + */
> > +void drm_crtc_vblank_off(struct drm_crtc *crtc)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned int pipe = drm_crtc_index(crtc);
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	struct drm_pending_vblank_event *e, *t;
> > +	struct timeval now;
> > +	unsigned long irqflags;
> > +	unsigned int seq;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	spin_lock_irqsave(&dev->event_lock, irqflags);
> > +
> > +	spin_lock(&dev->vbl_lock);
> > +	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> > +		      pipe, vblank->enabled, vblank->inmodeset);
> > +
> > +	/* Avoid redundant vblank disables without previous
> > +	 * drm_crtc_vblank_on(). */
> > +	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
> > +		drm_vblank_disable_and_save(dev, pipe);
> > +
> > +	wake_up(&vblank->queue);
> > +
> > +	/*
> > +	 * Prevent subsequent drm_vblank_get() from re-enabling
> > +	 * the vblank interrupt by bumping the refcount.
> > +	 */
> > +	if (!vblank->inmodeset) {
> > +		atomic_inc(&vblank->refcount);
> > +		vblank->inmodeset = 1;
> > +	}
> > +	spin_unlock(&dev->vbl_lock);
> > +
> > +	/* Send any queued vblank events, lest the natives grow disquiet */
> > +	seq = drm_vblank_count_and_time(dev, pipe, &now);
> > +
> > +	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> > +		if (e->pipe != pipe)
> > +			continue;
> > +		DRM_DEBUG("Sending premature vblank event on disable: "
> > +			  "wanted %u, current %u\n",
> > +			  e->event.sequence, seq);
> > +		list_del(&e->base.link);
> > +		drm_vblank_put(dev, pipe);
> > +		send_vblank_event(dev, e, seq, &now);
> > +	}
> > +	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> > +
> > +	/* Will be reset by the modeset helpers when re-enabling the crtc by
> > +	 * calling drm_calc_timestamping_constants(). */
> > +	vblank->hwmode.crtc_clock = 0;
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_off);
> > +
> > +/**
> > + * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
> > + * @crtc: CRTC in question
> > + *
> > + * Drivers can use this function to reset the vblank state to off at load time.
> > + * Drivers should use this together with the drm_crtc_vblank_off() and
> > + * drm_crtc_vblank_on() functions. The difference compared to
> > + * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
> > + * and hence doesn't need to call any driver hooks.
> > + */
> > +void drm_crtc_vblank_reset(struct drm_crtc *crtc)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned long irqflags;
> > +	unsigned int pipe = drm_crtc_index(crtc);
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +
> > +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > +	/*
> > +	 * Prevent subsequent drm_vblank_get() from enabling the vblank
> > +	 * interrupt by bumping the refcount.
> > +	 */
> > +	if (!vblank->inmodeset) {
> > +		atomic_inc(&vblank->refcount);
> > +		vblank->inmodeset = 1;
> > +	}
> > +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > +
> > +	WARN_ON(!list_empty(&dev->vblank_event_list));
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_reset);
> > +
> > +/**
> > + * drm_crtc_vblank_on - enable vblank events on a CRTC
> > + * @crtc: CRTC in question
> > + *
> > + * This functions restores the vblank interrupt state captured with
> > + * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and
> > + * drm_crtc_vblank_off() can be unbalanced and so can also be
> > unconditionally called
> > + * in driver load code to reflect the current hardware state of the crtc.
> > + */
> > +void drm_crtc_vblank_on(struct drm_crtc *crtc)
> > +{
> > +	struct drm_device *dev = crtc->dev;
> > +	unsigned int pipe = drm_crtc_index(crtc);
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	unsigned long irqflags;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > +	DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n",
> > +		      pipe, vblank->enabled, vblank->inmodeset);
> > +
> > +	/* Drop our private "prevent drm_vblank_get" refcount */
> > +	if (vblank->inmodeset) {
> > +		atomic_dec(&vblank->refcount);
> > +		vblank->inmodeset = 0;
> > +	}
> > +
> > +	drm_reset_vblank_timestamp(dev, pipe);
> > +
> > +	/*
> > +	 * re-enable interrupts if there are users left, or the
> > +	 * user wishes vblank interrupts to be enabled all the time.
> > +	 */
> > +	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
> > +		WARN_ON(drm_vblank_enable(dev, pipe));
> > +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > +}
> > +EXPORT_SYMBOL(drm_crtc_vblank_on);
> > +
> > +static void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
> > +					  unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +
> > +	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> > +	if (!dev->num_crtcs)
> > +		return;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	/*
> > +	 * To avoid all the problems that might happen if interrupts
> > +	 * were enabled/disabled around or between these calls, we just
> > +	 * have the kernel take a reference on the CRTC (just once though
> > +	 * to avoid corrupting the count if multiple, mismatch calls occur),
> > +	 * so that interrupts remain enabled in the interim.
> > +	 */
> > +	if (!vblank->inmodeset) {
> > +		vblank->inmodeset = 0x1;
> > +		if (drm_vblank_get(dev, pipe) == 0)
> > +			vblank->inmodeset |= 0x2;
> > +	}
> > +}
> > +
> > +static void drm_legacy_vblank_post_modeset(struct drm_device *dev,
> > +					   unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	unsigned long irqflags;
> > +
> > +	/* vblank is not initialized (IRQ not installed ?), or has been freed */
> > +	if (!dev->num_crtcs)
> > +		return;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return;
> > +
> > +	if (vblank->inmodeset) {
> > +		spin_lock_irqsave(&dev->vbl_lock, irqflags);
> > +		drm_reset_vblank_timestamp(dev, pipe);
> > +		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
> > +
> > +		if (vblank->inmodeset & 0x2)
> > +			drm_vblank_put(dev, pipe);
> > +
> > +		vblank->inmodeset = 0;
> > +	}
> > +}
> > +
> > +int drm_legacy_modeset_ctl(struct drm_device *dev, void *data,
> > +			   struct drm_file *file_priv)
> > +{
> > +	struct drm_modeset_ctl *modeset = data;
> > +	unsigned int pipe;
> > +
> > +	/* If drm_vblank_init() hasn't been called yet, just no-op */
> > +	if (!dev->num_crtcs)
> > +		return 0;
> > +
> > +	/* KMS drivers handle this internally */
> > +	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
> > +		return 0;
> > +
> > +	pipe = modeset->crtc;
> > +	if (pipe >= dev->num_crtcs)
> > +		return -EINVAL;
> > +
> > +	switch (modeset->cmd) {
> > +	case _DRM_PRE_MODESET:
> > +		drm_legacy_vblank_pre_modeset(dev, pipe);
> > +		break;
> > +	case _DRM_POST_MODESET:
> > +		drm_legacy_vblank_post_modeset(dev, pipe);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static inline bool vblank_passed(u32 seq, u32 ref)
> > +{
> > +	return (seq - ref) <= (1 << 23);
> > +}
> > +
> > +static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
> > +				  union drm_wait_vblank *vblwait,
> > +				  struct drm_file *file_priv)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	struct drm_pending_vblank_event *e;
> > +	struct timeval now;
> > +	unsigned long flags;
> > +	unsigned int seq;
> > +	int ret;
> > +
> > +	e = kzalloc(sizeof(*e), GFP_KERNEL);
> > +	if (e == NULL) {
> > +		ret = -ENOMEM;
> > +		goto err_put;
> > +	}
> > +
> > +	e->pipe = pipe;
> > +	e->event.base.type = DRM_EVENT_VBLANK;
> > +	e->event.base.length = sizeof(e->event);
> > +	e->event.user_data = vblwait->request.signal;
> > +
> > +	spin_lock_irqsave(&dev->event_lock, flags);
> > +
> > +	/*
> > +	 * drm_crtc_vblank_off() might have been called after we called
> > +	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
> > +	 * vblank disable, so no need for further locking.  The reference from
> > +	 * drm_vblank_get() protects against vblank disable from another source.
> > +	 */
> > +	if (!READ_ONCE(vblank->enabled)) {
> > +		ret = -EINVAL;
> > +		goto err_unlock;
> > +	}
> > +
> > +	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
> > +					    &e->event.base);
> > +
> > +	if (ret)
> > +		goto err_unlock;
> > +
> > +	seq = drm_vblank_count_and_time(dev, pipe, &now);
> > +
> > +	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
> > +		  vblwait->request.sequence, seq, pipe);
> > +
> > +	trace_drm_vblank_event_queued(file_priv, pipe,
> > +				      vblwait->request.sequence);
> > +
> > +	e->event.sequence = vblwait->request.sequence;
> > +	if (vblank_passed(seq, vblwait->request.sequence)) {
> > +		drm_vblank_put(dev, pipe);
> > +		send_vblank_event(dev, e, seq, &now);
> > +		vblwait->reply.sequence = seq;
> > +	} else {
> > +		/* drm_handle_vblank_events will call drm_vblank_put */
> > +		list_add_tail(&e->base.link, &dev->vblank_event_list);
> > +		vblwait->reply.sequence = vblwait->request.sequence;
> > +	}
> > +
> > +	spin_unlock_irqrestore(&dev->event_lock, flags);
> > +
> > +	return 0;
> > +
> > +err_unlock:
> > +	spin_unlock_irqrestore(&dev->event_lock, flags);
> > +	kfree(e);
> > +err_put:
> > +	drm_vblank_put(dev, pipe);
> > +	return ret;
> > +}
> > +
> > +static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
> > +{
> > +	if (vblwait->request.sequence)
> > +		return false;
> > +
> > +	return _DRM_VBLANK_RELATIVE ==
> > +		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
> > +					  _DRM_VBLANK_EVENT |
> > +					  _DRM_VBLANK_NEXTONMISS));
> > +}
> > +
> > +/*
> > + * Wait for VBLANK.
> > + *
> > + * \param inode device inode.
> > + * \param file_priv DRM file private.
> > + * \param cmd command.
> > + * \param data user argument, pointing to a drm_wait_vblank structure.
> > + * \return zero on success or a negative number on failure.
> > + *
> > + * This function enables the vblank interrupt on the pipe requested, then
> > + * sleeps waiting for the requested sequence number to occur, and drops
> > + * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
> > + * after a timeout with no further vblank waits scheduled).
> > + */
> > +int drm_wait_vblank(struct drm_device *dev, void *data,
> > +		    struct drm_file *file_priv)
> > +{
> > +	struct drm_vblank_crtc *vblank;
> > +	union drm_wait_vblank *vblwait = data;
> > +	int ret;
> > +	unsigned int flags, seq, pipe, high_pipe;
> > +
> > +	if (!dev->irq_enabled)
> > +		return -EINVAL;
> > +
> > +	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
> > +		return -EINVAL;
> > +
> > +	if (vblwait->request.type &
> > +	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> > +	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
> > +		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
> > +			  vblwait->request.type,
> > +			  (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
> > +			   _DRM_VBLANK_HIGH_CRTC_MASK));
> > +		return -EINVAL;
> > +	}
> > +
> > +	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
> > +	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
> > +	if (high_pipe)
> > +		pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
> > +	else
> > +		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
> > +	if (pipe >= dev->num_crtcs)
> > +		return -EINVAL;
> > +
> > +	vblank = &dev->vblank[pipe];
> > +
> > +	/* If the counter is currently enabled and accurate, short-circuit
> > +	 * queries to return the cached timestamp of the last vblank.
> > +	 */
> > +	if (dev->vblank_disable_immediate &&
> > +	    drm_wait_vblank_is_query(vblwait) &&
> > +	    READ_ONCE(vblank->enabled)) {
> > +		struct timeval now;
> > +
> > +		vblwait->reply.sequence =
> > +			drm_vblank_count_and_time(dev, pipe, &now);
> > +		vblwait->reply.tval_sec = now.tv_sec;
> > +		vblwait->reply.tval_usec = now.tv_usec;
> > +		return 0;
> > +	}
> > +
> > +	ret = drm_vblank_get(dev, pipe);
> > +	if (ret) {
> > +		DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
> > +		return ret;
> > +	}
> > +	seq = drm_vblank_count(dev, pipe);
> > +
> > +	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
> > +	case _DRM_VBLANK_RELATIVE:
> > +		vblwait->request.sequence += seq;
> > +		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
> > +	case _DRM_VBLANK_ABSOLUTE:
> > +		break;
> > +	default:
> > +		ret = -EINVAL;
> > +		goto done;
> > +	}
> > +
> > +	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
> > +	    vblank_passed(seq, vblwait->request.sequence))
> > +		vblwait->request.sequence = seq + 1;
> > +
> > +	if (flags & _DRM_VBLANK_EVENT) {
> > +		/* must hold on to the vblank ref until the event fires
> > +		 * drm_vblank_put will be called asynchronously
> > +		 */
> > +		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
> > +	}
> > +
> > +	if (vblwait->request.sequence != seq) {
> > +		DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
> > +			  vblwait->request.sequence, pipe);
> > +		DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
> > +			    vblank_passed(drm_vblank_count(dev, pipe),
> > +					  vblwait->request.sequence) ||
> > +			    !READ_ONCE(vblank->enabled));
> > +	}
> > +
> > +	if (ret != -EINTR) {
> > +		struct timeval now;
> > +
> > +		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
> > +		vblwait->reply.tval_sec = now.tv_sec;
> > +		vblwait->reply.tval_usec = now.tv_usec;
> > +
> > +		DRM_DEBUG("crtc %d returning %u to client\n",
> > +			  pipe, vblwait->reply.sequence);
> > +	} else {
> > +		DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
> > +	}
> > +
> > +done:
> > +	drm_vblank_put(dev, pipe);
> > +	return ret;
> > +}
> > +
> > +static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_pending_vblank_event *e, *t;
> > +	struct timeval now;
> > +	unsigned int seq;
> > +
> > +	assert_spin_locked(&dev->event_lock);
> > +
> > +	seq = drm_vblank_count_and_time(dev, pipe, &now);
> > +
> > +	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
> > +		if (e->pipe != pipe)
> > +			continue;
> > +		if (!vblank_passed(seq, e->event.sequence))
> > +			continue;
> > +
> > +		DRM_DEBUG("vblank event on %u, current %u\n",
> > +			  e->event.sequence, seq);
> > +
> > +		list_del(&e->base.link);
> > +		drm_vblank_put(dev, pipe);
> > +		send_vblank_event(dev, e, seq, &now);
> > +	}
> > +
> > +	trace_drm_vblank_event(pipe, seq);
> > +}
> > +
> > +/**
> > + * drm_handle_vblank - handle a vblank event
> > + * @dev: DRM device
> > + * @pipe: index of CRTC where this event occurred
> > + *
> > + * Drivers should call this routine in their vblank interrupt handlers to
> > + * update the vblank counter and send any signals that may be pending.
> > + *
> > + * This is the legacy version of drm_crtc_handle_vblank().
> > + */
> > +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
> > +{
> > +	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
> > +	unsigned long irqflags;
> > +	bool disable_irq;
> > +
> > +	if (WARN_ON_ONCE(!dev->num_crtcs))
> > +		return false;
> > +
> > +	if (WARN_ON(pipe >= dev->num_crtcs))
> > +		return false;
> > +
> > +	spin_lock_irqsave(&dev->event_lock, irqflags);
> > +
> > +	/* Need timestamp lock to prevent concurrent execution with
> > +	 * vblank enable/disable, as this would cause inconsistent
> > +	 * or corrupted timestamps and vblank counts.
> > +	 */
> > +	spin_lock(&dev->vblank_time_lock);
> > +
> > +	/* Vblank irq handling disabled. Nothing to do. */
> > +	if (!vblank->enabled) {
> > +		spin_unlock(&dev->vblank_time_lock);
> > +		spin_unlock_irqrestore(&dev->event_lock, irqflags);
> > +		return false;
> > +	}
> > +
> > +	drm_update_vblank_count(dev, pipe, true);
> > +
> > +	spin_unlock(&dev->vblank_time_lock);
> > +
> > +	wake_up(&vblank->queue);
> > +
> > +	/* With instant-off, we defer disabling the interrupt until after
> > +	 * we finish processing the following vblank after all events have
> > +	 * been signaled. The disable has to be last (after
> > +	 * drm_handle_vblank_events) so that the timestamp is always accurate.
> > +	 */
> > +	disable_irq = (dev->vblank_disable_immediate &&
> > +		       drm_vblank_offdelay > 0 &&
> > +		       !atomic_read(&vblank->refcount));
> > +
> > +	drm_handle_vblank_events(dev, pipe);
> > +
> > +	spin_unlock_irqrestore(&dev->event_lock, irqflags);
> > +
> > +	if (disable_irq)
> > +		vblank_disable_fn((unsigned long)vblank);
> > +
> > +	return true;
> > +}
> > +EXPORT_SYMBOL(drm_handle_vblank);
> > +
> > +/**
> > + * drm_crtc_handle_vblank - handle a vblank event
> > + * @crtc: where this event occurred
> > + *
> > + * Drivers should call this routine in their vblank interrupt handlers to
> > + * update the vblank counter and send any signals that may be pending.
> > + *
> > + * This is the native KMS version of drm_handle_vblank().
> > + *
> > + * Returns:
> > + * True if the event was successfully handled, false on failure.
> > + */
> > +bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
> > +{
> > +	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
> > +}
> > +EXPORT_SYMBOL(drm_crtc_handle_vblank);
> > diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> > index c363f2fdff31..2e0b76cceb97 100644
> > --- a/include/drm/drmP.h
> > +++ b/include/drm/drmP.h
> > @@ -80,6 +80,9 @@
> >  #include <drm/drm_debugfs.h>
> >  #include <drm/drm_ioctl.h>
> >  #include <drm/drm_sysfs.h>
> > +#include <drm/drm_vblank.h>
> > +#include <drm/drm_irq.h>
> > +
> >  
> >  struct module;
> >  
> > @@ -447,8 +450,6 @@ static inline bool
> > drm_drv_uses_atomic_modeset(struct drm_device *dev)
> >  	return dev->mode_config.funcs->atomic_commit != NULL;
> >  }
> >  
> > -#include <drm/drm_irq.h>
> > -
> >  #define DRM_SWITCH_POWER_ON 0
> >  #define DRM_SWITCH_POWER_OFF 1
> >  #define DRM_SWITCH_POWER_CHANGING 2
> > diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
> > index 5dd27ae5c47c..d66f7ee07fb5 100644
> > --- a/include/drm/drm_file.h
> > +++ b/include/drm/drm_file.h
> > @@ -40,6 +40,7 @@
> >  struct dma_fence;
> >  struct drm_file;
> >  struct drm_device;
> > +struct device;
> >  
> >  /*
> >   * FIXME: Not sure we want to have drm_minor here in the end, but to avoid
> > diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h
> > index 569ca86d4e1f..d77f6e65b1c6 100644
> > --- a/include/drm/drm_irq.h
> > +++ b/include/drm/drm_irq.h
> > @@ -24,165 +24,9 @@
> >  #ifndef _DRM_IRQ_H_
> >  #define _DRM_IRQ_H_
> >  
> > -#include <linux/seqlock.h>
> > -
> > -/**
> > - * struct drm_pending_vblank_event - pending vblank event tracking
> > - */
> > -struct drm_pending_vblank_event {
> > -	/**
> > -	 * @base: Base structure for tracking pending DRM events.
> > -	 */
> > -	struct drm_pending_event base;
> > -	/**
> > -	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
> > -	 */
> > -	unsigned int pipe;
> > -	/**
> > -	 * @event: Actual event which will be sent to userspace.
> > -	 */
> > -	struct drm_event_vblank event;
> > -};
> > -
> > -/**
> > - * struct drm_vblank_crtc - vblank tracking for a CRTC
> > - *
> > - * This structure tracks the vblank state for one CRTC.
> > - *
> > - * Note that for historical reasons - the vblank handling code is still shared
> > - * with legacy/non-kms drivers - this is a free-standing structure not directly
> > - * connected to &struct drm_crtc. But all public interface functions are taking
> > - * a &struct drm_crtc to hide this implementation detail.
> > - */
> > -struct drm_vblank_crtc {
> > -	/**
> > -	 * @dev: Pointer to the &drm_device.
> > -	 */
> > -	struct drm_device *dev;
> > -	/**
> > -	 * @queue: Wait queue for vblank waiters.
> > -	 */
> > -	wait_queue_head_t queue;	/**< VBLANK wait queue */
> > -	/**
> > -	 * @disable_timer: Disable timer for the delayed vblank disabling
> > -	 * hysteresis logic. Vblank disabling is controlled through the
> > -	 * drm_vblank_offdelay module option and the setting of the
> > -	 * &drm_device.max_vblank_count value.
> > -	 */
> > -	struct timer_list disable_timer;
> > -
> > -	/**
> > -	 * @seqlock: Protect vblank count and time.
> > -	 */
> > -	seqlock_t seqlock;		/* protects vblank count and time */
> > -
> > -	/**
> > -	 * @count: Current software vblank counter.
> > -	 */
> > -	u32 count;
> > -	/**
> > -	 * @time: Vblank timestamp corresponding to @count.
> > -	 */
> > -	struct timeval time;
> > -
> > -	/**
> > -	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
> > -	 * this refcount reaches 0 can the hardware interrupt be disabled using
> > -	 * @disable_timer.
> > -	 */
> > -	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
> > -	/**
> > -	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
> > -	 */
> > -	u32 last;
> > -	/**
> > -	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
> > -	 * For legacy driver bit 2 additionally tracks whether an additional
> > -	 * temporary vblank reference has been acquired to paper over the
> > -	 * hardware counter resetting/jumping. KMS drivers should instead just
> > -	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
> > -	 * save and restore the vblank count.
> > -	 */
> > -	unsigned int inmodeset;		/* Display driver is setting mode */
> > -	/**
> > -	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
> > -	 * structure.
> > -	 */
> > -	unsigned int pipe;
> > -	/**
> > -	 * @framedur_ns: Frame/Field duration in ns, used by
> > -	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> > -	 * drm_calc_timestamping_constants().
> > -	 */
> > -	int framedur_ns;
> > -	/**
> > -	 * @linedur_ns: Line duration in ns, used by
> > -	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> > -	 * drm_calc_timestamping_constants().
> > -	 */
> > -	int linedur_ns;
> > -
> > -	/**
> > -	 * @hwmode:
> > -	 *
> > -	 * Cache of the current hardware display mode. Only valid when @enabled
> > -	 * is set. This is used by helpers like
> > -	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
> > -	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
> > -	 * because that one is really hard to get from interrupt context.
> > -	 */
> > -	struct drm_display_mode hwmode;
> > -
> > -	/**
> > -	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
> > -	 * avoid double-disabling and hence corrupting saved state. Needed by
> > -	 * drivers not using atomic KMS, since those might go through their CRTC
> > -	 * disabling functions multiple times.
> > -	 */
> > -	bool enabled;
> > -};
> > +struct drm_device;
> >  
> >  int drm_irq_install(struct drm_device *dev, int irq);
> >  int drm_irq_uninstall(struct drm_device *dev);
> >  
> > -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
> > -u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
> > -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> > -				   struct timeval *vblanktime);
> > -void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> > -			       struct drm_pending_vblank_event *e);
> > -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> > -			      struct drm_pending_vblank_event *e);
> > -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
> > -bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
> > -int drm_crtc_vblank_get(struct drm_crtc *crtc);
> > -void drm_crtc_vblank_put(struct drm_crtc *crtc);
> > -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
> > -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
> > -void drm_crtc_vblank_off(struct drm_crtc *crtc);
> > -void drm_crtc_vblank_reset(struct drm_crtc *crtc);
> > -void drm_crtc_vblank_on(struct drm_crtc *crtc);
> > -void drm_vblank_cleanup(struct drm_device *dev);
> > -u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
> > -
> > -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> > -					   unsigned int pipe, int *max_error,
> > -					   struct timeval *vblank_time,
> > -					   bool in_vblank_irq);
> > -void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> > -				     const struct drm_display_mode *mode);
> > -
> > -/**
> > - * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
> > - * @crtc: which CRTC's vblank waitqueue to retrieve
> > - *
> > - * This function returns a pointer to the vblank waitqueue for the CRTC.
> > - * Drivers can use this to implement vblank waits using wait_event()
> > and related
> > - * functions.
> > - */
> > -static inline wait_queue_head_t *drm_crtc_vblank_waitqueue(struct
> > drm_crtc *crtc)
> > -{
> > -	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
> > -}
> > -
> >  #endif
> > diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h
> > index 59ccab402e85..9cd9e36f77b5 100644
> > --- a/include/drm/drm_prime.h
> > +++ b/include/drm/drm_prime.h
> > @@ -59,6 +59,8 @@ struct drm_device;
> >  struct drm_gem_object;
> >  struct drm_file;
> >  
> > +struct device;
> > +
> >  struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
> >  				     struct drm_gem_object *obj,
> >  				     int flags);
> > diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
> > new file mode 100644
> > index 000000000000..4cde47332dfa
> > --- /dev/null
> > +++ b/include/drm/drm_vblank.h
> > @@ -0,0 +1,181 @@
> > +/*
> > + * Copyright 2016 Intel Corp.
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> > + * OTHER DEALINGS IN THE SOFTWARE.
> > + */
> > +
> > +#ifndef _DRM_VBLANK_H_
> > +#define _DRM_VBLANK_H_
> > +
> > +#include <linux/seqlock.h>
> > +#include <linux/idr.h>
> > +#include <linux/poll.h>
> > +
> > +#include <drm/drm_file.h>
> > +#include <drm/drm_modes.h>
> > +#include <uapi/drm/drm.h>
> > +
> > +struct drm_device;
> > +struct drm_crtc;
> > +
> > +/**
> > + * struct drm_pending_vblank_event - pending vblank event tracking
> > + */
> > +struct drm_pending_vblank_event {
> > +	/**
> > +	 * @base: Base structure for tracking pending DRM events.
> > +	 */
> > +	struct drm_pending_event base;
> > +	/**
> > +	 * @pipe: drm_crtc_index() of the &drm_crtc this event is for.
> > +	 */
> > +	unsigned int pipe;
> > +	/**
> > +	 * @event: Actual event which will be sent to userspace.
> > +	 */
> > +	struct drm_event_vblank event;
> > +};
> > +
> > +/**
> > + * struct drm_vblank_crtc - vblank tracking for a CRTC
> > + *
> > + * This structure tracks the vblank state for one CRTC.
> > + *
> > + * Note that for historical reasons - the vblank handling code is still shared
> > + * with legacy/non-kms drivers - this is a free-standing structure not directly
> > + * connected to &struct drm_crtc. But all public interface functions are taking
> > + * a &struct drm_crtc to hide this implementation detail.
> > + */
> > +struct drm_vblank_crtc {
> > +	/**
> > +	 * @dev: Pointer to the &drm_device.
> > +	 */
> > +	struct drm_device *dev;
> > +	/**
> > +	 * @queue: Wait queue for vblank waiters.
> > +	 */
> > +	wait_queue_head_t queue;	/**< VBLANK wait queue */
> > +	/**
> > +	 * @disable_timer: Disable timer for the delayed vblank disabling
> > +	 * hysteresis logic. Vblank disabling is controlled through the
> > +	 * drm_vblank_offdelay module option and the setting of the
> > +	 * &drm_device.max_vblank_count value.
> > +	 */
> > +	struct timer_list disable_timer;
> > +
> > +	/**
> > +	 * @seqlock: Protect vblank count and time.
> > +	 */
> > +	seqlock_t seqlock;		/* protects vblank count and time */
> > +
> > +	/**
> > +	 * @count: Current software vblank counter.
> > +	 */
> > +	u32 count;
> > +	/**
> > +	 * @time: Vblank timestamp corresponding to @count.
> > +	 */
> > +	struct timeval time;
> > +
> > +	/**
> > +	 * @refcount: Number of users/waiters of the vblank interrupt. Only when
> > +	 * this refcount reaches 0 can the hardware interrupt be disabled using
> > +	 * @disable_timer.
> > +	 */
> > +	atomic_t refcount;		/* number of users of vblank interruptsper crtc */
> > +	/**
> > +	 * @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
> > +	 */
> > +	u32 last;
> > +	/**
> > +	 * @inmodeset: Tracks whether the vblank is disabled due to a modeset.
> > +	 * For legacy driver bit 2 additionally tracks whether an additional
> > +	 * temporary vblank reference has been acquired to paper over the
> > +	 * hardware counter resetting/jumping. KMS drivers should instead just
> > +	 * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
> > +	 * save and restore the vblank count.
> > +	 */
> > +	unsigned int inmodeset;		/* Display driver is setting mode */
> > +	/**
> > +	 * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
> > +	 * structure.
> > +	 */
> > +	unsigned int pipe;
> > +	/**
> > +	 * @framedur_ns: Frame/Field duration in ns, used by
> > +	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> > +	 * drm_calc_timestamping_constants().
> > +	 */
> > +	int framedur_ns;
> > +	/**
> > +	 * @linedur_ns: Line duration in ns, used by
> > +	 * drm_calc_vbltimestamp_from_scanoutpos() and computed by
> > +	 * drm_calc_timestamping_constants().
> > +	 */
> > +	int linedur_ns;
> > +
> > +	/**
> > +	 * @hwmode:
> > +	 *
> > +	 * Cache of the current hardware display mode. Only valid when @enabled
> > +	 * is set. This is used by helpers like
> > +	 * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the
> > +	 * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
> > +	 * because that one is really hard to get from interrupt context.
> > +	 */
> > +	struct drm_display_mode hwmode;
> > +
> > +	/**
> > +	 * @enabled: Tracks the enabling state of the corresponding &drm_crtc to
> > +	 * avoid double-disabling and hence corrupting saved state. Needed by
> > +	 * drivers not using atomic KMS, since those might go through their CRTC
> > +	 * disabling functions multiple times.
> > +	 */
> > +	bool enabled;
> > +};
> > +
> > +int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
> > +u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
> > +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> > +				   struct timeval *vblanktime);
> > +void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
> > +			       struct drm_pending_vblank_event *e);
> > +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> > +			      struct drm_pending_vblank_event *e);
> > +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
> > +bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
> > +int drm_crtc_vblank_get(struct drm_crtc *crtc);
> > +void drm_crtc_vblank_put(struct drm_crtc *crtc);
> > +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
> > +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
> > +void drm_crtc_vblank_off(struct drm_crtc *crtc);
> > +void drm_crtc_vblank_reset(struct drm_crtc *crtc);
> > +void drm_crtc_vblank_on(struct drm_crtc *crtc);
> > +void drm_vblank_cleanup(struct drm_device *dev);
> > +u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
> > +
> > +bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> > +					   unsigned int pipe, int *max_error,
> > +					   struct timeval *vblank_time,
> > +					   bool in_vblank_irq);
> > +void drm_calc_timestamping_constants(struct drm_crtc *crtc,
> > +				     const struct drm_display_mode *mode);
> > +wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
> > +#endif

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-31 16:57           ` Liviu Dudau
@ 2017-06-01  5:55             ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-06-01  5:55 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Daniel Vetter, Intel Graphics Development, DRI Development,
	Daniel Vetter

On Wed, May 31, 2017 at 05:57:07PM +0100, Liviu Dudau wrote:
> On Wed, May 31, 2017 at 06:41:05PM +0200, Daniel Vetter wrote:
> > On Wed, May 31, 2017 at 1:22 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> > > On Wed, May 31, 2017 at 01:03:34PM +0200, Daniel Vetter wrote:
> > >> On Wed, May 31, 2017 at 12:57 PM, Liviu Dudau <liviu.dudau@arm.com> wrote:
> > >> > On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
> > >> >> IRQs are properly shut down, so it almost works as race-free shutdown.
> > >> >> Except the irq is stopped after the vblank stuff, so boom anyway.
> > >> >> Proper way would be to call drm_atomic_helper_shutdown before any of
> > >> >> the kms things gets stopped. So no harm in removing the
> > >> >> drm_vblank_cleanup here really.
> > >> >
> > >> > Slightly confused on the implied message in the commit text: is "Proper way
> > >> > would be to call drm_atomic_helper_shutdown" a hint? A promise of a future
> > >> > patch? A message to the future us on how to fix things if they blow up?
> > >> >
> > >> > If calling drm_atomic_helper_shutdown() is the proper thing to do, why does the
> > >> > patch (and the series) avoids doing that? Lack of understanding of the driver's
> > >> > internal workings? Then I want to help, if I can understand the new direction.
> > >>
> > >> Yes, I wanted to not make things worse. If you look at the overall
> > >> result (especially last patch) I'm also trying to better document
> > >> stuff in the vblank area, but summarized, if you want to make sure
> > >> that vblank processing has stopped, you need to call drm_vblank_off on
> > >> each active crtc. The simplest way to get that is by using
> > >> drm_atomic_helper_shutdown(). Calling drm_vblank_cleanup doesn't
> > >> really do anything useful (see the last patch for the only valid
> > >> usecase there ever was).
> > >
> > > OK, so there should be a follow up patch adding drm_atomic_helper_shutdown(). I guess
> > > I need to call dibs on it? :)
> > 
> > I googled what "to call dibs on smth" means and still can't figure out
> > what you mean here. Can you pls explain?
> 
> Means I put my name on it. Urban dictionary on "call dibs" gives it a very gender
> specific usage, my intent was to convey the message that I will take it on my TODO list.

Ah ok, thanks for explaining. I was indeed thrown off be the ub entry ...
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [Intel-gfx] [PATCH 16/37] drm/hdlcd|mali: Drop drm_vblank_cleanup
  2017-05-31 16:37   ` Liviu Dudau
@ 2017-06-01  6:01     ` Daniel Vetter
  0 siblings, 0 replies; 94+ messages in thread
From: Daniel Vetter @ 2017-06-01  6:01 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Daniel Vetter, Intel Graphics Development, DRI Development,
	Daniel Vetter

On Wed, May 31, 2017 at 05:37:34PM +0100, Liviu Dudau wrote:
> On Wed, May 24, 2017 at 04:51:51PM +0200, Daniel Vetter wrote:
> > IRQs are properly shut down, so it almost works as race-free shutdown.
> > Except the irq is stopped after the vblank stuff, so boom anyway.
> > Proper way would be to call drm_atomic_helper_shutdown before any of
> > the kms things gets stopped. So no harm in removing the
> > drm_vblank_cleanup here really.
> > 
> > Same story for both hdlcd and mali.
> > 
> > v2: Move misplaced malidp hunk to this patch (Liviu).
> > 
> > Cc: Liviu Dudau <liviu.dudau@arm.com>
> > Cc: Brian Starkey <brian.starkey@arm.com>
> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
> 
> On the assumption that a subsequent patch will fix the issue highlighted in
> the commit message (doesn't have to be part of this series):
> 
> Acked-by: Liviu Dudau <liviu.dudau@arm.com>
> 
> I'm assuming that you are going to carry this patch through one of your trees,
> so I will not pull it into mali-dp tree.

Ok, applied to drm-misc-next, thanks for checking things.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 18/37] drm/exynos: Drop drm_vblank_cleanup
  2017-05-31  8:45     ` Daniel Vetter
@ 2017-06-01  6:15       ` Inki Dae
  2017-06-01  9:44         ` Daniel Vetter
  0 siblings, 1 reply; 94+ messages in thread
From: Inki Dae @ 2017-06-01  6:15 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Joonyoung Shim, Daniel Vetter, Intel Graphics Development