diff options
Diffstat (limited to 'drivers/gpu/drm/msm/msm_gem.c')
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.c | 160 |
1 files changed, 121 insertions, 39 deletions
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 2bae46c66a3..e587d251c59 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -17,6 +17,7 @@ #include <linux/spinlock.h> #include <linux/shmem_fs.h> +#include <linux/dma-buf.h> #include "msm_drv.h" #include "msm_gem.h" @@ -77,6 +78,21 @@ static void put_pages(struct drm_gem_object *obj) } } +struct page **msm_gem_get_pages(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct page **p; + mutex_lock(&dev->struct_mutex); + p = get_pages(obj); + mutex_unlock(&dev->struct_mutex); + return p; +} + +void msm_gem_put_pages(struct drm_gem_object *obj) +{ + /* when we start tracking the pin count, then do something here */ +} + int msm_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma) { @@ -162,6 +178,11 @@ out: case 0: case -ERESTARTSYS: case -EINTR: + case -EBUSY: + /* + * EBUSY is ok: this just means that another thread + * already did the job. + */ return VM_FAULT_NOPAGE; case -ENOMEM: return VM_FAULT_OOM; @@ -293,7 +314,17 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id, int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova) { + struct msm_gem_object *msm_obj = to_msm_bo(obj); int ret; + + /* this is safe right now because we don't unmap until the + * bo is deleted: + */ + if (msm_obj->domain[id].iova) { + *iova = msm_obj->domain[id].iova; + return 0; + } + mutex_lock(&obj->dev->struct_mutex); ret = msm_gem_get_iova_locked(obj, id, iova); mutex_unlock(&obj->dev->struct_mutex); @@ -363,8 +394,11 @@ void *msm_gem_vaddr(struct drm_gem_object *obj) return ret; } -int msm_gem_queue_inactive_work(struct drm_gem_object *obj, - struct work_struct *work) +/* setup callback for when bo is no longer busy.. + * TODO probably want to differentiate read vs write.. + */ +int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, + struct msm_fence_cb *cb) { struct drm_device *dev = obj->dev; struct msm_drm_private *priv = dev->dev_private; @@ -372,12 +406,13 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj, int ret = 0; mutex_lock(&dev->struct_mutex); - if (!list_empty(&work->entry)) { + if (!list_empty(&cb->work.entry)) { ret = -EINVAL; } else if (is_active(msm_obj)) { - list_add_tail(&work->entry, &msm_obj->inactive_work); + cb->fence = max(msm_obj->read_fence, msm_obj->write_fence); + list_add_tail(&cb->work.entry, &priv->fence_cbs); } else { - queue_work(priv->wq, work); + queue_work(priv->wq, &cb->work); } mutex_unlock(&dev->struct_mutex); @@ -410,16 +445,6 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj) msm_obj->write_fence = 0; list_del_init(&msm_obj->mm_list); list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - - while (!list_empty(&msm_obj->inactive_work)) { - struct work_struct *work; - - work = list_first_entry(&msm_obj->inactive_work, - struct work_struct, entry); - - list_del_init(&work->entry); - queue_work(priv->wq, work); - } } int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, @@ -510,10 +535,21 @@ void msm_gem_free_object(struct drm_gem_object *obj) drm_gem_free_mmap_offset(obj); - if (msm_obj->vaddr) - vunmap(msm_obj->vaddr); + if (obj->import_attach) { + if (msm_obj->vaddr) + dma_buf_vunmap(obj->import_attach->dmabuf, msm_obj->vaddr); - put_pages(obj); + /* Don't drop the pages for imported dmabuf, as they are not + * ours, just free the array we allocated: + */ + if (msm_obj->pages) + drm_free_large(msm_obj->pages); + + } else { + if (msm_obj->vaddr) + vunmap(msm_obj->vaddr); + put_pages(obj); + } if (msm_obj->resv == &msm_obj->_resv) reservation_object_fini(msm_obj->resv); @@ -549,17 +585,12 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, return ret; } -struct drm_gem_object *msm_gem_new(struct drm_device *dev, - uint32_t size, uint32_t flags) +static int msm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags, + struct drm_gem_object **obj) { struct msm_drm_private *priv = dev->dev_private; struct msm_gem_object *msm_obj; - struct drm_gem_object *obj = NULL; - int ret; - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - size = PAGE_ALIGN(size); switch (flags & MSM_BO_CACHE_MASK) { case MSM_BO_UNCACHED: @@ -569,21 +600,12 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, default: dev_err(dev->dev, "invalid cache flag: %x\n", (flags & MSM_BO_CACHE_MASK)); - ret = -EINVAL; - goto fail; + return -EINVAL; } msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); - if (!msm_obj) { - ret = -ENOMEM; - goto fail; - } - - obj = &msm_obj->base; - - ret = drm_gem_object_init(dev, obj, size); - if (ret) - goto fail; + if (!msm_obj) + return -ENOMEM; msm_obj->flags = flags; @@ -591,9 +613,69 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, reservation_object_init(msm_obj->resv); INIT_LIST_HEAD(&msm_obj->submit_entry); - INIT_LIST_HEAD(&msm_obj->inactive_work); list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + *obj = &msm_obj->base; + + return 0; +} + +struct drm_gem_object *msm_gem_new(struct drm_device *dev, + uint32_t size, uint32_t flags) +{ + struct drm_gem_object *obj; + int ret; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + size = PAGE_ALIGN(size); + + ret = msm_gem_new_impl(dev, size, flags, &obj); + if (ret) + goto fail; + + ret = drm_gem_object_init(dev, obj, size); + if (ret) + goto fail; + + return obj; + +fail: + if (obj) + drm_gem_object_unreference_unlocked(obj); + + return ERR_PTR(ret); +} + +struct drm_gem_object *msm_gem_import(struct drm_device *dev, + uint32_t size, struct sg_table *sgt) +{ + struct msm_gem_object *msm_obj; + struct drm_gem_object *obj; + int ret, npages; + + size = PAGE_ALIGN(size); + + ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); + if (ret) + goto fail; + + drm_gem_private_object_init(dev, obj, size); + + npages = size / PAGE_SIZE; + + msm_obj = to_msm_bo(obj); + msm_obj->sgt = sgt; + msm_obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); + if (!msm_obj->pages) { + ret = -ENOMEM; + goto fail; + } + + ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages); + if (ret) + goto fail; + return obj; fail: |