From c3a91a5262c0aa8a01573d32daeec054b2820e31 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Tue, 17 Sep 2013 21:01:47 +0900 Subject: [PATCH] drm/exynos: add dmabuf sync support for kms framework Change-Id: I5937869e5fa2ec2a41e864494eac0077ccdbdb51 Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 129 ++++++++++++++++++++++++++++-- drivers/gpu/drm/exynos/exynos_drm_fb.c | 73 +++++++++++++---- drivers/gpu/drm/exynos/exynos_drm_fb.h | 24 ++++++ drivers/gpu/drm/exynos/exynos_drm_plane.c | 13 +++ 4 files changed, 218 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 311a36e..4aee344 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -19,6 +19,10 @@ #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" #include "exynos_drm_plane.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_gem.h" + +#include #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ drm_crtc) @@ -51,6 +55,18 @@ struct exynos_drm_crtc { enum exynos_crtc_mode mode; wait_queue_head_t pending_flip_queue; atomic_t pending_flip; + struct list_head sync_committed; +}; + +static void exynos_drm_dmabuf_sync_free(void *priv) +{ + struct drm_pending_vblank_event *event = priv; + + event->event.reserved = 0; +} + +static struct dmabuf_sync_priv_ops dmabuf_sync_ops = { + .free = exynos_drm_dmabuf_sync_free, }; static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) @@ -105,6 +121,7 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct drm_plane *plane = exynos_crtc->plane; + struct dmabuf_sync *sync; unsigned int crtc_w; unsigned int crtc_h; int pipe = exynos_crtc->pipe; @@ -129,6 +146,15 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); + if (!dmabuf_sync_is_supported()) + return 0; + + sync = (struct dmabuf_sync *)exynos_drm_dmabuf_sync_work(crtc->fb); + if (IS_ERR(sync)) { + /* just ignore buffer synchronization this time. */ + return 0; + } + return 0; } @@ -169,7 +195,22 @@ static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { - return exynos_drm_crtc_mode_set_commit(crtc, x, y, old_fb); + struct dmabuf_sync *sync; + int ret; + + ret = exynos_drm_crtc_mode_set_commit(crtc, x, y, old_fb); + if (ret < 0) + return ret; + + if (!dmabuf_sync_is_supported()) + return 0; + + sync = (struct dmabuf_sync *)exynos_drm_dmabuf_sync_work(crtc->fb); + if (IS_ERR(sync)) + /* just ignore buffer synchronization this time. */ + return 0; + + return 0; } static void exynos_drm_crtc_disable(struct drm_crtc *crtc) @@ -207,9 +248,14 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, return -EINVAL; } - mutex_lock(&dev->struct_mutex); - if (event) { + struct exynos_drm_fb *exynos_fb; + struct drm_gem_object *obj; + struct dmabuf_sync *sync; + unsigned int i; + + mutex_lock(&dev->struct_mutex); + /* * the pipe from user always is 0 so we can set pipe number * of current owner to event. @@ -219,10 +265,53 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, ret = drm_vblank_get(dev, exynos_crtc->pipe); if (ret) { DRM_DEBUG("failed to acquire vblank counter\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } + + if (!dmabuf_sync_is_supported()) + goto out_fence; - goto out; + sync = dmabuf_sync_init("DRM", &dmabuf_sync_ops, event); + if (IS_ERR(sync)) { + WARN_ON(1); + goto out_fence; } + exynos_fb = to_exynos_fb(fb); + + for (i = 0; i < exynos_fb->buf_cnt; i++) { + if (!exynos_fb->exynos_gem_obj[i]) { + WARN_ON(1); + continue; + } + + obj = &exynos_fb->exynos_gem_obj[i]->base; + if (!obj->export_dma_buf) + continue; + + /* + * set dmabuf to fence and registers reservation + * object to reservation entry. + */ + ret = dmabuf_sync_get(sync, + obj->export_dma_buf, + DMA_BUF_ACCESS_DMA_R); + if (WARN_ON(ret < 0)) + continue; + } + + ret = dmabuf_sync_lock(sync); + if (ret < 0) { + dmabuf_sync_put_all(sync); + dmabuf_sync_fini(sync); + goto out_fence; + } + + event->event.reserved = (unsigned long)sync; + +out_fence: + spin_lock_irq(&dev->event_lock); list_add_tail(&event->base.link, &dev_priv->pageflip_event_list); @@ -240,11 +329,13 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, list_del(&event->base.link); spin_unlock_irq(&dev->event_lock); - goto out; + mutex_unlock(&dev->struct_mutex); + return ret; } + + mutex_unlock(&dev->struct_mutex); } -out: - mutex_unlock(&dev->struct_mutex); + return ret; } @@ -344,6 +435,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) return -ENOMEM; } + INIT_LIST_HEAD(&exynos_crtc->sync_committed); + crtc = &exynos_crtc->drm_crtc; private->crtc[nr] = crtc; @@ -392,6 +485,19 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc); unsigned long flags; + if (!list_empty(&exynos_crtc->sync_committed) && + dmabuf_sync_is_supported()) { + struct dmabuf_sync *sync; + + sync = list_first_entry(&exynos_crtc->sync_committed, + struct dmabuf_sync, list); + if (!dmabuf_sync_unlock(sync)) { + list_del_init(&sync->list); + dmabuf_sync_put_all(sync); + dmabuf_sync_fini(sync); + } + } + spin_lock_irqsave(&dev->event_lock, flags); list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, @@ -400,6 +506,15 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) if (crtc != e->pipe) continue; + if (e->event.reserved && dmabuf_sync_is_supported()) { + struct dmabuf_sync *sync; + + sync = (struct dmabuf_sync *)e->event.reserved; + e->event.reserved = 0; + list_add_tail(&sync->list, + &exynos_crtc->sync_committed); + } + list_del(&e->base.link); drm_send_vblank_event(dev, -1, e); drm_vblank_put(dev, crtc); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index ea39e0e..5530059 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -24,20 +24,7 @@ #include "exynos_drm_iommu.h" #include "exynos_drm_encoder.h" -#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) - -/* - * exynos specific framebuffer structure. - * - * @fb: drm framebuffer obejct. - * @buf_cnt: a buffer count to drm framebuffer. - * @exynos_gem_obj: array of exynos specific gem object containing a gem object. - */ -struct exynos_drm_fb { - struct drm_framebuffer fb; - unsigned int buf_cnt; - struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; -}; +#include static int check_fb_gem_memory_type(struct drm_device *drm_dev, struct exynos_drm_gem_obj *exynos_gem_obj) @@ -171,6 +158,64 @@ exynos_drm_framebuffer_init(struct drm_device *dev, return &exynos_fb->fb; } +#ifdef CONFIG_DMABUF_SYNC +void *exynos_drm_dmabuf_sync_work(struct drm_framebuffer *fb) +{ + struct exynos_drm_fb *exynos_fb; + struct drm_gem_object *obj; + struct dmabuf_sync *sync; + unsigned int i; + int ret = 0; + + sync = dmabuf_sync_init("DRM", NULL, NULL); + if (IS_ERR(sync)) { + WARN_ON(1); + goto out_dmabuf_sync; + } + + exynos_fb = to_exynos_fb(fb); + + for (i = 0; i < exynos_fb->buf_cnt; i++) { + if (!exynos_fb->exynos_gem_obj[i]) { + WARN_ON(1); + continue; + } + + obj = &exynos_fb->exynos_gem_obj[i]->base; + if (!obj->export_dma_buf) + continue; + + /* + * set dmabuf to fence and registers reservation + * object to reservation entry. + */ + ret = dmabuf_sync_get(sync, + obj->export_dma_buf, + DMA_BUF_ACCESS_DMA_R); + if (WARN_ON(ret < 0)) + continue; + + } + + ret = dmabuf_sync_lock(sync); + if (ret < 0) { + dmabuf_sync_put_all(sync); + dmabuf_sync_fini(sync); + sync = ERR_PTR(ret); + goto out_dmabuf_sync; + } + + /* buffer synchronization is done by wait_for_vblank so just signal. */ + dmabuf_sync_unlock(sync); + + dmabuf_sync_put_all(sync); + dmabuf_sync_fini(sync); + +out_dmabuf_sync: + return sync; +} +#endif + static u32 exynos_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd) { unsigned int cnt = 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h index 517471b..dbedbfc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h @@ -14,6 +14,21 @@ #ifndef _EXYNOS_DRM_FB_H_ #define _EXYNOS_DRM_FB_H +#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) + +/* + * exynos specific framebuffer structure. + * + * @fb: drm framebuffer obejct. + * @buf_cnt: a buffer count to drm framebuffer. + * @exynos_gem_obj: array of exynos specific gem object containing a gem object. + */ +struct exynos_drm_fb { + struct drm_framebuffer fb; + unsigned int buf_cnt; + struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; +}; + struct drm_framebuffer * exynos_drm_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, @@ -32,4 +47,13 @@ void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb, /* get a buffer count to drm framebuffer. */ unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb); +#ifdef CONFIG_DMABUF_SYNC +void *exynos_drm_dmabuf_sync_work(struct drm_framebuffer *fb); +#else +static inline void *exynos_drm_dmabuf_sync_work(struct drm_framebuffer *fb) +{ + return ERR_PTR(0); +} +#endif + #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index fcb0652..c9a034f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -18,6 +18,8 @@ #include "exynos_drm_gem.h" #include "exynos_drm_plane.h" +#include + #define to_exynos_plane(x) container_of(x, struct exynos_plane, base) struct exynos_plane { @@ -184,6 +186,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { + struct dmabuf_sync *sync; int ret; ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, @@ -197,6 +200,16 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, exynos_plane_commit(plane); exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); + if (!dmabuf_sync_is_supported()) + return 0; + + sync = (struct dmabuf_sync *)exynos_drm_dmabuf_sync_work(fb); + if (IS_ERR(sync)) { + WARN_ON(1); + /* just ignore buffer synchronization this time. */ + return 0; + } + return 0; } -- 2.7.4