diff options
Diffstat (limited to 'drivers/gpu/ion/sprd')
| -rw-r--r-- | drivers/gpu/ion/sprd/Makefile | 5 | ||||
| -rw-r--r-- | drivers/gpu/ion/sprd/compat_sprd_ion.c | 336 | ||||
| -rw-r--r-- | drivers/gpu/ion/sprd/compat_sprd_ion.h | 30 | ||||
| -rw-r--r-- | drivers/gpu/ion/sprd/sprd_fence.c | 469 | ||||
| -rw-r--r-- | drivers/gpu/ion/sprd/sprd_fence.h | 52 | ||||
| -rw-r--r-- | drivers/gpu/ion/sprd/sprd_ion.c | 1093 | ||||
| -rw-r--r-- | drivers/gpu/ion/sprd/sprd_ion_cma_heap.c | 279 |
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; +} |
