linux/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
<<
>>
Prefs
   1/*
   2 * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
   3 *
   4 * Copyright (C) 2011 Texas Instruments
   5 * Author: Rob Clark <rob.clark@linaro.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "omap_drv.h"
  21
  22#include <linux/dma-buf.h>
  23
  24static struct sg_table *omap_gem_map_dma_buf(
  25                struct dma_buf_attachment *attachment,
  26                enum dma_data_direction dir)
  27{
  28        struct drm_gem_object *obj = attachment->dmabuf->priv;
  29        struct sg_table *sg;
  30        dma_addr_t paddr;
  31        int ret;
  32
  33        sg = kzalloc(sizeof(*sg), GFP_KERNEL);
  34        if (!sg)
  35                return ERR_PTR(-ENOMEM);
  36
  37        /* camera, etc, need physically contiguous.. but we need a
  38         * better way to know this..
  39         */
  40        ret = omap_gem_get_paddr(obj, &paddr, true);
  41        if (ret)
  42                goto out;
  43
  44        ret = sg_alloc_table(sg, 1, GFP_KERNEL);
  45        if (ret)
  46                goto out;
  47
  48        sg_init_table(sg->sgl, 1);
  49        sg_dma_len(sg->sgl) = obj->size;
  50        sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(paddr)), obj->size, 0);
  51        sg_dma_address(sg->sgl) = paddr;
  52
  53        /* this should be after _get_paddr() to ensure we have pages attached */
  54        omap_gem_dma_sync(obj, dir);
  55
  56        return sg;
  57out:
  58        kfree(sg);
  59        return ERR_PTR(ret);
  60}
  61
  62static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
  63                struct sg_table *sg, enum dma_data_direction dir)
  64{
  65        struct drm_gem_object *obj = attachment->dmabuf->priv;
  66        omap_gem_put_paddr(obj);
  67        sg_free_table(sg);
  68        kfree(sg);
  69}
  70
  71static void omap_gem_dmabuf_release(struct dma_buf *buffer)
  72{
  73        struct drm_gem_object *obj = buffer->priv;
  74        /* release reference that was taken when dmabuf was exported
  75         * in omap_gem_prime_set()..
  76         */
  77        drm_gem_object_unreference_unlocked(obj);
  78}
  79
  80
  81static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer,
  82                size_t start, size_t len, enum dma_data_direction dir)
  83{
  84        struct drm_gem_object *obj = buffer->priv;
  85        struct page **pages;
  86        if (omap_gem_flags(obj) & OMAP_BO_TILED) {
  87                /* TODO we would need to pin at least part of the buffer to
  88                 * get de-tiled view.  For now just reject it.
  89                 */
  90                return -ENOMEM;
  91        }
  92        /* make sure we have the pages: */
  93        return omap_gem_get_pages(obj, &pages, true);
  94}
  95
  96static void omap_gem_dmabuf_end_cpu_access(struct dma_buf *buffer,
  97                size_t start, size_t len, enum dma_data_direction dir)
  98{
  99        struct drm_gem_object *obj = buffer->priv;
 100        omap_gem_put_pages(obj);
 101}
 102
 103
 104static void *omap_gem_dmabuf_kmap_atomic(struct dma_buf *buffer,
 105                unsigned long page_num)
 106{
 107        struct drm_gem_object *obj = buffer->priv;
 108        struct page **pages;
 109        omap_gem_get_pages(obj, &pages, false);
 110        omap_gem_cpu_sync(obj, page_num);
 111        return kmap_atomic(pages[page_num]);
 112}
 113
 114static void omap_gem_dmabuf_kunmap_atomic(struct dma_buf *buffer,
 115                unsigned long page_num, void *addr)
 116{
 117        kunmap_atomic(addr);
 118}
 119
 120static void *omap_gem_dmabuf_kmap(struct dma_buf *buffer,
 121                unsigned long page_num)
 122{
 123        struct drm_gem_object *obj = buffer->priv;
 124        struct page **pages;
 125        omap_gem_get_pages(obj, &pages, false);
 126        omap_gem_cpu_sync(obj, page_num);
 127        return kmap(pages[page_num]);
 128}
 129
 130static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer,
 131                unsigned long page_num, void *addr)
 132{
 133        struct drm_gem_object *obj = buffer->priv;
 134        struct page **pages;
 135        omap_gem_get_pages(obj, &pages, false);
 136        kunmap(pages[page_num]);
 137}
 138
 139/*
 140 * TODO maybe we can split up drm_gem_mmap to avoid duplicating
 141 * some here.. or at least have a drm_dmabuf_mmap helper.
 142 */
 143static int omap_gem_dmabuf_mmap(struct dma_buf *buffer,
 144                struct vm_area_struct *vma)
 145{
 146        struct drm_gem_object *obj = buffer->priv;
 147        int ret = 0;
 148
 149        if (WARN_ON(!obj->filp))
 150                return -EINVAL;
 151
 152        /* Check for valid size. */
 153        if (omap_gem_mmap_size(obj) < vma->vm_end - vma->vm_start) {
 154                ret = -EINVAL;
 155                goto out_unlock;
 156        }
 157
 158        if (!obj->dev->driver->gem_vm_ops) {
 159                ret = -EINVAL;
 160                goto out_unlock;
 161        }
 162
 163        vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
 164        vma->vm_ops = obj->dev->driver->gem_vm_ops;
 165        vma->vm_private_data = obj;
 166        vma->vm_page_prot =  pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
 167
 168        /* Take a ref for this mapping of the object, so that the fault
 169         * handler can dereference the mmap offset's pointer to the object.
 170         * This reference is cleaned up by the corresponding vm_close
 171         * (which should happen whether the vma was created by this call, or
 172         * by a vm_open due to mremap or partial unmap or whatever).
 173         */
 174        vma->vm_ops->open(vma);
 175
 176out_unlock:
 177
 178        return omap_gem_mmap_obj(obj, vma);
 179}
 180
 181static struct dma_buf_ops omap_dmabuf_ops = {
 182                .map_dma_buf = omap_gem_map_dma_buf,
 183                .unmap_dma_buf = omap_gem_unmap_dma_buf,
 184                .release = omap_gem_dmabuf_release,
 185                .begin_cpu_access = omap_gem_dmabuf_begin_cpu_access,
 186                .end_cpu_access = omap_gem_dmabuf_end_cpu_access,
 187                .kmap_atomic = omap_gem_dmabuf_kmap_atomic,
 188                .kunmap_atomic = omap_gem_dmabuf_kunmap_atomic,
 189                .kmap = omap_gem_dmabuf_kmap,
 190                .kunmap = omap_gem_dmabuf_kunmap,
 191                .mmap = omap_gem_dmabuf_mmap,
 192};
 193
 194struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
 195                struct drm_gem_object *obj, int flags)
 196{
 197        return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags);
 198}
 199
 200struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
 201                struct dma_buf *buffer)
 202{
 203        struct drm_gem_object *obj;
 204
 205        /* is this one of own objects? */
 206        if (buffer->ops == &omap_dmabuf_ops) {
 207                obj = buffer->priv;
 208                /* is it from our device? */
 209                if (obj->dev == dev) {
 210                        /*
 211                         * Importing dmabuf exported from out own gem increases
 212                         * refcount on gem itself instead of f_count of dmabuf.
 213                         */
 214                        drm_gem_object_reference(obj);
 215                        return obj;
 216                }
 217        }
 218
 219        /*
 220         * TODO add support for importing buffers from other devices..
 221         * for now we don't need this but would be nice to add eventually
 222         */
 223        return ERR_PTR(-EINVAL);
 224}
 225