This modifies the datatypes used by the vblank code to provide 64 bits
of vblank count.
The driver interfaces have been left using 32 bits of vblank count;
all of the code necessary to widen that value for the user API was
already included to handle devices returning fewer than 32-bits.
This will provide the necessary datatypes for the Vulkan API.
v2:
* Re-write wait_vblank ioctl to ABSOLUTE sequence
When an application uses the WAIT_VBLANK ioctl with RELATIVE
or NEXTONMISS bits set, the target vblank interval is updated
within the kernel. We need to write that target back to the
ioctl buffer and update the flags bits so that if the wait is
interrupted by a signal, when it is re-started, it will target
precisely the same vblank count as before.
* Leave driver API with 32-bit vblank count
v3:
* Rebase on top of Arnd Bergmann's patch which had
the switch to ktime_t parts.
[airlied: fix conflict with Ville vblank change].
Suggested-by: Michel Dänzer <michel@daenzer.net>
Suggested-by: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
(cherry picked from commit
2affbc16983e4fc90960bc7f70e7615f4228199b)
}
DRM_DEBUG_VBL("updating vblank count on crtc %u:"
}
DRM_DEBUG_VBL("updating vblank count on crtc %u:"
- " current=%u, diff=%u, hw=%u hw_last=%u\n",
+ " current=%llu, diff=%u, hw=%u hw_last=%u\n",
pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
* Returns:
* The software vblank counter.
*/
* Returns:
* The software vblank counter.
*/
-u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
+u64 drm_crtc_vblank_count(struct drm_crtc *crtc)
{
return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
}
EXPORT_SYMBOL(drm_crtc_vblank_count);
{
return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
}
EXPORT_SYMBOL(drm_crtc_vblank_count);
-static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
+/**
+ * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the
+ * system timestamp corresponding to that vblank counter value.
+ * @dev: DRM device
+ * @pipe: index of CRTC whose counter to retrieve
+ * @vblanktime: Pointer to ktime_t to receive the vblank timestamp.
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity. Returns corresponding system timestamp of the time
+ * of the vblank interval that corresponds to the current vblank counter value.
+ *
+ * This is the legacy version of drm_crtc_vblank_count_and_time().
+ */
+static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
ktime_t *vblanktime)
{
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
ktime_t *vblanktime)
{
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
unsigned int seq;
if (WARN_ON(pipe >= dev->num_crtcs)) {
unsigned int seq;
if (WARN_ON(pipe >= dev->num_crtcs)) {
* modesetting activity. Returns corresponding system timestamp of the time
* of the vblank interval that corresponds to the current vblank counter value.
*/
* modesetting activity. Returns corresponding system timestamp of the time
* of the vblank interval that corresponds to the current vblank counter value.
*/
-u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
ktime_t *vblanktime)
{
return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
ktime_t *vblanktime)
{
return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
static void send_vblank_event(struct drm_device *dev,
struct drm_pending_vblank_event *e,
static void send_vblank_event(struct drm_device *dev,
struct drm_pending_vblank_event *e,
- unsigned long seq, ktime_t now)
{
struct timespec64 tv = ktime_to_timespec64(now);
{
struct timespec64 tv = ktime_to_timespec64(now);
assert_spin_locked(&dev->event_lock);
e->pipe = pipe;
assert_spin_locked(&dev->event_lock);
e->pipe = pipe;
- e->event.sequence = drm_crtc_accurate_vblank_count(crtc) + 1;
+ e->sequence = drm_crtc_accurate_vblank_count(crtc) + 1;
e->event.crtc_id = crtc->base.id;
list_add_tail(&e->base.link, &dev->vblank_event_list);
}
e->event.crtc_id = crtc->base.id;
list_add_tail(&e->base.link, &dev->vblank_event_list);
}
struct drm_pending_vblank_event *e)
{
struct drm_device *dev = crtc->dev;
struct drm_pending_vblank_event *e)
{
struct drm_device *dev = crtc->dev;
- unsigned int seq, pipe = drm_crtc_index(crtc);
+ u64 seq;
+ unsigned int pipe = drm_crtc_index(crtc);
ktime_t now;
if (dev->num_crtcs > 0) {
ktime_t now;
if (dev->num_crtcs > 0) {
ktime_t now;
unsigned long irqflags;
ktime_t now;
unsigned long irqflags;
if (WARN_ON(pipe >= dev->num_crtcs))
return;
if (WARN_ON(pipe >= dev->num_crtcs))
return;
if (e->pipe != pipe)
continue;
DRM_DEBUG("Sending premature vblank event on disable: "
if (e->pipe != pipe)
continue;
DRM_DEBUG("Sending premature vblank event on disable: "
- "wanted %u, current %u\n",
- e->event.sequence, seq);
+ "wanted %llu, current %llu\n",
+ e->sequence, seq);
list_del(&e->base.link);
drm_vblank_put(dev, pipe);
send_vblank_event(dev, e, seq, now);
list_del(&e->base.link);
drm_vblank_put(dev, pipe);
send_vblank_event(dev, e, seq, now);
-static inline bool vblank_passed(u32 seq, u32 ref)
+static inline bool vblank_passed(u64 seq, u64 ref)
{
return (seq - ref) <= (1 << 23);
}
static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
{
return (seq - ref) <= (1 << 23);
}
static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
union drm_wait_vblank *vblwait,
struct drm_file *file_priv)
{
union drm_wait_vblank *vblwait,
struct drm_file *file_priv)
{
struct drm_pending_vblank_event *e;
ktime_t now;
unsigned long flags;
struct drm_pending_vblank_event *e;
ktime_t now;
unsigned long flags;
int ret;
e = kzalloc(sizeof(*e), GFP_KERNEL);
int ret;
e = kzalloc(sizeof(*e), GFP_KERNEL);
seq = drm_vblank_count_and_time(dev, pipe, &now);
seq = drm_vblank_count_and_time(dev, pipe, &now);
- DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
- vblwait->request.sequence, seq, pipe);
+ DRM_DEBUG("event on vblank count %llu, current %llu, crtc %u\n",
+ req_seq, seq, pipe);
- trace_drm_vblank_event_queued(file_priv, pipe,
- vblwait->request.sequence);
+ trace_drm_vblank_event_queued(file_priv, pipe, req_seq);
- e->event.sequence = vblwait->request.sequence;
- if (vblank_passed(seq, vblwait->request.sequence)) {
+ e->sequence = req_seq;
+ if (vblank_passed(seq, req_seq)) {
drm_vblank_put(dev, pipe);
send_vblank_event(dev, e, seq, now);
vblwait->reply.sequence = seq;
} else {
/* drm_handle_vblank_events will call drm_vblank_put */
list_add_tail(&e->base.link, &dev->vblank_event_list);
drm_vblank_put(dev, pipe);
send_vblank_event(dev, e, seq, now);
vblwait->reply.sequence = seq;
} else {
/* drm_handle_vblank_events will call drm_vblank_put */
list_add_tail(&e->base.link, &dev->vblank_event_list);
- vblwait->reply.sequence = vblwait->request.sequence;
+ vblwait->reply.sequence = req_seq;
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
_DRM_VBLANK_NEXTONMISS));
}
_DRM_VBLANK_NEXTONMISS));
}
+/*
+ * Widen a 32-bit param to 64-bits.
+ *
+ * \param narrow 32-bit value (missing upper 32 bits)
+ * \param near 64-bit value that should be 'close' to near
+ *
+ * This function returns a 64-bit value using the lower 32-bits from
+ * 'narrow' and constructing the upper 32-bits so that the result is
+ * as close as possible to 'near'.
+ */
+
+static u64 widen_32_to_64(u32 narrow, u64 near)
+{
+ return near + (s32) (narrow - near);
+}
+
static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe,
struct drm_wait_vblank_reply *reply)
{
static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe,
struct drm_wait_vblank_reply *reply)
{
struct drm_vblank_crtc *vblank;
union drm_wait_vblank *vblwait = data;
int ret;
struct drm_vblank_crtc *vblank;
union drm_wait_vblank *vblwait = data;
int ret;
- unsigned int flags, seq, pipe, high_pipe;
+ u64 req_seq, seq;
+ unsigned int flags, pipe, high_pipe;
if (!dev->irq_enabled)
return -EINVAL;
if (!dev->irq_enabled)
return -EINVAL;
switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
case _DRM_VBLANK_RELATIVE:
switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
case _DRM_VBLANK_RELATIVE:
- vblwait->request.sequence += seq;
+ req_seq = seq + vblwait->request.sequence;
+ vblwait->request.sequence = req_seq;
vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
case _DRM_VBLANK_ABSOLUTE:
case _DRM_VBLANK_ABSOLUTE:
+ req_seq = widen_32_to_64(vblwait->request.sequence, seq);
break;
default:
ret = -EINVAL;
break;
default:
ret = -EINVAL;
}
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
}
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
- vblank_passed(seq, vblwait->request.sequence))
- vblwait->request.sequence = seq + 1;
+ vblank_passed(seq, req_seq)) {
+ req_seq = seq + 1;
+ vblwait->request.type &= ~_DRM_VBLANK_NEXTONMISS;
+ vblwait->request.sequence = req_seq;
+ }
if (flags & _DRM_VBLANK_EVENT) {
/* must hold on to the vblank ref until the event fires
* drm_vblank_put will be called asynchronously
*/
if (flags & _DRM_VBLANK_EVENT) {
/* must hold on to the vblank ref until the event fires
* drm_vblank_put will be called asynchronously
*/
- return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
+ return drm_queue_vblank_event(dev, pipe, req_seq, vblwait, file_priv);
- if (vblwait->request.sequence != seq) {
- DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
- vblwait->request.sequence, pipe);
+ if (req_seq != seq) {
+ DRM_DEBUG("waiting on vblank count %llu, crtc %u\n",
+ req_seq, pipe);
DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
vblank_passed(drm_vblank_count(dev, pipe),
DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
vblank_passed(drm_vblank_count(dev, pipe),
- vblwait->request.sequence) ||
!READ_ONCE(vblank->enabled));
}
!READ_ONCE(vblank->enabled));
}
{
struct drm_pending_vblank_event *e, *t;
ktime_t now;
{
struct drm_pending_vblank_event *e, *t;
ktime_t now;
assert_spin_locked(&dev->event_lock);
assert_spin_locked(&dev->event_lock);
list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
if (e->pipe != pipe)
continue;
list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
if (e->pipe != pipe)
continue;
- if (!vblank_passed(seq, e->event.sequence))
+ if (!vblank_passed(seq, e->sequence))
- DRM_DEBUG("vblank event on %u, current %u\n",
- e->event.sequence, seq);
+ DRM_DEBUG("vblank event on %llu, current %llu\n",
+ e->sequence, seq);
list_del(&e->base.link);
drm_vblank_put(dev, pipe);
list_del(&e->base.link);
drm_vblank_put(dev, pipe);
* @pipe: drm_crtc_index() of the &drm_crtc this event is for.
*/
unsigned int pipe;
* @pipe: drm_crtc_index() of the &drm_crtc this event is for.
*/
unsigned int pipe;
+ /**
+ * @sequence: frame event should be triggered at
+ */
+ u64 sequence;
/**
* @event: Actual event which will be sent to userspace.
*/
/**
* @event: Actual event which will be sent to userspace.
*/
/**
* @count: Current software vblank counter.
*/
/**
* @count: Current software vblank counter.
*/
/**
* @time: Vblank timestamp corresponding to @count.
*/
/**
* @time: Vblank timestamp corresponding to @count.
*/
};
int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
};
int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
-u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
-u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+u64 drm_crtc_vblank_count(struct drm_crtc *crtc);
+u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
ktime_t *vblanktime);
void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
struct drm_pending_vblank_event *e);
ktime_t *vblanktime);
void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
struct drm_pending_vblank_event *e);