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@xxxxxxxx> Thanks for your review, I applied the these two patches. -Daniel > > -- > Stefan > > > Cc: Stefan Agner <stefan@xxxxxxxx> > > Signed-off-by: Daniel Vetter <daniel.vetter@xxxxxxxxx> > > --- > > 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@xxxxxxxxxxx> > > * \author Gareth Hughes <gareth@xxxxxxxxxxx> > > + * > > + * 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@xxxxxxxxxxx> > > + * \author Gareth Hughes <gareth@xxxxxxxxxxx> > > + * > > + * 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel