/*
*/
/**
* \file drm_irq.c
* IRQ support
*
* \author Rickard E. (Rik) Faith <faith@valinux.com>
* \author Gareth Hughes <gareth@valinux.com>
*/
/*
* Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com
*
* Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* Copyright (c) 2009, 2013, Intel Corporation.
* All Rights Reserved.
*
* 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,
* 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
* 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.h"
#include "drmP.h"
#include "drm_io32.h"
{
int ret;
return (ret);
}
/* LINTED */
{
int ret;
return (ret);
}
{
int i, ret;
if (pdev->msi_handle) {
/* Call ddi_intr_add_handler() */
for (i = 0; i < pdev->msi_actual; i++) {
if (ret != DDI_SUCCESS) {
DRM_DEBUG("ddi_intr_add_handler() failed");
return (ret);
}
}
/* Call ddi_intr_block_enable() for MSI */
} else {
/* Call ddi_intr_enable() for MSI non block enable */
for (i = 0; i < pdev->msi_actual; i++)
}
} else {
/* setup the interrupt handler */
DRM_ERROR("ddi_add_intr failed");
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
{
int i;
if (pdev->msi_handle) {
/* Disable all interrupts */
/* Call ddi_intr_block_disable() */
} else {
for (i = 0; i < pdev->msi_actual; i++)
}
/* Call ddi_intr_remove_handler() */
for (i = 0; i < pdev->msi_actual; i++){
}
} else {
}
}
int
{
int types;
int i, ret;
/* Get supported interrupt types */
DRM_DEBUG("ddi_intr_get_supported_types() failed");
return (DDI_FAILURE);
}
if (!(types & DDI_INTR_TYPE_MSI))
return (DDI_FAILURE);
/* Get number of interrupts */
DRM_DEBUG("ddi_intr_get_nintrs() failed, "
return (ret);
}
/* Get number of available interrupts */
DRM_DEBUG("ddi_intr_get_navail() failed, "
return (ret);
}
DRM_DEBUG("nitrs() returned %d, navail returned %d",
}
/* Allocate memory for MSI interrupts */
return (ret);
}
/*
* Get priority for first msi, assume remaining are all the same
*/
if (ret != DDI_SUCCESS) {
for(i = 0; i < actual; i++)
return (ret);
}
if (ret != DDI_SUCCESS) {
for(i = 0; i < actual; i++)
return (ret);
}
return (ret);
}
void
{
int i;
for (i = 0; i < pdev->msi_actual; i++)
}
/* Access macro for slots in vblank timestamp ringbuffer. */
((count) % DRM_VBLANKTIME_RBSIZE)])
/* Retry timestamp calculation up to 3 times to satisfy
* drm_timestamp_precision before giving up.
*/
/* Threshold in nanoseconds for detection of redundant
* vblank irq in drm_handle_vblank(). 1 msec should be ok.
*/
/**
* Get interrupt from bus id.
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg user argument, pointing to a drm_irq_busid structure.
* \return zero on success or a negative number on failure.
*
* Finds the PCI device with the specified bus id and gets its IRQ number.
* This IOCTL is deprecated, and will now return EINVAL for any busid not equal
* to that of the device that this DRM instance attached to.
*/
/* LINTED */
{
struct drm_irq_busid *p = data;
return -EINVAL;
return -EINVAL;
p->irq);
return 0;
}
/*
* Clear vblank timestamp buffer for a crtc.
*/
{
DRM_VBLANKTIME_RBSIZE * sizeof(struct timeval));
}
/*
* 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.
*/
{
unsigned long irqflags;
int vblrc;
/* 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.
*/
/* No further vblank irq's will be processed after
* this point. Get current hardware vblank count and
* vblank timestamp, repeat until they are consistent.
*
* FIXME: There is still a race condition here and in
* drm_update_vblank_count() which can cause off-by-one
* reinitialization of software vblank counter. If gpu
* vblank counter doesn't increment exactly at the leading
* edge of a vblank interval, then we can lose 1 count if
* we happen to execute between start of vblank and the
* delayed gpu counter increment.
*/
do {
} while (dev->last_vblank[crtc] != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc);
if (!count)
vblrc = 0;
/* Compute time difference to stored timestamp of last vblank
* as updated by last invocation of drm_handle_vblank() in vblank irq.
*/
/* If there is at least 1 msec difference between the last stored
* timestamp and tvblank, then we are currently executing our
* disable inside a new vblank interval, the tvblank timestamp
* corresponds to this new vblank interval and the irq handler
* for this vblank didn't run yet and won't run due to our disable.
* Therefore we need to do the job of drm_handle_vblank() and
* increment the vblank counter by one to account for this vblank.
*
* Skip this step if there isn't any high precision timestamp
* available. In that case we can't account for this and just
* hope for the best.
*/
}
/* Invalidate all timestamps while vblank irq's are off. */
}
{
unsigned long irqflags;
int i;
if (!dev->vblank_disable_allowed)
return;
dev->vblank_enabled[i]) {
DRM_DEBUG("disabling vblank on crtc %d\n", i);
}
}
}
{
/* Bail if the driver didn't call drm_vblank_init() */
return;
vblank_disable_fn((void *)dev);
}
{
dev);
goto err;
if (!dev->_vblank_count)
goto err;
if (!dev->vblank_refcount)
goto err;
if (!dev->vblank_enabled)
goto err;
if (!dev->last_vblank)
goto err;
if (!dev->last_vblank_wait)
goto err;
if (!dev->vblank_inmodeset)
goto err;
sizeof(struct timeval), GFP_KERNEL);
if (!dev->_vblank_time)
goto err;
DRM_INFO("Supports vblank timestamp caching Rev 1 (10.10.2010).\n");
/* Driver specific high-precision vblank timestamping supported? */
DRM_INFO("Driver supports precise vblank timestamp query.\n");
else
DRM_INFO("No driver support for vblank timestamp query.\n");
/* Zero per-crtc vblank stuff */
for (i = 0; i < num_crtcs; i++) {
}
dev->vblank_disable_allowed = 0;
return 0;
err:
return ret;
}
/* LINTED */
{
return;
}
if (!dev->irq_enabled)
return;
if (state) {
} else {
}
}
/**
* Install IRQ handler.
*
* \param dev DRM device.
*
* Initializes the IRQ related data. Installs the handler, calling the driver
* \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions
* before and after the installation.
*/
{
int ret;
return -EINVAL;
return -EINVAL;
/* Driver must have been initialized */
if (!dev->dev_private) {
return -EINVAL;
}
if (dev->irq_enabled) {
return -EBUSY;
}
/* Before installing handler */
/* Install handler */
if (ret != DDI_SUCCESS) {
DRM_ERROR("IRQ handler installation failed");
dev->irq_enabled = 0;
return -EFAULT;
}
/* After installing handler */
if (ret < 0) {
dev->irq_enabled = 0;
return ret;
}
dev->context_flag = 0;
return 0;
}
/**
* Uninstall the IRQ handler.
*
* \param dev DRM device.
*
* Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq.
*/
{
unsigned long irqflags;
int irq_enabled, i;
return -EINVAL;
dev->irq_enabled = 0;
/*
* Wake up any waiters so they don't hang.
*/
dev->vblank_enabled[i] = 0;
dev->last_vblank[i] =
}
}
if (!irq_enabled)
return -EINVAL;
return 0;
}
/**
* IRQ control ioctl.
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg user argument, pointing to a drm_control structure.
* \return zero on success or a negative number on failure.
*
* Calls irq_install() or irq_uninstall() according to \p arg.
*/
/* LINTED */
{
/* if we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */
case DRM_INST_HANDLER:
return 0;
return 0;
return -EINVAL;
return drm_irq_install(dev);
case DRM_UNINST_HANDLER:
return 0;
return 0;
return drm_irq_uninstall(dev);
default:
return -EINVAL;
}
}
/**
* drm_calc_timestamping_constants - 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.
*
* @crtc drm_crtc whose timestamp constants should be updated.
*
*/
{
/* Dot clock in Hz: */
/* Fields of interlaced scanout modes are only halve a frame duration.
*/
dotclock *= 2;
/* Valid dotclock? */
if (dotclock > 0) {
int frame_size;
/* Convert scanline length in pixels and video dot clock to
* line duration, frame duration and pixel duration in
* nanoseconds:
*/
1000000000), dotclock);
dotclock);
} else
DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n",
DRM_DEBUG("crtc %d: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n",
(int) linedur_ns, (int) pixeldur_ns);
}
/**
* drm_calc_vbltimestamp_from_scanoutpos - helper routine for kms
* drivers. 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.
*
* @dev: DRM device.
* @crtc: Which crtc's 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.
* @flags: Flags to pass to driver:
* 0 = Default.
* DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
* @refcrtc: drm_crtc* of crtc which defines scanout timing.
*
* Returns negative value on error, failure or if not supported in current
* video mode:
*
* -EINVAL - Invalid crtc.
* -EAGAIN - Temporary unavailable, e.g., called before initial modeset.
* -ENOTSUPP - Function not supported in current display mode.
* -EIO - Failed, e.g., due to failed scanout position query.
*
* Returns or'ed positive status flags on success:
*
* DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping.
* DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
*
*/
int *max_error,
struct timeval *vblank_time,
unsigned flags,
{
bool invbl;
return -EINVAL;
}
/* Scanout position query not supported? Should not happen. */
DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
return -EIO;
}
/* Durations of frames, lines, pixels in nanoseconds. */
/* If mode timing undefined, just return as no-op:
* Happens during initial modesetting of a crtc.
*/
return -EAGAIN;
}
/* 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++) {
/* Disable preemption to make it very likely to
* succeed in the first iteration even on PREEMPT_RT kernel.
*/
/* Get system timestamp before query. */
/* Get vertical and horizontal scanout pos. vpos, hpos. */
/* Get system timestamp after query. */
/* Return as no-op if scanout query unsupported or failed. */
if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n",
crtc, vbl_status);
return -EIO;
}
/* Accept result with < max_error nsecs timing uncertainty. */
break;
}
/* Noisy system timing? */
if (i == DRM_TIMESTAMP_MAXRETRIES) {
DRM_DEBUG("crtc %d: Noisy timestamp %d us > %d us [%d reps].\n",
}
/* Return upper bound of timestamp precision error. */
*max_error = (int) duration_ns;
/* Check if in vblank area:
* vpos is >=0 in video scanout area, but negative
* within vblank area, counting down the number of lines until
* start of scanout.
*/
/* 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.
*/
/* Is vpos outside nominal vblank area, but less than
* 1/100 of a frame height away from start of vblank?
* If so, assume this isn't a massively delayed vblank
* interrupt, but a vblank interrupt that fired a few
* microseconds before true start of vblank. Compensate
* by adding a full frame duration to the final timestamp.
* Happens, e.g., on ATI R500, R600.
*
* We only do this if DRM_CALLED_FROM_VBLIRQ.
*/
/* Signal this correction as "applied". */
vbl_status |= 0x8;
}
/* Subtract time delta from raw timestamp to get final
* vblank_time timestamp for end of vblank.
*/
DRM_DEBUG("crtc %d : v %d p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
(int) duration_ns/1000, i);
if (invbl)
return vbl_status;
}
/**
* drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
* vblank interval.
*
* @dev: DRM device
* @crtc: which crtc's vblank timestamp to retrieve
* @tvblank: Pointer to target struct timeval which should receive the timestamp
* @flags: Flags to pass to driver:
* 0 = Default.
* DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
*
* 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 non-zero if timestamp is considered to be very precise.
*/
{
int ret = 0;
/* Define requested maximum error on timestamps (nanoseconds). */
/* Query driver if possible and precision timestamping enabled. */
if (ret > 0)
}
/* GPU high precision timestamp query unsupported or failed.
* Return gettimeofday timestamp as best estimate.
*/
return 0;
}
/**
* drm_vblank_count - retrieve "cooked" vblank counter value
* @dev: DRM device
* @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.
*/
{
}
/**
* drm_vblank_count_and_time - retrieve "cooked" vblank counter value
* and the system timestamp corresponding to that vblank counter value.
*
* @dev: DRM device
* @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 value vblank counter
* value.
*/
struct timeval *vblanktime)
{
/* Read timestamp from slot of _vblank_time ringbuffer
* that corresponds to current vblank count. Retry if
* count has incremented during readout. This works like
* a seqlock.
*/
do {
return cur_vblank;
}
/* LINTED */
struct drm_pending_vblank_event *e,
{
}
/**
* drm_send_vblank_event - helper to send vblank event after pageflip
* @dev: DRM device
* @crtc: CRTC in question
* @e: the event to send
*
* Updates sequence # and timestamp on event, and sends it to userspace.
* Caller must hold event lock.
*/
struct drm_pending_vblank_event *e)
{
unsigned int seq;
if (crtc >= 0) {
} else {
seq = 0;
}
}
/**
* drm_update_vblank_count - update the master vblank counter
* @dev: DRM device
* @crtc: counter to update
*
* Call back into the driver to update the appropriate vblank counter
* (specified by @crtc). 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 dev->vbl_lock since this reads & writes
* device vblank fields.
*/
{
/*
* 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 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 {
/* Deal with counter wrap */
DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
}
DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
/* Reinitialize corresponding vblank timestamp if high-precision query
* available. Skip this step if query unsupported or failed. Will
* reinitialize delayed at next vblank interrupt in that case.
*/
if (rc) {
}
}
/**
* drm_vblank_get - get a reference count on vblank events
* @dev: DRM device
* @crtc: which CRTC to own
*
* Acquire a reference count on vblank events to avoid having them disabled
* while in use.
*
* RETURNS
* Zero on success, nonzero on failure.
*/
{
int ret = 0;
/* Going from 0->1 means we have to enable interrupts again */
DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n",
if (ret)
else {
}
}
} else {
}
}
return ret;
}
/**
* drm_vblank_put - give up ownership of vblank events
* @dev: DRM device
* @crtc: which counter to give up
*
* Release ownership of a given vblank counter, turning off interrupts
* if possible.
*/
{
/* Last user schedules interrupt disable */
(drm_vblank_offdelay > 0))
}
/**
* drm_vblank_off - disable vblank events on a CRTC
* @dev: DRM device
* @crtc: CRTC in question
*
* Caller must hold event lock.
*/
{
unsigned long irqflags;
struct drm_pending_vblank_event *e, *t;
unsigned int seq;
/* Send any queued vblank events, lest the natives grow disquiet */
continue;
DRM_DEBUG("Sending premature vblank event on disable: \
wanted %d, current %d\n",
}
}
/**
* drm_vblank_pre_modeset - account for vblanks across mode sets
* @dev: DRM device
* @crtc: CRTC in question
* @post: post or pre mode set?
*
* Account for vblank events across mode setting events, which will likely
* reset the hardware frame counter.
*/
{
/* vblank is not initialized (IRQ not installed ?) */
return;
/*
* To avoid all the problems that might happen if interrupts
* 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.
*/
}
}
{
unsigned long irqflags;
/* vblank is not initialized (IRQ not installed ?), or has been freed */
return;
}
}
/**
* drm_modeset_ctl - handle vblank event counter changes across mode switch
* @DRM_IOCTL_ARGS: standard ioctl arguments
*
* Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET
* ioctls around modesetting so that any lost vblank events are accounted for.
*
* Generally the counter will reset across mode sets. If interrupts are
* enabled around this call, we don't have to do anything since the counter
* will have already been incremented.
*/
/* LINTED */
{
unsigned int crtc;
/* If drm_vblank_init() hasn't been called yet, just no-op */
return 0;
/* KMS drivers handle this internally */
return 0;
return -EINVAL;
case _DRM_PRE_MODESET:
break;
case _DRM_POST_MODESET:
break;
default:
return -EINVAL;
}
return 0;
}
union drm_wait_vblank *vblwait,
{
struct drm_pending_vblank_event *e;
unsigned long flags;
unsigned int seq;
int ret;
e = kzalloc(sizeof *e, GFP_KERNEL);
if (e == NULL) {
goto err_put;
}
goto err_unlock;
}
}
DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
} else {
/* drm_handle_vblank_events will call drm_vblank_put */
}
return 0;
kfree(e, sizeof(*e));
return ret;
}
/**
* 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).
*/
/* LINTED */
{
int ret = 0;
return -EINVAL;
return -EINVAL;
DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
return -EINVAL;
}
if (high_crtc)
else
return -EINVAL;
if (ret) {
return ret;
}
case _DRM_VBLANK_RELATIVE:
/* LINTED */
case _DRM_VBLANK_ABSOLUTE:
break;
default:
goto done;
}
if (flags & _DRM_VBLANK_EVENT) {
/* must hold on to the vblank ref until the event fires
* drm_vblank_put will be called asynchronously
*/
}
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
}
DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
!dev->irq_enabled));
DRM_DEBUG("returning %d to client\n",
} else {
DRM_DEBUG("vblank wait interrupted by signal\n");
}
done:
return ret;
}
{
struct drm_pending_vblank_event *e, *t;
unsigned long flags;
unsigned int seq;
continue;
continue;
DRM_DEBUG("vblank event on %d, current %d\n",
}
}
/**
* drm_handle_vblank - handle a vblank event
* @dev: DRM device
* @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.
*/
{
unsigned long irqflags;
return false;
/* Need timestamp lock to prevent concurrent execution with
* or corrupted timestamps and vblank counts.
*/
/* Vblank irq handling disabled. Nothing to do. */
return false;
}
/* Fetch corresponding timestamp for this vblank interval from
* driver and store it in proper slot of timestamp ringbuffer.
*/
/* Get current timestamp and count. */
/* Compute time difference to timestamp of last vblank */
/* Update vblank timestamp and count if at least
* DRM_REDUNDANT_VBLIRQ_THRESH_NS nanoseconds
* difference between last stored timestamp and current
* timestamp. A smaller difference means basically
* identical timestamps. Happens if this vblank has
* been already processed and this is a redundant call,
* e.g., due to spurious vblank interrupts. We need to
* ignore those for accounting.
*/
/* Store new timestamp in ringbuffer. */
/* Increment cooked vblank count. This also atomically commits
* the timestamp computed above.
*/
} else {
DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n",
}
return true;
}