Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 174536
b: refs/heads/master
c: c182be3
h: refs/heads/master
v: v3
  • Loading branch information
Kristian Høgsberg authored and Dave Airlie committed Oct 26, 2009
1 parent 4b92afe commit 1de26de
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 5 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0a5c1e61dbaceb6ce56281a3128a6912b0dcd043
refs/heads/master: c182be37ed7cb04c344501b88b8fdb747016e6cf
98 changes: 96 additions & 2 deletions trunk/drivers/gpu/drm/drm_fops.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp,

INIT_LIST_HEAD(&priv->lhead);
INIT_LIST_HEAD(&priv->fbs);
INIT_LIST_HEAD(&priv->event_list);
init_waitqueue_head(&priv->event_wait);
priv->event_space = 4096; /* set aside 4k for event buffer */

if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_open(dev, priv);
Expand Down Expand Up @@ -413,6 +416,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp)
}
}

static void drm_events_release(struct drm_file *file_priv)
{
struct drm_device *dev = file_priv->minor->dev;
struct drm_pending_event *e, *et;
struct drm_pending_vblank_event *v, *vt;
unsigned long flags;

spin_lock_irqsave(&dev->event_lock, flags);

/* Remove pending flips */
list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
if (v->base.file_priv == file_priv) {
list_del(&v->base.link);
drm_vblank_put(dev, v->pipe);
v->base.destroy(&v->base);
}

/* Remove unconsumed events */
list_for_each_entry_safe(e, et, &file_priv->event_list, link)
e->destroy(e);

spin_unlock_irqrestore(&dev->event_lock, flags);
}

/**
* Release file.
*
Expand Down Expand Up @@ -451,6 +478,8 @@ int drm_release(struct inode *inode, struct file *filp)
if (file_priv->minor->master)
drm_master_release(dev, filp);

drm_events_release(file_priv);

if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_release(dev, file_priv);

Expand Down Expand Up @@ -544,9 +573,74 @@ int drm_release(struct inode *inode, struct file *filp)
}
EXPORT_SYMBOL(drm_release);

/** No-op. */
static bool
drm_dequeue_event(struct drm_file *file_priv,
size_t total, size_t max, struct drm_pending_event **out)
{
struct drm_device *dev = file_priv->minor->dev;
struct drm_pending_event *e;
unsigned long flags;
bool ret = false;

spin_lock_irqsave(&dev->event_lock, flags);

*out = NULL;
if (list_empty(&file_priv->event_list))
goto out;
e = list_first_entry(&file_priv->event_list,
struct drm_pending_event, link);
if (e->event->length + total > max)
goto out;

file_priv->event_space += e->event->length;
list_del(&e->link);
*out = e;
ret = true;

out:
spin_unlock_irqrestore(&dev->event_lock, flags);
return ret;
}

ssize_t drm_read(struct file *filp, char __user *buffer,
size_t count, loff_t *offset)
{
struct drm_file *file_priv = filp->private_data;
struct drm_pending_event *e;
size_t total;
ssize_t ret;

ret = wait_event_interruptible(file_priv->event_wait,
!list_empty(&file_priv->event_list));
if (ret < 0)
return ret;

total = 0;
while (drm_dequeue_event(file_priv, total, count, &e)) {
if (copy_to_user(buffer + total,
e->event, e->event->length)) {
total = -EFAULT;
break;
}

total += e->event->length;
e->destroy(e);
}

return total;
}
EXPORT_SYMBOL(drm_read);

unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
{
return 0;
struct drm_file *file_priv = filp->private_data;
unsigned int mask = 0;

poll_wait(filp, &file_priv->event_wait, wait);

if (!list_empty(&file_priv->event_list))
mask |= POLLIN | POLLRDNORM;

return mask;
}
EXPORT_SYMBOL(drm_poll);
95 changes: 95 additions & 0 deletions trunk/drivers/gpu/drm/drm_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,62 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
return ret;
}

