rk: ion: support cache ops for CMA heap
authorCMY <cmy@rock-chips.com>
Fri, 14 Mar 2014 09:41:32 +0000 (17:41 +0800)
committerCMY <cmy@rock-chips.com>
Fri, 14 Mar 2014 09:42:46 +0000 (17:42 +0800)
arch/arm/include/asm/dma-mapping.h [changed mode: 0644->0755]
drivers/staging/android/ion/ion.c
drivers/staging/android/ion/ion_cma_heap.c
drivers/staging/android/ion/ion_priv.h [changed mode: 0644->0755]
drivers/staging/android/ion/rockchip/rockchip_ion.c
include/linux/rockchip_ion.h [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index 5b579b9..939a00d
@@ -206,6 +206,31 @@ static inline void dma_free_writecombine(struct device *dev, size_t size,
        return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
 }
 
+static inline void *dma_alloc_nonconsistent(struct device *dev, size_t size,
+                                      dma_addr_t *dma_handle, gfp_t flag)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
+       return dma_alloc_attrs(dev, size, dma_handle, flag, &attrs);
+}
+
+static inline void dma_free_nonconsistent(struct device *dev, size_t size,
+                                    void *cpu_addr, dma_addr_t dma_handle)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
+       return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
+}
+
+static inline int dma_mmap_nonconsistent(struct device *dev,
+               struct vm_area_struct *vma, void *cpu_addr,
+               dma_addr_t dma_addr, size_t size)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
+       return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
+}
+
 /*
  * This can be called during early boot to increase the size of the atomic
  * coherent DMA pool above the default value of 256KiB. It must be called
index 2f49b3772086b06067edadcffb3da494dd975e1b..f7c6441f6386add19a2396976d74e334206a83f8 100755 (executable)
@@ -35,6 +35,7 @@
 #include <linux/debugfs.h>
 #include <linux/dma-buf.h>
 #include <linux/idr.h>
+#include <linux/rockchip_ion.h>
 
 #include "ion.h"
 #include "ion_priv.h"
@@ -1387,6 +1388,47 @@ static const struct file_operations ion_fops = {
        .compat_ioctl   = compat_ion_ioctl,
 };
 
+int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
+                       void *uaddr, unsigned long offset, unsigned long len,
+                       unsigned int cmd)
+{
+       struct ion_buffer *buffer;
+       int ret = -EINVAL;
+
+       mutex_lock(&client->lock);
+       if (!ion_handle_validate(client, handle)) {
+               pr_err("%s: invalid handle passed to do_cache_op.\n",
+                      __func__);
+               mutex_unlock(&client->lock);
+               return -EINVAL;
+       }
+       buffer = handle->buffer;
+       mutex_lock(&buffer->lock);
+
+       if (!ION_IS_CACHED(buffer->flags)) {
+               ret = 0;
+               goto out;
+       }
+
+       if (!handle->buffer->heap->ops->cache_op) {
+               pr_err("%s: cache_op is not implemented by this heap.\n",
+                      __func__);
+               ret = -ENODEV;
+               goto out;
+       }
+
+
+       ret = buffer->heap->ops->cache_op(buffer->heap, buffer, uaddr,
+                                               offset, len, cmd);
+
+out:
+       mutex_unlock(&buffer->lock);
+       mutex_unlock(&client->lock);
+       return ret;
+
+}
+EXPORT_SYMBOL(ion_do_cache_op);
+
 static size_t ion_debug_heap_total(struct ion_client *client,
                                   unsigned int id)
 {
index 4418bda7647402a9603ed2025615e8983d6e8594..bd606ca4eb801498c0d094613629faca10526821 100755 (executable)
@@ -20,6 +20,8 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
+#include <linux/rockchip_ion.h>
 
 #include "ion.h"
 #include "ion_priv.h"
@@ -37,6 +39,7 @@ struct ion_cma_buffer_info {
        void *cpu_addr;
        dma_addr_t handle;
        struct sg_table *table;
+       bool is_cached;
 };
 
 /*
@@ -69,8 +72,8 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
 
        dev_dbg(dev, "Request buffer allocation len %ld\n", len);
 
-       if (buffer->flags & ION_FLAG_CACHED)
-               return -EINVAL;
+//     if (buffer->flags & ION_FLAG_CACHED)
+//             return -EINVAL;
 
        if (align > PAGE_SIZE)
                return -EINVAL;
@@ -81,8 +84,13 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
                return ION_CMA_ALLOCATE_FAILED;
        }
 
-       info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle),
-                                               GFP_HIGHUSER | __GFP_ZERO);
+       if (!ION_IS_CACHED(flags)) {
+               info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle),
+                                                       GFP_HIGHUSER | __GFP_ZERO);
+       } else {
+               info->cpu_addr = dma_alloc_nonconsistent(dev, len,
+                                       &(info->handle), GFP_HIGHUSER | __GFP_ZERO/*0*/);
+       }
 
        if (!info->cpu_addr) {
                dev_err(dev, "Fail to allocate buffer\n");
@@ -95,6 +103,8 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
                goto free_mem;
        }
 
+       info->is_cached = ION_IS_CACHED(flags);
+
        if (ion_cma_get_sgtable
            (dev, info->table, info->cpu_addr, info->handle, len))
                goto free_table;
@@ -165,8 +175,13 @@ static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
        struct device *dev = cma_heap->dev;
        struct ion_cma_buffer_info *info = buffer->priv_virt;
 
