18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * drm_irq.c IRQ and vblank support
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * \author Rickard E. (Rik) Faith <faith@valinux.com>
58c2ecf20Sopenharmony_ci * \author Gareth Hughes <gareth@valinux.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
88c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
98c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
108c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
118c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
128c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
158c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
168c2ecf20Sopenharmony_ci * Software.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
218c2ecf20Sopenharmony_ci * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
228c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
238c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
248c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/export.h>
288c2ecf20Sopenharmony_ci#include <linux/kthread.h>
298c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
328c2ecf20Sopenharmony_ci#include <drm/drm_drv.h>
338c2ecf20Sopenharmony_ci#include <drm/drm_framebuffer.h>
348c2ecf20Sopenharmony_ci#include <drm/drm_managed.h>
358c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h>
368c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
378c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include "drm_internal.h"
408c2ecf20Sopenharmony_ci#include "drm_trace.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/**
438c2ecf20Sopenharmony_ci * DOC: vblank handling
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * From the computer's perspective, every time the monitor displays
468c2ecf20Sopenharmony_ci * a new frame the scanout engine has "scanned out" the display image
478c2ecf20Sopenharmony_ci * from top to bottom, one row of pixels at a time. The current row
488c2ecf20Sopenharmony_ci * of pixels is referred to as the current scanline.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * In addition to the display's visible area, there's usually a couple of
518c2ecf20Sopenharmony_ci * extra scanlines which aren't actually displayed on the screen.
528c2ecf20Sopenharmony_ci * These extra scanlines don't contain image data and are occasionally used
538c2ecf20Sopenharmony_ci * for features like audio and infoframes. The region made up of these
548c2ecf20Sopenharmony_ci * scanlines is referred to as the vertical blanking region, or vblank for
558c2ecf20Sopenharmony_ci * short.
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci * For historical reference, the vertical blanking period was designed to
588c2ecf20Sopenharmony_ci * give the electron gun (on CRTs) enough time to move back to the top of
598c2ecf20Sopenharmony_ci * the screen to start scanning out the next frame. Similar for horizontal
608c2ecf20Sopenharmony_ci * blanking periods. They were designed to give the electron gun enough
618c2ecf20Sopenharmony_ci * time to move back to the other side of the screen to start scanning the
628c2ecf20Sopenharmony_ci * next scanline.
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * ::
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci *    physical →   ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
688c2ecf20Sopenharmony_ci *    top of      |                                        |
698c2ecf20Sopenharmony_ci *    display     |                                        |
708c2ecf20Sopenharmony_ci *                |               New frame                |
718c2ecf20Sopenharmony_ci *                |                                        |
728c2ecf20Sopenharmony_ci *                |↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓|
738c2ecf20Sopenharmony_ci *                |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| ← Scanline,
748c2ecf20Sopenharmony_ci *                |↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓|   updates the
758c2ecf20Sopenharmony_ci *                |                                        |   frame as it
768c2ecf20Sopenharmony_ci *                |                                        |   travels down
778c2ecf20Sopenharmony_ci *                |                                        |   ("sacn out")
788c2ecf20Sopenharmony_ci *                |               Old frame                |
798c2ecf20Sopenharmony_ci *                |                                        |
808c2ecf20Sopenharmony_ci *                |                                        |
818c2ecf20Sopenharmony_ci *                |                                        |
828c2ecf20Sopenharmony_ci *                |                                        |   physical
838c2ecf20Sopenharmony_ci *                |                                        |   bottom of
848c2ecf20Sopenharmony_ci *    vertical    |⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽| ← display
858c2ecf20Sopenharmony_ci *    blanking    ┆xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx┆
868c2ecf20Sopenharmony_ci *    region   →  ┆xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx┆
878c2ecf20Sopenharmony_ci *                ┆xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx┆
888c2ecf20Sopenharmony_ci *    start of →   ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽
898c2ecf20Sopenharmony_ci *    new frame
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * "Physical top of display" is the reference point for the high-precision/
928c2ecf20Sopenharmony_ci * corrected timestamp.
938c2ecf20Sopenharmony_ci *
948c2ecf20Sopenharmony_ci * On a lot of display hardware, programming needs to take effect during the
958c2ecf20Sopenharmony_ci * vertical blanking period so that settings like gamma, the image buffer
968c2ecf20Sopenharmony_ci * buffer to be scanned out, etc. can safely be changed without showing
978c2ecf20Sopenharmony_ci * any visual artifacts on the screen. In some unforgiving hardware, some of
988c2ecf20Sopenharmony_ci * this programming has to both start and end in the same vblank. To help
998c2ecf20Sopenharmony_ci * with the timing of the hardware programming, an interrupt is usually
1008c2ecf20Sopenharmony_ci * available to notify the driver when it can start the updating of registers.
1018c2ecf20Sopenharmony_ci * The interrupt is in this context named the vblank interrupt.
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * The vblank interrupt may be fired at different points depending on the
1048c2ecf20Sopenharmony_ci * hardware. Some hardware implementations will fire the interrupt when the
1058c2ecf20Sopenharmony_ci * new frame start, other implementations will fire the interrupt at different
1068c2ecf20Sopenharmony_ci * points in time.
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * Vertical blanking plays a major role in graphics rendering. To achieve
1098c2ecf20Sopenharmony_ci * tear-free display, users must synchronize page flips and/or rendering to
1108c2ecf20Sopenharmony_ci * vertical blanking. The DRM API offers ioctls to perform page flips
1118c2ecf20Sopenharmony_ci * synchronized to vertical blanking and wait for vertical blanking.
1128c2ecf20Sopenharmony_ci *
1138c2ecf20Sopenharmony_ci * The DRM core handles most of the vertical blanking management logic, which
1148c2ecf20Sopenharmony_ci * involves filtering out spurious interrupts, keeping race-free blanking
1158c2ecf20Sopenharmony_ci * counters, coping with counter wrap-around and resets and keeping use counts.
1168c2ecf20Sopenharmony_ci * It relies on the driver to generate vertical blanking interrupts and
1178c2ecf20Sopenharmony_ci * optionally provide a hardware vertical blanking counter.
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci * Drivers must initialize the vertical blanking handling core with a call to
1208c2ecf20Sopenharmony_ci * drm_vblank_init(). Minimally, a driver needs to implement
1218c2ecf20Sopenharmony_ci * &drm_crtc_funcs.enable_vblank and &drm_crtc_funcs.disable_vblank plus call
1228c2ecf20Sopenharmony_ci * drm_crtc_handle_vblank() in its vblank interrupt handler for working vblank
1238c2ecf20Sopenharmony_ci * support.
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci * Vertical blanking interrupts can be enabled by the DRM core or by drivers
1268c2ecf20Sopenharmony_ci * themselves (for instance to handle page flipping operations).  The DRM core
1278c2ecf20Sopenharmony_ci * maintains a vertical blanking use count to ensure that the interrupts are not
1288c2ecf20Sopenharmony_ci * disabled while a user still needs them. To increment the use count, drivers
1298c2ecf20Sopenharmony_ci * call drm_crtc_vblank_get() and release the vblank reference again with
1308c2ecf20Sopenharmony_ci * drm_crtc_vblank_put(). In between these two calls vblank interrupts are
1318c2ecf20Sopenharmony_ci * guaranteed to be enabled.
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci * On many hardware disabling the vblank interrupt cannot be done in a race-free
1348c2ecf20Sopenharmony_ci * manner, see &drm_driver.vblank_disable_immediate and
1358c2ecf20Sopenharmony_ci * &drm_driver.max_vblank_count. In that case the vblank core only disables the
1368c2ecf20Sopenharmony_ci * vblanks after a timer has expired, which can be configured through the
1378c2ecf20Sopenharmony_ci * ``vblankoffdelay`` module parameter.
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * Drivers for hardware without support for vertical-blanking interrupts
1408c2ecf20Sopenharmony_ci * must not call drm_vblank_init(). For such drivers, atomic helpers will
1418c2ecf20Sopenharmony_ci * automatically generate fake vblank events as part of the display update.
1428c2ecf20Sopenharmony_ci * This functionality also can be controlled by the driver by enabling and
1438c2ecf20Sopenharmony_ci * disabling struct drm_crtc_state.no_vblank.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* Retry timestamp calculation up to 3 times to satisfy
1478c2ecf20Sopenharmony_ci * drm_timestamp_precision before giving up.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_ci#define DRM_TIMESTAMP_MAXRETRIES 3
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* Threshold in nanoseconds for detection of redundant
1528c2ecf20Sopenharmony_ci * vblank irq in drm_handle_vblank(). 1 msec should be ok.
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_ci#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic bool
1578c2ecf20Sopenharmony_cidrm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
1588c2ecf20Sopenharmony_ci			  ktime_t *tvblank, bool in_vblank_irq);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cimodule_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
1658c2ecf20Sopenharmony_cimodule_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
1668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
1678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void store_vblank(struct drm_device *dev, unsigned int pipe,
1708c2ecf20Sopenharmony_ci			 u32 vblank_count_inc,
1718c2ecf20Sopenharmony_ci			 ktime_t t_vblank, u32 last)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->vblank_time_lock);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	vblank->last = last;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	write_seqlock(&vblank->seqlock);
1808c2ecf20Sopenharmony_ci	vblank->time = t_vblank;
1818c2ecf20Sopenharmony_ci	atomic64_add(vblank_count_inc, &vblank->count);
1828c2ecf20Sopenharmony_ci	write_sequnlock(&vblank->seqlock);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return vblank->max_vblank_count ?: dev->max_vblank_count;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/*
1938c2ecf20Sopenharmony_ci * "No hw counter" fallback implementation of .get_vblank_counter() hook,
1948c2ecf20Sopenharmony_ci * if there is no useable hardware frame counter available.
1958c2ecf20Sopenharmony_ci */
1968c2ecf20Sopenharmony_cistatic u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	drm_WARN_ON_ONCE(dev, drm_max_vblank_count(dev, pipe) != 0);
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
2058c2ecf20Sopenharmony_ci		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		if (drm_WARN_ON(dev, !crtc))
2088c2ecf20Sopenharmony_ci			return 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		if (crtc->funcs->get_vblank_counter)
2118c2ecf20Sopenharmony_ci			return crtc->funcs->get_vblank_counter(crtc);
2128c2ecf20Sopenharmony_ci	} else if (dev->driver->get_vblank_counter) {
2138c2ecf20Sopenharmony_ci		return dev->driver->get_vblank_counter(dev, pipe);
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return drm_vblank_no_hw_counter(dev, pipe);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * Reset the stored timestamp for the current vblank count to correspond
2218c2ecf20Sopenharmony_ci * to the last vblank occurred.
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * Only to be called from drm_crtc_vblank_on().
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci * Note: caller must hold &drm_device.vbl_lock since this reads & writes
2268c2ecf20Sopenharmony_ci * device vblank fields.
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_cistatic void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u32 cur_vblank;
2318c2ecf20Sopenharmony_ci	bool rc;
2328c2ecf20Sopenharmony_ci	ktime_t t_vblank;
2338c2ecf20Sopenharmony_ci	int count = DRM_TIMESTAMP_MAXRETRIES;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	spin_lock(&dev->vblank_time_lock);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/*
2388c2ecf20Sopenharmony_ci	 * sample the current counter to avoid random jumps
2398c2ecf20Sopenharmony_ci	 * when drm_vblank_enable() applies the diff
2408c2ecf20Sopenharmony_ci	 */
2418c2ecf20Sopenharmony_ci	do {
2428c2ecf20Sopenharmony_ci		cur_vblank = __get_vblank_counter(dev, pipe);
2438c2ecf20Sopenharmony_ci		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
2448c2ecf20Sopenharmony_ci	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * Only reinitialize corresponding vblank timestamp if high-precision query
2488c2ecf20Sopenharmony_ci	 * available and didn't fail. Otherwise reinitialize delayed at next vblank
2498c2ecf20Sopenharmony_ci	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
2508c2ecf20Sopenharmony_ci	 */
2518c2ecf20Sopenharmony_ci	if (!rc)
2528c2ecf20Sopenharmony_ci		t_vblank = 0;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * +1 to make sure user will never see the same
2568c2ecf20Sopenharmony_ci	 * vblank counter value before and after a modeset
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	store_vblank(dev, pipe, 1, t_vblank, cur_vblank);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	spin_unlock(&dev->vblank_time_lock);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/*
2648c2ecf20Sopenharmony_ci * Call back into the driver to update the appropriate vblank counter
2658c2ecf20Sopenharmony_ci * (specified by @pipe).  Deal with wraparound, if it occurred, and
2668c2ecf20Sopenharmony_ci * update the last read value so we can deal with wraparound on the next
2678c2ecf20Sopenharmony_ci * call if necessary.
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * Only necessary when going from off->on, to account for frames we
2708c2ecf20Sopenharmony_ci * didn't get an interrupt for.
2718c2ecf20Sopenharmony_ci *
2728c2ecf20Sopenharmony_ci * Note: caller must hold &drm_device.vbl_lock since this reads & writes
2738c2ecf20Sopenharmony_ci * device vblank fields.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_cistatic void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
2768c2ecf20Sopenharmony_ci				    bool in_vblank_irq)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
2798c2ecf20Sopenharmony_ci	u32 cur_vblank, diff;
2808c2ecf20Sopenharmony_ci	bool rc;
2818c2ecf20Sopenharmony_ci	ktime_t t_vblank;
2828c2ecf20Sopenharmony_ci	int count = DRM_TIMESTAMP_MAXRETRIES;
2838c2ecf20Sopenharmony_ci	int framedur_ns = vblank->framedur_ns;
2848c2ecf20Sopenharmony_ci	u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/*
2878c2ecf20Sopenharmony_ci	 * Interrupts were disabled prior to this call, so deal with counter
2888c2ecf20Sopenharmony_ci	 * wrap if needed.
2898c2ecf20Sopenharmony_ci	 * NOTE!  It's possible we lost a full dev->max_vblank_count + 1 events
2908c2ecf20Sopenharmony_ci	 * here if the register is small or we had vblank interrupts off for
2918c2ecf20Sopenharmony_ci	 * a long time.
2928c2ecf20Sopenharmony_ci	 *
2938c2ecf20Sopenharmony_ci	 * We repeat the hardware vblank counter & timestamp query until
2948c2ecf20Sopenharmony_ci	 * we get consistent results. This to prevent races between gpu
2958c2ecf20Sopenharmony_ci	 * updating its hardware counter while we are retrieving the
2968c2ecf20Sopenharmony_ci	 * corresponding vblank timestamp.
2978c2ecf20Sopenharmony_ci	 */
2988c2ecf20Sopenharmony_ci	do {
2998c2ecf20Sopenharmony_ci		cur_vblank = __get_vblank_counter(dev, pipe);
3008c2ecf20Sopenharmony_ci		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
3018c2ecf20Sopenharmony_ci	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (max_vblank_count) {
3048c2ecf20Sopenharmony_ci		/* trust the hw counter when it's around */
3058c2ecf20Sopenharmony_ci		diff = (cur_vblank - vblank->last) & max_vblank_count;
3068c2ecf20Sopenharmony_ci	} else if (rc && framedur_ns) {
3078c2ecf20Sopenharmony_ci		u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		/*
3108c2ecf20Sopenharmony_ci		 * Figure out how many vblanks we've missed based
3118c2ecf20Sopenharmony_ci		 * on the difference in the timestamps and the
3128c2ecf20Sopenharmony_ci		 * frame/field duration.
3138c2ecf20Sopenharmony_ci		 */
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		drm_dbg_vbl(dev, "crtc %u: Calculating number of vblanks."
3168c2ecf20Sopenharmony_ci			    " diff_ns = %lld, framedur_ns = %d)\n",
3178c2ecf20Sopenharmony_ci			    pipe, (long long)diff_ns, framedur_ns);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		if (diff == 0 && in_vblank_irq)
3228c2ecf20Sopenharmony_ci			drm_dbg_vbl(dev, "crtc %u: Redundant vblirq ignored\n",
3238c2ecf20Sopenharmony_ci				    pipe);
3248c2ecf20Sopenharmony_ci	} else {
3258c2ecf20Sopenharmony_ci		/* some kind of default for drivers w/o accurate vbl timestamping */
3268c2ecf20Sopenharmony_ci		diff = in_vblank_irq ? 1 : 0;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/*
3308c2ecf20Sopenharmony_ci	 * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset
3318c2ecf20Sopenharmony_ci	 * interval? If so then vblank irqs keep running and it will likely
3328c2ecf20Sopenharmony_ci	 * happen that the hardware vblank counter is not trustworthy as it
3338c2ecf20Sopenharmony_ci	 * might reset at some point in that interval and vblank timestamps
3348c2ecf20Sopenharmony_ci	 * are not trustworthy either in that interval. Iow. this can result
3358c2ecf20Sopenharmony_ci	 * in a bogus diff >> 1 which must be avoided as it would cause
3368c2ecf20Sopenharmony_ci	 * random large forward jumps of the software vblank counter.
3378c2ecf20Sopenharmony_ci	 */
3388c2ecf20Sopenharmony_ci	if (diff > 1 && (vblank->inmodeset & 0x2)) {
3398c2ecf20Sopenharmony_ci		drm_dbg_vbl(dev,
3408c2ecf20Sopenharmony_ci			    "clamping vblank bump to 1 on crtc %u: diffr=%u"
3418c2ecf20Sopenharmony_ci			    " due to pre-modeset.\n", pipe, diff);
3428c2ecf20Sopenharmony_ci		diff = 1;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	drm_dbg_vbl(dev, "updating vblank count on crtc %u:"
3468c2ecf20Sopenharmony_ci		    " current=%llu, diff=%u, hw=%u hw_last=%u\n",
3478c2ecf20Sopenharmony_ci		    pipe, (unsigned long long)atomic64_read(&vblank->count),
3488c2ecf20Sopenharmony_ci		    diff, cur_vblank, vblank->last);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (diff == 0) {
3518c2ecf20Sopenharmony_ci		drm_WARN_ON_ONCE(dev, cur_vblank != vblank->last);
3528c2ecf20Sopenharmony_ci		return;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/*
3568c2ecf20Sopenharmony_ci	 * Only reinitialize corresponding vblank timestamp if high-precision query
3578c2ecf20Sopenharmony_ci	 * available and didn't fail, or we were called from the vblank interrupt.
3588c2ecf20Sopenharmony_ci	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
3598c2ecf20Sopenharmony_ci	 * for now, to mark the vblanktimestamp as invalid.
3608c2ecf20Sopenharmony_ci	 */
3618c2ecf20Sopenharmony_ci	if (!rc && !in_vblank_irq)
3628c2ecf20Sopenharmony_ci		t_vblank = 0;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	store_vblank(dev, pipe, diff, t_vblank, cur_vblank);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ciu64 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
3708c2ecf20Sopenharmony_ci	u64 count;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
3738c2ecf20Sopenharmony_ci		return 0;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	count = atomic64_read(&vblank->count);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/*
3788c2ecf20Sopenharmony_ci	 * This read barrier corresponds to the implicit write barrier of the
3798c2ecf20Sopenharmony_ci	 * write seqlock in store_vblank(). Note that this is the only place
3808c2ecf20Sopenharmony_ci	 * where we need an explicit barrier, since all other access goes
3818c2ecf20Sopenharmony_ci	 * through drm_vblank_count_and_time(), which already has the required
3828c2ecf20Sopenharmony_ci	 * read barrier curtesy of the read seqlock.
3838c2ecf20Sopenharmony_ci	 */
3848c2ecf20Sopenharmony_ci	smp_rmb();
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return count;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/**
3908c2ecf20Sopenharmony_ci * drm_crtc_accurate_vblank_count - retrieve the master vblank counter
3918c2ecf20Sopenharmony_ci * @crtc: which counter to retrieve
3928c2ecf20Sopenharmony_ci *
3938c2ecf20Sopenharmony_ci * This function is similar to drm_crtc_vblank_count() but this function
3948c2ecf20Sopenharmony_ci * interpolates to handle a race with vblank interrupts using the high precision
3958c2ecf20Sopenharmony_ci * timestamping support.
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * This is mostly useful for hardware that can obtain the scanout position, but
3988c2ecf20Sopenharmony_ci * doesn't have a hardware frame counter.
3998c2ecf20Sopenharmony_ci */
4008c2ecf20Sopenharmony_ciu64 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
4038c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
4048c2ecf20Sopenharmony_ci	u64 vblank;
4058c2ecf20Sopenharmony_ci	unsigned long flags;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	drm_WARN_ONCE(dev, drm_debug_enabled(DRM_UT_VBL) &&
4088c2ecf20Sopenharmony_ci		      !crtc->funcs->get_vblank_timestamp,
4098c2ecf20Sopenharmony_ci		      "This function requires support for accurate vblank timestamps.");
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vblank_time_lock, flags);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	drm_update_vblank_count(dev, pipe, false);
4148c2ecf20Sopenharmony_ci	vblank = drm_vblank_count(dev, pipe);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return vblank;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_accurate_vblank_count);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void __disable_vblank(struct drm_device *dev, unsigned int pipe)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
4258c2ecf20Sopenharmony_ci		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		if (drm_WARN_ON(dev, !crtc))
4288c2ecf20Sopenharmony_ci			return;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		if (crtc->funcs->disable_vblank)
4318c2ecf20Sopenharmony_ci			crtc->funcs->disable_vblank(crtc);
4328c2ecf20Sopenharmony_ci	} else {
4338c2ecf20Sopenharmony_ci		dev->driver->disable_vblank(dev, pipe);
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci/*
4388c2ecf20Sopenharmony_ci * Disable vblank irq's on crtc, make sure that last vblank count
4398c2ecf20Sopenharmony_ci * of hardware and corresponding consistent software vblank counter
4408c2ecf20Sopenharmony_ci * are preserved, even if there are any spurious vblank irq's after
4418c2ecf20Sopenharmony_ci * disable.
4428c2ecf20Sopenharmony_ci */
4438c2ecf20Sopenharmony_civoid drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
4468c2ecf20Sopenharmony_ci	unsigned long irqflags;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->vbl_lock);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* Prevent vblank irq processing while disabling vblank irqs,
4518c2ecf20Sopenharmony_ci	 * so no updates of timestamps or count can happen after we've
4528c2ecf20Sopenharmony_ci	 * disabled. Needed to prevent races in case of delayed irq's.
4538c2ecf20Sopenharmony_ci	 */
4548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/*
4578c2ecf20Sopenharmony_ci	 * Update vblank count and disable vblank interrupts only if the
4588c2ecf20Sopenharmony_ci	 * interrupts were enabled. This avoids calling the ->disable_vblank()
4598c2ecf20Sopenharmony_ci	 * operation in atomic context with the hardware potentially runtime
4608c2ecf20Sopenharmony_ci	 * suspended.
4618c2ecf20Sopenharmony_ci	 */
4628c2ecf20Sopenharmony_ci	if (!vblank->enabled)
4638c2ecf20Sopenharmony_ci		goto out;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/*
4668c2ecf20Sopenharmony_ci	 * Update the count and timestamp to maintain the
4678c2ecf20Sopenharmony_ci	 * appearance that the counter has been ticking all along until
4688c2ecf20Sopenharmony_ci	 * this time. This makes the count account for the entire time
4698c2ecf20Sopenharmony_ci	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
4708c2ecf20Sopenharmony_ci	 */
4718c2ecf20Sopenharmony_ci	drm_update_vblank_count(dev, pipe, false);
4728c2ecf20Sopenharmony_ci	__disable_vblank(dev, pipe);
4738c2ecf20Sopenharmony_ci	vblank->enabled = false;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ciout:
4768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic void vblank_disable_fn(struct timer_list *t)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = from_timer(vblank, t, disable_timer);
4828c2ecf20Sopenharmony_ci	struct drm_device *dev = vblank->dev;
4838c2ecf20Sopenharmony_ci	unsigned int pipe = vblank->pipe;
4848c2ecf20Sopenharmony_ci	unsigned long irqflags;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vbl_lock, irqflags);
4878c2ecf20Sopenharmony_ci	if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
4888c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "disabling vblank on crtc %u\n", pipe);
4898c2ecf20Sopenharmony_ci		drm_vblank_disable_and_save(dev, pipe);
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic void drm_vblank_init_release(struct drm_device *dev, void *ptr)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = ptr;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	drm_WARN_ON(dev, READ_ONCE(vblank->enabled) &&
4998c2ecf20Sopenharmony_ci		    drm_core_check_feature(dev, DRIVER_MODESET));
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	drm_vblank_destroy_worker(vblank);
5028c2ecf20Sopenharmony_ci	del_timer_sync(&vblank->disable_timer);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/**
5068c2ecf20Sopenharmony_ci * drm_vblank_init - initialize vblank support
5078c2ecf20Sopenharmony_ci * @dev: DRM device
5088c2ecf20Sopenharmony_ci * @num_crtcs: number of CRTCs supported by @dev
5098c2ecf20Sopenharmony_ci *
5108c2ecf20Sopenharmony_ci * This function initializes vblank support for @num_crtcs display pipelines.
5118c2ecf20Sopenharmony_ci * Cleanup is handled automatically through a cleanup function added with
5128c2ecf20Sopenharmony_ci * drmm_add_action_or_reset().
5138c2ecf20Sopenharmony_ci *
5148c2ecf20Sopenharmony_ci * Returns:
5158c2ecf20Sopenharmony_ci * Zero on success or a negative error code on failure.
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_ciint drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	int ret;
5208c2ecf20Sopenharmony_ci	unsigned int i;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	spin_lock_init(&dev->vbl_lock);
5238c2ecf20Sopenharmony_ci	spin_lock_init(&dev->vblank_time_lock);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	dev->vblank = drmm_kcalloc(dev, num_crtcs, sizeof(*dev->vblank), GFP_KERNEL);
5268c2ecf20Sopenharmony_ci	if (!dev->vblank)
5278c2ecf20Sopenharmony_ci		return -ENOMEM;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	dev->num_crtcs = num_crtcs;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	for (i = 0; i < num_crtcs; i++) {
5328c2ecf20Sopenharmony_ci		struct drm_vblank_crtc *vblank = &dev->vblank[i];
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		vblank->dev = dev;
5358c2ecf20Sopenharmony_ci		vblank->pipe = i;
5368c2ecf20Sopenharmony_ci		init_waitqueue_head(&vblank->queue);
5378c2ecf20Sopenharmony_ci		timer_setup(&vblank->disable_timer, vblank_disable_fn, 0);
5388c2ecf20Sopenharmony_ci		seqlock_init(&vblank->seqlock);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		ret = drmm_add_action_or_reset(dev, drm_vblank_init_release,
5418c2ecf20Sopenharmony_ci					       vblank);
5428c2ecf20Sopenharmony_ci		if (ret)
5438c2ecf20Sopenharmony_ci			return ret;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		ret = drm_vblank_worker_init(vblank);
5468c2ecf20Sopenharmony_ci		if (ret)
5478c2ecf20Sopenharmony_ci			return ret;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return 0;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_vblank_init);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/**
5558c2ecf20Sopenharmony_ci * drm_dev_has_vblank - test if vblanking has been initialized for
5568c2ecf20Sopenharmony_ci *                      a device
5578c2ecf20Sopenharmony_ci * @dev: the device
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Drivers may call this function to test if vblank support is
5608c2ecf20Sopenharmony_ci * initialized for a device. For most hardware this means that vblanking
5618c2ecf20Sopenharmony_ci * can also be enabled.
5628c2ecf20Sopenharmony_ci *
5638c2ecf20Sopenharmony_ci * Atomic helpers use this function to initialize
5648c2ecf20Sopenharmony_ci * &drm_crtc_state.no_vblank. See also drm_atomic_helper_check_modeset().
5658c2ecf20Sopenharmony_ci *
5668c2ecf20Sopenharmony_ci * Returns:
5678c2ecf20Sopenharmony_ci * True if vblanking has been initialized for the given device, false
5688c2ecf20Sopenharmony_ci * otherwise.
5698c2ecf20Sopenharmony_ci */
5708c2ecf20Sopenharmony_cibool drm_dev_has_vblank(const struct drm_device *dev)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	return dev->num_crtcs != 0;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_dev_has_vblank);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/**
5778c2ecf20Sopenharmony_ci * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC
5788c2ecf20Sopenharmony_ci * @crtc: which CRTC's vblank waitqueue to retrieve
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * This function returns a pointer to the vblank waitqueue for the CRTC.
5818c2ecf20Sopenharmony_ci * Drivers can use this to implement vblank waits using wait_event() and related
5828c2ecf20Sopenharmony_ci * functions.
5838c2ecf20Sopenharmony_ci */
5848c2ecf20Sopenharmony_ciwait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	return &crtc->dev->vblank[drm_crtc_index(crtc)].queue;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci/**
5928c2ecf20Sopenharmony_ci * drm_calc_timestamping_constants - calculate vblank timestamp constants
5938c2ecf20Sopenharmony_ci * @crtc: drm_crtc whose timestamp constants should be updated.
5948c2ecf20Sopenharmony_ci * @mode: display mode containing the scanout timings
5958c2ecf20Sopenharmony_ci *
5968c2ecf20Sopenharmony_ci * Calculate and store various constants which are later needed by vblank and
5978c2ecf20Sopenharmony_ci * swap-completion timestamping, e.g, by
5988c2ecf20Sopenharmony_ci * drm_crtc_vblank_helper_get_vblank_timestamp(). They are derived from
5998c2ecf20Sopenharmony_ci * CRTC's true scanout timing, so they take things like panel scaling or
6008c2ecf20Sopenharmony_ci * other adjustments into account.
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_civoid drm_calc_timestamping_constants(struct drm_crtc *crtc,
6038c2ecf20Sopenharmony_ci				     const struct drm_display_mode *mode)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
6068c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
6078c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
6088c2ecf20Sopenharmony_ci	int linedur_ns = 0, framedur_ns = 0;
6098c2ecf20Sopenharmony_ci	int dotclock = mode->crtc_clock;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (!drm_dev_has_vblank(dev))
6128c2ecf20Sopenharmony_ci		return;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
6158c2ecf20Sopenharmony_ci		return;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	/* Valid dotclock? */
6188c2ecf20Sopenharmony_ci	if (dotclock > 0) {
6198c2ecf20Sopenharmony_ci		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		/*
6228c2ecf20Sopenharmony_ci		 * Convert scanline length in pixels and video
6238c2ecf20Sopenharmony_ci		 * dot clock to line duration and frame duration
6248c2ecf20Sopenharmony_ci		 * in nanoseconds:
6258c2ecf20Sopenharmony_ci		 */
6268c2ecf20Sopenharmony_ci		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
6278c2ecf20Sopenharmony_ci		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		/*
6308c2ecf20Sopenharmony_ci		 * Fields of interlaced scanout modes are only half a frame duration.
6318c2ecf20Sopenharmony_ci		 */
6328c2ecf20Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
6338c2ecf20Sopenharmony_ci			framedur_ns /= 2;
6348c2ecf20Sopenharmony_ci	} else {
6358c2ecf20Sopenharmony_ci		drm_err(dev, "crtc %u: Can't calculate constants, dotclock = 0!\n",
6368c2ecf20Sopenharmony_ci			crtc->base.id);
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	vblank->linedur_ns  = linedur_ns;
6408c2ecf20Sopenharmony_ci	vblank->framedur_ns = framedur_ns;
6418c2ecf20Sopenharmony_ci	vblank->hwmode = *mode;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	drm_dbg_core(dev,
6448c2ecf20Sopenharmony_ci		     "crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
6458c2ecf20Sopenharmony_ci		     crtc->base.id, mode->crtc_htotal,
6468c2ecf20Sopenharmony_ci		     mode->crtc_vtotal, mode->crtc_vdisplay);
6478c2ecf20Sopenharmony_ci	drm_dbg_core(dev, "crtc %u: clock %d kHz framedur %d linedur %d\n",
6488c2ecf20Sopenharmony_ci		     crtc->base.id, dotclock, framedur_ns, linedur_ns);
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_calc_timestamping_constants);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci/**
6538c2ecf20Sopenharmony_ci * drm_crtc_vblank_helper_get_vblank_timestamp_internal - precise vblank
6548c2ecf20Sopenharmony_ci *                                                        timestamp helper
6558c2ecf20Sopenharmony_ci * @crtc: CRTC whose vblank timestamp to retrieve
6568c2ecf20Sopenharmony_ci * @max_error: Desired maximum allowable error in timestamps (nanosecs)
6578c2ecf20Sopenharmony_ci *             On return contains true maximum error of timestamp
6588c2ecf20Sopenharmony_ci * @vblank_time: Pointer to time which should receive the timestamp
6598c2ecf20Sopenharmony_ci * @in_vblank_irq:
6608c2ecf20Sopenharmony_ci *     True when called from drm_crtc_handle_vblank().  Some drivers
6618c2ecf20Sopenharmony_ci *     need to apply some workarounds for gpu-specific vblank irq quirks
6628c2ecf20Sopenharmony_ci *     if flag is set.
6638c2ecf20Sopenharmony_ci * @get_scanout_position:
6648c2ecf20Sopenharmony_ci *     Callback function to retrieve the scanout position. See
6658c2ecf20Sopenharmony_ci *     @struct drm_crtc_helper_funcs.get_scanout_position.
6668c2ecf20Sopenharmony_ci *
6678c2ecf20Sopenharmony_ci * Implements calculation of exact vblank timestamps from given drm_display_mode
6688c2ecf20Sopenharmony_ci * timings and current video scanout position of a CRTC.
6698c2ecf20Sopenharmony_ci *
6708c2ecf20Sopenharmony_ci * The current implementation only handles standard video modes. For double scan
6718c2ecf20Sopenharmony_ci * and interlaced modes the driver is supposed to adjust the hardware mode
6728c2ecf20Sopenharmony_ci * (taken from &drm_crtc_state.adjusted mode for atomic modeset drivers) to
6738c2ecf20Sopenharmony_ci * match the scanout position reported.
6748c2ecf20Sopenharmony_ci *
6758c2ecf20Sopenharmony_ci * Note that atomic drivers must call drm_calc_timestamping_constants() before
6768c2ecf20Sopenharmony_ci * enabling a CRTC. The atomic helpers already take care of that in
6778c2ecf20Sopenharmony_ci * drm_atomic_helper_calc_timestamping_constants().
6788c2ecf20Sopenharmony_ci *
6798c2ecf20Sopenharmony_ci * Returns:
6808c2ecf20Sopenharmony_ci *
6818c2ecf20Sopenharmony_ci * Returns true on success, and false on failure, i.e. when no accurate
6828c2ecf20Sopenharmony_ci * timestamp could be acquired.
6838c2ecf20Sopenharmony_ci */
6848c2ecf20Sopenharmony_cibool
6858c2ecf20Sopenharmony_cidrm_crtc_vblank_helper_get_vblank_timestamp_internal(
6868c2ecf20Sopenharmony_ci	struct drm_crtc *crtc, int *max_error, ktime_t *vblank_time,
6878c2ecf20Sopenharmony_ci	bool in_vblank_irq,
6888c2ecf20Sopenharmony_ci	drm_vblank_get_scanout_position_func get_scanout_position)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
6918c2ecf20Sopenharmony_ci	unsigned int pipe = crtc->index;
6928c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
6938c2ecf20Sopenharmony_ci	struct timespec64 ts_etime, ts_vblank_time;
6948c2ecf20Sopenharmony_ci	ktime_t stime, etime;
6958c2ecf20Sopenharmony_ci	bool vbl_status;
6968c2ecf20Sopenharmony_ci	const struct drm_display_mode *mode;
6978c2ecf20Sopenharmony_ci	int vpos, hpos, i;
6988c2ecf20Sopenharmony_ci	int delta_ns, duration_ns;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (pipe >= dev->num_crtcs) {
7018c2ecf20Sopenharmony_ci		drm_err(dev, "Invalid crtc %u\n", pipe);
7028c2ecf20Sopenharmony_ci		return false;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* Scanout position query not supported? Should not happen. */
7068c2ecf20Sopenharmony_ci	if (!get_scanout_position) {
7078c2ecf20Sopenharmony_ci		drm_err(dev, "Called from CRTC w/o get_scanout_position()!?\n");
7088c2ecf20Sopenharmony_ci		return false;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(dev))
7128c2ecf20Sopenharmony_ci		mode = &vblank->hwmode;
7138c2ecf20Sopenharmony_ci	else
7148c2ecf20Sopenharmony_ci		mode = &crtc->hwmode;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* If mode timing undefined, just return as no-op:
7178c2ecf20Sopenharmony_ci	 * Happens during initial modesetting of a crtc.
7188c2ecf20Sopenharmony_ci	 */
7198c2ecf20Sopenharmony_ci	if (mode->crtc_clock == 0) {
7208c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "crtc %u: Noop due to uninitialized mode.\n",
7218c2ecf20Sopenharmony_ci			     pipe);
7228c2ecf20Sopenharmony_ci		drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev));
7238c2ecf20Sopenharmony_ci		return false;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	/* Get current scanout position with system timestamp.
7278c2ecf20Sopenharmony_ci	 * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times
7288c2ecf20Sopenharmony_ci	 * if single query takes longer than max_error nanoseconds.
7298c2ecf20Sopenharmony_ci	 *
7308c2ecf20Sopenharmony_ci	 * This guarantees a tight bound on maximum error if
7318c2ecf20Sopenharmony_ci	 * code gets preempted or delayed for some reason.
7328c2ecf20Sopenharmony_ci	 */
7338c2ecf20Sopenharmony_ci	for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
7348c2ecf20Sopenharmony_ci		/*
7358c2ecf20Sopenharmony_ci		 * Get vertical and horizontal scanout position vpos, hpos,
7368c2ecf20Sopenharmony_ci		 * and bounding timestamps stime, etime, pre/post query.
7378c2ecf20Sopenharmony_ci		 */
7388c2ecf20Sopenharmony_ci		vbl_status = get_scanout_position(crtc, in_vblank_irq,
7398c2ecf20Sopenharmony_ci						  &vpos, &hpos,
7408c2ecf20Sopenharmony_ci						  &stime, &etime,
7418c2ecf20Sopenharmony_ci						  mode);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		/* Return as no-op if scanout query unsupported or failed. */
7448c2ecf20Sopenharmony_ci		if (!vbl_status) {
7458c2ecf20Sopenharmony_ci			drm_dbg_core(dev,
7468c2ecf20Sopenharmony_ci				     "crtc %u : scanoutpos query failed.\n",
7478c2ecf20Sopenharmony_ci				     pipe);
7488c2ecf20Sopenharmony_ci			return false;
7498c2ecf20Sopenharmony_ci		}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		/* Compute uncertainty in timestamp of scanout position query. */
7528c2ecf20Sopenharmony_ci		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		/* Accept result with <  max_error nsecs timing uncertainty. */
7558c2ecf20Sopenharmony_ci		if (duration_ns <= *max_error)
7568c2ecf20Sopenharmony_ci			break;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Noisy system timing? */
7608c2ecf20Sopenharmony_ci	if (i == DRM_TIMESTAMP_MAXRETRIES) {
7618c2ecf20Sopenharmony_ci		drm_dbg_core(dev,
7628c2ecf20Sopenharmony_ci			     "crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
7638c2ecf20Sopenharmony_ci			     pipe, duration_ns / 1000, *max_error / 1000, i);
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* Return upper bound of timestamp precision error. */
7678c2ecf20Sopenharmony_ci	*max_error = duration_ns;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/* Convert scanout position into elapsed time at raw_time query
7708c2ecf20Sopenharmony_ci	 * since start of scanout at first display scanline. delta_ns
7718c2ecf20Sopenharmony_ci	 * can be negative if start of scanout hasn't happened yet.
7728c2ecf20Sopenharmony_ci	 */
7738c2ecf20Sopenharmony_ci	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
7748c2ecf20Sopenharmony_ci			   mode->crtc_clock);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* Subtract time delta from raw timestamp to get final
7778c2ecf20Sopenharmony_ci	 * vblank_time timestamp for end of vblank.
7788c2ecf20Sopenharmony_ci	 */
7798c2ecf20Sopenharmony_ci	*vblank_time = ktime_sub_ns(etime, delta_ns);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	if (!drm_debug_enabled(DRM_UT_VBL))
7828c2ecf20Sopenharmony_ci		return true;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	ts_etime = ktime_to_timespec64(etime);
7858c2ecf20Sopenharmony_ci	ts_vblank_time = ktime_to_timespec64(*vblank_time);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	drm_dbg_vbl(dev,
7888c2ecf20Sopenharmony_ci		    "crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, %d rep]\n",
7898c2ecf20Sopenharmony_ci		    pipe, hpos, vpos,
7908c2ecf20Sopenharmony_ci		    (u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000,
7918c2ecf20Sopenharmony_ci		    (u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000,
7928c2ecf20Sopenharmony_ci		    duration_ns / 1000, i);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return true;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_internal);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/**
7998c2ecf20Sopenharmony_ci * drm_crtc_vblank_helper_get_vblank_timestamp - precise vblank timestamp
8008c2ecf20Sopenharmony_ci *                                               helper
8018c2ecf20Sopenharmony_ci * @crtc: CRTC whose vblank timestamp to retrieve
8028c2ecf20Sopenharmony_ci * @max_error: Desired maximum allowable error in timestamps (nanosecs)
8038c2ecf20Sopenharmony_ci *             On return contains true maximum error of timestamp
8048c2ecf20Sopenharmony_ci * @vblank_time: Pointer to time which should receive the timestamp
8058c2ecf20Sopenharmony_ci * @in_vblank_irq:
8068c2ecf20Sopenharmony_ci *     True when called from drm_crtc_handle_vblank().  Some drivers
8078c2ecf20Sopenharmony_ci *     need to apply some workarounds for gpu-specific vblank irq quirks
8088c2ecf20Sopenharmony_ci *     if flag is set.
8098c2ecf20Sopenharmony_ci *
8108c2ecf20Sopenharmony_ci * Implements calculation of exact vblank timestamps from given drm_display_mode
8118c2ecf20Sopenharmony_ci * timings and current video scanout position of a CRTC. This can be directly
8128c2ecf20Sopenharmony_ci * used as the &drm_crtc_funcs.get_vblank_timestamp implementation of a kms
8138c2ecf20Sopenharmony_ci * driver if &drm_crtc_helper_funcs.get_scanout_position is implemented.
8148c2ecf20Sopenharmony_ci *
8158c2ecf20Sopenharmony_ci * The current implementation only handles standard video modes. For double scan
8168c2ecf20Sopenharmony_ci * and interlaced modes the driver is supposed to adjust the hardware mode
8178c2ecf20Sopenharmony_ci * (taken from &drm_crtc_state.adjusted mode for atomic modeset drivers) to
8188c2ecf20Sopenharmony_ci * match the scanout position reported.
8198c2ecf20Sopenharmony_ci *
8208c2ecf20Sopenharmony_ci * Note that atomic drivers must call drm_calc_timestamping_constants() before
8218c2ecf20Sopenharmony_ci * enabling a CRTC. The atomic helpers already take care of that in
8228c2ecf20Sopenharmony_ci * drm_atomic_helper_calc_timestamping_constants().
8238c2ecf20Sopenharmony_ci *
8248c2ecf20Sopenharmony_ci * Returns:
8258c2ecf20Sopenharmony_ci *
8268c2ecf20Sopenharmony_ci * Returns true on success, and false on failure, i.e. when no accurate
8278c2ecf20Sopenharmony_ci * timestamp could be acquired.
8288c2ecf20Sopenharmony_ci */
8298c2ecf20Sopenharmony_cibool drm_crtc_vblank_helper_get_vblank_timestamp(struct drm_crtc *crtc,
8308c2ecf20Sopenharmony_ci						 int *max_error,
8318c2ecf20Sopenharmony_ci						 ktime_t *vblank_time,
8328c2ecf20Sopenharmony_ci						 bool in_vblank_irq)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	return drm_crtc_vblank_helper_get_vblank_timestamp_internal(
8358c2ecf20Sopenharmony_ci		crtc, max_error, vblank_time, in_vblank_irq,
8368c2ecf20Sopenharmony_ci		crtc->helper_private->get_scanout_position);
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci/**
8418c2ecf20Sopenharmony_ci * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
8428c2ecf20Sopenharmony_ci *                             vblank interval
8438c2ecf20Sopenharmony_ci * @dev: DRM device
8448c2ecf20Sopenharmony_ci * @pipe: index of CRTC whose vblank timestamp to retrieve
8458c2ecf20Sopenharmony_ci * @tvblank: Pointer to target time which should receive the timestamp
8468c2ecf20Sopenharmony_ci * @in_vblank_irq:
8478c2ecf20Sopenharmony_ci *     True when called from drm_crtc_handle_vblank().  Some drivers
8488c2ecf20Sopenharmony_ci *     need to apply some workarounds for gpu-specific vblank irq quirks
8498c2ecf20Sopenharmony_ci *     if flag is set.
8508c2ecf20Sopenharmony_ci *
8518c2ecf20Sopenharmony_ci * Fetches the system timestamp corresponding to the time of the most recent
8528c2ecf20Sopenharmony_ci * vblank interval on specified CRTC. May call into kms-driver to
8538c2ecf20Sopenharmony_ci * compute the timestamp with a high-precision GPU specific method.
8548c2ecf20Sopenharmony_ci *
8558c2ecf20Sopenharmony_ci * Returns zero if timestamp originates from uncorrected do_gettimeofday()
8568c2ecf20Sopenharmony_ci * call, i.e., it isn't very precisely locked to the true vblank.
8578c2ecf20Sopenharmony_ci *
8588c2ecf20Sopenharmony_ci * Returns:
8598c2ecf20Sopenharmony_ci * True if timestamp is considered to be very precise, false otherwise.
8608c2ecf20Sopenharmony_ci */
8618c2ecf20Sopenharmony_cistatic bool
8628c2ecf20Sopenharmony_cidrm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
8638c2ecf20Sopenharmony_ci			  ktime_t *tvblank, bool in_vblank_irq)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
8668c2ecf20Sopenharmony_ci	bool ret = false;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	/* Define requested maximum error on timestamps (nanoseconds). */
8698c2ecf20Sopenharmony_ci	int max_error = (int) drm_timestamp_precision * 1000;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* Query driver if possible and precision timestamping enabled. */
8728c2ecf20Sopenharmony_ci	if (crtc && crtc->funcs->get_vblank_timestamp && max_error > 0) {
8738c2ecf20Sopenharmony_ci		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		ret = crtc->funcs->get_vblank_timestamp(crtc, &max_error,
8768c2ecf20Sopenharmony_ci							tvblank, in_vblank_irq);
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* GPU high precision timestamp query unsupported or failed.
8808c2ecf20Sopenharmony_ci	 * Return current monotonic/gettimeofday timestamp as best estimate.
8818c2ecf20Sopenharmony_ci	 */
8828c2ecf20Sopenharmony_ci	if (!ret)
8838c2ecf20Sopenharmony_ci		*tvblank = ktime_get();
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	return ret;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci/**
8898c2ecf20Sopenharmony_ci * drm_crtc_vblank_count - retrieve "cooked" vblank counter value
8908c2ecf20Sopenharmony_ci * @crtc: which counter to retrieve
8918c2ecf20Sopenharmony_ci *
8928c2ecf20Sopenharmony_ci * Fetches the "cooked" vblank count value that represents the number of
8938c2ecf20Sopenharmony_ci * vblank events since the system was booted, including lost events due to
8948c2ecf20Sopenharmony_ci * modesetting activity. Note that this timer isn't correct against a racing
8958c2ecf20Sopenharmony_ci * vblank interrupt (since it only reports the software vblank counter), see
8968c2ecf20Sopenharmony_ci * drm_crtc_accurate_vblank_count() for such use-cases.
8978c2ecf20Sopenharmony_ci *
8988c2ecf20Sopenharmony_ci * Note that for a given vblank counter value drm_crtc_handle_vblank()
8998c2ecf20Sopenharmony_ci * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
9008c2ecf20Sopenharmony_ci * provide a barrier: Any writes done before calling
9018c2ecf20Sopenharmony_ci * drm_crtc_handle_vblank() will be visible to callers of the later
9028c2ecf20Sopenharmony_ci * functions, iff the vblank count is the same or a later one.
9038c2ecf20Sopenharmony_ci *
9048c2ecf20Sopenharmony_ci * See also &drm_vblank_crtc.count.
9058c2ecf20Sopenharmony_ci *
9068c2ecf20Sopenharmony_ci * Returns:
9078c2ecf20Sopenharmony_ci * The software vblank counter.
9088c2ecf20Sopenharmony_ci */
9098c2ecf20Sopenharmony_ciu64 drm_crtc_vblank_count(struct drm_crtc *crtc)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_count);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/**
9168c2ecf20Sopenharmony_ci * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
9178c2ecf20Sopenharmony_ci *     system timestamp corresponding to that vblank counter value.
9188c2ecf20Sopenharmony_ci * @dev: DRM device
9198c2ecf20Sopenharmony_ci * @pipe: index of CRTC whose counter to retrieve
9208c2ecf20Sopenharmony_ci * @vblanktime: Pointer to ktime_t to receive the vblank timestamp.
9218c2ecf20Sopenharmony_ci *
9228c2ecf20Sopenharmony_ci * Fetches the "cooked" vblank count value that represents the number of
9238c2ecf20Sopenharmony_ci * vblank events since the system was booted, including lost events due to
9248c2ecf20Sopenharmony_ci * modesetting activity. Returns corresponding system timestamp of the time
9258c2ecf20Sopenharmony_ci * of the vblank interval that corresponds to the current vblank counter value.
9268c2ecf20Sopenharmony_ci *
9278c2ecf20Sopenharmony_ci * This is the legacy version of drm_crtc_vblank_count_and_time().
9288c2ecf20Sopenharmony_ci */
9298c2ecf20Sopenharmony_cistatic u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
9308c2ecf20Sopenharmony_ci				     ktime_t *vblanktime)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
9338c2ecf20Sopenharmony_ci	u64 vblank_count;
9348c2ecf20Sopenharmony_ci	unsigned int seq;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) {
9378c2ecf20Sopenharmony_ci		*vblanktime = 0;
9388c2ecf20Sopenharmony_ci		return 0;
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	do {
9428c2ecf20Sopenharmony_ci		seq = read_seqbegin(&vblank->seqlock);
9438c2ecf20Sopenharmony_ci		vblank_count = atomic64_read(&vblank->count);
9448c2ecf20Sopenharmony_ci		*vblanktime = vblank->time;
9458c2ecf20Sopenharmony_ci	} while (read_seqretry(&vblank->seqlock, seq));
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return vblank_count;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci/**
9518c2ecf20Sopenharmony_ci * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
9528c2ecf20Sopenharmony_ci *     and the system timestamp corresponding to that vblank counter value
9538c2ecf20Sopenharmony_ci * @crtc: which counter to retrieve
9548c2ecf20Sopenharmony_ci * @vblanktime: Pointer to time to receive the vblank timestamp.
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci * Fetches the "cooked" vblank count value that represents the number of
9578c2ecf20Sopenharmony_ci * vblank events since the system was booted, including lost events due to
9588c2ecf20Sopenharmony_ci * modesetting activity. Returns corresponding system timestamp of the time
9598c2ecf20Sopenharmony_ci * of the vblank interval that corresponds to the current vblank counter value.
9608c2ecf20Sopenharmony_ci *
9618c2ecf20Sopenharmony_ci * Note that for a given vblank counter value drm_crtc_handle_vblank()
9628c2ecf20Sopenharmony_ci * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
9638c2ecf20Sopenharmony_ci * provide a barrier: Any writes done before calling
9648c2ecf20Sopenharmony_ci * drm_crtc_handle_vblank() will be visible to callers of the later
9658c2ecf20Sopenharmony_ci * functions, iff the vblank count is the same or a later one.
9668c2ecf20Sopenharmony_ci *
9678c2ecf20Sopenharmony_ci * See also &drm_vblank_crtc.count.
9688c2ecf20Sopenharmony_ci */
9698c2ecf20Sopenharmony_ciu64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
9708c2ecf20Sopenharmony_ci				   ktime_t *vblanktime)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
9738c2ecf20Sopenharmony_ci					 vblanktime);
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic void send_vblank_event(struct drm_device *dev,
9788c2ecf20Sopenharmony_ci		struct drm_pending_vblank_event *e,
9798c2ecf20Sopenharmony_ci		u64 seq, ktime_t now)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	struct timespec64 tv;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	switch (e->event.base.type) {
9848c2ecf20Sopenharmony_ci	case DRM_EVENT_VBLANK:
9858c2ecf20Sopenharmony_ci	case DRM_EVENT_FLIP_COMPLETE:
9868c2ecf20Sopenharmony_ci		tv = ktime_to_timespec64(now);
9878c2ecf20Sopenharmony_ci		e->event.vbl.sequence = seq;
9888c2ecf20Sopenharmony_ci		/*
9898c2ecf20Sopenharmony_ci		 * e->event is a user space structure, with hardcoded unsigned
9908c2ecf20Sopenharmony_ci		 * 32-bit seconds/microseconds. This is safe as we always use
9918c2ecf20Sopenharmony_ci		 * monotonic timestamps since linux-4.15
9928c2ecf20Sopenharmony_ci		 */
9938c2ecf20Sopenharmony_ci		e->event.vbl.tv_sec = tv.tv_sec;
9948c2ecf20Sopenharmony_ci		e->event.vbl.tv_usec = tv.tv_nsec / 1000;
9958c2ecf20Sopenharmony_ci		break;
9968c2ecf20Sopenharmony_ci	case DRM_EVENT_CRTC_SEQUENCE:
9978c2ecf20Sopenharmony_ci		if (seq)
9988c2ecf20Sopenharmony_ci			e->event.seq.sequence = seq;
9998c2ecf20Sopenharmony_ci		e->event.seq.time_ns = ktime_to_ns(now);
10008c2ecf20Sopenharmony_ci		break;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, seq);
10038c2ecf20Sopenharmony_ci	drm_send_event_locked(dev, &e->base);
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci/**
10078c2ecf20Sopenharmony_ci * drm_crtc_arm_vblank_event - arm vblank event after pageflip
10088c2ecf20Sopenharmony_ci * @crtc: the source CRTC of the vblank event
10098c2ecf20Sopenharmony_ci * @e: the event to send
10108c2ecf20Sopenharmony_ci *
10118c2ecf20Sopenharmony_ci * A lot of drivers need to generate vblank events for the very next vblank
10128c2ecf20Sopenharmony_ci * interrupt. For example when the page flip interrupt happens when the page
10138c2ecf20Sopenharmony_ci * flip gets armed, but not when it actually executes within the next vblank
10148c2ecf20Sopenharmony_ci * period. This helper function implements exactly the required vblank arming
10158c2ecf20Sopenharmony_ci * behaviour.
10168c2ecf20Sopenharmony_ci *
10178c2ecf20Sopenharmony_ci * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an
10188c2ecf20Sopenharmony_ci * atomic commit must ensure that the next vblank happens at exactly the same
10198c2ecf20Sopenharmony_ci * time as the atomic commit is committed to the hardware. This function itself
10208c2ecf20Sopenharmony_ci * does **not** protect against the next vblank interrupt racing with either this
10218c2ecf20Sopenharmony_ci * function call or the atomic commit operation. A possible sequence could be:
10228c2ecf20Sopenharmony_ci *
10238c2ecf20Sopenharmony_ci * 1. Driver commits new hardware state into vblank-synchronized registers.
10248c2ecf20Sopenharmony_ci * 2. A vblank happens, committing the hardware state. Also the corresponding
10258c2ecf20Sopenharmony_ci *    vblank interrupt is fired off and fully processed by the interrupt
10268c2ecf20Sopenharmony_ci *    handler.
10278c2ecf20Sopenharmony_ci * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
10288c2ecf20Sopenharmony_ci * 4. The event is only send out for the next vblank, which is wrong.
10298c2ecf20Sopenharmony_ci *
10308c2ecf20Sopenharmony_ci * An equivalent race can happen when the driver calls
10318c2ecf20Sopenharmony_ci * drm_crtc_arm_vblank_event() before writing out the new hardware state.
10328c2ecf20Sopenharmony_ci *
10338c2ecf20Sopenharmony_ci * The only way to make this work safely is to prevent the vblank from firing
10348c2ecf20Sopenharmony_ci * (and the hardware from committing anything else) until the entire atomic
10358c2ecf20Sopenharmony_ci * commit sequence has run to completion. If the hardware does not have such a
10368c2ecf20Sopenharmony_ci * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
10378c2ecf20Sopenharmony_ci * Instead drivers need to manually send out the event from their interrupt
10388c2ecf20Sopenharmony_ci * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
10398c2ecf20Sopenharmony_ci * possible race with the hardware committing the atomic update.
10408c2ecf20Sopenharmony_ci *
10418c2ecf20Sopenharmony_ci * Caller must hold a vblank reference for the event @e acquired by a
10428c2ecf20Sopenharmony_ci * drm_crtc_vblank_get(), which will be dropped when the next vblank arrives.
10438c2ecf20Sopenharmony_ci */
10448c2ecf20Sopenharmony_civoid drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
10458c2ecf20Sopenharmony_ci			       struct drm_pending_vblank_event *e)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
10488c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->event_lock);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	e->pipe = pipe;
10538c2ecf20Sopenharmony_ci	e->sequence = drm_crtc_accurate_vblank_count(crtc) + 1;
10548c2ecf20Sopenharmony_ci	list_add_tail(&e->base.link, &dev->vblank_event_list);
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_arm_vblank_event);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci/**
10598c2ecf20Sopenharmony_ci * drm_crtc_send_vblank_event - helper to send vblank event after pageflip
10608c2ecf20Sopenharmony_ci * @crtc: the source CRTC of the vblank event
10618c2ecf20Sopenharmony_ci * @e: the event to send
10628c2ecf20Sopenharmony_ci *
10638c2ecf20Sopenharmony_ci * Updates sequence # and timestamp on event for the most recently processed
10648c2ecf20Sopenharmony_ci * vblank, and sends it to userspace.  Caller must hold event lock.
10658c2ecf20Sopenharmony_ci *
10668c2ecf20Sopenharmony_ci * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
10678c2ecf20Sopenharmony_ci * situation, especially to send out events for atomic commit operations.
10688c2ecf20Sopenharmony_ci */
10698c2ecf20Sopenharmony_civoid drm_crtc_send_vblank_event(struct drm_crtc *crtc,
10708c2ecf20Sopenharmony_ci				struct drm_pending_vblank_event *e)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
10738c2ecf20Sopenharmony_ci	u64 seq;
10748c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
10758c2ecf20Sopenharmony_ci	ktime_t now;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (drm_dev_has_vblank(dev)) {
10788c2ecf20Sopenharmony_ci		seq = drm_vblank_count_and_time(dev, pipe, &now);
10798c2ecf20Sopenharmony_ci	} else {
10808c2ecf20Sopenharmony_ci		seq = 0;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		now = ktime_get();
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci	e->pipe = pipe;
10858c2ecf20Sopenharmony_ci	send_vblank_event(dev, e, seq, now);
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_send_vblank_event);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_cistatic int __enable_vblank(struct drm_device *dev, unsigned int pipe)
10908c2ecf20Sopenharmony_ci{
10918c2ecf20Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
10928c2ecf20Sopenharmony_ci		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		if (drm_WARN_ON(dev, !crtc))
10958c2ecf20Sopenharmony_ci			return 0;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		if (crtc->funcs->enable_vblank)
10988c2ecf20Sopenharmony_ci			return crtc->funcs->enable_vblank(crtc);
10998c2ecf20Sopenharmony_ci	} else if (dev->driver->enable_vblank) {
11008c2ecf20Sopenharmony_ci		return dev->driver->enable_vblank(dev, pipe);
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return -EINVAL;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
11098c2ecf20Sopenharmony_ci	int ret = 0;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->vbl_lock);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	spin_lock(&dev->vblank_time_lock);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	if (!vblank->enabled) {
11168c2ecf20Sopenharmony_ci		/*
11178c2ecf20Sopenharmony_ci		 * Enable vblank irqs under vblank_time_lock protection.
11188c2ecf20Sopenharmony_ci		 * All vblank count & timestamp updates are held off
11198c2ecf20Sopenharmony_ci		 * until we are done reinitializing master counter and
11208c2ecf20Sopenharmony_ci		 * timestamps. Filtercode in drm_handle_vblank() will
11218c2ecf20Sopenharmony_ci		 * prevent double-accounting of same vblank interval.
11228c2ecf20Sopenharmony_ci		 */
11238c2ecf20Sopenharmony_ci		ret = __enable_vblank(dev, pipe);
11248c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "enabling vblank on crtc %u, ret: %d\n",
11258c2ecf20Sopenharmony_ci			     pipe, ret);
11268c2ecf20Sopenharmony_ci		if (ret) {
11278c2ecf20Sopenharmony_ci			atomic_dec(&vblank->refcount);
11288c2ecf20Sopenharmony_ci		} else {
11298c2ecf20Sopenharmony_ci			drm_update_vblank_count(dev, pipe, 0);
11308c2ecf20Sopenharmony_ci			/* drm_update_vblank_count() includes a wmb so we just
11318c2ecf20Sopenharmony_ci			 * need to ensure that the compiler emits the write
11328c2ecf20Sopenharmony_ci			 * to mark the vblank as enabled after the call
11338c2ecf20Sopenharmony_ci			 * to drm_update_vblank_count().
11348c2ecf20Sopenharmony_ci			 */
11358c2ecf20Sopenharmony_ci			WRITE_ONCE(vblank->enabled, true);
11368c2ecf20Sopenharmony_ci		}
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	spin_unlock(&dev->vblank_time_lock);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	return ret;
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ciint drm_vblank_get(struct drm_device *dev, unsigned int pipe)
11458c2ecf20Sopenharmony_ci{
11468c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
11478c2ecf20Sopenharmony_ci	unsigned long irqflags;
11488c2ecf20Sopenharmony_ci	int ret = 0;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	if (!drm_dev_has_vblank(dev))
11518c2ecf20Sopenharmony_ci		return -EINVAL;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
11548c2ecf20Sopenharmony_ci		return -EINVAL;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->vbl_lock, irqflags);
11578c2ecf20Sopenharmony_ci	/* Going from 0->1 means we have to enable interrupts again */
11588c2ecf20Sopenharmony_ci	if (atomic_add_return(1, &vblank->refcount) == 1) {
11598c2ecf20Sopenharmony_ci		ret = drm_vblank_enable(dev, pipe);
11608c2ecf20Sopenharmony_ci	} else {
11618c2ecf20Sopenharmony_ci		if (!vblank->enabled) {
11628c2ecf20Sopenharmony_ci			atomic_dec(&vblank->refcount);
11638c2ecf20Sopenharmony_ci			ret = -EINVAL;
11648c2ecf20Sopenharmony_ci		}
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	return ret;
11698c2ecf20Sopenharmony_ci}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci/**
11728c2ecf20Sopenharmony_ci * drm_crtc_vblank_get - get a reference count on vblank events
11738c2ecf20Sopenharmony_ci * @crtc: which CRTC to own
11748c2ecf20Sopenharmony_ci *
11758c2ecf20Sopenharmony_ci * Acquire a reference count on vblank events to avoid having them disabled
11768c2ecf20Sopenharmony_ci * while in use.
11778c2ecf20Sopenharmony_ci *
11788c2ecf20Sopenharmony_ci * Returns:
11798c2ecf20Sopenharmony_ci * Zero on success or a negative error code on failure.
11808c2ecf20Sopenharmony_ci */
11818c2ecf20Sopenharmony_ciint drm_crtc_vblank_get(struct drm_crtc *crtc)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_get);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_civoid drm_vblank_put(struct drm_device *dev, unsigned int pipe)
11888c2ecf20Sopenharmony_ci{
11898c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
11928c2ecf20Sopenharmony_ci		return;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, atomic_read(&vblank->refcount) == 0))
11958c2ecf20Sopenharmony_ci		return;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	/* Last user schedules interrupt disable */
11988c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&vblank->refcount)) {
11998c2ecf20Sopenharmony_ci		if (drm_vblank_offdelay == 0)
12008c2ecf20Sopenharmony_ci			return;
12018c2ecf20Sopenharmony_ci		else if (drm_vblank_offdelay < 0)
12028c2ecf20Sopenharmony_ci			vblank_disable_fn(&vblank->disable_timer);
12038c2ecf20Sopenharmony_ci		else if (!dev->vblank_disable_immediate)
12048c2ecf20Sopenharmony_ci			mod_timer(&vblank->disable_timer,
12058c2ecf20Sopenharmony_ci				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci/**
12108c2ecf20Sopenharmony_ci * drm_crtc_vblank_put - give up ownership of vblank events
12118c2ecf20Sopenharmony_ci * @crtc: which counter to give up
12128c2ecf20Sopenharmony_ci *
12138c2ecf20Sopenharmony_ci * Release ownership of a given vblank counter, turning off interrupts
12148c2ecf20Sopenharmony_ci * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
12158c2ecf20Sopenharmony_ci */
12168c2ecf20Sopenharmony_civoid drm_crtc_vblank_put(struct drm_crtc *crtc)
12178c2ecf20Sopenharmony_ci{
12188c2ecf20Sopenharmony_ci	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_put);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci/**
12238c2ecf20Sopenharmony_ci * drm_wait_one_vblank - wait for one vblank
12248c2ecf20Sopenharmony_ci * @dev: DRM device
12258c2ecf20Sopenharmony_ci * @pipe: CRTC index
12268c2ecf20Sopenharmony_ci *
12278c2ecf20Sopenharmony_ci * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
12288c2ecf20Sopenharmony_ci * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
12298c2ecf20Sopenharmony_ci * due to lack of driver support or because the crtc is off.
12308c2ecf20Sopenharmony_ci *
12318c2ecf20Sopenharmony_ci * This is the legacy version of drm_crtc_wait_one_vblank().
12328c2ecf20Sopenharmony_ci */
12338c2ecf20Sopenharmony_civoid drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
12368c2ecf20Sopenharmony_ci	int ret;
12378c2ecf20Sopenharmony_ci	u64 last;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
12408c2ecf20Sopenharmony_ci		return;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	ret = drm_vblank_get(dev, pipe);
12438c2ecf20Sopenharmony_ci	if (drm_WARN(dev, ret, "vblank not available on crtc %i, ret=%i\n",
12448c2ecf20Sopenharmony_ci		     pipe, ret))
12458c2ecf20Sopenharmony_ci		return;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	last = drm_vblank_count(dev, pipe);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	ret = wait_event_timeout(vblank->queue,
12508c2ecf20Sopenharmony_ci				 last != drm_vblank_count(dev, pipe),
12518c2ecf20Sopenharmony_ci				 msecs_to_jiffies(100));
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	drm_WARN(dev, ret == 0, "vblank wait timed out on crtc %i\n", pipe);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	drm_vblank_put(dev, pipe);
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_wait_one_vblank);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci/**
12608c2ecf20Sopenharmony_ci * drm_crtc_wait_one_vblank - wait for one vblank
12618c2ecf20Sopenharmony_ci * @crtc: DRM crtc
12628c2ecf20Sopenharmony_ci *
12638c2ecf20Sopenharmony_ci * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
12648c2ecf20Sopenharmony_ci * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
12658c2ecf20Sopenharmony_ci * due to lack of driver support or because the crtc is off.
12668c2ecf20Sopenharmony_ci */
12678c2ecf20Sopenharmony_civoid drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
12708c2ecf20Sopenharmony_ci}
12718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_wait_one_vblank);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci/**
12748c2ecf20Sopenharmony_ci * drm_crtc_vblank_off - disable vblank events on a CRTC
12758c2ecf20Sopenharmony_ci * @crtc: CRTC in question
12768c2ecf20Sopenharmony_ci *
12778c2ecf20Sopenharmony_ci * Drivers can use this function to shut down the vblank interrupt handling when
12788c2ecf20Sopenharmony_ci * disabling a crtc. This function ensures that the latest vblank frame count is
12798c2ecf20Sopenharmony_ci * stored so that drm_vblank_on can restore it again.
12808c2ecf20Sopenharmony_ci *
12818c2ecf20Sopenharmony_ci * Drivers must use this function when the hardware vblank counter can get
12828c2ecf20Sopenharmony_ci * reset, e.g. when suspending or disabling the @crtc in general.
12838c2ecf20Sopenharmony_ci */
12848c2ecf20Sopenharmony_civoid drm_crtc_vblank_off(struct drm_crtc *crtc)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
12878c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
12888c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
12898c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *e, *t;
12908c2ecf20Sopenharmony_ci	ktime_t now;
12918c2ecf20Sopenharmony_ci	u64 seq;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
12948c2ecf20Sopenharmony_ci		return;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	/*
12978c2ecf20Sopenharmony_ci	 * Grab event_lock early to prevent vblank work from being scheduled
12988c2ecf20Sopenharmony_ci	 * while we're in the middle of shutting down vblank interrupts
12998c2ecf20Sopenharmony_ci	 */
13008c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->event_lock);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	spin_lock(&dev->vbl_lock);
13038c2ecf20Sopenharmony_ci	drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n",
13048c2ecf20Sopenharmony_ci		    pipe, vblank->enabled, vblank->inmodeset);
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	/* Avoid redundant vblank disables without previous
13078c2ecf20Sopenharmony_ci	 * drm_crtc_vblank_on(). */
13088c2ecf20Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset)
13098c2ecf20Sopenharmony_ci		drm_vblank_disable_and_save(dev, pipe);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	wake_up(&vblank->queue);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/*
13148c2ecf20Sopenharmony_ci	 * Prevent subsequent drm_vblank_get() from re-enabling
13158c2ecf20Sopenharmony_ci	 * the vblank interrupt by bumping the refcount.
13168c2ecf20Sopenharmony_ci	 */
13178c2ecf20Sopenharmony_ci	if (!vblank->inmodeset) {
13188c2ecf20Sopenharmony_ci		atomic_inc(&vblank->refcount);
13198c2ecf20Sopenharmony_ci		vblank->inmodeset = 1;
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci	spin_unlock(&dev->vbl_lock);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	/* Send any queued vblank events, lest the natives grow disquiet */
13248c2ecf20Sopenharmony_ci	seq = drm_vblank_count_and_time(dev, pipe, &now);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
13278c2ecf20Sopenharmony_ci		if (e->pipe != pipe)
13288c2ecf20Sopenharmony_ci			continue;
13298c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "Sending premature vblank event on disable: "
13308c2ecf20Sopenharmony_ci			     "wanted %llu, current %llu\n",
13318c2ecf20Sopenharmony_ci			     e->sequence, seq);
13328c2ecf20Sopenharmony_ci		list_del(&e->base.link);
13338c2ecf20Sopenharmony_ci		drm_vblank_put(dev, pipe);
13348c2ecf20Sopenharmony_ci		send_vblank_event(dev, e, seq, now);
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	/* Cancel any leftover pending vblank work */
13388c2ecf20Sopenharmony_ci	drm_vblank_cancel_pending_works(vblank);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	/* Will be reset by the modeset helpers when re-enabling the crtc by
13438c2ecf20Sopenharmony_ci	 * calling drm_calc_timestamping_constants(). */
13448c2ecf20Sopenharmony_ci	vblank->hwmode.crtc_clock = 0;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	/* Wait for any vblank work that's still executing to finish */
13478c2ecf20Sopenharmony_ci	drm_vblank_flush_worker(vblank);
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_off);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci/**
13528c2ecf20Sopenharmony_ci * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
13538c2ecf20Sopenharmony_ci * @crtc: CRTC in question
13548c2ecf20Sopenharmony_ci *
13558c2ecf20Sopenharmony_ci * Drivers can use this function to reset the vblank state to off at load time.
13568c2ecf20Sopenharmony_ci * Drivers should use this together with the drm_crtc_vblank_off() and
13578c2ecf20Sopenharmony_ci * drm_crtc_vblank_on() functions. The difference compared to
13588c2ecf20Sopenharmony_ci * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
13598c2ecf20Sopenharmony_ci * and hence doesn't need to call any driver hooks.
13608c2ecf20Sopenharmony_ci *
13618c2ecf20Sopenharmony_ci * This is useful for recovering driver state e.g. on driver load, or on resume.
13628c2ecf20Sopenharmony_ci */
13638c2ecf20Sopenharmony_civoid drm_crtc_vblank_reset(struct drm_crtc *crtc)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
13668c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
13678c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->vbl_lock);
13708c2ecf20Sopenharmony_ci	/*
13718c2ecf20Sopenharmony_ci	 * Prevent subsequent drm_vblank_get() from enabling the vblank
13728c2ecf20Sopenharmony_ci	 * interrupt by bumping the refcount.
13738c2ecf20Sopenharmony_ci	 */
13748c2ecf20Sopenharmony_ci	if (!vblank->inmodeset) {
13758c2ecf20Sopenharmony_ci		atomic_inc(&vblank->refcount);
13768c2ecf20Sopenharmony_ci		vblank->inmodeset = 1;
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->vbl_lock);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	drm_WARN_ON(dev, !list_empty(&dev->vblank_event_list));
13818c2ecf20Sopenharmony_ci	drm_WARN_ON(dev, !list_empty(&vblank->pending_work));
13828c2ecf20Sopenharmony_ci}
13838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_reset);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci/**
13868c2ecf20Sopenharmony_ci * drm_crtc_set_max_vblank_count - configure the hw max vblank counter value
13878c2ecf20Sopenharmony_ci * @crtc: CRTC in question
13888c2ecf20Sopenharmony_ci * @max_vblank_count: max hardware vblank counter value
13898c2ecf20Sopenharmony_ci *
13908c2ecf20Sopenharmony_ci * Update the maximum hardware vblank counter value for @crtc
13918c2ecf20Sopenharmony_ci * at runtime. Useful for hardware where the operation of the
13928c2ecf20Sopenharmony_ci * hardware vblank counter depends on the currently active
13938c2ecf20Sopenharmony_ci * display configuration.
13948c2ecf20Sopenharmony_ci *
13958c2ecf20Sopenharmony_ci * For example, if the hardware vblank counter does not work
13968c2ecf20Sopenharmony_ci * when a specific connector is active the maximum can be set
13978c2ecf20Sopenharmony_ci * to zero. And when that specific connector isn't active the
13988c2ecf20Sopenharmony_ci * maximum can again be set to the appropriate non-zero value.
13998c2ecf20Sopenharmony_ci *
14008c2ecf20Sopenharmony_ci * If used, must be called before drm_vblank_on().
14018c2ecf20Sopenharmony_ci */
14028c2ecf20Sopenharmony_civoid drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
14038c2ecf20Sopenharmony_ci				   u32 max_vblank_count)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
14068c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
14078c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	drm_WARN_ON(dev, dev->max_vblank_count);
14108c2ecf20Sopenharmony_ci	drm_WARN_ON(dev, !READ_ONCE(vblank->inmodeset));
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	vblank->max_vblank_count = max_vblank_count;
14138c2ecf20Sopenharmony_ci}
14148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_set_max_vblank_count);
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci/**
14178c2ecf20Sopenharmony_ci * drm_crtc_vblank_on - enable vblank events on a CRTC
14188c2ecf20Sopenharmony_ci * @crtc: CRTC in question
14198c2ecf20Sopenharmony_ci *
14208c2ecf20Sopenharmony_ci * This functions restores the vblank interrupt state captured with
14218c2ecf20Sopenharmony_ci * drm_crtc_vblank_off() again and is generally called when enabling @crtc. Note
14228c2ecf20Sopenharmony_ci * that calls to drm_crtc_vblank_on() and drm_crtc_vblank_off() can be
14238c2ecf20Sopenharmony_ci * unbalanced and so can also be unconditionally called in driver load code to
14248c2ecf20Sopenharmony_ci * reflect the current hardware state of the crtc.
14258c2ecf20Sopenharmony_ci */
14268c2ecf20Sopenharmony_civoid drm_crtc_vblank_on(struct drm_crtc *crtc)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
14298c2ecf20Sopenharmony_ci	unsigned int pipe = drm_crtc_index(crtc);
14308c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
14338c2ecf20Sopenharmony_ci		return;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->vbl_lock);
14368c2ecf20Sopenharmony_ci	drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n",
14378c2ecf20Sopenharmony_ci		    pipe, vblank->enabled, vblank->inmodeset);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	/* Drop our private "prevent drm_vblank_get" refcount */
14408c2ecf20Sopenharmony_ci	if (vblank->inmodeset) {
14418c2ecf20Sopenharmony_ci		atomic_dec(&vblank->refcount);
14428c2ecf20Sopenharmony_ci		vblank->inmodeset = 0;
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	drm_reset_vblank_timestamp(dev, pipe);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	/*
14488c2ecf20Sopenharmony_ci	 * re-enable interrupts if there are users left, or the
14498c2ecf20Sopenharmony_ci	 * user wishes vblank interrupts to be enabled all the time.
14508c2ecf20Sopenharmony_ci	 */
14518c2ecf20Sopenharmony_ci	if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0)
14528c2ecf20Sopenharmony_ci		drm_WARN_ON(dev, drm_vblank_enable(dev, pipe));
14538c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->vbl_lock);
14548c2ecf20Sopenharmony_ci}
14558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_on);
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci/**
14588c2ecf20Sopenharmony_ci * drm_vblank_restore - estimate missed vblanks and update vblank count.
14598c2ecf20Sopenharmony_ci * @dev: DRM device
14608c2ecf20Sopenharmony_ci * @pipe: CRTC index
14618c2ecf20Sopenharmony_ci *
14628c2ecf20Sopenharmony_ci * Power manamement features can cause frame counter resets between vblank
14638c2ecf20Sopenharmony_ci * disable and enable. Drivers can use this function in their
14648c2ecf20Sopenharmony_ci * &drm_crtc_funcs.enable_vblank implementation to estimate missed vblanks since
14658c2ecf20Sopenharmony_ci * the last &drm_crtc_funcs.disable_vblank using timestamps and update the
14668c2ecf20Sopenharmony_ci * vblank counter.
14678c2ecf20Sopenharmony_ci *
14688c2ecf20Sopenharmony_ci * This function is the legacy version of drm_crtc_vblank_restore().
14698c2ecf20Sopenharmony_ci */
14708c2ecf20Sopenharmony_civoid drm_vblank_restore(struct drm_device *dev, unsigned int pipe)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	ktime_t t_vblank;
14738c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank;
14748c2ecf20Sopenharmony_ci	int framedur_ns;
14758c2ecf20Sopenharmony_ci	u64 diff_ns;
14768c2ecf20Sopenharmony_ci	u32 cur_vblank, diff = 1;
14778c2ecf20Sopenharmony_ci	int count = DRM_TIMESTAMP_MAXRETRIES;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
14808c2ecf20Sopenharmony_ci		return;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->vbl_lock);
14838c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->vblank_time_lock);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	vblank = &dev->vblank[pipe];
14868c2ecf20Sopenharmony_ci	drm_WARN_ONCE(dev,
14878c2ecf20Sopenharmony_ci		      drm_debug_enabled(DRM_UT_VBL) && !vblank->framedur_ns,
14888c2ecf20Sopenharmony_ci		      "Cannot compute missed vblanks without frame duration\n");
14898c2ecf20Sopenharmony_ci	framedur_ns = vblank->framedur_ns;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	do {
14928c2ecf20Sopenharmony_ci		cur_vblank = __get_vblank_counter(dev, pipe);
14938c2ecf20Sopenharmony_ci		drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
14948c2ecf20Sopenharmony_ci	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
14978c2ecf20Sopenharmony_ci	if (framedur_ns)
14988c2ecf20Sopenharmony_ci		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	drm_dbg_vbl(dev,
15028c2ecf20Sopenharmony_ci		    "missed %d vblanks in %lld ns, frame duration=%d ns, hw_diff=%d\n",
15038c2ecf20Sopenharmony_ci		    diff, diff_ns, framedur_ns, cur_vblank - vblank->last);
15048c2ecf20Sopenharmony_ci	store_vblank(dev, pipe, diff, t_vblank, cur_vblank);
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_vblank_restore);
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci/**
15098c2ecf20Sopenharmony_ci * drm_crtc_vblank_restore - estimate missed vblanks and update vblank count.
15108c2ecf20Sopenharmony_ci * @crtc: CRTC in question
15118c2ecf20Sopenharmony_ci *
15128c2ecf20Sopenharmony_ci * Power manamement features can cause frame counter resets between vblank
15138c2ecf20Sopenharmony_ci * disable and enable. Drivers can use this function in their
15148c2ecf20Sopenharmony_ci * &drm_crtc_funcs.enable_vblank implementation to estimate missed vblanks since
15158c2ecf20Sopenharmony_ci * the last &drm_crtc_funcs.disable_vblank using timestamps and update the
15168c2ecf20Sopenharmony_ci * vblank counter.
15178c2ecf20Sopenharmony_ci */
15188c2ecf20Sopenharmony_civoid drm_crtc_vblank_restore(struct drm_crtc *crtc)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	drm_vblank_restore(crtc->dev, drm_crtc_index(crtc));
15218c2ecf20Sopenharmony_ci}
15228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_vblank_restore);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic void drm_legacy_vblank_pre_modeset(struct drm_device *dev,
15258c2ecf20Sopenharmony_ci					  unsigned int pipe)
15268c2ecf20Sopenharmony_ci{
15278c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	/* vblank is not initialized (IRQ not installed ?), or has been freed */
15308c2ecf20Sopenharmony_ci	if (!drm_dev_has_vblank(dev))
15318c2ecf20Sopenharmony_ci		return;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
15348c2ecf20Sopenharmony_ci		return;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	/*
15378c2ecf20Sopenharmony_ci	 * To avoid all the problems that might happen if interrupts
15388c2ecf20Sopenharmony_ci	 * were enabled/disabled around or between these calls, we just
15398c2ecf20Sopenharmony_ci	 * have the kernel take a reference on the CRTC (just once though
15408c2ecf20Sopenharmony_ci	 * to avoid corrupting the count if multiple, mismatch calls occur),
15418c2ecf20Sopenharmony_ci	 * so that interrupts remain enabled in the interim.
15428c2ecf20Sopenharmony_ci	 */
15438c2ecf20Sopenharmony_ci	if (!vblank->inmodeset) {
15448c2ecf20Sopenharmony_ci		vblank->inmodeset = 0x1;
15458c2ecf20Sopenharmony_ci		if (drm_vblank_get(dev, pipe) == 0)
15468c2ecf20Sopenharmony_ci			vblank->inmodeset |= 0x2;
15478c2ecf20Sopenharmony_ci	}
15488c2ecf20Sopenharmony_ci}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic void drm_legacy_vblank_post_modeset(struct drm_device *dev,
15518c2ecf20Sopenharmony_ci					   unsigned int pipe)
15528c2ecf20Sopenharmony_ci{
15538c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	/* vblank is not initialized (IRQ not installed ?), or has been freed */
15568c2ecf20Sopenharmony_ci	if (!drm_dev_has_vblank(dev))
15578c2ecf20Sopenharmony_ci		return;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
15608c2ecf20Sopenharmony_ci		return;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	if (vblank->inmodeset) {
15638c2ecf20Sopenharmony_ci		spin_lock_irq(&dev->vbl_lock);
15648c2ecf20Sopenharmony_ci		drm_reset_vblank_timestamp(dev, pipe);
15658c2ecf20Sopenharmony_ci		spin_unlock_irq(&dev->vbl_lock);
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci		if (vblank->inmodeset & 0x2)
15688c2ecf20Sopenharmony_ci			drm_vblank_put(dev, pipe);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci		vblank->inmodeset = 0;
15718c2ecf20Sopenharmony_ci	}
15728c2ecf20Sopenharmony_ci}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ciint drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data,
15758c2ecf20Sopenharmony_ci				 struct drm_file *file_priv)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	struct drm_modeset_ctl *modeset = data;
15788c2ecf20Sopenharmony_ci	unsigned int pipe;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	/* If drm_vblank_init() hasn't been called yet, just no-op */
15818c2ecf20Sopenharmony_ci	if (!drm_dev_has_vblank(dev))
15828c2ecf20Sopenharmony_ci		return 0;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	/* KMS drivers handle this internally */
15858c2ecf20Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
15868c2ecf20Sopenharmony_ci		return 0;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	pipe = modeset->crtc;
15898c2ecf20Sopenharmony_ci	if (pipe >= dev->num_crtcs)
15908c2ecf20Sopenharmony_ci		return -EINVAL;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	switch (modeset->cmd) {
15938c2ecf20Sopenharmony_ci	case _DRM_PRE_MODESET:
15948c2ecf20Sopenharmony_ci		drm_legacy_vblank_pre_modeset(dev, pipe);
15958c2ecf20Sopenharmony_ci		break;
15968c2ecf20Sopenharmony_ci	case _DRM_POST_MODESET:
15978c2ecf20Sopenharmony_ci		drm_legacy_vblank_post_modeset(dev, pipe);
15988c2ecf20Sopenharmony_ci		break;
15998c2ecf20Sopenharmony_ci	default:
16008c2ecf20Sopenharmony_ci		return -EINVAL;
16018c2ecf20Sopenharmony_ci	}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	return 0;
16048c2ecf20Sopenharmony_ci}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_cistatic int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
16078c2ecf20Sopenharmony_ci				  u64 req_seq,
16088c2ecf20Sopenharmony_ci				  union drm_wait_vblank *vblwait,
16098c2ecf20Sopenharmony_ci				  struct drm_file *file_priv)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
16128c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *e;
16138c2ecf20Sopenharmony_ci	ktime_t now;
16148c2ecf20Sopenharmony_ci	u64 seq;
16158c2ecf20Sopenharmony_ci	int ret;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
16188c2ecf20Sopenharmony_ci	if (e == NULL) {
16198c2ecf20Sopenharmony_ci		ret = -ENOMEM;
16208c2ecf20Sopenharmony_ci		goto err_put;
16218c2ecf20Sopenharmony_ci	}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	e->pipe = pipe;
16248c2ecf20Sopenharmony_ci	e->event.base.type = DRM_EVENT_VBLANK;
16258c2ecf20Sopenharmony_ci	e->event.base.length = sizeof(e->event.vbl);
16268c2ecf20Sopenharmony_ci	e->event.vbl.user_data = vblwait->request.signal;
16278c2ecf20Sopenharmony_ci	e->event.vbl.crtc_id = 0;
16288c2ecf20Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
16298c2ecf20Sopenharmony_ci		struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		if (crtc)
16328c2ecf20Sopenharmony_ci			e->event.vbl.crtc_id = crtc->base.id;
16338c2ecf20Sopenharmony_ci	}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->event_lock);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	/*
16388c2ecf20Sopenharmony_ci	 * drm_crtc_vblank_off() might have been called after we called
16398c2ecf20Sopenharmony_ci	 * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
16408c2ecf20Sopenharmony_ci	 * vblank disable, so no need for further locking.  The reference from
16418c2ecf20Sopenharmony_ci	 * drm_vblank_get() protects against vblank disable from another source.
16428c2ecf20Sopenharmony_ci	 */
16438c2ecf20Sopenharmony_ci	if (!READ_ONCE(vblank->enabled)) {
16448c2ecf20Sopenharmony_ci		ret = -EINVAL;
16458c2ecf20Sopenharmony_ci		goto err_unlock;
16468c2ecf20Sopenharmony_ci	}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
16498c2ecf20Sopenharmony_ci					    &e->event.base);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	if (ret)
16528c2ecf20Sopenharmony_ci		goto err_unlock;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	seq = drm_vblank_count_and_time(dev, pipe, &now);
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	drm_dbg_core(dev, "event on vblank count %llu, current %llu, crtc %u\n",
16578c2ecf20Sopenharmony_ci		     req_seq, seq, pipe);
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	trace_drm_vblank_event_queued(file_priv, pipe, req_seq);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	e->sequence = req_seq;
16628c2ecf20Sopenharmony_ci	if (drm_vblank_passed(seq, req_seq)) {
16638c2ecf20Sopenharmony_ci		drm_vblank_put(dev, pipe);
16648c2ecf20Sopenharmony_ci		send_vblank_event(dev, e, seq, now);
16658c2ecf20Sopenharmony_ci		vblwait->reply.sequence = seq;
16668c2ecf20Sopenharmony_ci	} else {
16678c2ecf20Sopenharmony_ci		/* drm_handle_vblank_events will call drm_vblank_put */
16688c2ecf20Sopenharmony_ci		list_add_tail(&e->base.link, &dev->vblank_event_list);
16698c2ecf20Sopenharmony_ci		vblwait->reply.sequence = req_seq;
16708c2ecf20Sopenharmony_ci	}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	return 0;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_cierr_unlock:
16778c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
16788c2ecf20Sopenharmony_ci	kfree(e);
16798c2ecf20Sopenharmony_cierr_put:
16808c2ecf20Sopenharmony_ci	drm_vblank_put(dev, pipe);
16818c2ecf20Sopenharmony_ci	return ret;
16828c2ecf20Sopenharmony_ci}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	if (vblwait->request.sequence)
16878c2ecf20Sopenharmony_ci		return false;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	return _DRM_VBLANK_RELATIVE ==
16908c2ecf20Sopenharmony_ci		(vblwait->request.type & (_DRM_VBLANK_TYPES_MASK |
16918c2ecf20Sopenharmony_ci					  _DRM_VBLANK_EVENT |
16928c2ecf20Sopenharmony_ci					  _DRM_VBLANK_NEXTONMISS));
16938c2ecf20Sopenharmony_ci}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci/*
16968c2ecf20Sopenharmony_ci * Widen a 32-bit param to 64-bits.
16978c2ecf20Sopenharmony_ci *
16988c2ecf20Sopenharmony_ci * \param narrow 32-bit value (missing upper 32 bits)
16998c2ecf20Sopenharmony_ci * \param near 64-bit value that should be 'close' to near
17008c2ecf20Sopenharmony_ci *
17018c2ecf20Sopenharmony_ci * This function returns a 64-bit value using the lower 32-bits from
17028c2ecf20Sopenharmony_ci * 'narrow' and constructing the upper 32-bits so that the result is
17038c2ecf20Sopenharmony_ci * as close as possible to 'near'.
17048c2ecf20Sopenharmony_ci */
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_cistatic u64 widen_32_to_64(u32 narrow, u64 near)
17078c2ecf20Sopenharmony_ci{
17088c2ecf20Sopenharmony_ci	return near + (s32) (narrow - near);
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe,
17128c2ecf20Sopenharmony_ci				  struct drm_wait_vblank_reply *reply)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	ktime_t now;
17158c2ecf20Sopenharmony_ci	struct timespec64 ts;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	/*
17188c2ecf20Sopenharmony_ci	 * drm_wait_vblank_reply is a UAPI structure that uses 'long'
17198c2ecf20Sopenharmony_ci	 * to store the seconds. This is safe as we always use monotonic
17208c2ecf20Sopenharmony_ci	 * timestamps since linux-4.15.
17218c2ecf20Sopenharmony_ci	 */
17228c2ecf20Sopenharmony_ci	reply->sequence = drm_vblank_count_and_time(dev, pipe, &now);
17238c2ecf20Sopenharmony_ci	ts = ktime_to_timespec64(now);
17248c2ecf20Sopenharmony_ci	reply->tval_sec = (u32)ts.tv_sec;
17258c2ecf20Sopenharmony_ci	reply->tval_usec = ts.tv_nsec / 1000;
17268c2ecf20Sopenharmony_ci}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ciint drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
17298c2ecf20Sopenharmony_ci			  struct drm_file *file_priv)
17308c2ecf20Sopenharmony_ci{
17318c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
17328c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank;
17338c2ecf20Sopenharmony_ci	union drm_wait_vblank *vblwait = data;
17348c2ecf20Sopenharmony_ci	int ret;
17358c2ecf20Sopenharmony_ci	u64 req_seq, seq;
17368c2ecf20Sopenharmony_ci	unsigned int pipe_index;
17378c2ecf20Sopenharmony_ci	unsigned int flags, pipe, high_pipe;
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	if (!dev->irq_enabled)
17408c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
17438c2ecf20Sopenharmony_ci		return -EINVAL;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (vblwait->request.type &
17468c2ecf20Sopenharmony_ci	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
17478c2ecf20Sopenharmony_ci	      _DRM_VBLANK_HIGH_CRTC_MASK)) {
17488c2ecf20Sopenharmony_ci		drm_dbg_core(dev,
17498c2ecf20Sopenharmony_ci			     "Unsupported type value 0x%x, supported mask 0x%x\n",
17508c2ecf20Sopenharmony_ci			     vblwait->request.type,
17518c2ecf20Sopenharmony_ci			     (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK |
17528c2ecf20Sopenharmony_ci			      _DRM_VBLANK_HIGH_CRTC_MASK));
17538c2ecf20Sopenharmony_ci		return -EINVAL;
17548c2ecf20Sopenharmony_ci	}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
17578c2ecf20Sopenharmony_ci	high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
17588c2ecf20Sopenharmony_ci	if (high_pipe)
17598c2ecf20Sopenharmony_ci		pipe_index = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
17608c2ecf20Sopenharmony_ci	else
17618c2ecf20Sopenharmony_ci		pipe_index = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	/* Convert lease-relative crtc index into global crtc index */
17648c2ecf20Sopenharmony_ci	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
17658c2ecf20Sopenharmony_ci		pipe = 0;
17668c2ecf20Sopenharmony_ci		drm_for_each_crtc(crtc, dev) {
17678c2ecf20Sopenharmony_ci			if (drm_lease_held(file_priv, crtc->base.id)) {
17688c2ecf20Sopenharmony_ci				if (pipe_index == 0)
17698c2ecf20Sopenharmony_ci					break;
17708c2ecf20Sopenharmony_ci				pipe_index--;
17718c2ecf20Sopenharmony_ci			}
17728c2ecf20Sopenharmony_ci			pipe++;
17738c2ecf20Sopenharmony_ci		}
17748c2ecf20Sopenharmony_ci	} else {
17758c2ecf20Sopenharmony_ci		pipe = pipe_index;
17768c2ecf20Sopenharmony_ci	}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	if (pipe >= dev->num_crtcs)
17798c2ecf20Sopenharmony_ci		return -EINVAL;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	vblank = &dev->vblank[pipe];
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	/* If the counter is currently enabled and accurate, short-circuit
17848c2ecf20Sopenharmony_ci	 * queries to return the cached timestamp of the last vblank.
17858c2ecf20Sopenharmony_ci	 */
17868c2ecf20Sopenharmony_ci	if (dev->vblank_disable_immediate &&
17878c2ecf20Sopenharmony_ci	    drm_wait_vblank_is_query(vblwait) &&
17888c2ecf20Sopenharmony_ci	    READ_ONCE(vblank->enabled)) {
17898c2ecf20Sopenharmony_ci		drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
17908c2ecf20Sopenharmony_ci		return 0;
17918c2ecf20Sopenharmony_ci	}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci	ret = drm_vblank_get(dev, pipe);
17948c2ecf20Sopenharmony_ci	if (ret) {
17958c2ecf20Sopenharmony_ci		drm_dbg_core(dev,
17968c2ecf20Sopenharmony_ci			     "crtc %d failed to acquire vblank counter, %d\n",
17978c2ecf20Sopenharmony_ci			     pipe, ret);
17988c2ecf20Sopenharmony_ci		return ret;
17998c2ecf20Sopenharmony_ci	}
18008c2ecf20Sopenharmony_ci	seq = drm_vblank_count(dev, pipe);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
18038c2ecf20Sopenharmony_ci	case _DRM_VBLANK_RELATIVE:
18048c2ecf20Sopenharmony_ci		req_seq = seq + vblwait->request.sequence;
18058c2ecf20Sopenharmony_ci		vblwait->request.sequence = req_seq;
18068c2ecf20Sopenharmony_ci		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
18078c2ecf20Sopenharmony_ci		break;
18088c2ecf20Sopenharmony_ci	case _DRM_VBLANK_ABSOLUTE:
18098c2ecf20Sopenharmony_ci		req_seq = widen_32_to_64(vblwait->request.sequence, seq);
18108c2ecf20Sopenharmony_ci		break;
18118c2ecf20Sopenharmony_ci	default:
18128c2ecf20Sopenharmony_ci		ret = -EINVAL;
18138c2ecf20Sopenharmony_ci		goto done;
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
18178c2ecf20Sopenharmony_ci	    drm_vblank_passed(seq, req_seq)) {
18188c2ecf20Sopenharmony_ci		req_seq = seq + 1;
18198c2ecf20Sopenharmony_ci		vblwait->request.type &= ~_DRM_VBLANK_NEXTONMISS;
18208c2ecf20Sopenharmony_ci		vblwait->request.sequence = req_seq;
18218c2ecf20Sopenharmony_ci	}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	if (flags & _DRM_VBLANK_EVENT) {
18248c2ecf20Sopenharmony_ci		/* must hold on to the vblank ref until the event fires
18258c2ecf20Sopenharmony_ci		 * drm_vblank_put will be called asynchronously
18268c2ecf20Sopenharmony_ci		 */
18278c2ecf20Sopenharmony_ci		return drm_queue_vblank_event(dev, pipe, req_seq, vblwait, file_priv);
18288c2ecf20Sopenharmony_ci	}
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	if (req_seq != seq) {
18318c2ecf20Sopenharmony_ci		int wait;
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "waiting on vblank count %llu, crtc %u\n",
18348c2ecf20Sopenharmony_ci			     req_seq, pipe);
18358c2ecf20Sopenharmony_ci		wait = wait_event_interruptible_timeout(vblank->queue,
18368c2ecf20Sopenharmony_ci			drm_vblank_passed(drm_vblank_count(dev, pipe), req_seq) ||
18378c2ecf20Sopenharmony_ci				      !READ_ONCE(vblank->enabled),
18388c2ecf20Sopenharmony_ci			msecs_to_jiffies(3000));
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci		switch (wait) {
18418c2ecf20Sopenharmony_ci		case 0:
18428c2ecf20Sopenharmony_ci			/* timeout */
18438c2ecf20Sopenharmony_ci			ret = -EBUSY;
18448c2ecf20Sopenharmony_ci			break;
18458c2ecf20Sopenharmony_ci		case -ERESTARTSYS:
18468c2ecf20Sopenharmony_ci			/* interrupted by signal */
18478c2ecf20Sopenharmony_ci			ret = -EINTR;
18488c2ecf20Sopenharmony_ci			break;
18498c2ecf20Sopenharmony_ci		default:
18508c2ecf20Sopenharmony_ci			ret = 0;
18518c2ecf20Sopenharmony_ci			break;
18528c2ecf20Sopenharmony_ci		}
18538c2ecf20Sopenharmony_ci	}
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	if (ret != -EINTR) {
18568c2ecf20Sopenharmony_ci		drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "crtc %d returning %u to client\n",
18598c2ecf20Sopenharmony_ci			     pipe, vblwait->reply.sequence);
18608c2ecf20Sopenharmony_ci	} else {
18618c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "crtc %d vblank wait interrupted by signal\n",
18628c2ecf20Sopenharmony_ci			     pipe);
18638c2ecf20Sopenharmony_ci	}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_cidone:
18668c2ecf20Sopenharmony_ci	drm_vblank_put(dev, pipe);
18678c2ecf20Sopenharmony_ci	return ret;
18688c2ecf20Sopenharmony_ci}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cistatic void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
18718c2ecf20Sopenharmony_ci{
18728c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
18738c2ecf20Sopenharmony_ci	bool high_prec = false;
18748c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *e, *t;
18758c2ecf20Sopenharmony_ci	ktime_t now;
18768c2ecf20Sopenharmony_ci	u64 seq;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->event_lock);
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	seq = drm_vblank_count_and_time(dev, pipe, &now);
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
18838c2ecf20Sopenharmony_ci		if (e->pipe != pipe)
18848c2ecf20Sopenharmony_ci			continue;
18858c2ecf20Sopenharmony_ci		if (!drm_vblank_passed(seq, e->sequence))
18868c2ecf20Sopenharmony_ci			continue;
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci		drm_dbg_core(dev, "vblank event on %llu, current %llu\n",
18898c2ecf20Sopenharmony_ci			     e->sequence, seq);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci		list_del(&e->base.link);
18928c2ecf20Sopenharmony_ci		drm_vblank_put(dev, pipe);
18938c2ecf20Sopenharmony_ci		send_vblank_event(dev, e, seq, now);
18948c2ecf20Sopenharmony_ci	}
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci	if (crtc && crtc->funcs->get_vblank_timestamp)
18978c2ecf20Sopenharmony_ci		high_prec = true;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	trace_drm_vblank_event(pipe, seq, now, high_prec);
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci/**
19038c2ecf20Sopenharmony_ci * drm_handle_vblank - handle a vblank event
19048c2ecf20Sopenharmony_ci * @dev: DRM device
19058c2ecf20Sopenharmony_ci * @pipe: index of CRTC where this event occurred
19068c2ecf20Sopenharmony_ci *
19078c2ecf20Sopenharmony_ci * Drivers should call this routine in their vblank interrupt handlers to
19088c2ecf20Sopenharmony_ci * update the vblank counter and send any signals that may be pending.
19098c2ecf20Sopenharmony_ci *
19108c2ecf20Sopenharmony_ci * This is the legacy version of drm_crtc_handle_vblank().
19118c2ecf20Sopenharmony_ci */
19128c2ecf20Sopenharmony_cibool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
19158c2ecf20Sopenharmony_ci	unsigned long irqflags;
19168c2ecf20Sopenharmony_ci	bool disable_irq;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, !drm_dev_has_vblank(dev)))
19198c2ecf20Sopenharmony_ci		return false;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
19228c2ecf20Sopenharmony_ci		return false;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->event_lock, irqflags);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	/* Need timestamp lock to prevent concurrent execution with
19278c2ecf20Sopenharmony_ci	 * vblank enable/disable, as this would cause inconsistent
19288c2ecf20Sopenharmony_ci	 * or corrupted timestamps and vblank counts.
19298c2ecf20Sopenharmony_ci	 */
19308c2ecf20Sopenharmony_ci	spin_lock(&dev->vblank_time_lock);
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	/* Vblank irq handling disabled. Nothing to do. */
19338c2ecf20Sopenharmony_ci	if (!vblank->enabled) {
19348c2ecf20Sopenharmony_ci		spin_unlock(&dev->vblank_time_lock);
19358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->event_lock, irqflags);
19368c2ecf20Sopenharmony_ci		return false;
19378c2ecf20Sopenharmony_ci	}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	drm_update_vblank_count(dev, pipe, true);
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	spin_unlock(&dev->vblank_time_lock);
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	wake_up(&vblank->queue);
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	/* With instant-off, we defer disabling the interrupt until after
19468c2ecf20Sopenharmony_ci	 * we finish processing the following vblank after all events have
19478c2ecf20Sopenharmony_ci	 * been signaled. The disable has to be last (after
19488c2ecf20Sopenharmony_ci	 * drm_handle_vblank_events) so that the timestamp is always accurate.
19498c2ecf20Sopenharmony_ci	 */
19508c2ecf20Sopenharmony_ci	disable_irq = (dev->vblank_disable_immediate &&
19518c2ecf20Sopenharmony_ci		       drm_vblank_offdelay > 0 &&
19528c2ecf20Sopenharmony_ci		       !atomic_read(&vblank->refcount));
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	drm_handle_vblank_events(dev, pipe);
19558c2ecf20Sopenharmony_ci	drm_handle_vblank_works(vblank);
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->event_lock, irqflags);
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	if (disable_irq)
19608c2ecf20Sopenharmony_ci		vblank_disable_fn(&vblank->disable_timer);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	return true;
19638c2ecf20Sopenharmony_ci}
19648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_handle_vblank);
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci/**
19678c2ecf20Sopenharmony_ci * drm_crtc_handle_vblank - handle a vblank event
19688c2ecf20Sopenharmony_ci * @crtc: where this event occurred
19698c2ecf20Sopenharmony_ci *
19708c2ecf20Sopenharmony_ci * Drivers should call this routine in their vblank interrupt handlers to
19718c2ecf20Sopenharmony_ci * update the vblank counter and send any signals that may be pending.
19728c2ecf20Sopenharmony_ci *
19738c2ecf20Sopenharmony_ci * This is the native KMS version of drm_handle_vblank().
19748c2ecf20Sopenharmony_ci *
19758c2ecf20Sopenharmony_ci * Note that for a given vblank counter value drm_crtc_handle_vblank()
19768c2ecf20Sopenharmony_ci * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
19778c2ecf20Sopenharmony_ci * provide a barrier: Any writes done before calling
19788c2ecf20Sopenharmony_ci * drm_crtc_handle_vblank() will be visible to callers of the later
19798c2ecf20Sopenharmony_ci * functions, iff the vblank count is the same or a later one.
19808c2ecf20Sopenharmony_ci *
19818c2ecf20Sopenharmony_ci * See also &drm_vblank_crtc.count.
19828c2ecf20Sopenharmony_ci *
19838c2ecf20Sopenharmony_ci * Returns:
19848c2ecf20Sopenharmony_ci * True if the event was successfully handled, false on failure.
19858c2ecf20Sopenharmony_ci */
19868c2ecf20Sopenharmony_cibool drm_crtc_handle_vblank(struct drm_crtc *crtc)
19878c2ecf20Sopenharmony_ci{
19888c2ecf20Sopenharmony_ci	return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
19898c2ecf20Sopenharmony_ci}
19908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_handle_vblank);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci/*
19938c2ecf20Sopenharmony_ci * Get crtc VBLANK count.
19948c2ecf20Sopenharmony_ci *
19958c2ecf20Sopenharmony_ci * \param dev DRM device
19968c2ecf20Sopenharmony_ci * \param data user arguement, pointing to a drm_crtc_get_sequence structure.
19978c2ecf20Sopenharmony_ci * \param file_priv drm file private for the user's open file descriptor
19988c2ecf20Sopenharmony_ci */
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ciint drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data,
20018c2ecf20Sopenharmony_ci				struct drm_file *file_priv)
20028c2ecf20Sopenharmony_ci{
20038c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
20048c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank;
20058c2ecf20Sopenharmony_ci	int pipe;
20068c2ecf20Sopenharmony_ci	struct drm_crtc_get_sequence *get_seq = data;
20078c2ecf20Sopenharmony_ci	ktime_t now;
20088c2ecf20Sopenharmony_ci	bool vblank_enabled;
20098c2ecf20Sopenharmony_ci	int ret;
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
20128c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	if (!dev->irq_enabled)
20158c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	crtc = drm_crtc_find(dev, file_priv, get_seq->crtc_id);
20188c2ecf20Sopenharmony_ci	if (!crtc)
20198c2ecf20Sopenharmony_ci		return -ENOENT;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	pipe = drm_crtc_index(crtc);
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	vblank = &dev->vblank[pipe];
20248c2ecf20Sopenharmony_ci	vblank_enabled = dev->vblank_disable_immediate && READ_ONCE(vblank->enabled);
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	if (!vblank_enabled) {
20278c2ecf20Sopenharmony_ci		ret = drm_crtc_vblank_get(crtc);
20288c2ecf20Sopenharmony_ci		if (ret) {
20298c2ecf20Sopenharmony_ci			drm_dbg_core(dev,
20308c2ecf20Sopenharmony_ci				     "crtc %d failed to acquire vblank counter, %d\n",
20318c2ecf20Sopenharmony_ci				     pipe, ret);
20328c2ecf20Sopenharmony_ci			return ret;
20338c2ecf20Sopenharmony_ci		}
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci	drm_modeset_lock(&crtc->mutex, NULL);
20368c2ecf20Sopenharmony_ci	if (crtc->state)
20378c2ecf20Sopenharmony_ci		get_seq->active = crtc->state->enable;
20388c2ecf20Sopenharmony_ci	else
20398c2ecf20Sopenharmony_ci		get_seq->active = crtc->enabled;
20408c2ecf20Sopenharmony_ci	drm_modeset_unlock(&crtc->mutex);
20418c2ecf20Sopenharmony_ci	get_seq->sequence = drm_vblank_count_and_time(dev, pipe, &now);
20428c2ecf20Sopenharmony_ci	get_seq->sequence_ns = ktime_to_ns(now);
20438c2ecf20Sopenharmony_ci	if (!vblank_enabled)
20448c2ecf20Sopenharmony_ci		drm_crtc_vblank_put(crtc);
20458c2ecf20Sopenharmony_ci	return 0;
20468c2ecf20Sopenharmony_ci}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci/*
20498c2ecf20Sopenharmony_ci * Queue a event for VBLANK sequence
20508c2ecf20Sopenharmony_ci *
20518c2ecf20Sopenharmony_ci * \param dev DRM device
20528c2ecf20Sopenharmony_ci * \param data user arguement, pointing to a drm_crtc_queue_sequence structure.
20538c2ecf20Sopenharmony_ci * \param file_priv drm file private for the user's open file descriptor
20548c2ecf20Sopenharmony_ci */
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ciint drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
20578c2ecf20Sopenharmony_ci				  struct drm_file *file_priv)
20588c2ecf20Sopenharmony_ci{
20598c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
20608c2ecf20Sopenharmony_ci	struct drm_vblank_crtc *vblank;
20618c2ecf20Sopenharmony_ci	int pipe;
20628c2ecf20Sopenharmony_ci	struct drm_crtc_queue_sequence *queue_seq = data;
20638c2ecf20Sopenharmony_ci	ktime_t now;
20648c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *e;
20658c2ecf20Sopenharmony_ci	u32 flags;
20668c2ecf20Sopenharmony_ci	u64 seq;
20678c2ecf20Sopenharmony_ci	u64 req_seq;
20688c2ecf20Sopenharmony_ci	int ret;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_MODESET))
20718c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	if (!dev->irq_enabled)
20748c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_ci	crtc = drm_crtc_find(dev, file_priv, queue_seq->crtc_id);
20778c2ecf20Sopenharmony_ci	if (!crtc)
20788c2ecf20Sopenharmony_ci		return -ENOENT;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	flags = queue_seq->flags;
20818c2ecf20Sopenharmony_ci	/* Check valid flag bits */
20828c2ecf20Sopenharmony_ci	if (flags & ~(DRM_CRTC_SEQUENCE_RELATIVE|
20838c2ecf20Sopenharmony_ci		      DRM_CRTC_SEQUENCE_NEXT_ON_MISS))
20848c2ecf20Sopenharmony_ci		return -EINVAL;
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	pipe = drm_crtc_index(crtc);
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	vblank = &dev->vblank[pipe];
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
20918c2ecf20Sopenharmony_ci	if (e == NULL)
20928c2ecf20Sopenharmony_ci		return -ENOMEM;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	ret = drm_crtc_vblank_get(crtc);
20958c2ecf20Sopenharmony_ci	if (ret) {
20968c2ecf20Sopenharmony_ci		drm_dbg_core(dev,
20978c2ecf20Sopenharmony_ci			     "crtc %d failed to acquire vblank counter, %d\n",
20988c2ecf20Sopenharmony_ci			     pipe, ret);
20998c2ecf20Sopenharmony_ci		goto err_free;
21008c2ecf20Sopenharmony_ci	}
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	seq = drm_vblank_count_and_time(dev, pipe, &now);
21038c2ecf20Sopenharmony_ci	req_seq = queue_seq->sequence;
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	if (flags & DRM_CRTC_SEQUENCE_RELATIVE)
21068c2ecf20Sopenharmony_ci		req_seq += seq;
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && drm_vblank_passed(seq, req_seq))
21098c2ecf20Sopenharmony_ci		req_seq = seq + 1;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	e->pipe = pipe;
21128c2ecf20Sopenharmony_ci	e->event.base.type = DRM_EVENT_CRTC_SEQUENCE;
21138c2ecf20Sopenharmony_ci	e->event.base.length = sizeof(e->event.seq);
21148c2ecf20Sopenharmony_ci	e->event.seq.user_data = queue_seq->user_data;
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->event_lock);
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	/*
21198c2ecf20Sopenharmony_ci	 * drm_crtc_vblank_off() might have been called after we called
21208c2ecf20Sopenharmony_ci	 * drm_crtc_vblank_get(). drm_crtc_vblank_off() holds event_lock around the
21218c2ecf20Sopenharmony_ci	 * vblank disable, so no need for further locking.  The reference from
21228c2ecf20Sopenharmony_ci	 * drm_crtc_vblank_get() protects against vblank disable from another source.
21238c2ecf20Sopenharmony_ci	 */
21248c2ecf20Sopenharmony_ci	if (!READ_ONCE(vblank->enabled)) {
21258c2ecf20Sopenharmony_ci		ret = -EINVAL;
21268c2ecf20Sopenharmony_ci		goto err_unlock;
21278c2ecf20Sopenharmony_ci	}
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	ret = drm_event_reserve_init_locked(dev, file_priv, &e->base,
21308c2ecf20Sopenharmony_ci					    &e->event.base);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	if (ret)
21338c2ecf20Sopenharmony_ci		goto err_unlock;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	e->sequence = req_seq;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	if (drm_vblank_passed(seq, req_seq)) {
21388c2ecf20Sopenharmony_ci		drm_crtc_vblank_put(crtc);
21398c2ecf20Sopenharmony_ci		send_vblank_event(dev, e, seq, now);
21408c2ecf20Sopenharmony_ci		queue_seq->sequence = seq;
21418c2ecf20Sopenharmony_ci	} else {
21428c2ecf20Sopenharmony_ci		/* drm_handle_vblank_events will call drm_vblank_put */
21438c2ecf20Sopenharmony_ci		list_add_tail(&e->base.link, &dev->vblank_event_list);
21448c2ecf20Sopenharmony_ci		queue_seq->sequence = req_seq;
21458c2ecf20Sopenharmony_ci	}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
21488c2ecf20Sopenharmony_ci	return 0;
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_cierr_unlock:
21518c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
21528c2ecf20Sopenharmony_ci	drm_crtc_vblank_put(crtc);
21538c2ecf20Sopenharmony_cierr_free:
21548c2ecf20Sopenharmony_ci	kfree(e);
21558c2ecf20Sopenharmony_ci	return ret;
21568c2ecf20Sopenharmony_ci}
21578c2ecf20Sopenharmony_ci
2158