static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
union drm_wait_vblank *vblwait,
struct drm_file *file_priv)
{
struct drm_pending_vblank_event *e;
struct timeval now;
unsigned long flags;
unsigned int seq;

e = kzalloc(sizeof *e, GFP_KERNEL);
if (e == NULL)
return -ENOMEM;

e->pipe = pipe;
e->event.base.type = DRM_EVENT_VBLANK;
e->event.base.length = sizeof e->event;
e->event.user_data = vblwait->request.signal;
e->base.event = &e->event.base;
e->base.file_priv = file_priv;
e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;

do_gettimeofday(&now);
spin_lock_irqsave(&dev->event_lock, flags);

if (file_priv->event_space < sizeof e->event) {
spin_unlock_irqrestore(&dev->event_lock, flags);
kfree(e);
return -ENOMEM;
}

file_priv->event_space -= sizeof e->event;
seq = drm_vblank_count(dev, pipe);
if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
(seq - vblwait->request.sequence) <= (1 << 23)) {
vblwait->request.sequence = seq + 1;
}

DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
vblwait->request.sequence, seq, pipe);

e->event.sequence = vblwait->request.sequence;
if ((seq - vblwait->request.sequence) <= (1 << 23)) {
e->event.tv_sec = now.tv_sec;
e->event.tv_usec = now.tv_usec;
drm_vblank_put(dev, e->pipe);
list_add_tail(&e->base.link, &e->base.file_priv->event_list);
wake_up_interruptible(&e->base.file_priv->event_wait);
} else {
list_add_tail(&e->base.link, &dev->vblank_event_list);
}

spin_unlock_irqrestore(&dev->event_lock, flags);

return 0;
}

/**
* Wait for VBLANK.
*
Expand Down Expand Up @@ -582,6 +638,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
goto done;
}

if (flags & _DRM_VBLANK_EVENT)
return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);

if ((flags & _DRM_VBLANK_NEXTONMISS) &&
(seq - vblwait->request.sequence) <= (1<<23)) {
vblwait->request.sequence = seq + 1;
Expand Down Expand Up @@ -614,6 +673,38 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
return ret;
}

void drm_handle_vblank_events(struct drm_device *dev, int crtc)
{
struct drm_pending_vblank_event *e, *t;
struct timeval now;
unsigned long flags;
unsigned int seq;

do_gettimeofday(&now);
seq = drm_vblank_count(dev, crtc);

spin_lock_irqsave(&dev->event_lock, flags);

list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
if (e->pipe != crtc)
continue;
if ((seq - e->event.sequence) > (1<<23))
continue;

DRM_DEBUG("vblank event on %d, current %d\n",
e->event.sequence, seq);

e->event.sequence = seq;
e->event.tv_sec = now.tv_sec;
e->event.tv_usec = now.tv_usec;
drm_vblank_put(dev, e->pipe);
list_move_tail(&e->base.link, &e->base.file_priv->event_list);
wake_up_interruptible(&e->base.file_priv->event_wait);
}

spin_unlock_irqrestore(&dev->event_lock, flags);
}

/**
* drm_handle_vblank - handle a vblank event
* @dev: DRM device
Expand All @@ -624,7 +715,11 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
*/
void drm_handle_vblank(struct drm_device *dev, int crtc)
{
if (!dev->num_crtcs)
return;

atomic_inc(&dev->_vblank_count[crtc]);
DRM_WAKEUP(&dev->vbl_queue[crtc]);
drm_handle_vblank_events(dev, crtc);
}
EXPORT_SYMBOL(drm_handle_vblank);
2 changes: 2 additions & 0 deletions trunk/drivers/gpu/drm/drm_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
INIT_LIST_HEAD(&dev->ctxlist);
INIT_LIST_HEAD(&dev->vmalist);
INIT_LIST_HEAD(&dev->maplist);
INIT_LIST_HEAD(&dev->vblank_event_list);

