From 4e8354884daa2ee3e491bae69a81f85a2d1ca8ba Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 3 Aug 2009 11:31:53 -0400 Subject: [PATCH] change for general drm code to implement kms-flip feature Signed-off-by: Fei Jiang --- drivers/gpu/drm/drm_crtc.c | 128 ++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 1 + drivers/gpu/drm/drm_irq.c | 30 ++++++++++ include/drm/drm.h | 1 + include/drm/drmP.h | 9 +++ include/drm/drm_crtc.h | 12 ++++ include/drm/drm_mode.h | 16 ++++++ 7 files changed, 197 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8fab789..3ada446 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2452,3 +2452,131 @@ 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) +{ +#if 0 + struct drm_pending_flip *pending; +#endif + + 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; + } +#if 0 + pending = kzalloc(sizeof *pending, GFP_KERNEL); + if (pending == NULL) + return -ENOMEM; +#endif + 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 0 + 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); +#endif + + /* + * 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); +retry_set: + ret = (*crtc->funcs->set_base)(crtc, 0, 0, NULL); + if (ret == -ERESTARTSYS) + goto retry_set; + + if (ret) { + DRM_ERROR("set_base failed: %d\n", ret); + goto out_unlock; + } + + mutex_unlock(&dev->struct_mutex); + + return 0; + +out_unlock: + mutex_unlock(&dev->struct_mutex); +#if 0 + kfree(pending); +#endif + return ret; +} + diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 1ce7977..761c2ec --- 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_irq.c b/drivers/gpu/drm/drm_irq.c index b4a3dbc..d5104df --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -71,6 +71,28 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, return 0; } +#if 0 +static void drm_flip_work_func(struct work_struct *work) +{ + struct drm_device *dev = + container_of(work, struct drm_device, flip_work); +#if 0 + struct drm_pending_flip *f, *t; +#endif + 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); +} +#endif + static void vblank_disable_fn(unsigned long arg) { struct drm_device *dev = (struct drm_device *)arg; @@ -161,6 +183,11 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) atomic_set(&dev->vblank_refcount[i], 0); } +#if 0 + INIT_LIST_HEAD(&dev->flip_list); + INIT_WORK(&dev->flip_work, drm_flip_work_func); +#endif + dev->vblank_disable_allowed = 0; return 0; @@ -626,5 +653,8 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) { atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queue[crtc]); +#if 0 + schedule_work(&dev->flip_work); +#endif } EXPORT_SYMBOL(drm_handle_vblank); diff --git a/include/drm/drm.h b/include/drm/drm.h index 7cb50bd..78bd91b --- 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 diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c5122bf..36f9e6a --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -976,6 +976,15 @@ struct drm_device { cycles_t ctx_start; cycles_t lck_start; + struct work_struct flip_work; + +#if 0 + /** + * List of objects waiting on flip completion + */ + struct list_head flip_list; +#endif + struct fasync_struct *buf_async;/**< Processes waiting for SIGIO */ wait_queue_head_t buf_readers; /**< Processes waiting to read */ wait_queue_head_t buf_writers; /**< Processes waiting to ctx switch */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 7300fb8..742c870 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -331,6 +331,16 @@ 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); }; /** @@ -736,4 +746,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_mode.h b/include/drm/drm_mode.h index ae304cc..464b779 --- 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 -- 1.5.3.4