On Thu, 14 May 2009 23:04:51 -0400 Kristian Høgsberg <k...@bitplanet.net> wrote:
> From: Kristian Høgsberg <k...@redhat.com> > > This patch adds a vblank synced pageflip ioctl for to the modesetting > family of ioctls. The ioctl takes a crtc and an fb and schedules a > pageflip to the new fb at the next coming vertical blank event. This > feature lets userspace implement tear-free updating of the screen > contents with hw-guaranteed low latency page flipping. > > The ioctl is asynchronous in that it returns immediately and then > later notifies the client by making an event available for reading on > the drm fd. This lets applications add the drm fd to their main loop > and handle other tasks while waiting for the flip to happen. The > event includes the time of the flip, the frame counter and a 64 bit > opaque token provided by user space in the ioctl. > > Based on initial work and suggestions from > Jesse Barnes <jbar...@virtuousgeek.org> and > Jakob Bornecrantz <wallbra...@gmail.com>. > > Signed-off-by: Kristian Høgsberg <k...@redhat.com> > --- > > This update addresses a few of the issues brought up by Jakob: > > - the u64 user_data field is moved the page flip event > from the drm header struct. > > - the implementation of the page flip ioctl is moved to > drm_crtc.h instead of drm_crtc_helper.c > > and cleanup in drm_release() is simpler and should clean up > everything (it was missing an unpin before, now we just use the > same code as the normal drm_read() case). > > What's still missing is: > > - don't block on setting the domain in set_base > > - the synchronous version of the ioctl; userspace can > just use the ioctl followed by a read loop. Here's an update that makes the set_base async using a new work_struct. I'll push the libdrm changes into the kms-pageflip branch now too. The set_base thing is really ugly; apparently on x86 there's a set_base macro in the global namespace now, gross. Hopefully that will be fixed before this is pushed upstream. Thanks, -- Jesse Barnes, Intel Open Source Technology Center diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 94a7688..54dac6c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -34,6 +34,8 @@ #include "drmP.h" #include "drm_crtc.h" +#undef set_base + struct drm_prop_enum_list { int type; char *name; @@ -342,6 +344,24 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) EXPORT_SYMBOL(drm_framebuffer_cleanup); /** + * drm_crtc_async_work - do a set_base call from a work queue + * @work: work struct + * + * Called when a set_base call is queued by the page flip code. This + * allows the flip ioctl itself to return immediately and allow userspace + * to continue working. + */ +static void drm_crtc_async_work(struct work_struct *work) +{ + struct drm_crtc *crtc = container_of(work, struct drm_crtc, async_work); + struct drm_device *dev = crtc->dev; + + mutex_lock(&dev->struct_mutex); + crtc->funcs->set_base(crtc, 0, 0, NULL); + mutex_unlock(&dev->struct_mutex); +} + +/** * drm_crtc_init - Initialise a new CRTC object * @dev: DRM device * @crtc: CRTC object to init @@ -352,17 +372,19 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * * Inits a new object created as base part of an driver crtc object. */ -void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, +void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, int pipe, const struct drm_crtc_funcs *funcs) { crtc->dev = dev; crtc->funcs = funcs; + crtc->pipe = pipe; mutex_lock(&dev->mode_config.mutex); drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++; + INIT_WORK(&crtc->async_work, drm_crtc_async_work); mutex_unlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_crtc_init); @@ -381,6 +403,9 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; + mutex_lock(&dev->mode_config.mutex); + flush_work(&crtc->async_work); + if (crtc->gamma_store) { kfree(crtc->gamma_store); crtc->gamma_store = NULL; @@ -388,6 +413,7 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); + mutex_unlock(&dev->mode_config.mutex); dev->mode_config.num_crtc--; } EXPORT_SYMBOL(drm_crtc_cleanup); @@ -2447,3 +2473,117 @@ out: mutex_unlock(&dev->mode_config.mutex); return ret; } + +/** + * drm_mode_page_flip_ioctl - page flip ioctl + * @dev: DRM device + * @data: ioctl args + * @file_priv: file private data + * + * The page flip ioctl replaces the current front buffer with a new + * one, using the CRTC's set_base function, which should just update + * the front buffer base pointer. It's up to set_base to make + * sure the update doesn't result in tearing (on some hardware the + * base register is double buffered, so this is easy). + * + * Note that this covers just the simple case of flipping the front + * buffer immediately. Interval handling and interlaced modes have to + * be handled by userspace, or with new ioctls. + */ +int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_pending_flip *pending; + struct drm_mode_page_flip *flip_data = data; + struct drm_mode_object *drm_obj, *fb_obj; + struct drm_crtc *crtc; + int ret = 0; + + if (!(drm_core_check_feature(dev, DRIVER_MODESET))) + return -ENODEV; + + /* + * Reject unknown flags so future userspace knows what we (don't) + * support + */ + if (flip_data->flags & (~DRM_MODE_PAGE_FLIP_FLAGS_MASK)) { + DRM_DEBUG("bad page flip flags\n"); + return -EINVAL; + } + + pending = kzalloc(sizeof *pending, GFP_KERNEL); + if (pending == NULL) + return -ENOMEM; + + mutex_lock(&dev->struct_mutex); + + fb_obj = drm_mode_object_find(dev, flip_data->fb_id, + DRM_MODE_OBJECT_FB); + if (!fb_obj) { + DRM_DEBUG("unknown fb %d\n", flip_data->fb_id); + ret = -ENOENT; + goto out_unlock; + } + + drm_obj = drm_mode_object_find(dev, flip_data->crtc_id, + DRM_MODE_OBJECT_CRTC); + if (!drm_obj) { + DRM_DEBUG("unknown crtc %d\n", flip_data->crtc_id); + ret = -ENOENT; + goto out_unlock; + } + crtc = obj_to_crtc(drm_obj); + if (!crtc->enabled) { + DRM_DEBUG("crtc %d not enabled\n", flip_data->crtc_id); + ret = -EINVAL; + goto out_unlock; + } + + if (crtc->fb->funcs->unpin == NULL) { + DRM_DEBUG("fb for crtc %d does not support delayed unpin\n", + flip_data->crtc_id); + ret = -ENODEV; + goto out_unlock; + } + + pending->crtc = crtc; + pending->old_fb = crtc->fb; + pending->pipe = crtc->pipe; + pending->event.base.type = DRM_EVENT_MODE_PAGE_FLIP; + pending->event.base.length = sizeof pending->event; + pending->event.user_data = flip_data->user_data; + pending->pending_event.event = &pending->event.base; + pending->pending_event.file_priv = file_priv; + pending->pending_event.destroy = + (void (*) (struct drm_pending_event *)) kfree; + + /* Get vblank ref for completion handling */ + ret = drm_vblank_get(dev, crtc->pipe); + if (ret) { + DRM_DEBUG("failed to take vblank ref\n"); + goto out_unlock; + } + + pending->frame = drm_vblank_count(dev, crtc->pipe); + list_add_tail(&pending->link, &dev->flip_list); + + /* + * The set_base call will change the domain on the new fb, + * which will force the rendering to finish and block the + * ioctl. We need to do this last part from a work queue, to + * avoid blocking userspace here. + */ + crtc->fb = obj_to_fb(fb_obj); + + schedule_work(&crtc->async_work); + + mutex_unlock(&dev->struct_mutex); + + return 0; + +out_unlock: + mutex_unlock(&dev->struct_mutex); + kfree(pending); + + return ret; +} diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index a04639d..4aed52d 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -593,8 +593,10 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (drm_mode_equal(&saved_mode, &crtc->mode)) { if (saved_x != crtc->x || saved_y != crtc->y || depth_changed || bpp_changed) { - ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, - old_fb); + mutex_lock(&dev->struct_mutex); + ret = !(*crtc->funcs->set_base)(crtc, crtc->x, crtc->y, + old_fb); + mutex_unlock(&dev->struct_mutex); goto done; } } @@ -836,7 +838,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) } /* mode_set_base is not a required function */ - if (fb_changed && !crtc_funcs->mode_set_base) + if (fb_changed && !set->crtc->funcs->set_base) mode_changed = true; if (mode_changed) { @@ -864,8 +866,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) old_fb = set->crtc->fb; if (set->crtc->fb != set->fb) set->crtc->fb = set->fb; - ret = crtc_funcs->mode_set_base(set->crtc, - set->x, set->y, old_fb); + mutex_lock(&dev->struct_mutex); + ret = (*set->crtc->funcs->set_base)(set->crtc, + set->x, set->y, old_fb); + mutex_unlock(&dev->struct_mutex); if (ret != 0) goto fail_set_mode; } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index c4ada8b..6cc61c4 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -145,6 +145,7 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 09a3571..4b1b461 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -257,6 +257,8 @@ 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); if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); @@ -429,6 +431,9 @@ int drm_release(struct inode *inode, struct file *filp) { struct drm_file *file_priv = filp->private_data; struct drm_device *dev = file_priv->minor->dev; + struct drm_pending_flip *f, *ft; + struct drm_pending_event *e, *et; + int retcode = 0; lock_kernel(); @@ -451,6 +456,19 @@ int drm_release(struct inode *inode, struct file *filp) if (file_priv->minor->master) drm_master_release(dev, filp); + mutex_lock(&dev->struct_mutex); + + /* Remove pending flips */ + list_for_each_entry_safe(f, ft, &dev->flip_list, link) + if (f->pending_event.file_priv == file_priv) + drm_finish_pending_flip(dev, f, 0); + + /* Remove unconsumed events */ + list_for_each_entry_safe(e, et, &file_priv->event_list, link) + e->destroy(e); + + mutex_unlock(&dev->struct_mutex); + if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); @@ -544,9 +562,55 @@ int drm_release(struct inode *inode, struct file *filp) } EXPORT_SYMBOL(drm_release); -/** No-op. */ +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_device *dev = file_priv->minor->dev; + struct drm_pending_event *event; + ssize_t total, ret; + + ret = wait_event_interruptible(file_priv->event_wait, + !list_empty(&file_priv->event_list)); + if (ret < 0) + return ret; + + total = 0; + while (!list_empty(&file_priv->event_list)) { + mutex_lock(&dev->struct_mutex); + event = list_first_entry(&file_priv->event_list, + struct drm_pending_event, link); + if (total + event->event->length > count) { + mutex_unlock(&dev->struct_mutex); + break; + } + list_del(&event->link); + mutex_unlock(&dev->struct_mutex); + + if (copy_to_user(buffer + total, + event->event, event->event->length)) { + total = -EFAULT; + break; + } + + total += event->event->length; + event->destroy(event); + } + + 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); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 93e677a..2825666 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -34,6 +34,7 @@ */ #include "drmP.h" +#include "drm_crtc_helper.h" #include <linux/interrupt.h> /* For task queue support */ @@ -71,6 +72,43 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, return 0; } +#define vblank_after(a,b) ((long)(b) - (long)(a) < 0) + +void drm_finish_pending_flip(struct drm_device *dev, + struct drm_pending_flip *f, u32 frame) +{ + struct timeval now; + + f->event.frame = frame; + do_gettimeofday(&now); + f->event.tv_sec = now.tv_sec; + f->event.tv_usec = now.tv_usec; + drm_vblank_put(dev, f->pipe); + list_del_init(&f->link); + list_add_tail(&f->pending_event.link, + &f->pending_event.file_priv->event_list); + f->old_fb->funcs->unpin(f->old_fb); + wake_up_interruptible(&f->pending_event.file_priv->event_wait); +} + +static void drm_flip_work_func(struct work_struct *work) +{ + struct drm_device *dev = + container_of(work, struct drm_device, flip_work); + struct drm_pending_flip *f, *t; + u32 frame; + + mutex_lock(&dev->struct_mutex); + + list_for_each_entry_safe(f, t, &dev->flip_list, link) { + frame = drm_vblank_count(dev, f->pipe); + if (vblank_after(frame, f->frame)) + drm_finish_pending_flip(dev, f, frame); + } + + mutex_unlock(&dev->struct_mutex); +} + static void vblank_disable_fn(unsigned long arg) { struct drm_device *dev = (struct drm_device *)arg; @@ -173,6 +211,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) atomic_set(&dev->vblank_refcount[i], 0); } + INIT_LIST_HEAD(&dev->flip_list); + INIT_WORK(&dev->flip_work, drm_flip_work_func); dev->vblank_disable_allowed = 0; return 0; @@ -632,5 +672,7 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) { atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queue[crtc]); + schedule_work(&dev->flip_work); } EXPORT_SYMBOL(drm_handle_vblank); + diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 98560e1..85dde09 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -198,6 +198,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 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3387cf3..663edf2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -662,6 +662,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, u32 dspcntr, alignment; int ret; + BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + /* no fb bound */ if (!crtc->fb) { DRM_DEBUG("No FB bound\n"); @@ -697,17 +699,14 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, BUG(); } - mutex_lock(&dev->struct_mutex); ret = i915_gem_object_pin(intel_fb->obj, alignment); if (ret != 0) { - mutex_unlock(&dev->struct_mutex); return ret; } ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1); if (ret != 0) { i915_gem_object_unpin(intel_fb->obj); - mutex_unlock(&dev->struct_mutex); return ret; } @@ -731,7 +730,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, default: DRM_ERROR("Unknown color depth\n"); i915_gem_object_unpin(intel_fb->obj); - mutex_unlock(&dev->struct_mutex); return -EINVAL; } if (IS_I965G(dev)) { @@ -759,13 +757,11 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, I915_READ(dspbase); } - intel_wait_for_vblank(dev); - if (old_fb) { intel_fb = to_intel_framebuffer(old_fb); + intel_wait_for_vblank(dev); i915_gem_object_unpin(intel_fb->obj); } - mutex_unlock(&dev->struct_mutex); if (!dev->primary->master) return 0; @@ -785,8 +781,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } - - /** * Sets the power management mode of the pipe and plane. * @@ -1314,7 +1308,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(dspcntr_reg, dspcntr); /* Flush the plane changes */ + mutex_lock(&dev->struct_mutex); ret = intel_pipe_set_base(crtc, x, y, old_fb); + mutex_unlock(&dev->struct_mutex); if (ret != 0) return ret; @@ -1756,7 +1752,6 @@ static const struct drm_crtc_helper_funcs intel_helper_funcs = { .dpms = intel_crtc_dpms, .mode_fixup = intel_crtc_mode_fixup, .mode_set = intel_crtc_mode_set, - .mode_set_base = intel_pipe_set_base, .prepare = intel_crtc_prepare, .commit = intel_crtc_commit, }; @@ -1767,6 +1762,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .gamma_set = intel_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, .destroy = intel_crtc_destroy, + .set_base = intel_pipe_set_base, }; @@ -1779,7 +1775,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) if (intel_crtc == NULL) return; - drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); + drm_crtc_init(dev, &intel_crtc->base, pipe, &intel_crtc_funcs); drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); intel_crtc->pipe = pipe; @@ -1969,9 +1965,18 @@ static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, return drm_gem_handle_create(file_priv, object, handle); } +static void intel_user_framebuffer_unpin(struct drm_framebuffer *fb) +{ + struct intel_framebuffer *intel_fb; + + intel_fb = to_intel_framebuffer(fb); + i915_gem_object_unpin(intel_fb->obj); +} + static const struct drm_framebuffer_funcs intel_fb_funcs = { .destroy = intel_user_framebuffer_destroy, .create_handle = intel_user_framebuffer_create_handle, + .unpin = intel_user_framebuffer_unpin }; int intel_framebuffer_create(struct drm_device *dev, diff --git a/include/drm/drm.h b/include/drm/drm.h index 7cb50bd..1920323 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -686,6 +686,7 @@ struct drm_gem_open { #define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) #define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) #define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) +#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOW( 0xB0, struct drm_mode_page_flip) /** * Device specific ioctls should only be in their respective headers @@ -698,6 +699,30 @@ 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. + */ +struct drm_event { + __u32 type; + __u32 length; +}; + +#define DRM_EVENT_MODE_PAGE_FLIP 0x01 + +struct drm_event_page_flip { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 frame; +}; + /* typedef area */ #ifndef __KERNEL__ typedef struct drm_clip_rect drm_clip_rect_t; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c8c4221..d70462e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -378,6 +378,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; @@ -401,6 +409,9 @@ 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; }; /** Wait queue */ @@ -869,6 +880,16 @@ struct drm_minor { struct drm_mode_group mode_group; }; +struct drm_pending_flip { + struct drm_pending_event pending_event; + struct drm_framebuffer *old_fb; + struct drm_crtc *crtc; + u32 frame; + int pipe; + struct list_head link; + struct drm_event_page_flip event; +}; + /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. @@ -968,6 +989,13 @@ struct drm_device { u32 max_vblank_count; /**< size of vblank counter register */ + struct work_struct flip_work; + + /** + * List of objects waiting on flip completion + */ + struct list_head flip_list; + /*...@} */ cycles_t ctx_start; cycles_t lck_start; @@ -1104,6 +1132,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) */ @@ -1270,6 +1300,8 @@ extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc); extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc); extern int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern void drm_finish_pending_flip(struct drm_device *dev, + struct drm_pending_flip *f, u32 frame); /* AGP/GART support (drm_agpsupport.h) */ extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3c1924c..066c6d3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -238,6 +238,12 @@ struct drm_display_info { }; struct drm_framebuffer_funcs { + /* + * Unpin the old fb after setting a mode. Must be called + * after the old framebuffer is no longer visible, ie, after + * the next vblank, typically. + */ + void (*unpin)(struct drm_framebuffer *fb); void (*destroy)(struct drm_framebuffer *framebuffer); int (*create_handle)(struct drm_framebuffer *fb, struct drm_file *file_priv, @@ -331,17 +337,29 @@ struct drm_crtc_funcs { void (*destroy)(struct drm_crtc *crtc); int (*set_config)(struct drm_mode_set *set); + + /* + * Move the crtc on the current fb to the given position. + * This function is optional. If old_fb is provided, the + * function will wait for vblank and unpin it. If old_fb is + * NULL, nothing is unpinned and the caller must call + * mode_unpin_fb to release the old framebuffer. + */ + int (*set_base)(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); }; /** * drm_crtc - central CRTC control structure * @enabled: is this CRTC enabled? + * @pipe: pipe number (as seen by DRM vblank functions) * @x: x position on screen * @y: y position on screen * @desired_mode: new desired mode * @desired_x: desired x for desired_mode * @desired_y: desired y for desired_mode * @funcs: CRTC control functions + * @async_work: work queue for async set base calls * * Each CRTC may have one or more connectors associated with it. This structure * allows the CRTC to be controlled. @@ -359,6 +377,7 @@ struct drm_crtc { struct drm_display_mode mode; + int pipe; int x, y; struct drm_display_mode *desired_mode; int desired_x, desired_y; @@ -368,6 +387,9 @@ struct drm_crtc { uint32_t gamma_size; uint16_t *gamma_store; + /* Allow async set_pipe_base calls for flipping */ + struct work_struct async_work; + /* if you are using the helper */ void *helper_private; }; @@ -586,6 +608,7 @@ struct drm_mode_config { extern void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + int pipe, const struct drm_crtc_funcs *funcs); extern void drm_crtc_cleanup(struct drm_crtc *crtc); @@ -733,4 +756,6 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern bool drm_detect_hdmi_monitor(struct edid *edid); +extern int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); #endif /* __DRM_CRTC_H__ */ diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index ec073d8..81fc313 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -56,10 +56,6 @@ struct drm_crtc_helper_funcs { int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); - - /* Move the crtc on the current fb to the given position *optional* */ - int (*mode_set_base)(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb); }; struct drm_encoder_helper_funcs { diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index ae304cc..464b779 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -265,4 +265,20 @@ struct drm_mode_crtc_lut { __u64 blue; }; +#define DRM_MODE_PAGE_FLIP_WAIT (1<<0) /* block on previous page flip */ +#define DRM_MODE_PAGE_FLIP_FLAGS_MASK (DRM_MODE_PAGE_FLIP_WAIT) + +struct drm_mode_page_flip { + /** Handle of new front buffer */ + __u32 fb_id; + __u32 crtc_id; + + /* 64 bit cookie returned to userspace in the page flip event. */ + __u64 user_data; + /** + * page flip flags (wait on flip only for now) + */ + __u32 flags; +}; + #endif ------------------------------------------------------------------------------ Crystal Reports - New Free Runtime and 30 Day Trial Check out the new simplified licensing option that enables unlimited royalty-free distribution of the report engine for externally facing server and web deployment. http://p.sf.net/sfu/businessobjects -- _______________________________________________ Dri-devel mailing list Dri-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dri-devel