summaryrefslogtreecommitdiff
path: root/drivers/gpu/ion/sprd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/ion/sprd')
-rw-r--r--drivers/gpu/ion/sprd/Makefile5
-rw-r--r--drivers/gpu/ion/sprd/compat_sprd_ion.c336
-rw-r--r--drivers/gpu/ion/sprd/compat_sprd_ion.h30
-rw-r--r--drivers/gpu/ion/sprd/sprd_fence.c469
-rw-r--r--drivers/gpu/ion/sprd/sprd_fence.h52
-rw-r--r--drivers/gpu/ion/sprd/sprd_ion.c1093
-rw-r--r--drivers/gpu/ion/sprd/sprd_ion_cma_heap.c279
7 files changed, 2264 insertions, 0 deletions
diff --git a/drivers/gpu/ion/sprd/Makefile b/drivers/gpu/ion/sprd/Makefile
new file mode 100644
index 00000000..8de25d86
--- /dev/null
+++ b/drivers/gpu/ion/sprd/Makefile
@@ -0,0 +1,5 @@
+obj-y += sprd_ion.o sprd_ion_cma_heap.o sprd_fence.o
+
+ifdef CONFIG_COMPAT
+obj-$(CONFIG_ION) += compat_sprd_ion.o
+endif
diff --git a/drivers/gpu/ion/sprd/compat_sprd_ion.c b/drivers/gpu/ion/sprd/compat_sprd_ion.c
new file mode 100644
index 00000000..37141596
--- /dev/null
+++ b/drivers/gpu/ion/sprd/compat_sprd_ion.c
@@ -0,0 +1,336 @@
+/*
+ * drivers/gpu/ion/compat_ion.c
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+#include <linux/ion.h>
+#include <video/ion_sprd.h>
+#include "sprd_fence.h"
+#include "compat_sprd_ion.h"
+
+/* See kernel/include/video/ion_sprd.h for the definition of these structs */
+struct compat_ion_phys_data {
+ compat_int_t fd_buffer;
+ compat_ulong_t phys;
+ compat_size_t size;
+};
+
+struct compat_ion_msync_data {
+ compat_int_t fd_buffer;
+ compat_uptr_t vaddr;
+ compat_uptr_t paddr;
+ compat_size_t size;
+};
+
+struct compat_ion_mmu_data {
+ compat_int_t master_id;
+ compat_int_t fd_buffer;
+ compat_ulong_t iova_addr;
+ compat_size_t iova_size;
+};
+
+static int compat_get_ion_phys_data(
+ struct compat_ion_phys_data __user *data32,
+ struct ion_phys_data __user *data)
+{
+ compat_int_t i;
+ int err;
+
+ err = get_user(i, &data32->fd_buffer);
+ err |= put_user(i, &data->fd_buffer);
+
+ return err;
+};
+
+static int compat_put_ion_phys_data(
+ struct compat_ion_phys_data __user *data32,
+ struct ion_phys_data __user *data)
+{
+ compat_ulong_t ul;
+ compat_size_t s;
+ int err;
+
+ err = get_user(ul, &data->phys);
+ err |= put_user(ul, &data32->phys);
+ err |= get_user(s, &data->size);
+ err |= put_user(s, &data32->size);
+
+ return err;
+};
+
+static int compat_get_ion_msync_data(
+ struct compat_ion_msync_data __user *data32,
+ struct ion_msync_data __user *data)
+{
+ compat_int_t i;
+ compat_uptr_t up;
+ compat_size_t s;
+ int err;
+
+ err = get_user(i, &data32->fd_buffer);
+ err |= put_user(i, &data->fd_buffer);
+ err |= get_user(up, &data32->vaddr);
+ err |= put_user(up, &data->vaddr);
+ err |= get_user(up, &data32->paddr);
+ err |= put_user(up, &data->paddr);
+ err |= get_user(s, &data32->size);
+ err |= put_user(s, &data->size);
+
+ return err;
+};
+
+static int compat_get_ion_mmu_data(
+ struct compat_ion_mmu_data __user *data32,
+ struct ion_mmu_data __user *data)
+{
+ compat_int_t i;
+ compat_ulong_t ul;
+ compat_size_t s;
+ int err;
+
+ err = get_user(i, &data32->master_id);
+ err |= put_user(i, &data->master_id);
+ err |= get_user(i, &data32->fd_buffer);
+ err |= put_user(i, &data->fd_buffer);
+ err |= get_user(ul, &data32->iova_addr);
+ err |= put_user(ul, &data->iova_addr);
+ err |= get_user(s, &data32->iova_size);
+ err |= put_user(s, &data->iova_size);
+
+ return err;
+};
+
+static int compat_put_ion_mmu_data(
+ struct compat_ion_mmu_data __user *data32,
+ struct ion_mmu_data __user *data)
+{
+ compat_int_t i;
+ compat_ulong_t ul;
+ compat_size_t s;
+ int err;
+
+ err = get_user(i, &data->master_id);
+ err |= put_user(i, &data32->master_id);
+ err |= get_user(i, &data->fd_buffer);
+ err |= put_user(i, &data32->fd_buffer);
+ err |= get_user(ul, &data->iova_addr);
+ err |= put_user(ul, &data32->iova_addr);
+ err |= get_user(s, &data->iova_size);
+ err |= put_user(s, &data32->iova_size);
+
+ return err;
+};
+
+long compat_sprd_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+#if 0
+ struct ion_client *client = filp->private_data;
+ struct ion_device *dev = client->dev;
+
+ if (!filp->f_op || !dev->custom_ioctl)
+ return -ENOTTY;
+#endif
+ long ret;
+
+ pr_debug("%s, cmd: %u", __FUNCTION__, cmd);
+ switch (cmd) {
+ case ION_SPRD_CUSTOM_PHYS:
+ {
+ struct compat_ion_phys_data __user *data32;
+ struct ion_phys_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_phys_data(data32, data);
+ if (err)
+ return err;
+#if 0
+ ret = dev->custom_ioctl(client, ION_SPRD_CUSTOM_PHYS,
+ (unsigned long)data);
+#else
+ ret = sprd_ion_ioctl(filp, ION_SPRD_CUSTOM_PHYS, (unsigned long)data);
+#endif
+ err = compat_put_ion_phys_data(data32, data);
+ return ret ? ret : err;
+ }
+ case ION_SPRD_CUSTOM_MSYNC:
+ {
+ struct compat_ion_msync_data __user *data32;
+ struct ion_msync_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_msync_data(data32, data);
+ if (err)
+ return err;
+#if 0
+ ret = dev->custom_ioctl(client, ION_SPRD_CUSTOM_MSYNC,
+ (unsigned long)data);
+#else
+ ret = sprd_ion_ioctl(filp, ION_SPRD_CUSTOM_MSYNC, (unsigned long)data);
+#endif
+ return ret;
+ }
+#if defined(CONFIG_SPRD_IOMMU)
+ case ION_SPRD_CUSTOM_GSP_MAP:
+ {
+ struct compat_ion_mmu_data __user *data32;
+ struct ion_mmu_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_mmu_data(data32, data);
+ if (err)
+ return err;
+#if 0
+ ret = dev->custom_ioctl(client, ION_SPRD_CUSTOM_GSP_MAP,
+ (unsigned long)data);
+#else
+ ret = sprd_ion_ioctl(filp, ION_SPRD_CUSTOM_GSP_MAP, (unsigned long)data);
+#endif
+ err = compat_put_ion_mmu_data(data32, data);
+ return ret ? ret : err;
+ }
+ case ION_SPRD_CUSTOM_GSP_UNMAP:
+ {
+ struct compat_ion_mmu_data __user *data32;
+ struct ion_mmu_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_mmu_data(data32, data);
+ if (err)
+ return err;
+#if 0
+ ret = dev->custom_ioctl(client, ION_SPRD_CUSTOM_GSP_UNMAP,
+ (unsigned long)data);
+#else
+ ret = sprd_ion_ioctl(filp, ION_SPRD_CUSTOM_GSP_UNMAP, (unsigned long)data);
+#endif
+ err = compat_put_ion_mmu_data(data32, data);
+ return ret ? ret : err;
+ }
+ case ION_SPRD_CUSTOM_MM_MAP:
+ {
+ struct compat_ion_mmu_data __user *data32;
+ struct ion_mmu_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_mmu_data(data32, data);
+ if (err)
+ return err;
+#if 0
+ ret = dev->custom_ioctl(client, ION_SPRD_CUSTOM_MM_MAP,
+ (unsigned long)data);
+#else
+ ret = sprd_ion_ioctl(filp, ION_SPRD_CUSTOM_MM_MAP, (unsigned long)data);
+#endif
+ err = compat_put_ion_mmu_data(data32, data);
+ return ret ? ret : err;
+ }
+ case ION_SPRD_CUSTOM_MM_UNMAP:
+ {
+ struct compat_ion_mmu_data __user *data32;
+ struct ion_mmu_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_mmu_data(data32, data);
+ if (err)
+ return err;
+#if 0
+ ret = dev->custom_ioctl(client, ION_SPRD_CUSTOM_MM_UNMAP,
+ (unsigned long)data);
+#else
+ ret = sprd_ion_ioctl(filp, ION_SPRD_CUSTOM_MM_UNMAP, (unsigned long)data);
+#endif
+ err = compat_put_ion_mmu_data(data32, data);
+ return ret ? ret : err;
+ }
+#endif
+ case ION_SPRD_CUSTOM_FENCE_CREATE:
+ {
+ int ret = -1;
+ struct ion_fence_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ pr_err("FENCE_CREATE user data is err\n");
+ return -EFAULT;
+ }
+
+ ret = sprd_fence_build(&data);
+ if (ret != 0) {
+ pr_err("sprd_fence_build failed\n");
+ return -EFAULT;
+ }
+
+ if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+ sprd_fence_destroy(&data);
+ pr_err("copy_to_user fence failed\n");
+ return -EFAULT;
+ }
+
+ break;
+ }
+ case ION_SPRD_CUSTOM_FENCE_SIGNAL:
+ {
+ struct ion_fence_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ pr_err("FENCE_CREATE user data is err\n");
+ return -EFAULT;
+ }
+
+ sprd_fence_signal(&data);
+
+ break;
+ }
+ case ION_SPRD_CUSTOM_FENCE_DUP:
+ {
+ break;
+
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
diff --git a/drivers/gpu/ion/sprd/compat_sprd_ion.h b/drivers/gpu/ion/sprd/compat_sprd_ion.h
new file mode 100644
index 00000000..a7dc3857
--- /dev/null
+++ b/drivers/gpu/ion/sprd/compat_sprd_ion.h
@@ -0,0 +1,30 @@
+/*
+
+ * drivers/gpu/ion/compat_ion.h
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_COMPAT_SPRD_ION_H
+#define _LINUX_COMPAT_SPRD_ION_H
+
+#if IS_ENABLED(CONFIG_COMPAT)
+
+long compat_sprd_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
+#else
+
+#define compat_sprd_ion_ioctl NULL
+
+#endif /* CONFIG_COMPAT */
+#endif /* _LINUX_COMPAT_SPRD_ION_H */
diff --git a/drivers/gpu/ion/sprd/sprd_fence.c b/drivers/gpu/ion/sprd/sprd_fence.c
new file mode 100644
index 00000000..0baafe89
--- /dev/null
+++ b/drivers/gpu/ion/sprd/sprd_fence.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sprd_fence.h"
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+
+#define WAIT_FENCE_TIMEOUT 200
+
+/*
+ * Porting from software sync
+ * (sw_sync.h sw_sync.c)
+ * */
+static int sprd_sync_cmp(u32 a, u32 b)
+{
+ if (a == b)
+ return 0;
+
+ return ((s32)a - (s32)b) < 0 ? -1 : 1;
+}
+
+struct sync_pt *sprd_sync_pt_create(struct sprd_sync_timeline *obj, u32 value)
+{
+ struct sprd_sync_pt *pt;
+
+ pt = (struct sprd_sync_pt *)
+ sync_pt_create(&obj->obj, sizeof(struct sprd_sync_pt));
+
+ pt->value = value;
+
+ return (struct sync_pt *)pt;
+}
+
+static struct sync_pt *sprd_sync_pt_dup(struct sync_pt *sync_pt)
+{
+ struct sprd_sync_pt *pt = (struct sprd_sync_pt *) sync_pt;
+ struct sprd_sync_timeline *obj =
+ (struct sprd_sync_timeline *)sync_pt->parent;
+
+ return (struct sync_pt *) sprd_sync_pt_create(obj, pt->value);
+}
+
+static int sprd_sync_pt_has_signaled(struct sync_pt *sync_pt)
+{
+ struct sprd_sync_pt *pt = (struct sprd_sync_pt *)sync_pt;
+ struct sprd_sync_timeline *obj =
+ (struct sprd_sync_timeline *)sync_pt->parent;
+
+ return sprd_sync_cmp(obj->value, pt->value) >= 0;
+}
+
+static int sprd_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
+{
+ struct sprd_sync_pt *pt_a = (struct sprd_sync_pt *)a;
+ struct sprd_sync_pt *pt_b = (struct sprd_sync_pt *)b;
+
+ return sprd_sync_cmp(pt_a->value, pt_b->value);
+}
+
+
+static int sprd_sync_fill_driver_data(struct sync_pt *sync_pt,
+ void *data, int size)
+{
+ struct sprd_sync_pt *pt = (struct sprd_sync_pt *)sync_pt;
+
+ if (size < sizeof(pt->value))
+ return -ENOMEM;
+
+ memcpy(data, &pt->value, sizeof(pt->value));
+
+ return sizeof(pt->value);
+}
+
+static void sprd_sync_timeline_value_str(struct sync_timeline *sync_timeline,
+ char *str, int size)
+{
+ struct sprd_sync_timeline *timeline =
+ (struct sprd_sync_timeline *)sync_timeline;
+ snprintf(str, size, "%d", timeline->value);
+}
+
+static void sprd_sync_pt_value_str(struct sync_pt *sync_pt,
+ char *str, int size)
+{
+ struct sprd_sync_pt *pt = (struct sprd_sync_pt *)sync_pt;
+ snprintf(str, size, "%d", pt->value);
+}
+
+static struct sync_timeline_ops sprd_sync_timeline_ops = {
+ .driver_name = "sprd_sync",
+ .dup = sprd_sync_pt_dup,
+ .has_signaled = sprd_sync_pt_has_signaled,
+ .compare = sprd_sync_pt_compare,
+ .fill_driver_data = sprd_sync_fill_driver_data,
+ .timeline_value_str = sprd_sync_timeline_value_str,
+ .pt_value_str = sprd_sync_pt_value_str,
+};
+
+struct sprd_sync_timeline *sprd_sync_timeline_create(const char *name)
+{
+ struct sprd_sync_timeline *obj = (struct sprd_sync_timeline *)
+ sync_timeline_create(&sprd_sync_timeline_ops,
+ sizeof(struct sprd_sync_timeline),
+ name);
+
+ return obj;
+}
+
+void sprd_sync_timeline_inc(struct sprd_sync_timeline *obj, u32 inc)
+{
+ obj->value += inc;
+
+ sync_timeline_signal(&obj->obj);
+}
+
+/*
+ * Porting software sync done
+ * */
+
+/*
+ * user interface
+ * */
+
+/*
+ * Now build two timeline,
+ * one for PrimaryDisplayDevice,
+ * the other for VirtualDisplayDevice.
+ * If we only use single timeline, the later Device will
+ * block the former Device.
+ * */
+static struct sync_timeline_data sprd_timeline;
+static struct sync_timeline_data sprd_timeline_virtual;
+int sprd_create_timeline(enum SPRD_DEVICE_SYNC_TYPE type)
+{
+ if (type == SPRD_DEVICE_PRIMARY_SYNC)
+ {
+ mutex_lock(&(sprd_timeline.sync_mutex));
+ if (sprd_timeline.timeline == NULL)
+ {
+ sprd_timeline.timeline = sprd_sync_timeline_create("sprd-timeline");
+ if (sprd_timeline.timeline == NULL)
+ {
+ printk(KERN_ERR "create_timeline, cannot create time line\n");
+ mutex_unlock(&(sprd_timeline.sync_mutex));
+ return -ENOMEM;
+ }
+ else
+ {
+ sprd_timeline.timeline_value = 0;
+ }
+ }
+ mutex_unlock(&(sprd_timeline.sync_mutex));
+ }
+ else if (type == SPRD_DEVICE_VIRTUAL_SYNC)
+ {
+ mutex_lock(&(sprd_timeline_virtual.sync_mutex));
+ if (sprd_timeline_virtual.timeline == NULL)
+ {
+ sprd_timeline_virtual.timeline = sprd_sync_timeline_create("sprd-timeline-virtual");
+ if (sprd_timeline_virtual.timeline == NULL)
+ {
+ printk(KERN_ERR "create_timeline, cannot create virtual time line\n");
+ mutex_unlock(&(sprd_timeline_virtual.sync_mutex));
+ return -ENOMEM;
+ }
+ else
+ {
+ sprd_timeline_virtual.timeline_value = 0;
+ }
+ }
+ mutex_unlock(&(sprd_timeline_virtual.sync_mutex));
+ }
+
+ return 0;
+}
+
+int sprd_destroy_timeline(struct sync_timeline_data *parent)
+{
+ struct sprd_sync_timeline *obj = NULL;
+
+ if (parent->timeline == NULL)
+ {
+ printk(KERN_ERR "sprd_fence timeline has been released\n");
+ return 0;
+ }
+
+ mutex_lock(&(parent->sync_mutex));
+ obj = parent->timeline;
+ sync_timeline_destroy(&obj->obj);
+ mutex_unlock(&(parent->sync_mutex));
+
+ return 0;
+}
+
+int open_sprd_sync_timeline(void)
+{
+ int ret = -1;
+ static int init = 0;
+
+ if (init == 0)
+ {
+ sprd_timeline.timeline_value = -1;
+ sprd_timeline.timeline = NULL;
+ mutex_init(&(sprd_timeline.sync_mutex));
+
+ sprd_timeline_virtual.timeline_value = -1;
+ sprd_timeline_virtual.timeline = NULL;
+ mutex_init(&(sprd_timeline_virtual.sync_mutex));
+
+ init = 1;
+ }
+
+ ret = sprd_create_timeline(SPRD_DEVICE_PRIMARY_SYNC);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ ret = sprd_create_timeline(SPRD_DEVICE_VIRTUAL_SYNC);
+
+ return ret;
+}
+
+int close_sprd_sync_timeline(void)
+{
+ int ret = -1;
+
+ ret = sprd_destroy_timeline(&sprd_timeline);
+
+ ret = sprd_destroy_timeline(&sprd_timeline_virtual);
+
+ return ret;
+}
+
+int sprd_fence_create(struct ion_fence_data *data, char *name)
+{
+ int fd = get_unused_fd();
+ struct sync_pt *pt;
+ struct sync_fence *fence;
+ struct sync_timeline_data *parent = NULL;
+
+ if (data == NULL || name == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_create input para is NULL\n");
+ return -EFAULT;
+ }
+
+
+ if (fd < 0)
+ {
+ fd = get_unused_fd();
+ if (fd < 0)
+ {
+ printk(KERN_ERR "sprd_sync_pt_create failed to get fd\n");
+ }
+ return -EFAULT;
+ }
+
+ if (data->device_type == SPRD_DEVICE_PRIMARY_SYNC)
+ {
+ parent = &sprd_timeline;
+ }
+ else if (data->device_type == SPRD_DEVICE_VIRTUAL_SYNC)
+ {
+ parent = &sprd_timeline_virtual;
+ }
+
+ if (parent == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_create failed to get sync timeline\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&(parent->sync_mutex));
+
+ pt = sprd_sync_pt_create(parent->timeline, parent->timeline_value + data->life_value);
+ if (pt == NULL)
+ {
+ printk(KERN_ERR "sprd_sync_pt_create failed\n");
+ goto err;
+ }
+
+ fence = sync_fence_create(name, pt);
+ if (fence == NULL)
+ {
+ sync_pt_free(pt);
+ printk(KERN_ERR "sprd_create_fence failed\n");
+ goto err;
+ }
+
+ sync_fence_install(fence, fd);
+
+ mutex_unlock(&(parent->sync_mutex));
+
+ pr_debug("create a fence: %p, fd: %d, life_value: %d, name: %s\n",
+ (void *)fence, fd, data->life_value, name);
+
+ return fd;
+
+err:
+ put_unused_fd(fd);
+ mutex_unlock(&(parent->sync_mutex));
+ return -ENOMEM;
+}
+
+int sprd_fence_destroy(struct ion_fence_data *data)
+{
+ if (data == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_destroy parameters NULL\n");
+ return -EFAULT;
+ }
+
+ if (data->release_fence_fd >= 0)
+ {
+ struct sync_fence *fence = sync_fence_fdget(data->release_fence_fd);
+ if (fence == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_destroy failed fence == NULL\n");
+ return -EFAULT;
+ }
+ }
+ else if (data->retired_fence_fd >= 0)
+ {
+ struct sync_fence *fence = sync_fence_fdget(data->retired_fence_fd);
+ if (fence == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_destroy failed fence == NULL\n");
+ } return -EFAULT;
+ }
+
+ return 0;
+}
+int sprd_fence_build(struct ion_fence_data *data)
+{
+ if (data == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_build input para is NULL\n");
+ return -EFAULT;
+ }
+
+ data->release_fence_fd = sprd_fence_create(data, "HWCRelease");
+ if (data->release_fence_fd < 0)
+ {
+ printk(KERN_ERR "sprd_fence_build failed to create release fence\n");
+ return -ENOMEM;
+ }
+
+ data->retired_fence_fd = sprd_fence_create(data, "HWCRetire");
+ if (data->retired_fence_fd < 0)
+ {
+ printk(KERN_ERR "sprd_fence_build failed to create release fence\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int sprd_fence_signal_timeline(struct sync_timeline_data *parent)
+{
+ if (parent == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_signal_timeline timeline is NULL\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&(parent->sync_mutex));
+
+ if (parent->timeline)
+ {
+ sprd_sync_timeline_inc(parent->timeline, 1);
+ parent->timeline_value++;
+
+ /*
+ * For avoiding overflow
+ * */
+ if (parent->timeline_value < 0)
+ {
+ parent->timeline_value = 0;
+ }
+ }
+
+ mutex_unlock(&(parent->sync_mutex));
+
+ pr_debug("sprd_signal_fence value: %d\n", parent->timeline_value);
+
+ return 0;
+}
+int sprd_fence_signal(struct ion_fence_data *data)
+{
+ struct sync_timeline_data *parent = NULL;
+ enum SPRD_DEVICE_SYNC_TYPE device_type= SPRD_DEVICE_PRIMARY_SYNC;
+
+ if (data == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_signal parameter is NULL\n");
+ return -EFAULT;
+ }
+
+ if (data->device_type == SPRD_DEVICE_PRIMARY_SYNC)
+ {
+ parent = &sprd_timeline;
+ }
+ else if (data->device_type == SPRD_DEVICE_VIRTUAL_SYNC)
+ {
+ parent = &sprd_timeline_virtual;
+ }
+
+ if (parent == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_signal failed to get sync timeline\n");
+ return -EFAULT;
+ }
+
+ sprd_fence_signal_timeline(parent);
+ pr_debug("sprd_signal_fence device_type: %d\n", data->device_type);
+
+ return 0;
+}
+
+int sprd_fence_wait(int fence_fd)
+{
+ int ret = 0;
+
+ struct sync_fence *fence = NULL;
+
+ if (fence_fd < 0)
+ {
+ printk(KERN_ERR "sprd_wait_fence input parameters is NULL\n");
+ return -EFAULT;
+ }
+
+ fence = sync_fence_fdget(fence_fd);
+ if (fence == NULL)
+ {
+ printk(KERN_ERR "sprd_fence_wait failed fence == NULL\n");
+ return -EFAULT;
+ }
+
+ ret = sync_fence_wait(fence, WAIT_FENCE_TIMEOUT);
+ sync_fence_put(fence);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "sync_fence_wait failed, ret = %x\n", ret);
+ }
+
+ pr_debug("sprd_wait_fence wait fence: %p done\n", (void *)fence);
+
+ return ret;
+}
+
+struct sync_pt *sprd_fence_dup(struct sync_pt *sync_pt)
+{
+ struct sync_pt *pt = NULL;
+
+ pt = sprd_sync_pt_dup(sync_pt);
+
+ return pt;
+}
diff --git a/drivers/gpu/ion/sprd/sprd_fence.h b/drivers/gpu/ion/sprd/sprd_fence.h
new file mode 100644
index 00000000..6916ccf4
--- /dev/null
+++ b/drivers/gpu/ion/sprd/sprd_fence.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SPRD_FENCE_H_
+#define _SPRD_FENCE_H_
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/sync.h>
+#include <video/ion_sprd.h>
+
+struct sprd_sync_timeline {
+ struct sync_timeline obj;
+ u32 value;
+};
+
+struct sprd_sync_pt {
+ struct sync_pt pt;
+ u32 value;
+};
+
+struct sprd_sync_create_fence_data {
+ __u32 value;
+ char name[32];
+ __s32 fence; /* fd of new fence */
+};
+
+struct sync_timeline_data {
+ int timeline_value;
+ struct mutex sync_mutex;
+ struct sprd_sync_timeline *timeline;
+};
+
+extern int open_sprd_sync_timeline(void);
+extern int close_sprd_sync_timeline(void);
+extern int sprd_fence_build(struct ion_fence_data *data);
+extern int sprd_fence_destroy(struct ion_fence_data *data);
+extern int sprd_fence_signal(struct ion_fence_data *data);
+extern int sprd_fence_wait(int fence_fd);
+extern struct sync_pt *sprd_fence_dup(struct sync_pt *sync_pt);
+
+#endif
diff --git a/drivers/gpu/ion/sprd/sprd_ion.c b/drivers/gpu/ion/sprd/sprd_ion.c
new file mode 100644
index 00000000..fa31c4b4
--- /dev/null
+++ b/drivers/gpu/ion/sprd/sprd_ion.c
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/ion.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <video/ion_sprd.h>
+#include "../ion_priv.h"
+
+#include <asm/cacheflush.h>
+
+#include "sprd_fence.h"
+#include <linux/dma-buf.h>
+
+struct ion_device *idev;
+int num_heaps = 0;
+struct ion_heap **heaps;
+
+#if 1
+static unsigned long user_va2pa(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t *pgd = pgd_offset(mm, addr);
+ unsigned long pa = 0;
+
+ if (!pgd_none(*pgd)) {
+ pud_t *pud = pud_offset(pgd, addr);
+ if (!pud_none(*pud)) {
+ pmd_t *pmd = pmd_offset(pud, addr);
+ if (!pmd_none(*pmd)) {
+ pte_t *ptep, pte;
+
+ ptep = pte_offset_map(pmd, addr);
+ pte = *ptep;
+ if (pte_present(pte))
+ pa = pte_val(pte) & PAGE_MASK;
+ pte_unmap(ptep);
+ }
+ }
+ }
+
+ return pa;
+}
+#endif
+
+struct ion_client *sprd_ion_client_create(const char *name)
+{
+ return ion_client_create(idev,name);
+}
+EXPORT_SYMBOL(sprd_ion_client_create);
+
+int sprd_ion_get_gsp_addr(struct ion_addr_data *data)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+ struct ion_buffer *buffer;
+
+ dmabuf = dma_buf_get(data->fd_buffer);
+ if (IS_ERR(dmabuf)) {
+ pr_err("sprd_ion_get_gsp_addr() dmabuf=0x%lx dma_buf_get error!\n", (unsigned long)dmabuf);
+ return -1;
+ }
+ /* if this memory came from ion */
+#if 0
+ if (dmabuf->ops != &dma_buf_ops) {
+ pr_err("%s: can not import dmabuf from another exporter\n",
+ __func__);
+ dma_buf_put(dmabuf);
+ return ERR_PTR(-EINVAL);
+ }
+#endif
+ buffer = dmabuf->priv;
+ dma_buf_put(dmabuf);
+
+ if (ION_HEAP_TYPE_SYSTEM == buffer->heap->type) {
+#if defined(CONFIG_SPRD_IOMMU)
+ mutex_lock(&buffer->lock);
+ if(0 == buffer->iomap_cnt[IOMMU_GSP]) {
+ buffer->iova[IOMMU_GSP] = sprd_iova_alloc(IOMMU_GSP, buffer->size);
+ ret = sprd_iova_map(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer);
+ }
+ if (ret) {
+ pr_err("%s, sprd_iova_map error, iova: 0x%lx, ret: %d!\n",
+ __func__, buffer->iova[IOMMU_GSP], ret);
+ sprd_iova_free(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer->size);
+ buffer->iova[IOMMU_GSP] = 0;
+ data->iova_addr = 0;
+ data->size = 0;
+ } else {
+ buffer->iomap_cnt[IOMMU_GSP]++;
+ data->iova_addr = buffer->iova[IOMMU_GSP];
+ data->size = buffer->size;
+ }
+ data->iova_enabled = true;
+ mutex_unlock(&buffer->lock);
+#else
+ ret = -1;
+#endif
+ } else {
+ if (!buffer->heap->ops->phys) {
+ pr_err("%s: ion_phys is not implemented by this heap.\n",
+ __func__);
+ return -ENODEV;
+ }
+ ret = buffer->heap->ops->phys(buffer->heap, buffer, &(data->phys_addr), &(data->size));
+ data->iova_enabled = false;
+ if (ret) {
+ pr_err("%s, get phys error %d!\n", __func__, ret);
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(sprd_ion_get_gsp_addr);
+
+int sprd_ion_free_gsp_addr(int fd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+ struct ion_buffer *buffer;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf)) {
+ pr_err("sprd_ion_free_gsp_addr() dmabuf=0x%lx dma_buf_get error!\n", (unsigned long)dmabuf);
+ return -1;
+ }
+ /* if this memory came from ion */
+#if 0
+ if (dmabuf->ops != &dma_buf_ops) {
+ pr_err("%s: can not import dmabuf from another exporter\n",
+ __func__);
+ dma_buf_put(dmabuf);
+ return ERR_PTR(-EINVAL);
+ }
+#endif
+ buffer = dmabuf->priv;
+ dma_buf_put(dmabuf);
+
+ if (ION_HEAP_TYPE_SYSTEM == buffer->heap->type) {
+#if defined(CONFIG_SPRD_IOMMU)
+ mutex_lock(&buffer->lock);
+ if (buffer->iomap_cnt[IOMMU_GSP] > 0) {
+ buffer->iomap_cnt[IOMMU_GSP]--;
+ if(0 == buffer->iomap_cnt[IOMMU_GSP]) {
+ ret = sprd_iova_unmap(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer);
+ sprd_iova_free(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer->size);
+ buffer->iova[IOMMU_GSP] = 0;
+ }
+ }
+ mutex_unlock(&buffer->lock);
+#else
+ ret = -1;
+#endif
+ }
+
+ if (ret) {
+ pr_err("sprd_ion_free_gsp_addr, error %d!\n",ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(sprd_ion_free_gsp_addr);
+
+long sprd_ion_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ion_client *client = filp->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case ION_SPRD_CUSTOM_PHYS:
+ {
+ struct ion_phys_data data;
+ struct ion_handle *handle;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl alloc copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl alloc handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ ret = ion_phys(client, handle, &data.phys, &data.size);
+ ion_free(client, handle);
+
+ if (ret) {
+ pr_err("sprd_heap_ioctl alloc ret=0x%x error!\n",ret);
+ return ret;
+ }
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl alloc copy_to_user error!\n");
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl alloc paddress=0x%lx size=0x%zx\n",data.phys,data.size);
+ break;
+ }
+ case ION_SPRD_CUSTOM_MSYNC:
+ {
+ struct ion_msync_data data;
+ void *v_addr;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl free copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ if ((unsigned long)data.vaddr & (PAGE_SIZE - 1)) {
+ pr_err("sprd_heap_ioctl free data.vaddr=%p error!\n",data.vaddr);
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl free vaddress=%p paddress=%p size=0x%zx\n",data.vaddr,data.paddr,data.size);
+#ifdef CONFIG_ARCH_SCX35L64
+ __dma_flush_range(data.vaddr, data.vaddr + data.size);
+#else
+ dmac_flush_range(data.vaddr, data.vaddr + data.size);
+#endif
+ v_addr = data.vaddr;
+ while (v_addr < data.vaddr + data.size) {
+ unsigned long phy_addr = user_va2pa(current->mm, (unsigned long)v_addr);
+ if (phy_addr) {
+#ifdef CONFIG_ARCH_SCX35L64
+ /* not implement in arm64 now */
+#else
+ outer_flush_range(phy_addr, phy_addr + PAGE_SIZE);
+#endif
+ }
+ v_addr += PAGE_SIZE;
+ }
+ break;
+ }
+#if defined(CONFIG_SPRD_IOMMU)
+ case ION_SPRD_CUSTOM_GSP_MAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp map copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl gsp map handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (0 == buffer->iomap_cnt[IOMMU_GSP]) {
+ buffer->iova[IOMMU_GSP] = sprd_iova_alloc(IOMMU_GSP, buffer->size);
+ ret = sprd_iova_map(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer);
+ }
+ if (ret) {
+ pr_err("sprd_heap_ioctl gsp map sprd_iova_map error, iova: 0x%lx, ret: %d!\n",
+ buffer->iova[IOMMU_GSP], ret);
+ sprd_iova_free(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer->size);
+ buffer->iova[IOMMU_GSP] = 0;
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ } else {
+ buffer->iomap_cnt[IOMMU_GSP]++;
+ data.iova_addr = buffer->iova[IOMMU_GSP];
+ data.iova_size = buffer->size;
+ }
+ mutex_unlock(&buffer->lock);
+ ion_free(client, handle);
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp map copy_to_user error!\n");
+ sprd_iova_free(IOMMU_GSP, data.iova_addr, data.iova_size);
+ return -EFAULT;
+ }
+ break;
+ }
+ case ION_SPRD_CUSTOM_GSP_UNMAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp unmap copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl gsp unmap handle=0x%lx ion_import_dma_buf error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (buffer->iomap_cnt[IOMMU_GSP] > 0) {
+ buffer->iomap_cnt[IOMMU_GSP]--;
+ if (0 == buffer->iomap_cnt[IOMMU_GSP]) {
+ ret = sprd_iova_unmap(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer);
+ sprd_iova_free(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer->size);
+ buffer->iova[IOMMU_GSP] = 0;
+ }
+ }
+ mutex_unlock(&buffer->lock);
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ ion_free(client, handle);
+ if (ret) {
+ pr_err("sprd_heap_ioctl gsp unmap sprd_iova_unmap error %d!\n",ret);
+ return ret;
+ }
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp unmap copy_to_user error!\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ case ION_SPRD_CUSTOM_MM_MAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm map copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl mm map handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (0 == buffer->iomap_cnt[IOMMU_MM]) {
+ buffer->iova[IOMMU_MM] = sprd_iova_alloc(IOMMU_MM, buffer->size);
+ ret = sprd_iova_map(IOMMU_MM, buffer->iova[IOMMU_MM], buffer);
+ }
+ if (ret) {
+ pr_err("sprd_heap_ioctl mm map sprd_iova_map error, iova: 0x%lx, ret: %d!\n",
+ buffer->iova[IOMMU_MM], ret);
+ sprd_iova_free(IOMMU_MM, buffer->iova[IOMMU_MM], buffer->size);
+ buffer->iova[IOMMU_MM] = 0;
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ } else {
+ buffer->iomap_cnt[IOMMU_MM]++;
+ data.iova_addr = buffer->iova[IOMMU_MM];
+ data.iova_size = buffer->size;
+ }
+ mutex_unlock(&buffer->lock);
+ ion_free(client, handle);
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm map copy_to_user error!\n");
+ sprd_iova_free(IOMMU_MM,data.iova_addr,data.iova_size);
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl mm map vaddress=0x%lx size=0x%zx\n",data.iova_addr,data.iova_size);
+ break;
+ }
+ case ION_SPRD_CUSTOM_MM_UNMAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm unmap copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl mm unmap vaddress=0x%lx size=0x%zx\n",data.iova_addr,data.iova_size);
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl mm unmap handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (buffer->iomap_cnt[IOMMU_MM] > 0) {
+ buffer->iomap_cnt[IOMMU_MM]--;
+ if (0 == buffer->iomap_cnt[IOMMU_MM]) {
+ ret = sprd_iova_unmap(IOMMU_MM, buffer->iova[IOMMU_MM], buffer);
+ sprd_iova_free(IOMMU_MM, buffer->iova[IOMMU_MM], buffer->size);
+ buffer->iova[IOMMU_MM] = 0;
+ }
+ }
+ mutex_unlock(&buffer->lock);
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ ion_free(client, handle);
+ if (ret) {
+ pr_err("sprd_heap_ioctl mm unmap ret=0x%x error!\n",ret);
+ return ret;
+ }
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm unmap copy_to_user error!\n");
+ return -EFAULT;
+ }
+ break;
+ }
+#endif
+ case ION_SPRD_CUSTOM_FENCE_CREATE:
+ {
+ int ret = -1;
+ struct ion_fence_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ pr_err("FENCE_CREATE user data is err\n");
+ return -EFAULT;
+ }
+
+ ret = sprd_fence_build(&data);
+ if (ret != 0) {
+ pr_err("sprd_fence_build failed\n");
+ return -EFAULT;
+ }
+
+ if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+ sprd_fence_destroy(&data);
+ pr_err("copy_to_user fence failed\n");
+ return -EFAULT;
+ }
+
+ break;
+ }
+ case ION_SPRD_CUSTOM_FENCE_SIGNAL:
+ {
+ struct ion_fence_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ pr_err("FENCE_CREATE user data is err\n");
+ return -EFAULT;
+ }
+
+ sprd_fence_signal(&data);
+
+ break;
+ }
+ case ION_SPRD_CUSTOM_FENCE_DUP:
+ {
+ break;
+
+ }
+ default:
+ pr_err("sprd_ion Do not support cmd: %d\n", cmd);
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(sprd_ion_ioctl);
+
+static long sprd_heap_ioctl(struct ion_client *client, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case ION_SPRD_CUSTOM_PHYS:
+ {
+ struct ion_phys_data data;
+ struct ion_handle *handle;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl alloc copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl alloc handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ ret = ion_phys(client, handle, &data.phys, &data.size);
+ ion_free(client, handle);
+
+ if (ret) {
+ pr_err("sprd_heap_ioctl alloc ret=0x%x error!\n",ret);
+ return ret;
+ }
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl alloc copy_to_user error!\n");
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl alloc paddress=0x%lx size=0x%zx\n",data.phys,data.size);
+ break;
+ }
+ case ION_SPRD_CUSTOM_MSYNC:
+ {
+#if 0
+ struct ion_msync_data data;
+ void *kaddr;
+ void *paddr;
+ size_t size;
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ return -EFAULT;
+ }
+ kaddr = data.vaddr;
+ paddr = data.paddr;
+ size = data.size;
+ dmac_flush_range(kaddr, kaddr + size);
+ outer_clean_range((phys_addr_t)paddr, (phys_addr_t)(paddr + size));
+
+/*maybe open in future if support discrete page map so keep this code unremoved here*/
+#else
+ struct ion_msync_data data;
+ void *v_addr;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl free copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ if ((unsigned long)data.vaddr & (PAGE_SIZE - 1)) {
+ pr_err("sprd_heap_ioctl free data.vaddr=%p error!\n",data.vaddr);
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl free vaddress=%p paddress=%p size=0x%zx\n",data.vaddr,data.paddr,data.size);
+#ifdef CONFIG_ARCH_SCX35L64
+ __dma_flush_range(data.vaddr, data.vaddr + data.size);
+#else
+ dmac_flush_range(data.vaddr, data.vaddr + data.size);
+#endif
+ v_addr = data.vaddr;
+ while (v_addr < data.vaddr + data.size) {
+ unsigned long phy_addr = user_va2pa(current->mm, (unsigned long)v_addr);
+ if (phy_addr) {
+#ifdef CONFIG_ARCH_SCX35L64
+ /* not implement in arm64 now */
+#else
+ outer_flush_range(phy_addr, phy_addr + PAGE_SIZE);
+#endif
+ }
+ v_addr += PAGE_SIZE;
+ }
+#endif
+ break;
+ }
+#if defined(CONFIG_SPRD_IOMMU)
+ case ION_SPRD_CUSTOM_GSP_MAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp map copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl gsp map handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (0 == buffer->iomap_cnt[IOMMU_GSP]) {
+ buffer->iova[IOMMU_GSP] = sprd_iova_alloc(IOMMU_GSP, buffer->size);
+ ret = sprd_iova_map(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer);
+ }
+ if (ret) {
+ pr_err("sprd_heap_ioctl gsp map sprd_iova_map error, iova: 0x%lx, ret: %d!\n",
+ buffer->iova[IOMMU_GSP], ret);
+ sprd_iova_free(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer->size);
+ buffer->iova[IOMMU_GSP] = 0;
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ } else {
+ buffer->iomap_cnt[IOMMU_GSP]++;
+ data.iova_addr = buffer->iova[IOMMU_GSP];
+ data.iova_size = buffer->size;
+ }
+ mutex_unlock(&buffer->lock);
+ ion_free(client, handle);
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp map copy_to_user error!\n");
+ sprd_iova_free(IOMMU_GSP, data.iova_addr, data.iova_size);
+ return -EFAULT;
+ }
+ break;
+ }
+ case ION_SPRD_CUSTOM_GSP_UNMAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp unmap copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl gsp unmap handle=0x%lx ion_import_dma_buf error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (buffer->iomap_cnt[IOMMU_GSP] > 0) {
+ buffer->iomap_cnt[IOMMU_GSP]--;
+ if (0 == buffer->iomap_cnt[IOMMU_GSP]) {
+ ret = sprd_iova_unmap(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer);
+ sprd_iova_free(IOMMU_GSP, buffer->iova[IOMMU_GSP], buffer->size);
+ buffer->iova[IOMMU_GSP] = 0;
+ }
+ }
+ mutex_unlock(&buffer->lock);
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ ion_free(client, handle);
+ if (ret) {
+ pr_err("sprd_heap_ioctl gsp unmap sprd_iova_unmap error %d!\n",ret);
+ return ret;
+ }
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl gsp unmap copy_to_user error!\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ case ION_SPRD_CUSTOM_MM_MAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm map copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl mm map handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (0 == buffer->iomap_cnt[IOMMU_MM]) {
+ buffer->iova[IOMMU_MM] = sprd_iova_alloc(IOMMU_MM, buffer->size);
+ ret = sprd_iova_map(IOMMU_MM, buffer->iova[IOMMU_MM], buffer);
+ }
+ if (ret) {
+ pr_err("sprd_heap_ioctl mm map sprd_iova_map error, iova: 0x%lx, ret: %d!\n",
+ buffer->iova[IOMMU_MM], ret);
+ sprd_iova_free(IOMMU_MM, buffer->iova[IOMMU_MM], buffer->size);
+ buffer->iova[IOMMU_MM] = 0;
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ } else {
+ buffer->iomap_cnt[IOMMU_MM]++;
+ data.iova_addr = buffer->iova[IOMMU_MM];
+ data.iova_size = buffer->size;
+ }
+ mutex_unlock(&buffer->lock);
+ ion_free(client, handle);
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm map copy_to_user error!\n");
+ sprd_iova_free(IOMMU_MM,data.iova_addr,data.iova_size);
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl mm map vaddress=0x%lx size=0x%zx\n",data.iova_addr,data.iova_size);
+ break;
+ }
+ case ION_SPRD_CUSTOM_MM_UNMAP:
+ {
+ struct ion_mmu_data data;
+ struct ion_handle *handle;
+ struct ion_buffer *buffer;
+
+ if (copy_from_user(&data, (void __user *)arg,
+ sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm unmap copy_from_user error!\n");
+ return -EFAULT;
+ }
+
+ pr_debug("sprd_heap_ioctl mm unmap vaddress=0x%lx size=0x%zx\n",data.iova_addr,data.iova_size);
+ handle = ion_import_dma_buf(client, data.fd_buffer);
+
+ if (IS_ERR(handle)) {
+ pr_err("sprd_heap_ioctl mm unmap handle=0x%lx error!\n", (unsigned long)handle);
+ return PTR_ERR(handle);
+ }
+
+ buffer = ion_handle_buffer(handle);
+
+ mutex_lock(&buffer->lock);
+ if (buffer->iomap_cnt[IOMMU_MM] > 0) {
+ buffer->iomap_cnt[IOMMU_MM]--;
+ if (0 == buffer->iomap_cnt[IOMMU_MM]) {
+ ret = sprd_iova_unmap(IOMMU_MM, buffer->iova[IOMMU_MM], buffer);
+ sprd_iova_free(IOMMU_MM, buffer->iova[IOMMU_MM], buffer->size);
+ buffer->iova[IOMMU_MM] = 0;
+ }
+ }
+ mutex_unlock(&buffer->lock);
+ data.iova_addr = 0;
+ data.iova_size = 0;
+ ion_free(client, handle);
+ if (ret) {
+ pr_err("sprd_heap_ioctl mm unmap ret=0x%x error!\n",ret);
+ return ret;
+ }
+
+ if (copy_to_user((void __user *)arg,
+ &data, sizeof(data))) {
+ pr_err("sprd_heap_ioctl mm unmap copy_to_user error!\n");
+ return -EFAULT;
+ }
+ break;
+ }
+#endif
+ case ION_SPRD_CUSTOM_FENCE_CREATE:
+ {
+ int ret = -1;
+ struct ion_fence_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ pr_err("FENCE_CREATE user data is err\n");
+ return -EFAULT;
+ }
+
+ ret = sprd_fence_build(&data);
+ if (ret != 0) {
+ pr_err("sprd_fence_build failed\n");
+ return -EFAULT;
+ }
+
+ if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+ sprd_fence_destroy(&data);
+ pr_err("copy_to_user fence failed\n");
+ return -EFAULT;
+ }
+
+ break;
+ }
+ case ION_SPRD_CUSTOM_FENCE_SIGNAL:
+ {
+ struct ion_fence_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ pr_err("FENCE_CREATE user data is err\n");
+ return -EFAULT;
+ }
+
+ sprd_fence_signal(&data);
+
+ break;
+ }
+ case ION_SPRD_CUSTOM_FENCE_DUP:
+ {
+ break;
+
+ }
+ default:
+ pr_err("sprd_ion Do not support cmd: %d\n", cmd);
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+
+extern struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *heap_data, struct device *dev);
+extern void ion_cma_heap_destroy(struct ion_heap *heap);
+
+
+static struct ion_heap *__ion_heap_create(struct ion_platform_heap *heap_data, struct device *dev)
+{
+ struct ion_heap *heap = NULL;
+
+ switch ((int)heap_data->type) {
+ case ION_HEAP_TYPE_CUSTOM:
+ heap = ion_cma_heap_create(heap_data, dev);
+ break;
+ default:
+ return ion_heap_create(heap_data);
+ }
+
+ if (IS_ERR_OR_NULL(heap)) {
+ pr_err("%s: error creating heap %s type %d base %lu size %zd\n",
+ __func__, heap_data->name, heap_data->type,
+ heap_data->base, heap_data->size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ heap->name = heap_data->name;
+ heap->id = heap_data->id;
+
+ return heap;
+}
+
+static void __ion_heap_destroy(struct ion_heap *heap)
+{
+ if (!heap)
+ return;
+
+ switch ((int)heap->type) {
+ case ION_HEAP_TYPE_CUSTOM:
+ ion_cma_heap_destroy(heap);
+ break;
+ default:
+ ion_heap_destroy(heap);
+ }
+}
+
+static struct ion_platform_data *sprd_ion_parse_dt(struct platform_device *pdev)
+{
+ int i = 0, ret = 0;
+ const struct device_node *parent = pdev->dev.of_node;
+ struct device_node *child = NULL;
+ struct ion_platform_data *pdata = NULL;
+ struct ion_platform_heap *ion_heaps = NULL;
+ struct platform_device *new_dev = NULL;
+ uint32_t val = 0, type = 0;
+ const char *name;
+ uint32_t out_values[2];
+
+ for_each_child_of_node(parent, child)
+ num_heaps++;
+ if (!num_heaps)
+ return ERR_PTR(-EINVAL);
+
+ pr_info("%s: num_heaps=%d\n", __func__, num_heaps);
+
+ pdata = kzalloc(sizeof(struct ion_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ ion_heaps = kzalloc(sizeof(struct ion_platform_heap)*num_heaps, GFP_KERNEL);
+ if (!ion_heaps) {
+ kfree(pdata);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pdata->heaps = ion_heaps;
+ pdata->nr = num_heaps;
+
+ for_each_child_of_node(parent, child) {
+ new_dev = of_platform_device_create(child, NULL, &pdev->dev);
+ if (!new_dev) {
+ pr_err("Failed to create device %s\n", child->name);
+ goto out;
+ }
+
+ pdata->heaps[i].priv = &new_dev->dev;
+
+ ret = of_property_read_u32(child, "reg", &val);
+ if (ret) {
+ pr_err("%s: Unable to find reg key, ret=%d", __func__, ret);
+ goto out;
+ }
+ pdata->heaps[i].id = val;
+
+ ret = of_property_read_string(child, "reg-names", &name);
+ if (ret) {
+ pr_err("%s: Unable to find reg-names key, ret=%d", __func__, ret);
+ goto out;
+ }
+ pdata->heaps[i].name = name;
+
+ ret = of_property_read_u32(child, "sprd,ion-heap-type", &type);
+ if (ret) {
+ pr_err("%s: Unable to find ion-heap-type key, ret=%d", __func__, ret);
+ goto out;
+ }
+ pdata->heaps[i].type = type;
+
+ ret = of_property_read_u32_array(child, "sprd,ion-heap-mem",
+ out_values, 2);
+ if (!ret) {
+ pdata->heaps[i].base = out_values[0];
+ pdata->heaps[i].size = out_values[1];
+ }
+
+ pr_info("%s: heaps[%d]: %s type: %d base: %lu size %u\n",
+ __func__, i, pdata->heaps[i].name, pdata->heaps[i].type,
+ pdata->heaps[i].base, pdata->heaps[i].size);
+ ++i;
+ }
+ return pdata;
+out:
+ kfree(pdata->heaps);
+ kfree(pdata);
+ return ERR_PTR(ret);
+}
+
+int sprd_ion_probe(struct platform_device *pdev)
+{
+ int i = 0, ret = -1;
+ struct ion_platform_data *pdata = NULL;
+ uint32_t need_free_pdata;
+
+ if(pdev->dev.of_node) {
+ pdata = sprd_ion_parse_dt(pdev);
+ if (IS_ERR(pdata)) {
+ return PTR_ERR(pdata);
+ }
+ need_free_pdata = 1;
+ } else {
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ pr_err("sprd_ion_probe failed: No platform data!\n");
+ return -ENODEV;
+ }
+
+ num_heaps = pdata->nr;
+ if (!num_heaps)
+ return -EINVAL;
+ need_free_pdata = 0;
+ }
+
+ heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL);
+ if(!heaps) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ idev = ion_device_create(&sprd_heap_ioctl);
+ if (IS_ERR_OR_NULL(idev)) {
+ pr_err("%s,idev is null\n", __FUNCTION__);
+ kfree(heaps);
+ ret = PTR_ERR(idev);
+ goto out1;
+ }
+
+ /* create the heaps as specified in the board file */
+ for (i = 0; i < num_heaps; i++) {
+ struct ion_platform_heap *heap_data = &pdata->heaps[i];
+
+ if(!pdev->dev.of_node) {
+ heap_data->priv = &pdev->dev;
+ }
+ heaps[i] = __ion_heap_create(heap_data, &pdev->dev);
+ if (IS_ERR_OR_NULL(heaps[i])) {
+ pr_err("%s,heaps is null, i:%d\n", __FUNCTION__,i);
+ ret = PTR_ERR(heaps[i]);
+ goto out;
+ }
+ ion_device_add_heap(idev, heaps[i]);
+ }
+ platform_set_drvdata(pdev, idev);
+
+ ret = open_sprd_sync_timeline();
+ if (ret != 0) {
+ pr_err("%s: sprd_create_timeline failed\n", __func__);
+ goto out;
+ }
+
+ if(need_free_pdata) {
+ kfree(pdata->heaps);
+ kfree(pdata);
+ }
+ return 0;
+out:
+ for (i = 0; i < num_heaps; i++) {
+ if (heaps[i])
+ ion_heap_destroy(heaps[i]);
+ }
+ kfree(heaps);
+out1:
+ if(need_free_pdata) {
+ kfree(pdata->heaps);
+ kfree(pdata);
+ }
+ return ret;
+}
+
+int sprd_ion_remove(struct platform_device *pdev)
+{
+ struct ion_device *idev = platform_get_drvdata(pdev);
+ int i;
+
+ ion_device_destroy(idev);
+ for (i = 0; i < num_heaps; i++)
+ __ion_heap_destroy(heaps[i]);
+ kfree(heaps);
+
+ close_sprd_sync_timeline();
+
+ return 0;
+}
+
+static const struct of_device_id sprd_ion_ids[] __initconst = {
+ { .compatible = "sprd,ion-sprd"},
+ {},
+};
+
+static struct platform_driver ion_driver = {
+ .probe = sprd_ion_probe,
+ .remove = sprd_ion_remove,
+ .driver = {
+ .name = "ion-sprd" ,
+ .of_match_table = of_match_ptr(sprd_ion_ids),
+ }
+};
+
+static int __init ion_init(void)
+{
+ int result;
+ result= platform_driver_register(&ion_driver);
+ pr_info("%s,result:%d\n",__FUNCTION__,result);
+ return result;
+}
+
+static void __exit ion_exit(void)
+{
+ platform_driver_unregister(&ion_driver);
+}
+
+module_init(ion_init);
+module_exit(ion_exit);
+
diff --git a/drivers/gpu/ion/sprd/sprd_ion_cma_heap.c b/drivers/gpu/ion/sprd/sprd_ion_cma_heap.c
new file mode 100644
index 00000000..ef5be551
--- /dev/null
+++ b/drivers/gpu/ion/sprd/sprd_ion_cma_heap.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/spinlock.h>
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ion.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include "../ion_priv.h"
+
+#ifndef CONFIG_ARCH_SCX35L64
+#include <asm/mach/map.h>
+#endif
+#include <video/ion_sprd.h>
+#include <linux/device.h>
+#include <linux/dma-contiguous.h>
+#include <linux/genalloc.h>
+
+#ifdef CONFIG_ION_DEBUG
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+#define ION_CMA_ALLOCATE_FAIL -1
+
+struct device *ion_dev;
+
+struct ion_cma_heap {
+ struct ion_heap heap;
+ struct gen_pool *pool;
+ ion_phys_addr_t poolbase;
+ size_t poolsize;
+};
+
+ion_phys_addr_t ion_cma_allocate(struct ion_heap *heap,
+ unsigned long size,
+ unsigned long align)
+{
+ struct ion_cma_heap *cma_heap =
+ container_of(heap, struct ion_cma_heap, heap);
+ if(cma_heap->pool == NULL)
+ {
+ struct page *page;
+ ion_phys_addr_t phys;
+ int pagecount = ((PAGE_ALIGN(size)) >> PAGE_SHIFT);
+ struct timeval val;
+ int start, end;
+ do_gettimeofday(&val);
+ start = (val.tv_sec * 1000000 + val.tv_usec) / 1000;
+ page = dma_alloc_from_contiguous(ion_dev, pagecount, get_order(size));
+ do_gettimeofday(&val);
+ end = (val.tv_sec * 1000000 + val.tv_usec) / 1000;
+ if(!page) {
+ pr_err("ion: malloc dma_alloc_from_contiguous() failed size:0x%lx , pageCount:%d\n" , size , pagecount);
+ return ION_CMA_ALLOCATE_FAIL;
+ }
+ phys = page_to_phys(page);
+ printk("ion: malloc from cma mem: size=%08lx,phy addr=%08lx, time=%dms\n", size, phys, end-start);
+ return phys;
+ } else {
+ unsigned long offset = gen_pool_alloc(cma_heap->pool, size);
+ pr_debug("ion: malloc from reserved mem: size=%08lx, pool=%p, offset=%08lx \n", size, cma_heap->pool, offset);
+ if (!offset)
+ return ION_CARVEOUT_ALLOCATE_FAIL;
+ return offset;
+ }
+}
+
+void ion_cma_free(struct ion_heap *heap, ion_phys_addr_t addr,
+ unsigned long size)
+{
+ struct ion_cma_heap *cma_heap =
+ container_of(heap, struct ion_cma_heap, heap);
+
+ if (addr == ION_CMA_ALLOCATE_FAIL)
+ return;
+ /*free reserved memory*/
+ if((cma_heap->pool != NULL) && ((addr < (cma_heap->poolbase +cma_heap->poolsize)) && (addr >= cma_heap->poolbase)))
+ {
+ pr_debug("ion: free reserved mem: size=%08lx, pool=%p, base:%08lx , phyaddr=%08lx \n", size, cma_heap->pool, cma_heap->poolbase , addr);
+ gen_pool_free(cma_heap->pool, addr, size);
+ }
+ else
+ {
+ struct page *page;
+ int pagecount = ((PAGE_ALIGN(size)) >> PAGE_SHIFT);
+ page = phys_to_page(addr);
+ pr_debug("ion: free cma mem: size=%08lx, phyAddr=%08lx \n", size, addr);
+ dma_release_from_contiguous(ion_dev, page, pagecount);
+ }
+ return;
+}
+
+static int ion_cma_heap_phys(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ *addr = buffer->priv_phys;
+ *len = buffer->size;
+ return 0;
+}
+
+static int ion_cma_heap_allocate(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long size, unsigned long align,
+ unsigned long flags)
+{
+ buffer->priv_phys = ion_cma_allocate(heap, size, align);
+ pr_debug("pgprot_noncached flags 0x%lx\n",flags);
+ if(flags&(1<<31))
+ buffer->flags |= (1<<31);
+ else
+ buffer->flags &= (~(1<<31));
+ buffer->flags |= (flags & 0x7FFF0000);/*for debug*/
+ return buffer->priv_phys == ION_CMA_ALLOCATE_FAIL ? -ENOMEM : 0;
+}
+
+static void ion_cma_heap_free(struct ion_buffer *buffer)
+{
+ struct ion_heap *heap = buffer->heap;
+
+ ion_cma_free(heap, buffer->priv_phys, buffer->size);
+ buffer->priv_phys = ION_CMA_ALLOCATE_FAIL;
+}
+
+struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ struct sg_table *table;
+ int ret;
+
+ table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret) {
+ kfree(table);
+ return ERR_PTR(ret);
+ }
+ sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size,
+ 0);
+ return table;
+}
+
+void ion_cma_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ sg_free_table(buffer->sg_table);
+ kfree(buffer->sg_table);
+ buffer->sg_table = NULL;
+}
+
+void *ion_cma_heap_map_kernel(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+#ifndef CONFIG_ARCH_SCX35L64
+ int mtype = MT_MEMORY_NONCACHED;
+#else
+ pgprot_t mtype = 11;
+#endif
+
+ if (buffer->flags & ION_FLAG_CACHED)
+#ifndef CONFIG_ARCH_SCX35L64
+ mtype = MT_MEMORY;
+#else
+ mtype = 9;
+#endif
+
+#ifndef CONFIG_ARCH_SCX35L64
+ return __arm_ioremap(buffer->priv_phys, buffer->size,
+ mtype);
+#else
+ return __ioremap(buffer->priv_phys, buffer->priv_phys, mtype);
+#endif
+}
+
+void ion_cma_heap_unmap_kernel(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+#ifndef CONFIG_ARCH_SCX35L64
+ __arm_iounmap(buffer->vaddr);
+#else
+ __iounmap(buffer->vaddr);
+#endif
+
+ buffer->vaddr = NULL;
+ return;
+}
+
+int ion_cma_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
+ struct vm_area_struct *vma)
+{
+ if((buffer->flags & (1<<31)) )
+ {
+ pr_debug("pgprot_cached buffer->flags 0x%lx\n",buffer->flags);
+ return remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+ buffer->size,
+ (vma->vm_page_prot));
+
+ }
+ else
+ {
+ pr_debug("pgprot_noncached buffer->flags 0x%lx\n",buffer->flags);
+ return remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ pgprot_noncached(vma->vm_page_prot));
+
+ }
+}
+
+static struct ion_heap_ops cma_heap_ops = {
+ .allocate = ion_cma_heap_allocate,
+ .free = ion_cma_heap_free,
+ .map_dma = ion_cma_heap_map_dma,
+ .unmap_dma = ion_cma_heap_unmap_dma,
+ .phys = ion_cma_heap_phys,
+ .map_user = ion_cma_heap_map_user,
+ .map_kernel = ion_cma_heap_map_kernel,
+ .unmap_kernel = ion_cma_heap_unmap_kernel,
+};
+
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *heap_data, struct device *dev)
+{
+ struct ion_cma_heap *cma_heap;
+ cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL);
+ if (!cma_heap)
+ return ERR_PTR(-ENOMEM);
+ if(heap_data->size != 0)
+ {
+ cma_heap->pool = gen_pool_create(12 , -1);
+ if (!cma_heap->pool) {
+ kfree(cma_heap);
+ return ERR_PTR(-ENOMEM);
+ }
+ cma_heap->poolbase = heap_data->base;
+ gen_pool_add(cma_heap->pool, cma_heap->poolbase, heap_data->size,
+ -1);
+ cma_heap->poolsize = heap_data->size;
+ }
+ else
+ {
+ cma_heap->pool = NULL;
+ cma_heap->poolbase = 0;
+ cma_heap->poolsize = 0;
+ }
+ cma_heap->heap.ops = &cma_heap_ops;
+ cma_heap->heap.type = ION_HEAP_TYPE_CUSTOM;
+ ion_dev = dev;
+ return &cma_heap->heap;
+}
+
+void ion_cma_heap_destroy(struct ion_heap *heap)
+{
+ struct ion_cma_heap *cma_heap =
+ container_of(heap, struct ion_cma_heap, heap);
+ if(cma_heap->pool)
+ {
+ gen_pool_destroy(cma_heap->pool);
+ }
+
+ kfree(cma_heap);
+ cma_heap = NULL;
+}