spin_lock_init(&dev->count_lock);
spin_lock_init(&dev->drw_lock);
spin_lock_init(&dev->event_lock);
init_timer(&dev->timer);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/gpu/drm/i915/i915_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ static struct drm_driver driver = {
.mmap = drm_gem_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
.read = drm_read,
#ifdef CONFIG_COMPAT
.compat_ioctl = i915_compat_ioctl,
#endif
Expand Down
33 changes: 31 additions & 2 deletions trunk/include/drm/drm.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,15 +454,16 @@ struct drm_irq_busid {
enum drm_vblank_seq_type {
_DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
_DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
_DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */
_DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
_DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
_DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
_DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking, unsupported */
};

#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE)
#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \
_DRM_VBLANK_NEXTONMISS)
#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \
_DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)

struct drm_wait_vblank_request {
enum drm_vblank_seq_type type;
Expand Down Expand Up @@ -698,6 +699,34 @@ struct drm_gem_open {
#define DRM_COMMAND_BASE 0x40
#define DRM_COMMAND_END 0xA0

/**
* Header for events written back to userspace on the drm fd. The
* type defines the type of event, the length specifies the total
* length of the event (including the header), and user_data is
* typically a 64 bit value passed with the ioctl that triggered the
* event. A read on the drm fd will always only return complete
* events, that is, if for example the read buffer is 100 bytes, and
* there are two 64 byte events pending, only one will be returned.
*
* Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and
* up are chipset specific.
*/
struct drm_event {
__u32 type;
__u32 length;
};

#define DRM_EVENT_VBLANK 0x01

struct drm_event_vblank {
struct drm_event base;
__u64 user_data;
__u32 tv_sec;
__u32 tv_usec;
__u32 sequence;
__u32 reserved;
};

/* typedef area */
#ifndef __KERNEL__
typedef struct drm_clip_rect drm_clip_rect_t;
Expand Down
26 changes: 26 additions & 0 deletions trunk/include/drm/drmP.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,14 @@ struct drm_buf_entry {
struct drm_freelist freelist;
};

/* Event queued up for userspace to read */
struct drm_pending_event {
struct drm_event *event;
struct list_head link;
struct drm_file *file_priv;
void (*destroy)(struct drm_pending_event *event);
};

/** File private data */
struct drm_file {
int authenticated;
Expand All @@ -449,6 +457,10 @@ struct drm_file {
struct drm_master *master; /* master this node is currently associated with
N.B. not always minor->master */
struct list_head fbs;

wait_queue_head_t event_wait;
struct list_head event_list;
int event_space;
};

/** Wait queue */
Expand Down Expand Up @@ -897,6 +909,12 @@ struct drm_minor {
struct drm_mode_group mode_group;
};

struct drm_pending_vblank_event {
struct drm_pending_event base;
int pipe;
struct drm_event_vblank event;
};

/**
* DRM device structure. This structure represent a complete card that
* may contain multiple heads.
Expand Down Expand Up @@ -996,6 +1014,12 @@ struct drm_device {

u32 max_vblank_count; /**< size of vblank counter register */

/**
* List of events
*/
struct list_head vblank_event_list;
spinlock_t event_lock;

/*@} */
cycles_t ctx_start;
cycles_t lck_start;
Expand Down Expand Up @@ -1132,6 +1156,8 @@ extern int drm_lastclose(struct drm_device *dev);
extern int drm_open(struct inode *inode, struct file *filp);
extern int drm_stub_open(struct inode *inode, struct file *filp);
extern int drm_fasync(int fd, struct file *filp, int on);
extern ssize_t drm_read(struct file *filp, char __user *buffer,
size_t count, loff_t *offset);
extern int drm_release(struct inode *inode, struct file *filp);

/* Mapping support (drm_vm.h) */
Expand Down

0 comments on commit 1de26de

Please sign in to comment.