-       return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle,
-                                buffer->size);
+
+       if (info->is_cached)
+               return dma_mmap_nonconsistent(dev, vma, info->cpu_addr,
+                               info->handle, buffer->size);
+       else
+               return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle,
+                               buffer->size);
 }
 
 static void *ion_cma_map_kernel(struct ion_heap *heap,
@@ -182,6 +197,36 @@ static void ion_cma_unmap_kernel(struct ion_heap *heap,
 {
 }
 
+int ion_cma_cache_ops(struct ion_heap *heap,
+                       struct ion_buffer *buffer, void *vaddr,
+                       unsigned int offset, unsigned int length,
+                       unsigned int cmd)
+{
+       struct ion_cma_buffer_info *info = buffer->priv_virt;
+       void (*outer_cache_op)(phys_addr_t, phys_addr_t);
+
+       switch (cmd) {
+       case ION_IOC_CLEAN_CACHES:
+               dmac_map_area(vaddr, length, DMA_TO_DEVICE);
+               outer_cache_op = outer_clean_range;
+               break;
+       case ION_IOC_INV_CACHES:
+               dmac_unmap_area(vaddr, length, DMA_FROM_DEVICE);
+               outer_cache_op = outer_inv_range;
+               break;
+       case ION_IOC_CLEAN_INV_CACHES:
+               dmac_flush_range(vaddr, vaddr + length);
+               outer_cache_op = outer_flush_range;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       outer_cache_op(info->handle, info->handle + length);
+
+       return 0;
+}
+
 static struct ion_heap_ops ion_cma_ops = {
        .allocate = ion_cma_allocate,
        .free = ion_cma_free,
@@ -191,6 +236,7 @@ static struct ion_heap_ops ion_cma_ops = {
        .map_user = ion_cma_mmap,
        .map_kernel = ion_cma_map_kernel,
        .unmap_kernel = ion_cma_unmap_kernel,
+       .cache_op = ion_cma_cache_ops,
 };
 
 struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)
old mode 100644 (file)
new mode 100755 (executable)
index 9bcd077..696aca0
@@ -122,6 +122,9 @@ struct ion_heap_ops {
        int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer,
                         struct vm_area_struct *vma);
        int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan);
+       int (*cache_op)(struct ion_heap *heap, struct ion_buffer *buffer,
+                       void *vaddr, unsigned int offset,
+                       unsigned int length, unsigned int cmd);
 };
 
 /**
index 48c76eef0e8772f5bc9f0a210eafadcaf70175b0..5673ad315b9724f1a613ab438894800e1376e34a 100755 (executable)
@@ -44,6 +44,10 @@ extern struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
                                                int id);
 extern int ion_handle_put(struct ion_handle *handle);
 
+extern int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
+                       void *uaddr, unsigned long offset, unsigned long len,
+                       unsigned int cmd);
+
 static struct ion_heap_desc ion_heap_meta[] = {
        {
                .id             = ION_SYSTEM_HEAP_ID,
@@ -219,6 +223,31 @@ struct ion_client *rockchip_ion_client_create(const char *name)
 }
 EXPORT_SYMBOL(rockchip_ion_client_create);
 
+static int check_vaddr_bounds(unsigned long start, unsigned long end)
+{
+       struct mm_struct *mm = current->active_mm;
+       struct vm_area_struct *vma;
+       int ret = 1;
+
+       if (end < start)
+               goto out;
+
+       down_read(&mm->mmap_sem);
+       vma = find_vma(mm, start);
+       if (vma && vma->vm_start < end) {
+               if (start < vma->vm_start)
+                       goto out_up;
+               if (end > vma->vm_end)
+                       goto out_up;
+               ret = 0;
+       }
+
+out_up:
+       up_read(&mm->mmap_sem);
+out:
+       return ret;
+}
+
 static long rockchip_custom_ioctl (struct ion_client *client, unsigned int cmd,
                              unsigned long arg)
 {
@@ -228,7 +257,50 @@ static long rockchip_custom_ioctl (struct ion_client *client, unsigned int cmd,
        case ION_IOC_CLEAN_CACHES:
        case ION_IOC_INV_CACHES:
        case ION_IOC_CLEAN_INV_CACHES:
+       {
+               struct ion_flush_data data;
+               unsigned long start, end;
+               struct ion_handle *handle = NULL;
+               int ret;
+               int is_import_fd = 0;
+
+               if (copy_from_user(&data, (void __user *)arg,
+                                       sizeof(struct ion_flush_data)))
+                       return -EFAULT;
+
+               start = (unsigned long) data.vaddr;
+               end = (unsigned long) data.vaddr + data.length;
+
+               if (check_vaddr_bounds(start, end)) {
+                       pr_err("%s: virtual address %p is out of bounds\n",
+                               __func__, data.vaddr);
+                       return -EINVAL;
+               }
+
+               handle = ion_handle_get_by_id(client, data.handle);
+
+               if (IS_ERR(handle)) {
+                       handle = ion_import_dma_buf(client, data.fd);
+                       is_import_fd = 1;
+                       if (IS_ERR(handle)) {
+                               pr_info("%s: Could not import handle: %d\n",
+                                       __func__, (int)handle);
+                               return -EINVAL;
+                       }
+               }
+
+               ret = ion_do_cache_op(client, handle,
+                               data.vaddr, data.offset, data.length,cmd);
+
+               if (is_import_fd)
+                       ion_free(client, handle);
+               else
+                       ion_handle_put(handle);
+
+               if (ret < 0)
+                       return ret;
                break;
+       }
        case ION_IOC_GET_PHYS:
        {
                struct ion_phys_data data;
old mode 100644 (file)
new mode 100755 (executable)