summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_irq.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2017-05-31 11:21:46 +0200
committerDaniel Vetter <daniel.vetter@ffwll.ch>2017-06-01 08:02:14 +0200
commit3ed4351a83ca05d3cd886ade6900be1067aa7903 (patch)
treeee4b504626138e9eb71312180de592ba55c35c69 /drivers/gpu/drm/drm_irq.c
parent9a8d5e4a53dc0f14334d7b19b9107639fcfefd35 (diff)
drm: Extract drm_vblank.[hc]
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> Reviewed-by: Stefan Agner <stefan@agner.ch> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Link: http://patchwork.freedesktop.org/patch/msgid/20170531092146.12528-1-daniel.vetter@ffwll.ch
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r--drivers/gpu/drm/drm_irq.c1623
1 files changed, 22 insertions, 1601 deletions
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));
-}
<