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> -- 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 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx