HACK: ion: Revert all ion code back to android-hikey-linaro-4.4
Reverts all the ion code to the android-hikey-linaro-4.4 branch. This adds the hisi ion changes, etc. Hopefully we can get rid fo this soon. Signed-off-by: John Stultz <john.stultz@linaro.org>
This commit is contained in:
@@ -33,22 +33,16 @@ config ION_TEGRA
|
||||
help
|
||||
Choose this option if you wish to use ion on an nVidia Tegra.
|
||||
|
||||
config ION_POOL_CACHE_POLICY
|
||||
bool "Ion set page pool cache policy"
|
||||
depends on ION && X86
|
||||
default y if X86
|
||||
help
|
||||
Choose this option if need to explicity set cache policy of the
|
||||
pages in the page pool.
|
||||
|
||||
config ION_HISI
|
||||
tristate "Ion for Hisilicon"
|
||||
tristate "Hisilicon ION driver"
|
||||
depends on ARCH_HISI && ION
|
||||
select ION_OF
|
||||
help
|
||||
Choose this option if you wish to use ion on Hisilicon Platform.
|
||||
|
||||
source "drivers/staging/android/ion/hisilicon/Kconfig"
|
||||
|
||||
config ION_OF
|
||||
bool "Devicetree support for Ion"
|
||||
depends on ION && OF_ADDRESS
|
||||
help
|
||||
Provides base support for defining Ion heaps in devicetree
|
||||
and setting them up. Also includes functions for platforms
|
||||
to parse the devicetree and expand for their own custom
|
||||
extensions
|
||||
|
||||
If using Ion and devicetree, you should say Y here
|
||||
Choose this option if you wish to use ion on an HISI target.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
obj-$(CONFIG_ION) += ion.o ion-ioctl.o ion_heap.o \
|
||||
ion_page_pool.o ion_system_heap.o \
|
||||
obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
|
||||
ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o
|
||||
obj-$(CONFIG_ION_TEST) += ion_test.o
|
||||
ifdef CONFIG_COMPAT
|
||||
@@ -8,6 +7,7 @@ endif
|
||||
|
||||
obj-$(CONFIG_ION_DUMMY) += ion_dummy_driver.o
|
||||
obj-$(CONFIG_ION_TEGRA) += tegra/
|
||||
obj-$(CONFIG_ION_HISI) += hisilicon/
|
||||
obj-$(CONFIG_ION_OF) += ion_of.o
|
||||
|
||||
obj-$(CONFIG_ION_HISI) += hisi/
|
||||
obj-$(CONFIG_ION_HISI_CPUDRAW) += ion_cpudraw_heap.o
|
||||
obj-$(CONFIG_ION_HISI_SECCM) += ion_seccm_heap.o
|
||||
obj-$(CONFIG_ION_HISI_DMA_POOL) += ion_dma_pool_heap.o
|
||||
|
||||
@@ -39,12 +39,37 @@ struct compat_ion_handle_data {
|
||||
compat_int_t handle;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HISI_IOMMU
|
||||
struct compat_iommu_map_format {
|
||||
compat_ulong_t iova_start;
|
||||
compat_ulong_t iova_size;
|
||||
compat_ulong_t iommu_ptb_base;
|
||||
compat_ulong_t iommu_iova_base;
|
||||
compat_ulong_t header_size;
|
||||
compat_ulong_t phys_page_line;
|
||||
compat_ulong_t virt_page_line;
|
||||
compat_ulong_t is_tile;
|
||||
compat_ulong_t prot;
|
||||
};
|
||||
|
||||
struct compat_ion_map_iommu_data {
|
||||
compat_int_t handle;
|
||||
struct compat_iommu_map_format format;
|
||||
};
|
||||
#endif
|
||||
|
||||
#define COMPAT_ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
|
||||
struct compat_ion_allocation_data)
|
||||
#define COMPAT_ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, \
|
||||
struct compat_ion_handle_data)
|
||||
#define COMPAT_ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, \
|
||||
struct compat_ion_custom_data)
|
||||
#ifdef CONFIG_HISI_IOMMU
|
||||
#define COMPAT_ION_IOC_MAP_IOMMU _IOWR(ION_IOC_MAGIC, 8, \
|
||||
struct compat_ion_map_iommu_data)
|
||||
#define COMPAT_ION_IOC_UNMAP_IOMMU _IOWR(ION_IOC_MAGIC, 9, \
|
||||
struct compat_ion_map_iommu_data)
|
||||
#endif
|
||||
|
||||
static int compat_get_ion_allocation_data(
|
||||
struct compat_ion_allocation_data __user *data32,
|
||||
@@ -121,6 +146,71 @@ static int compat_get_ion_custom_data(
|
||||
return err;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HISI_IOMMU
|
||||
static int compat_put_ion_map_iommu_data(
|
||||
struct compat_ion_map_iommu_data __user *data32,
|
||||
struct ion_map_iommu_data __user *data)
|
||||
{
|
||||
compat_ulong_t l;
|
||||
compat_int_t i;
|
||||
int err;
|
||||
|
||||
err = get_user(i, &data->handle);
|
||||
err |= put_user(i, &data32->handle);
|
||||
err |= get_user(l, &data->format.iova_start);
|
||||
err |= put_user(l, &data32->format.iova_start);
|
||||
err |= get_user(l, &data->format.iova_size);
|
||||
err |= put_user(l, &data32->format.iova_size);
|
||||
err |= get_user(l, &data->format.iommu_ptb_base);
|
||||
err |= put_user(l, &data32->format.iommu_ptb_base);
|
||||
err |= get_user(l, &data->format.iommu_iova_base);
|
||||
err |= put_user(l, &data32->format.iommu_iova_base);
|
||||
err |= get_user(l, &data->format.header_size);
|
||||
err |= put_user(l, &data32->format.header_size);
|
||||
err |= get_user(l, &data->format.phys_page_line);
|
||||
err |= put_user(l, &data32->format.phys_page_line);
|
||||
err |= get_user(l, &data->format.virt_page_line);
|
||||
err |= put_user(l, &data32->format.virt_page_line);
|
||||
err |= get_user(l, &data->format.is_tile);
|
||||
err |= put_user(l, &data32->format.is_tile);
|
||||
err |= get_user(l, &data->format.prot);
|
||||
err |= put_user(l, &data32->format.prot);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int compat_get_ion_map_iommu_data(
|
||||
struct compat_ion_map_iommu_data __user *data32,
|
||||
struct ion_map_iommu_data __user *data)
|
||||
{
|
||||
compat_ulong_t l;
|
||||
compat_int_t i;
|
||||
int err;
|
||||
|
||||
err = get_user(i, &data32->handle);
|
||||
err |= put_user(i, &data->handle);
|
||||
err |= get_user(l, &data32->format.iova_start);
|
||||
err |= put_user(l, &data->format.iova_start);
|
||||
err |= get_user(l, &data32->format.iova_size);
|
||||
err |= put_user(l, &data->format.iova_size);
|
||||
err |= get_user(l, &data32->format.iommu_ptb_base);
|
||||
err |= put_user(l, &data->format.iommu_ptb_base);
|
||||
err |= get_user(l, &data32->format.iommu_iova_base);
|
||||
err |= put_user(l, &data->format.iommu_iova_base);
|
||||
err |= get_user(l, &data->format.header_size);
|
||||
err |= put_user(l, &data32->format.header_size);
|
||||
err |= get_user(l, &data32->format.phys_page_line);
|
||||
err |= put_user(l, &data->format.phys_page_line);
|
||||
err |= get_user(l, &data32->format.virt_page_line);
|
||||
err |= put_user(l, &data->format.virt_page_line);
|
||||
err |= get_user(l, &data32->format.is_tile);
|
||||
err |= put_user(l, &data->format.is_tile);
|
||||
err |= get_user(l, &data32->format.prot);
|
||||
err |= put_user(l, &data->format.prot);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret;
|
||||
@@ -137,7 +227,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
data32 = compat_ptr(arg);
|
||||
data = compat_alloc_user_space(sizeof(*data));
|
||||
if (!data)
|
||||
if (data == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
err = compat_get_ion_allocation_data(data32, data);
|
||||
@@ -156,7 +246,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
data32 = compat_ptr(arg);
|
||||
data = compat_alloc_user_space(sizeof(*data));
|
||||
if (!data)
|
||||
if (data == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
err = compat_get_ion_handle_data(data32, data);
|
||||
@@ -173,7 +263,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
data32 = compat_ptr(arg);
|
||||
data = compat_alloc_user_space(sizeof(*data));
|
||||
if (!data)
|
||||
if (data == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
err = compat_get_ion_custom_data(data32, data);
|
||||
@@ -183,10 +273,51 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return filp->f_op->unlocked_ioctl(filp, ION_IOC_CUSTOM,
|
||||
(unsigned long)data);
|
||||
}
|
||||
#ifdef CONFIG_HISI_IOMMU
|
||||
case COMPAT_ION_IOC_MAP_IOMMU:
|
||||
{
|
||||
struct compat_ion_map_iommu_data __user *data32;
|
||||
struct ion_map_iommu_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_map_iommu_data(data32, data);
|
||||
if (err)
|
||||
return err;
|
||||
ret = filp->f_op->unlocked_ioctl(filp, ION_IOC_MAP_IOMMU,
|
||||
(unsigned long)data);
|
||||
err = compat_put_ion_map_iommu_data(data32, data);
|
||||
return ret ? ret : err;
|
||||
}
|
||||
case COMPAT_ION_IOC_UNMAP_IOMMU:
|
||||
{
|
||||
struct compat_ion_map_iommu_data __user *data32;
|
||||
struct ion_map_iommu_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_map_iommu_data(data32, data);
|
||||
if (err)
|
||||
return err;
|
||||
ret = filp->f_op->unlocked_ioctl(filp, ION_IOC_UNMAP_IOMMU,
|
||||
(unsigned long)data);
|
||||
err = compat_put_ion_map_iommu_data(data32, data);
|
||||
return ret ? ret : err;
|
||||
}
|
||||
#endif
|
||||
case ION_IOC_SHARE:
|
||||
case ION_IOC_MAP:
|
||||
case ION_IOC_IMPORT:
|
||||
case ION_IOC_SYNC:
|
||||
case ION_IOC_INV:
|
||||
return filp->f_op->unlocked_ioctl(filp, cmd,
|
||||
(unsigned long)compat_ptr(arg));
|
||||
default:
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
Ion Memory Manager
|
||||
|
||||
Ion is a memory manager that allows for sharing of buffers via dma-buf.
|
||||
Ion allows for different types of allocation via an abstraction called
|
||||
a 'heap'. A heap represents a specific type of memory. Each heap has
|
||||
a different type. There can be multiple instances of the same heap
|
||||
type.
|
||||
|
||||
Specific heap instances are tied to heap IDs. Heap IDs are not to be specified
|
||||
in the devicetree.
|
||||
|
||||
Required properties for Ion
|
||||
|
||||
- compatible: "linux,ion" PLUS a compatible property for the device
|
||||
|
||||
All child nodes of a linux,ion node are interpreted as heaps
|
||||
|
||||
required properties for heaps
|
||||
|
||||
- compatible: compatible string for a heap type PLUS a compatible property
|
||||
for the specific instance of the heap. Current heap types
|
||||
-- linux,ion-heap-system
|
||||
-- linux,ion-heap-system-contig
|
||||
-- linux,ion-heap-carveout
|
||||
-- linux,ion-heap-chunk
|
||||
-- linux,ion-heap-dma
|
||||
-- linux,ion-heap-custom
|
||||
|
||||
Optional properties
|
||||
- memory-region: A phandle to a memory region. Required for DMA heap type
|
||||
(see reserved-memory.txt for details on the reservation)
|
||||
|
||||
Example:
|
||||
|
||||
ion {
|
||||
compatbile = "hisilicon,ion", "linux,ion";
|
||||
|
||||
ion-system-heap {
|
||||
compatbile = "hisilicon,system-heap", "linux,ion-heap-system"
|
||||
};
|
||||
|
||||
ion-camera-region {
|
||||
compatible = "hisilicon,camera-heap", "linux,ion-heap-dma"
|
||||
memory-region = <&camera_region>;
|
||||
};
|
||||
|
||||
ion-fb-region {
|
||||
compatbile = "hisilicon,fb-heap", "linux,ion-heap-dma"
|
||||
memory-region = <&fb_region>;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
ccflags-y += -I$(srctree)/drivers/staging/android/ion
|
||||
ccflags-y += -I$(srctree)/drivers/hisi/ap/platform/$(TARGET_PRODUCT)
|
||||
|
||||
obj-$(CONFIG_HISI_SMARTPOOL_OPT)+= hisi_ion_smart_pool.o
|
||||
obj-y += hisi_cpudraw_alloc.o
|
||||
obj-$(CONFIG_ION_HISI)+= of_hisi_ion.o
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "hisi_cpudraw_alloc.h"
|
||||
|
||||
#define KERNEL_DEBUG
|
||||
|
||||
#if defined(KERNEL_DEBUG)
|
||||
#define ALOGD(format, arg...) pr_info("[cpubuffer] " format, ##arg);
|
||||
#define ALOGD_INFO(format, arg...) pr_info("[cpubuffer] " format, ##arg);
|
||||
#else
|
||||
#define ALOGD(format, arg...)
|
||||
#define ALOGD_INFO(format, arg...)
|
||||
#endif
|
||||
|
||||
struct list_head idlenode_list;
|
||||
struct list_head allocatednode_list;
|
||||
struct mutex list_lock;
|
||||
|
||||
|
||||
|
||||
static int get_split_mode(mem_node_t * node, int bytew, int byteh)
|
||||
{
|
||||
int split_mode;
|
||||
|
||||
if(bytew < node->cpubuf.bytew){
|
||||
split_mode = SPLIT_MODE_W;
|
||||
}else if(byteh <= node->cpubuf.byteh){
|
||||
split_mode = SPLIT_MODE_H;
|
||||
}else{
|
||||
split_mode = SPLIT_MODE_NULL;
|
||||
}
|
||||
|
||||
ALOGD_INFO("cpubuffer info:split_mode=%d\n", split_mode);
|
||||
return split_mode;
|
||||
}
|
||||
|
||||
void memlist_dump(void)
|
||||
{
|
||||
mem_node_t *node;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
if(list_empty(&idlenode_list)){
|
||||
ALOGD("DUMP_IDLE:idlenode_list is empty\n");
|
||||
}
|
||||
|
||||
if(list_empty(&allocatednode_list)){
|
||||
ALOGD("DUMP_BUSY:allocatednode_list is empty\n");
|
||||
}
|
||||
|
||||
list_for_each_entry(node, &idlenode_list, node_head){
|
||||
ALOGD("DUMP_IDLE:phy=0x%8lx w=%6d h=%6d stride=%6d size=%6d ref=%d this=0x%p father=0x%p\n",
|
||||
node->cpubuf.phy_addr,
|
||||
node->cpubuf.bytew,
|
||||
node->cpubuf.byteh,
|
||||
node->cpubuf.stride,
|
||||
node->cpubuf.size,
|
||||
node->ref,
|
||||
node->thisnode,
|
||||
node->father
|
||||
);
|
||||
}
|
||||
|
||||
list_for_each_entry(node, &allocatednode_list, node_head){
|
||||
ALOGD("DUMP_BUSY:phy=0x%8lx w=%6d h=%6d stride=%6d size=%6d ref=%d this=0x%p father=0x%p\n",
|
||||
node->cpubuf.phy_addr,
|
||||
node->cpubuf.bytew,
|
||||
node->cpubuf.byteh,
|
||||
node->cpubuf.stride,
|
||||
node->cpubuf.size,
|
||||
node->ref,
|
||||
node->thisnode,
|
||||
node->father
|
||||
);
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
|
||||
void memlist_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&idlenode_list);
|
||||
INIT_LIST_HEAD(&allocatednode_list);
|
||||
mutex_init(&list_lock);
|
||||
}
|
||||
|
||||
|
||||
static mem_node_t *split_node(mem_node_t *father, int bytew, int byteh)
|
||||
{
|
||||
int split_mode;
|
||||
mem_node_t *node[2];
|
||||
int newbytew[2];
|
||||
int newbyteh[2];
|
||||
int newaddr[2];
|
||||
int i;
|
||||
|
||||
if(father->cpubuf.bytew < bytew || father->cpubuf.byteh < byteh){
|
||||
ALOGD_INFO("cpubuffer info:idle memory not enough left=(w=%d h=%d) need=(w=%d h=%d)\n",
|
||||
father->cpubuf.bytew, father->cpubuf.byteh, bytew, byteh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
split_mode = get_split_mode(father, bytew, byteh);
|
||||
|
||||
if(split_mode != SPLIT_MODE_NULL){
|
||||
if(split_mode == SPLIT_MODE_W){
|
||||
newbytew[0] = bytew;
|
||||
newbytew[1] = father->cpubuf.bytew - bytew;
|
||||
newaddr[0] = father->cpubuf.phy_addr;
|
||||
newaddr[1] = father->cpubuf.phy_addr + bytew;
|
||||
newbyteh[0] = father->cpubuf.byteh;
|
||||
newbyteh[1] = father->cpubuf.byteh;
|
||||
}else{
|
||||
newbyteh[0] = byteh;
|
||||
newbyteh[1] = father->cpubuf.byteh - byteh;
|
||||
newaddr[0] = father->cpubuf.phy_addr;
|
||||
newaddr[1] = father->cpubuf.phy_addr + father->cpubuf.stride*byteh;
|
||||
newbytew[0] = father->cpubuf.bytew;
|
||||
newbytew[1] = father->cpubuf.bytew;
|
||||
}
|
||||
|
||||
for(i = 0; i < 2; i++){
|
||||
node[i] = (mem_node_t *)kmalloc(sizeof(mem_node_t), GFP_KERNEL);
|
||||
if(node[i] == NULL){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node[i]->father = father;
|
||||
node[i]->thisnode = node[i];
|
||||
node[i]->ref = father->ref + 1;
|
||||
node[i]->cpubuf.phy_addr = newaddr[i];
|
||||
node[i]->cpubuf.bytew = newbytew[i];
|
||||
node[i]->cpubuf.byteh = newbyteh[i];
|
||||
node[i]->cpubuf.stride = father->cpubuf.stride;
|
||||
node[i]->cpubuf.size = newbytew[i]*newbyteh[i];
|
||||
}
|
||||
|
||||
list_del(&father->node_head);
|
||||
list_add(&node[1]->node_head, &idlenode_list);
|
||||
return node[0];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static mem_node_t *node_alloc_direct(struct gen_pool *pool, int size, int stride, int byteh)
|
||||
{
|
||||
int phy_addr = 0;
|
||||
mem_node_t *newnode = NULL;
|
||||
|
||||
newnode = (mem_node_t *)kmalloc(sizeof(mem_node_t), GFP_KERNEL); //replace with kmalloc
|
||||
if(newnode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
phy_addr = gen_pool_alloc(pool, size); //gen_pool_alloc
|
||||
if(phy_addr == 0){
|
||||
kfree(newnode);
|
||||
ALOGD("cpubuffer err:no more memory ,need size=%d\n", size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALOGD_INFO("MEMORY info:direct alloc node=0x%p size=%d\n", newnode, size);
|
||||
|
||||
newnode->thisnode = newnode;
|
||||
newnode->father = NULL;
|
||||
newnode->ref = 0;
|
||||
newnode->cpubuf.phy_addr = phy_addr;
|
||||
newnode->cpubuf.stride = stride;
|
||||
newnode->cpubuf.bytew = stride;
|
||||
newnode->cpubuf.byteh = byteh;
|
||||
newnode->cpubuf.size = size;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static mem_node_t *node_alloc(struct gen_pool *pool, int size, int bytew, int byteh)
|
||||
{
|
||||
int stride = size / byteh;
|
||||
mem_node_t *node = NULL, *newnode = NULL;
|
||||
|
||||
if(size < bytew*byteh || stride < bytew){
|
||||
ALOGD("cpubuffer err:alloc parameter err size=%d w=%d h=%d\n", size, bytew, byteh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do{
|
||||
list_for_each_entry(node, &idlenode_list, node_head){
|
||||
if(stride == node->cpubuf.stride){
|
||||
newnode = split_node(node, bytew, byteh);
|
||||
if(newnode != NULL){
|
||||
return newnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newnode= node_alloc_direct(pool, size, stride, byteh);
|
||||
if(newnode != NULL){
|
||||
list_add(&newnode->node_head, &idlenode_list);
|
||||
}
|
||||
} while(newnode != NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static mem_node_t * memge_node(mem_node_t * twins, mem_node_t *freenode)
|
||||
{
|
||||
mem_node_t *father;
|
||||
|
||||
if(twins->father != freenode->father){
|
||||
ALOGD("cpubuffer err:merge node but different father\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
father = freenode->father;
|
||||
list_del(&twins->node_head);
|
||||
|
||||
twins->cpubuf.thisnode = NULL;
|
||||
freenode->cpubuf.thisnode = NULL;
|
||||
kfree(twins);
|
||||
kfree(freenode);
|
||||
return father;
|
||||
}
|
||||
|
||||
|
||||
static void node_free_direct(struct gen_pool *pool, mem_node_t * tofreenode)
|
||||
{
|
||||
mem_node_t *father = tofreenode;
|
||||
|
||||
if(father == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("father->ref=%d\n", father->ref);
|
||||
|
||||
if(father != NULL && father->ref == 0){
|
||||
pr_info("MEMORY info:direct free node=0x%p size=%d\n", father, father->cpubuf.size);
|
||||
father->cpubuf.thisnode = NULL;
|
||||
gen_pool_free(pool, father->cpubuf.phy_addr, father->cpubuf.size);
|
||||
kfree(father);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_free(struct gen_pool *pool, mem_node_t * tofreenode)
|
||||
{
|
||||
mem_node_t *freenode = tofreenode;
|
||||
mem_node_t *father = NULL;
|
||||
|
||||
if(tofreenode == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
do{
|
||||
mem_node_t *twins = NULL;
|
||||
mem_node_t *node = NULL;
|
||||
|
||||
list_for_each_entry(node, &idlenode_list, node_head){
|
||||
if(node->father == freenode->father){
|
||||
twins = node;
|
||||
}
|
||||
}
|
||||
|
||||
if(twins == NULL){
|
||||
list_add(&freenode->node_head, &idlenode_list);
|
||||
father = NULL;
|
||||
}else{
|
||||
int ref;
|
||||
|
||||
ref = twins->ref;
|
||||
father = memge_node(twins, freenode);
|
||||
freenode = father;
|
||||
ALOGD_INFO("cpubuffer info:merge this_ref=%d father_ref=%d father=0x%p\n", ref, father->ref, father);
|
||||
}
|
||||
}while(father != NULL && father->ref != 0);
|
||||
|
||||
node_free_direct(pool, father);
|
||||
}
|
||||
|
||||
unsigned long cpubuffer_alloc(struct gen_pool *pool, int size, int bytew, int byteh)
|
||||
{
|
||||
mem_node_t *node = NULL;
|
||||
cpubuf_handle_t * cpubuf = NULL;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
node = node_alloc(pool, size , bytew, byteh);
|
||||
|
||||
if(node != NULL){
|
||||
node->cpubuf.thisnode = (void *)node->thisnode;
|
||||
cpubuf = &node->cpubuf;
|
||||
list_add(&node->node_head, &allocatednode_list);
|
||||
mutex_unlock(&list_lock);
|
||||
return cpubuf->phy_addr;
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cpubuffer_free(struct gen_pool *pool, unsigned long phy_addr)
|
||||
{
|
||||
mem_node_t *node = NULL;
|
||||
cpubuf_handle_t *cpubuf = NULL;
|
||||
|
||||
pr_info("free addr=0x%lx\n", phy_addr);
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_for_each_entry(node, &allocatednode_list, node_head){
|
||||
pr_info("free node_phy_addr=0x%lx addr=0x%lx\n", node->cpubuf.phy_addr, phy_addr);
|
||||
if(node->cpubuf.phy_addr == phy_addr){
|
||||
cpubuf = &node->cpubuf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&node->node_head);
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
if(cpubuf == NULL){
|
||||
pr_info("1free addr=0x%lx\n", phy_addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(node != cpubuf->thisnode){
|
||||
pr_info("2free addr=0x%lx\n", phy_addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
if(cpubuf->stride == 0 && cpubuf->bytew == 0 && cpubuf->byteh == 0){
|
||||
node_free_direct(pool, node);
|
||||
}else{
|
||||
node_free(pool, node);
|
||||
}
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef _CPUBUFFER_ALLOC_H_
|
||||
#define _CPUBUFFER_ALLOC_H_
|
||||
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
enum{
|
||||
SPLIT_MODE_NULL = 0x0,
|
||||
SPLIT_MODE_W,
|
||||
SPLIT_MODE_H
|
||||
};
|
||||
|
||||
typedef struct cpubuf_handle_t{
|
||||
unsigned long phy_addr;
|
||||
int bytew; // 256 bytes aligned
|
||||
int byteh; // 16 pixel aligned
|
||||
int stride;
|
||||
|
||||
int size; //only for ref = 0 node
|
||||
void *thisnode; //only for free
|
||||
}cpubuf_handle_t;
|
||||
|
||||
typedef struct mem_node_t{
|
||||
struct list_head node_head;
|
||||
struct mem_node_t *thisnode;
|
||||
struct mem_node_t *father;
|
||||
unsigned int ref;
|
||||
|
||||
cpubuf_handle_t cpubuf;
|
||||
}mem_node_t;
|
||||
|
||||
void memlist_init(void);
|
||||
void memlist_dump(void);
|
||||
unsigned long cpubuffer_alloc(struct gen_pool *pool, int size, int bytew, int byteh);
|
||||
int cpubuffer_free(struct gen_pool *pool, unsigned long phy_addr);
|
||||
|
||||
|
||||
#endif //_CPUBUFFER_ALLOC_H_
|
||||
@@ -0,0 +1,69 @@
|
||||
static size_t ion_client_total(struct ion_client *client)
|
||||
{
|
||||
size_t size = 0;
|
||||
struct rb_node *n;
|
||||
|
||||
mutex_lock(&client->lock);
|
||||
for (n = rb_first(&client->handles); n; n = rb_next(n)) {
|
||||
struct ion_handle *handle = rb_entry(n,
|
||||
struct ion_handle, node);
|
||||
if (!(handle->import) && (handle->buffer->heap->type !=
|
||||
ION_HEAP_TYPE_CARVEOUT)) {
|
||||
if (handle->buffer->cpudraw_sg_table)
|
||||
size += handle->buffer->cpu_buffer_size;
|
||||
else
|
||||
size += handle->buffer->size;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&client->lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
unsigned long hisi_ion_total(void)
|
||||
{
|
||||
return (unsigned long)atomic_long_read(&ion_total_size);
|
||||
}
|
||||
|
||||
int hisi_ion_memory_info(bool verbose)
|
||||
{
|
||||
struct rb_node *n;
|
||||
struct ion_device *dev = get_ion_device();
|
||||
|
||||
if (!dev)
|
||||
return -1;
|
||||
pr_info("ion total size:%ld\n", atomic_long_read(&ion_total_size));
|
||||
if (!verbose)
|
||||
return 0;
|
||||
down_read(&dev->client_lock);
|
||||
for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
|
||||
struct ion_client *client = rb_entry(n,
|
||||
struct ion_client, node);
|
||||
size_t size = ion_client_total(client);
|
||||
|
||||
if (!size)
|
||||
continue;
|
||||
if (client->task) {
|
||||
char task_comm[TASK_COMM_LEN];
|
||||
|
||||
get_task_comm(task_comm, client->task);
|
||||
pr_info("%16.s %16u %16zu\n",
|
||||
task_comm, client->pid, size);
|
||||
} else {
|
||||
pr_info("%16.s %16u %16zu\n",
|
||||
client->name, client->pid, size);
|
||||
}
|
||||
}
|
||||
up_read(&dev->client_lock);
|
||||
mutex_lock(&dev->buffer_lock);
|
||||
for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
|
||||
struct ion_buffer *buffer = rb_entry(n, struct ion_buffer,
|
||||
node);
|
||||
|
||||
if (!buffer->handle_count &&
|
||||
(buffer->heap->type != ION_HEAP_TYPE_CARVEOUT))
|
||||
pr_info("%16.s %16u %16zu\n", buffer->task_comm,
|
||||
buffer->pid, buffer->size);
|
||||
}
|
||||
mutex_unlock(&dev->buffer_lock);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2016 hisilicon, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "smartpool: " fmt
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/hisi/ion-iommu.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "ion.h"
|
||||
#include "hisi_ion_smart_pool.h"
|
||||
|
||||
/*for pclin*/
|
||||
/*lint -save -e846 -e514 -e866 -e30 -e84 -e712 -e701 -e40 -e578 -e528*/
|
||||
/*lint -save -e522 -e838 -e737 -e84 -e774 -e845 -e527 -e531 -e702 -e753*/
|
||||
/*lint -save -e713 -e732 -e438 -e778 -e708 -e21 -e528 -e756*/
|
||||
|
||||
static bool smart_pool_enable = true;
|
||||
static int smart_pool_alloc_size;
|
||||
|
||||
struct task_struct *smart_pool_thread;
|
||||
static wait_queue_head_t smart_pool_wait;
|
||||
static unsigned int smart_pool_wait_flag;
|
||||
|
||||
int smart_pool_water_mark = 24 * 64 * 4;
|
||||
|
||||
static unsigned int smart_pool_orders[] = {9, 8, 4, 2, 0};
|
||||
|
||||
static const int smart_pool_num_orders = ARRAY_SIZE(smart_pool_orders);
|
||||
|
||||
#define SMART_POOL_MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
bool ion_smart_is_graphic_buffer(struct ion_buffer *buffer)
|
||||
{
|
||||
if (NULL == buffer) {
|
||||
pr_err("%s: buffer is NULL!\n", __func__);
|
||||
return false;
|
||||
}
|
||||
return !!(buffer->flags & ION_FLAG_GRAPHIC_BUFFER);
|
||||
}
|
||||
|
||||
void ion_smart_set_water_mark(int water_mark)
|
||||
{
|
||||
smart_pool_water_mark = water_mark;
|
||||
}
|
||||
|
||||
static int sp_order_to_index(unsigned int order)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < smart_pool_num_orders; i++) {
|
||||
if (order == smart_pool_orders[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
BUG();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline unsigned long sp_order_to_size(int order)
|
||||
{
|
||||
return PAGE_SIZE << order;
|
||||
}
|
||||
|
||||
static int sp_ion_page_pool_total(struct ion_page_pool *pool)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (NULL == pool) {
|
||||
pr_err("%s: pool is NULL!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = pool->low_count + pool->high_count;
|
||||
|
||||
return count << pool->order;
|
||||
}
|
||||
|
||||
static int sp_pool_total_pages(struct ion_smart_pool *pool)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
if (NULL == pool) {
|
||||
pr_err("%s: pool is NULL!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < smart_pool_num_orders; i++)
|
||||
count += sp_ion_page_pool_total(pool->pools[i]);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void ion_smart_sp_init_page(struct page *page)
|
||||
{
|
||||
unsigned long len;
|
||||
|
||||
if (NULL == page) {
|
||||
pr_err("%s: page is NULL!\n", __func__);
|
||||
return;
|
||||
}
|
||||
len = PAGE_SIZE << compound_order(page);
|
||||
memset(page_address(page), 0, len);
|
||||
__flush_dcache_area(page_address(page), len);
|
||||
}
|
||||
|
||||
static int sp_fill_pool_once(struct ion_page_pool *pool)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (NULL == pool) {
|
||||
pr_err("%s: pool is NULL!\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
page = ion_page_pool_alloc_pages(pool);
|
||||
if (NULL == page)
|
||||
return -ENOMEM;
|
||||
ion_smart_sp_init_page(page);
|
||||
ion_page_pool_free(pool, page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ion_smart_pool_kworkthread(void *p)
|
||||
{
|
||||
int i;
|
||||
struct ion_smart_pool *pool;
|
||||
int ret;
|
||||
|
||||
if (NULL == p) {
|
||||
pr_err("%s: p is NULL!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pool = (struct ion_smart_pool *)p;
|
||||
while (true) {
|
||||
ret = wait_event_interruptible(smart_pool_wait,
|
||||
(smart_pool_wait_flag == 1));
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
smart_pool_wait_flag = 0;
|
||||
for (i = 0; i < smart_pool_num_orders; i++) {
|
||||
while (sp_pool_total_pages(pool) <
|
||||
smart_pool_water_mark) {
|
||||
if (sp_fill_pool_once(pool->pools[i]) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 2; i < smart_pool_num_orders; i++) {
|
||||
while (sp_ion_page_pool_total(pool->pools[i]) <
|
||||
LOWORDER_WATER_MASK) {
|
||||
if (sp_fill_pool_once(pool->pools[i]) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct page *ion_smart_pool_allocate(struct ion_smart_pool *pool,
|
||||
unsigned long size, unsigned int max_order)
|
||||
{
|
||||
int i;
|
||||
struct page *page;
|
||||
|
||||
if (NULL == pool) {
|
||||
pr_err("%s: pool is NULL!\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < smart_pool_num_orders; i++) {
|
||||
if (size < sp_order_to_size(smart_pool_orders[i]))
|
||||
continue;
|
||||
if (max_order < smart_pool_orders[i])
|
||||
continue;
|
||||
|
||||
page = ion_page_pool_alloc(pool->pools[i]);
|
||||
if (!page)
|
||||
continue;
|
||||
if (smart_pool_alloc_size) {
|
||||
smart_pool_alloc_size +=
|
||||
PAGE_SIZE << compound_order(page);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ion_smart_pool_wakeup_process(void)
|
||||
{
|
||||
if (!smart_pool_enable)
|
||||
return;
|
||||
smart_pool_wait_flag = 1;
|
||||
wake_up_interruptible(&smart_pool_wait);
|
||||
}
|
||||
|
||||
void ion_smart_pool_all_free(struct ion_smart_pool *pool, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (NULL == pool) {
|
||||
pr_err("%s: smart_pool is NULL!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < smart_pool_num_orders; i++)
|
||||
ion_page_pool_shrink(pool->pools[i], gfp_mask, nr_to_scan);
|
||||
}
|
||||
|
||||
int ion_smart_pool_free(struct ion_smart_pool *pool, struct page *page)
|
||||
{
|
||||
int order;
|
||||
|
||||
if (!smart_pool_enable) {
|
||||
ion_smart_pool_all_free(pool, __GFP_HIGHMEM, MAX_POOL_SIZE);
|
||||
return -1;
|
||||
}
|
||||
if ((NULL == pool) || (NULL == page)) {
|
||||
pr_err("%s: pool/page is NULL!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
order = compound_order(page);
|
||||
|
||||
if (sp_pool_total_pages(pool) < MAX_POOL_SIZE) {
|
||||
ion_smart_sp_init_page(page);
|
||||
ion_page_pool_free(pool->pools[sp_order_to_index(order)], page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ion_smart_pool_shrink(struct ion_smart_pool *smart_pool,
|
||||
struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
int nr_max_free;
|
||||
int nr_to_free;
|
||||
int nr_total = 0;
|
||||
|
||||
if ((NULL == smart_pool) || (NULL == pool)) {
|
||||
pr_err("%s: smartpool/pool is NULL!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nr_to_scan == 0)
|
||||
return ion_page_pool_shrink(pool, gfp_mask, 0);
|
||||
|
||||
nr_max_free = sp_pool_total_pages(smart_pool) -
|
||||
(smart_pool_water_mark + LOWORDER_WATER_MASK);
|
||||
nr_to_free = SMART_POOL_MIN(nr_max_free, nr_to_scan);
|
||||
|
||||
if (nr_to_free <= 0)
|
||||
return 0;
|
||||
|
||||
nr_total = ion_page_pool_shrink(pool, gfp_mask, nr_to_free);
|
||||
return nr_total;
|
||||
}
|
||||
|
||||
void ion_smart_pool_debug_show_total(struct seq_file *s,
|
||||
struct ion_smart_pool *smart_pool)
|
||||
{
|
||||
if ((NULL == s) || (NULL == smart_pool)) {
|
||||
pr_err("%s: s/smart_pool is NULL!\n", __func__);
|
||||
return;
|
||||
}
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
seq_printf(s, "in smart pool = %d total\n",
|
||||
sp_pool_total_pages(smart_pool) * 4 / 1024);
|
||||
}
|
||||
|
||||
struct ion_smart_pool *ion_smart_pool_create(void)
|
||||
{
|
||||
struct ion_smart_pool *smart_pool =
|
||||
kzalloc(sizeof(struct ion_smart_pool) +
|
||||
sizeof(struct ion_page_pool *) * smart_pool_num_orders,
|
||||
GFP_KERNEL);
|
||||
bool graphic_buffer_flag = true;
|
||||
|
||||
if (NULL == smart_pool) {
|
||||
pr_err("%s: smart_pool is NULL!\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ion_system_heap_create_pools(smart_pool->pools,
|
||||
graphic_buffer_flag))
|
||||
goto free_heap;
|
||||
|
||||
init_waitqueue_head(&smart_pool_wait);
|
||||
smart_pool_thread = kthread_run(ion_smart_pool_kworkthread, smart_pool,
|
||||
"%s", "smartpool");
|
||||
if (IS_ERR(smart_pool_thread)) {
|
||||
pr_err("%s: kthread_create failed!\n", __func__);
|
||||
goto destroy_pools;
|
||||
}
|
||||
|
||||
ion_smart_pool_wakeup_process();
|
||||
|
||||
return smart_pool;
|
||||
|
||||
destroy_pools:
|
||||
ion_system_heap_destroy_pools(smart_pool->pools);
|
||||
|
||||
free_heap:
|
||||
kfree(smart_pool);
|
||||
smart_pool = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_param_named(debug_smart_pool_enable, smart_pool_enable, bool, 0644);
|
||||
MODULE_PARM_DESC(debug_smart_pool_enable, "enable smart pool");
|
||||
|
||||
module_param_named(debug_smart_pool_alloc_size, smart_pool_alloc_size, int,
|
||||
0644);
|
||||
MODULE_PARM_DESC(debug_smart_pool_alloc_size, "alloc size from smartpool");
|
||||
/*lint -restore*/
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Hislicon, 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 _ION_SMART_POOL_H
|
||||
#define _ION_SMART_POOL_H
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/types.h>
|
||||
#include "ion_priv.h"
|
||||
|
||||
#define LOWORDER_WATER_MASK (64*4)
|
||||
#define MAX_POOL_SIZE (128*64*4)
|
||||
|
||||
struct ion_smart_pool {
|
||||
struct ion_page_pool *pools[0];
|
||||
};
|
||||
bool ion_smart_is_graphic_buffer(struct ion_buffer *buffer);
|
||||
void ion_smart_pool_debug_show_total(struct seq_file *s,
|
||||
struct ion_smart_pool *smart_pool);
|
||||
void ion_smart_sp_init_page(struct page *page);
|
||||
struct page *ion_smart_pool_allocate(struct ion_smart_pool *pool,
|
||||
unsigned long size,
|
||||
unsigned int max_order);
|
||||
int ion_smart_pool_free(struct ion_smart_pool *pool, struct page *page);
|
||||
int ion_smart_pool_shrink(struct ion_smart_pool *smart_pool,
|
||||
struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
int nr_to_scan);
|
||||
struct ion_smart_pool *ion_smart_pool_create(void);
|
||||
void ion_smart_pool_wakeup_process(void);
|
||||
void ion_smart_set_water_mark(int water_mark);
|
||||
#endif /* _ION_SMART_POOL_H */
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Hisilicon hisi ION Driver
|
||||
*
|
||||
* Copyright (c) 2015 Hisilicon Limited.
|
||||
*
|
||||
* Author: Chen Feng <puck.chen@hisilicon.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "Ion: " fmt
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hisi/hisi_ion.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <ion_priv.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <asm/cputype.h>
|
||||
|
||||
|
||||
#define MAX_HISI_ION_DYNAMIC_AREA_NAME_LEN 64
|
||||
struct hisi_ion_dynamic_area {
|
||||
phys_addr_t base;
|
||||
unsigned long size;
|
||||
char name[MAX_HISI_ION_DYNAMIC_AREA_NAME_LEN];
|
||||
};
|
||||
|
||||
struct hisi_ion_type_table {
|
||||
const char *name;
|
||||
enum ion_heap_type type;
|
||||
};
|
||||
|
||||
static const struct hisi_ion_type_table ion_type_table[] = {
|
||||
{"ion_system", ION_HEAP_TYPE_SYSTEM},
|
||||
{"ion_system_contig", ION_HEAP_TYPE_SYSTEM_CONTIG},
|
||||
{"ion_carveout", ION_HEAP_TYPE_CARVEOUT},
|
||||
{"ion_chunk", ION_HEAP_TYPE_CHUNK},
|
||||
{"ion_dma", ION_HEAP_TYPE_DMA},
|
||||
{"ion_custom", ION_HEAP_TYPE_CUSTOM},
|
||||
};
|
||||
|
||||
static struct ion_device *idev;
|
||||
static int num_heaps;
|
||||
static struct ion_heap **heaps;
|
||||
static struct ion_platform_heap **heaps_data;
|
||||
|
||||
|
||||
#define MAX_HISI_ION_DYNAMIC_AREA_NUM 5
|
||||
static struct hisi_ion_dynamic_area ion_dynamic_area_table[MAX_HISI_ION_DYNAMIC_AREA_NUM];
|
||||
static int ion_dynamic_area_count = 0;
|
||||
|
||||
static int add_dynamic_area(phys_addr_t base, unsigned long len, const char* name)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = ion_dynamic_area_count;
|
||||
|
||||
if (i < MAX_HISI_ION_DYNAMIC_AREA_NUM) {
|
||||
ion_dynamic_area_table[i].base = base;
|
||||
ion_dynamic_area_table[i].size = len;
|
||||
strncpy(ion_dynamic_area_table[i].name, name,
|
||||
MAX_HISI_ION_DYNAMIC_AREA_NAME_LEN-1);
|
||||
ion_dynamic_area_table[i].name[MAX_HISI_ION_DYNAMIC_AREA_NAME_LEN-1] = '\0';
|
||||
pr_err("insert heap-name %s \n", ion_dynamic_area_table[i].name);
|
||||
|
||||
ion_dynamic_area_count ++;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static struct hisi_ion_dynamic_area* find_dynamic_area_by_name(const char* name)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (!name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (; i < MAX_HISI_ION_DYNAMIC_AREA_NUM; i++) {
|
||||
pr_err("name = %s, table name %s \n", name, ion_dynamic_area_table[i].name);
|
||||
if (!strcmp(name, ion_dynamic_area_table[i].name)) {
|
||||
return &ion_dynamic_area_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __init hisi_ion_reserve_area(struct reserved_mem *rmem)
|
||||
{
|
||||
char *status = NULL;
|
||||
int namesize = 0;
|
||||
const char* heapname;
|
||||
|
||||
status = (char *)of_get_flat_dt_prop(rmem->fdt_node, "status", NULL);
|
||||
if (status && (strncmp(status, "ok", strlen("ok")) != 0))
|
||||
return 0;
|
||||
|
||||
heapname = of_get_flat_dt_prop(rmem->fdt_node, "heap-name", &namesize);
|
||||
if (!heapname || (namesize <= 0)) {
|
||||
pr_err("no 'heap-name' property namesize=%d\n", namesize);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pr_info("base 0x%llx, size is 0x%llx, node name %s, heap-name %s namesize %d,"
|
||||
"[%d][%d][%d][%d]\n",
|
||||
rmem->base, rmem->size, rmem->name, heapname, namesize,
|
||||
heapname[0],heapname[1],heapname[2],heapname[3] );
|
||||
|
||||
if (add_dynamic_area(rmem->base, rmem->size, heapname)) {
|
||||
pr_err("fail to add to dynamic area \n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
RESERVEDMEM_OF_DECLARE(hisi_ion, "hisi_ion", hisi_ion_reserve_area);
|
||||
|
||||
struct ion_device *get_ion_device(void) {
|
||||
return idev;
|
||||
}
|
||||
|
||||
static void ion_pm_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void ion_flush_all_cpus_caches(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
struct ion_client *hisi_ion_client_create(const char *name)
|
||||
{
|
||||
return ion_client_create(idev, name);
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_ion_client_create);
|
||||
|
||||
static long hisi_ion_custom_ioctl(struct ion_client *client,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern int hisi_ion_enable_iommu(struct platform_device *pdev);
|
||||
|
||||
static int get_type_by_name(const char *name, enum ion_heap_type *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ion_type_table); i++) {
|
||||
if (strcmp(name, ion_type_table[i].name))
|
||||
continue;
|
||||
|
||||
*type = ion_type_table[i].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hisi_set_platform_data(struct platform_device *pdev)
|
||||
{
|
||||
unsigned int base = 0;
|
||||
unsigned int size = 0;
|
||||
unsigned int id = 0;
|
||||
const char *heap_name;
|
||||
const char *type_name;
|
||||
const char *status;
|
||||
enum ion_heap_type type = 0;
|
||||
int ret = 0;
|
||||
struct device_node *np;
|
||||
struct device_node *phandle_node;
|
||||
struct property *prop;
|
||||
struct ion_platform_heap *p_data;
|
||||
const struct device_node *dt_node = pdev->dev.of_node;
|
||||
int index = 0;
|
||||
|
||||
for_each_child_of_node(dt_node, np)
|
||||
num_heaps++;
|
||||
|
||||
heaps_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_platform_heap *) *
|
||||
num_heaps,
|
||||
GFP_KERNEL);
|
||||
if (!heaps_data)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_child_of_node(dt_node, np) {
|
||||
ret = of_property_read_string(np, "status", &status);
|
||||
if (!ret) {
|
||||
if (strncmp("ok", status, strlen("ok")))
|
||||
continue;
|
||||
}
|
||||
|
||||
phandle_node = of_parse_phandle(np, "heap-name", 0);
|
||||
if (phandle_node) {
|
||||
int len;
|
||||
|
||||
ret = of_property_read_string(phandle_node, "status", &status);
|
||||
if (!ret) {
|
||||
if (strncmp("ok", status, strlen("ok")))
|
||||
continue;
|
||||
}
|
||||
|
||||
prop = of_find_property(phandle_node, "heap-name", &len);
|
||||
if (!prop) {
|
||||
pr_err("no heap-name in phandle of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!prop->value || !prop->length) {
|
||||
pr_err("%s %s %d, node %s, invalid phandle, value=%p,length=%d\n",
|
||||
__FILE__,__FUNCTION__,__LINE__,
|
||||
np->name, prop->value, prop->length );
|
||||
continue;
|
||||
} else {
|
||||
heap_name = prop->value;
|
||||
}
|
||||
} else {
|
||||
ret = of_property_read_string(np, "heap-name", &heap_name);
|
||||
if (ret < 0) {
|
||||
pr_err("invalid heap-name in node %s, please check the name \n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pr_err("node name [%s], heap-name [%s]\n", np->name, heap_name);
|
||||
|
||||
ret = of_property_read_u32(np, "heap-id", &id);
|
||||
if (ret < 0) {
|
||||
pr_err("check the id %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "heap-base", &base);
|
||||
if (ret < 0) {
|
||||
pr_err("check the base of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "heap-size", &size);
|
||||
if (ret < 0) {
|
||||
pr_err("check the size of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_property_read_string(np, "heap-type", &type_name);
|
||||
if (ret < 0) {
|
||||
pr_err("check the type of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = get_type_by_name(type_name, &type);
|
||||
if (ret < 0) {
|
||||
pr_err("type name error %s!\n", type_name);
|
||||
continue;
|
||||
}
|
||||
pr_err("heap index %d : name %s base 0x%x size 0x%x id %d type %d\n",
|
||||
index, heap_name, base, size, id, type);
|
||||
|
||||
p_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_platform_heap),
|
||||
GFP_KERNEL);
|
||||
if (!p_data)
|
||||
return -ENOMEM;
|
||||
|
||||
p_data->name = heap_name;
|
||||
p_data->base = base;
|
||||
p_data->size = size;
|
||||
p_data->id = id;
|
||||
p_data->type = type;
|
||||
p_data->priv = (void *)&pdev->dev;
|
||||
|
||||
if (!p_data->base && !p_data->size) {
|
||||
struct hisi_ion_dynamic_area* area = NULL;
|
||||
pr_err("heap %s base =0, try to find dynamic area \n", p_data->name);
|
||||
area = find_dynamic_area_by_name(p_data->name);
|
||||
if (area) {
|
||||
p_data->base = area->base;
|
||||
p_data->size = area->size;
|
||||
pr_err("have found heap name %s base = 0x%lx, size %zu\n",
|
||||
p_data->name,
|
||||
p_data->base, p_data->size);
|
||||
}
|
||||
}
|
||||
|
||||
heaps_data[index] = p_data;
|
||||
index++;
|
||||
}
|
||||
num_heaps = index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_ion_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
static struct ion_platform_heap *p_heap;
|
||||
|
||||
idev = ion_device_create(hisi_ion_custom_ioctl);
|
||||
err = hisi_set_platform_data(pdev);
|
||||
if (err) {
|
||||
pr_err("ion set platform data error!\n");
|
||||
goto err_free_idev;
|
||||
}
|
||||
heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_heap *) * num_heaps,
|
||||
GFP_KERNEL);
|
||||
if (!heaps) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_idev;
|
||||
}
|
||||
|
||||
/* FIXME will move to iommu driver*/
|
||||
if (hisi_ion_enable_iommu(pdev)) {
|
||||
dev_info(&pdev->dev, "enable iommu fail \n");
|
||||
err = -EINVAL;
|
||||
goto err_free_idev;
|
||||
}
|
||||
ion_pm_init();
|
||||
|
||||
/*
|
||||
* create the heaps as specified in the dts file
|
||||
*/
|
||||
for (i = 0; i < num_heaps; i++) {
|
||||
p_heap = heaps_data[i];
|
||||
|
||||
pr_info("id %d name %s base %lu size %lu\n",
|
||||
i, p_heap->name, p_heap->base, p_heap->size);
|
||||
|
||||
heaps[i] = ion_heap_create(p_heap);
|
||||
if (IS_ERR_OR_NULL(heaps[i])) {
|
||||
pr_err("error add %s of type %d with %lx@%lx\n",
|
||||
p_heap->name, p_heap->type,
|
||||
p_heap->base, (unsigned long)p_heap->size);
|
||||
continue;
|
||||
}
|
||||
|
||||
ion_device_add_heap(idev, heaps[i]);
|
||||
|
||||
pr_info("adding heap %s of type %d with %lx@%lx\n",
|
||||
p_heap->name, p_heap->type,
|
||||
p_heap->base, (unsigned long)p_heap->size);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_free_idev:
|
||||
ion_device_destroy(idev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hisi_ion_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_heaps; i++) {
|
||||
ion_heap_destroy(heaps[i]);
|
||||
heaps[i] = NULL;
|
||||
}
|
||||
ion_device_destroy(idev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hisi_ion_match_table[] = {
|
||||
{.compatible = "hisilicon,hisi-ion"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver hisi_ion_driver = {
|
||||
.probe = hisi_ion_probe,
|
||||
.remove = hisi_ion_remove,
|
||||
.driver = {
|
||||
.name = "ion-hisi",
|
||||
.of_match_table = hisi_ion_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init hisi_ion_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&hisi_ion_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
subsys_initcall(hisi_ion_init);
|
||||
@@ -1,5 +0,0 @@
|
||||
config HI6220_ION
|
||||
bool "Hi6220 ION Driver"
|
||||
depends on ARCH_HISI && ION
|
||||
help
|
||||
Build the Hisilicon Hi6220 ion driver.
|
||||
@@ -1 +1,2 @@
|
||||
obj-$(CONFIG_HI6220_ION) += hi6220_ion.o
|
||||
ccflags-y += -I$(srctree)/drivers/staging/android
|
||||
obj-y += hisi_ion.o
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Hisilicon Hi6220 ION Driver
|
||||
*
|
||||
* Copyright (c) 2015 Hisilicon Limited.
|
||||
*
|
||||
* Author: Chen Feng <puck.chen@hisilicon.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "Ion: " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
#include "../ion_priv.h"
|
||||
#include "../ion.h"
|
||||
#include "../ion_of.h"
|
||||
|
||||
struct hisi_ion_dev {
|
||||
struct ion_heap **heaps;
|
||||
struct ion_device *idev;
|
||||
struct ion_platform_data *data;
|
||||
};
|
||||
|
||||
static struct ion_of_heap hisi_heaps[] = {
|
||||
PLATFORM_HEAP("hisilicon,sys_user", 0,
|
||||
ION_HEAP_TYPE_SYSTEM, "sys_user"),
|
||||
PLATFORM_HEAP("hisilicon,sys_contig", 1,
|
||||
ION_HEAP_TYPE_SYSTEM_CONTIG, "sys_contig"),
|
||||
PLATFORM_HEAP("hisilicon,cma", ION_HEAP_TYPE_DMA, ION_HEAP_TYPE_DMA,
|
||||
"cma"),
|
||||
{}
|
||||
};
|
||||
|
||||
static int hi6220_ion_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_ion_dev *ipdev;
|
||||
int i;
|
||||
|
||||
ipdev = devm_kzalloc(&pdev->dev, sizeof(*ipdev), GFP_KERNEL);
|
||||
if (!ipdev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ipdev);
|
||||
|
||||
ipdev->idev = ion_device_create(NULL);
|
||||
if (IS_ERR(ipdev->idev))
|
||||
return PTR_ERR(ipdev->idev);
|
||||
|
||||
ipdev->data = ion_parse_dt(pdev, hisi_heaps);
|
||||
if (IS_ERR(ipdev->data))
|
||||
return PTR_ERR(ipdev->data);
|
||||
|
||||
ipdev->heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_heap) * ipdev->data->nr,
|
||||
GFP_KERNEL);
|
||||
if (!ipdev->heaps) {
|
||||
ion_destroy_platform_data(ipdev->data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < ipdev->data->nr; i++) {
|
||||
ipdev->heaps[i] = ion_heap_create(&ipdev->data->heaps[i]);
|
||||
if (!ipdev->heaps) {
|
||||
ion_destroy_platform_data(ipdev->data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ion_device_add_heap(ipdev->idev, ipdev->heaps[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi6220_ion_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_ion_dev *ipdev;
|
||||
int i;
|
||||
|
||||
ipdev = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < ipdev->data->nr; i++)
|
||||
ion_heap_destroy(ipdev->heaps[i]);
|
||||
|
||||
ion_destroy_platform_data(ipdev->data);
|
||||
ion_device_destroy(ipdev->idev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hi6220_ion_match_table[] = {
|
||||
{.compatible = "hisilicon,hi6220-ion"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver hi6220_ion_driver = {
|
||||
.probe = hi6220_ion_probe,
|
||||
.remove = hi6220_ion_remove,
|
||||
.driver = {
|
||||
.name = "ion-hi6220",
|
||||
.of_match_table = hi6220_ion_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init hi6220_ion_init(void)
|
||||
{
|
||||
return platform_driver_register(&hi6220_ion_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(hi6220_ion_init);
|
||||
@@ -0,0 +1,266 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#define pr_fmt(fmt) "ion: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hisi_ion.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include "../ion_priv.h"
|
||||
|
||||
struct hisi_ion_name_id_table {
|
||||
const char *name;
|
||||
unsigned int id;
|
||||
};
|
||||
|
||||
static struct hisi_ion_name_id_table name_id_table[] = {
|
||||
{"fb", ION_FB_HEAP_ID},
|
||||
{"vpu", ION_VPU_HEAP_ID},
|
||||
{"jpu", ION_JPU_HEAP_ID},
|
||||
{"gralloc-carveout", ION_GRALLOC_HEAP_ID},
|
||||
{"overlay", ION_OVERLAY_HEAP_ID},
|
||||
{"sys_user", ION_SYSTEM_HEAP_ID},
|
||||
{"sys_contig", ION_SYSTEM_CONTIG_HEAP_ID},
|
||||
{"cma", ION_HEAP_TYPE_DMA},
|
||||
};
|
||||
|
||||
struct hisi_ion_type_id_table {
|
||||
const char *name;
|
||||
enum ion_heap_type type;
|
||||
};
|
||||
|
||||
static struct hisi_ion_type_id_table type_id_table[] = {
|
||||
{"ion_system_contig", ION_HEAP_TYPE_SYSTEM_CONTIG},
|
||||
{"ion_system", ION_HEAP_TYPE_SYSTEM},
|
||||
{"ion_carveout", ION_HEAP_TYPE_CARVEOUT},
|
||||
{"ion_chunk", ION_HEAP_TYPE_CHUNK},
|
||||
{"ion_dma", ION_HEAP_TYPE_DMA},
|
||||
{"ion_custom", ION_HEAP_TYPE_CUSTOM},
|
||||
{"ion_cma", ION_HEAP_TYPE_DMA},
|
||||
};
|
||||
|
||||
#define HISI_ION_HEAP_NUM 16
|
||||
|
||||
static struct ion_platform_data hisi_ion_platform_data = {0};
|
||||
static struct ion_platform_heap hisi_ion_platform_heap[HISI_ION_HEAP_NUM] = {{0} };
|
||||
|
||||
static struct ion_device *hisi_ion_device;
|
||||
static struct ion_heap *hisi_ion_heap[HISI_ION_HEAP_NUM] = {NULL};
|
||||
|
||||
int hisi_ion_get_heap_info(unsigned int id, struct ion_heap_info_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(!data);
|
||||
|
||||
for (i = 0; i < hisi_ion_platform_data.nr; i++) {
|
||||
if (hisi_ion_platform_heap[i].id == id) {
|
||||
data->heap_phy = hisi_ion_platform_heap[i].base;
|
||||
data->heap_size = hisi_ion_platform_heap[i].size;
|
||||
strncpy((void *)data->name, (void *)hisi_ion_platform_heap[i].name, HISI_ION_NAME_LEN);
|
||||
pr_info("heap info : id %d name %s phy 0x%llx size %u\n",
|
||||
id, data->name, data->heap_phy, data->heap_size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pr_err("in %s please check the id %d\n", __func__, id);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(hisi_ion_get_heap_info);
|
||||
|
||||
struct ion_device *get_ion_device(void)
|
||||
{
|
||||
return hisi_ion_device;
|
||||
}
|
||||
EXPORT_SYMBOL(get_ion_device);
|
||||
|
||||
static int get_id_by_name(const char *name, unsigned int *id)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = sizeof(name_id_table)/sizeof(name_id_table[0]);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (strncmp(name, name_id_table[i].name, HISI_ION_NAME_LEN))
|
||||
continue;
|
||||
|
||||
*id = name_id_table[i].id;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_type_by_name(const char *name, enum ion_heap_type *type)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = sizeof(type_id_table)/sizeof(type_id_table[0]);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (strncmp(name, type_id_table[i].name, HISI_ION_NAME_LEN))
|
||||
continue;
|
||||
|
||||
*type = type_id_table[i].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static u64 hisi_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device ion_cma_device = {
|
||||
.name = "ion-cma-device",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.dma_mask = &hisi_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
}
|
||||
};
|
||||
|
||||
static int hisi_ion_setup_platform_data(struct platform_device *dev)
|
||||
{
|
||||
struct device_node *node, *np;
|
||||
const char *heap_name;
|
||||
const char *type_name;
|
||||
unsigned int id;
|
||||
unsigned int range[2] = {0, 0};
|
||||
enum ion_heap_type type;
|
||||
int ret;
|
||||
int index = 0;
|
||||
|
||||
node = dev->dev.of_node;
|
||||
for_each_child_of_node(node, np) {
|
||||
ret = of_property_read_string(np, "heap-name", &heap_name);
|
||||
if (ret < 0) {
|
||||
pr_err("in node %s please check the name property of node %s\n", __func__, np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = get_id_by_name(heap_name, &id);
|
||||
if (ret < 0) {
|
||||
pr_err("in node %s please check the name %s\n", __func__, heap_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np, "heap-range", range, ARRAY_SIZE(range));
|
||||
if (ret < 0) {
|
||||
pr_err("in node %s please check the range property of node %s\n", __func__, np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ret = of_property_read_string(np, "heap-type", &type_name);
|
||||
if (ret < 0) {
|
||||
pr_err("in node %s please check the type property of node %s\n", __func__, np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = get_type_by_name(type_name, &type);
|
||||
if (ret < 0) {
|
||||
pr_err("in node %s please check the type %s\n", __func__, type_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
hisi_ion_platform_heap[index].name = heap_name;
|
||||
hisi_ion_platform_heap[index].base = range[0];
|
||||
hisi_ion_platform_heap[index].size = range[1];
|
||||
hisi_ion_platform_heap[index].id = id;
|
||||
hisi_ion_platform_heap[index].type = type;
|
||||
if (type == ION_HEAP_TYPE_DMA) {
|
||||
// ion_cma_device.dev.archdata.dma_ops = swiotlb_dma_ops;
|
||||
hisi_ion_platform_heap[index].priv =
|
||||
(void *)&ion_cma_device.dev;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
hisi_ion_platform_data.nr = index;
|
||||
hisi_ion_platform_data.heaps = hisi_ion_platform_heap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_ion_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i, err;
|
||||
struct ion_heap *heap;
|
||||
struct ion_platform_heap *heap_data;
|
||||
|
||||
if (hisi_ion_setup_platform_data(pdev)) {
|
||||
pr_err("hisi_ion_setup_platform_data is failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hisi_ion_device = ion_device_create(NULL);
|
||||
if (IS_ERR_OR_NULL(hisi_ion_device))
|
||||
return PTR_ERR(hisi_ion_device);
|
||||
/*
|
||||
* create the heaps as specified in the board file
|
||||
*/
|
||||
for (i = 0; i < hisi_ion_platform_data.nr; i++) {
|
||||
heap_data = &hisi_ion_platform_data.heaps[i];
|
||||
heap = ion_heap_create(heap_data);
|
||||
if (IS_ERR_OR_NULL(heap)) {
|
||||
err = PTR_ERR(heap);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ion_device_add_heap(hisi_ion_device, heap);
|
||||
hisi_ion_heap[i] = heap;
|
||||
}
|
||||
platform_set_drvdata(pdev, hisi_ion_device);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
for (i = 0; i < HISI_ION_HEAP_NUM; i++) {
|
||||
if (!hisi_ion_heap[i])
|
||||
continue;
|
||||
ion_heap_destroy(hisi_ion_heap[i]);
|
||||
hisi_ion_heap[i] = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hisi_ion_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
ion_device_destroy(hisi_ion_device);
|
||||
for (i = 0; i < HISI_ION_HEAP_NUM; i++) {
|
||||
if (!hisi_ion_heap[i])
|
||||
continue;
|
||||
ion_heap_destroy(hisi_ion_heap[i]);
|
||||
hisi_ion_heap[i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id hisi_ion_match_table[] = {
|
||||
{.compatible = "hisilicon,ion"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver hisi_ion_driver = {
|
||||
.probe = hisi_ion_probe,
|
||||
.remove = hisi_ion_remove,
|
||||
.driver = {
|
||||
.name = "ion",
|
||||
.of_match_table = hisi_ion_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hisi_ion_driver);
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2011 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/kernel.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
#include "compat_ion.h"
|
||||
|
||||
union ion_ioctl_arg {
|
||||
struct ion_fd_data fd;
|
||||
struct ion_allocation_data allocation;
|
||||
struct ion_handle_data handle;
|
||||
struct ion_custom_data custom;
|
||||
struct ion_heap_query query;
|
||||
};
|
||||
|
||||
static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case ION_IOC_HEAP_QUERY:
|
||||
ret = arg->query.reserved0 != 0;
|
||||
ret |= arg->query.reserved1 != 0;
|
||||
ret |= arg->query.reserved2 != 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
/* fix up the cases where the ioctl direction bits are incorrect */
|
||||
static unsigned int ion_ioctl_dir(unsigned int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case ION_IOC_SYNC:
|
||||
case ION_IOC_FREE:
|
||||
case ION_IOC_CUSTOM:
|
||||
return _IOC_WRITE;
|
||||
default:
|
||||
return _IOC_DIR(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ion_client *client = filp->private_data;
|
||||
struct ion_device *dev = client->dev;
|
||||
struct ion_handle *cleanup_handle = NULL;
|
||||
int ret = 0;
|
||||
unsigned int dir;
|
||||
union ion_ioctl_arg data;
|
||||
|
||||
dir = ion_ioctl_dir(cmd);
|
||||
|
||||
if (_IOC_SIZE(cmd) > sizeof(data))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The copy_from_user is unconditional here for both read and write
|
||||
* to do the validate. If there is no write for the ioctl, the
|
||||
* buffer is cleared
|
||||
*/
|
||||
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = validate_ioctl_arg(cmd, &data);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return ret;
|
||||
|
||||
if (!(dir & _IOC_WRITE))
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
switch (cmd) {
|
||||
case ION_IOC_ALLOC:
|
||||
{
|
||||
struct ion_handle *handle;
|
||||
|
||||
handle = ion_alloc(client, data.allocation.len,
|
||||
data.allocation.align,
|
||||
data.allocation.heap_id_mask,
|
||||
data.allocation.flags);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
|
||||
data.allocation.handle = handle->id;
|
||||
|
||||
cleanup_handle = handle;
|
||||
break;
|
||||
}
|
||||
case ION_IOC_FREE:
|
||||
{
|
||||
struct ion_handle *handle;
|
||||
|
||||
mutex_lock(&client->lock);
|
||||
handle = ion_handle_get_by_id_nolock(client, data.handle.handle);
|
||||
if (IS_ERR(handle)) {
|
||||
mutex_unlock(&client->lock);
|
||||
return PTR_ERR(handle);
|
||||
}
|
||||
ion_free_nolock(client, handle);
|
||||
ion_handle_put_nolock(handle);
|
||||
mutex_unlock(&client->lock);
|
||||
break;
|
||||
}
|
||||
case ION_IOC_SHARE:
|
||||
case ION_IOC_MAP:
|
||||
{
|
||||
struct ion_handle *handle;
|
||||
|
||||
handle = ion_handle_get_by_id(client, data.handle.handle);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
data.fd.fd = ion_share_dma_buf_fd(client, handle);
|
||||
ion_handle_put(handle);
|
||||
if (data.fd.fd < 0)
|
||||
ret = data.fd.fd;
|
||||
break;
|
||||
}
|
||||
case ION_IOC_IMPORT:
|
||||
{
|
||||
struct ion_handle *handle;
|
||||
|
||||
handle = ion_import_dma_buf_fd(client, data.fd.fd);
|
||||
if (IS_ERR(handle))
|
||||
ret = PTR_ERR(handle);
|
||||
else
|
||||
data.handle.handle = handle->id;
|
||||
break;
|
||||
}
|
||||
case ION_IOC_SYNC:
|
||||
{
|
||||
ret = ion_sync_for_device(client, data.fd.fd);
|
||||
break;
|
||||
}
|
||||
case ION_IOC_CUSTOM:
|
||||
{
|
||||
if (!dev->custom_ioctl)
|
||||
return -ENOTTY;
|
||||
ret = dev->custom_ioctl(client, data.custom.cmd,
|
||||
data.custom.arg);
|
||||
break;
|
||||
}
|
||||
case ION_IOC_HEAP_QUERY:
|
||||
ret = ion_query_heaps(client, &data.query);
|
||||
break;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
if (dir & _IOC_READ) {
|
||||
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
|
||||
if (cleanup_handle)
|
||||
ion_free(client, cleanup_handle);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
+585
-130
File diff suppressed because it is too large
Load Diff
@@ -72,6 +72,17 @@ struct ion_platform_data {
|
||||
struct ion_platform_heap *heaps;
|
||||
};
|
||||
|
||||
/**
|
||||
* ion_reserve() - reserve memory for ion heaps if applicable
|
||||
* @data: platform data specifying starting physical address and
|
||||
* size
|
||||
*
|
||||
* Calls memblock reserve to set aside memory for heaps that are
|
||||
* located at specific memory addresses or of specific sizes not
|
||||
* managed by the kernel
|
||||
*/
|
||||
void ion_reserve(struct ion_platform_data *data);
|
||||
|
||||
/**
|
||||
* ion_client_create() - allocate a client and returns it
|
||||
* @dev: the global ion device
|
||||
@@ -118,6 +129,36 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
|
||||
*/
|
||||
void ion_free(struct ion_client *client, struct ion_handle *handle);
|
||||
|
||||
/**
|
||||
* ion_phys - returns the physical address and len of a handle
|
||||
* @client: the client
|
||||
* @handle: the handle
|
||||
* @addr: a pointer to put the address in
|
||||
* @len: a pointer to put the length in
|
||||
*
|
||||
* This function queries the heap for a particular handle to get the
|
||||
* handle's physical address. It't output is only correct if
|
||||
* a heap returns physically contiguous memory -- in other cases
|
||||
* this api should not be implemented -- ion_sg_table should be used
|
||||
* instead. Returns -EINVAL if the handle is invalid. This has
|
||||
* no implications on the reference counting of the handle --
|
||||
* the returned value may not be valid if the caller is not
|
||||
* holding a reference.
|
||||
*/
|
||||
int ion_phys(struct ion_client *client, struct ion_handle *handle,
|
||||
ion_phys_addr_t *addr, size_t *len);
|
||||
|
||||
/**
|
||||
* ion_map_dma - return an sg_table describing a handle
|
||||
* @client: the client
|
||||
* @handle: the handle
|
||||
*
|
||||
* This function returns the sg_table describing
|
||||
* a particular ion handle.
|
||||
*/
|
||||
struct sg_table *ion_sg_table(struct ion_client *client,
|
||||
struct ion_handle *handle);
|
||||
|
||||
/**
|
||||
* ion_map_kernel - create mapping for the given handle
|
||||
* @client: the client
|
||||
@@ -151,26 +192,39 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client,
|
||||
int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle);
|
||||
|
||||
/**
|
||||
* ion_import_dma_buf() - get ion_handle from dma-buf
|
||||
* ion_map_iommu() - create iommu mapping for the given handle
|
||||
* @client: the client
|
||||
* @dmabuf: the dma-buf
|
||||
*
|
||||
* Get the ion_buffer associated with the dma-buf and return the ion_handle.
|
||||
* If no ion_handle exists for this buffer, return newly created ion_handle.
|
||||
* If dma-buf from another exporter is passed, return ERR_PTR(-EINVAL)
|
||||
* @handle: the handle
|
||||
* @format: the format of iommu mapping
|
||||
*/
|
||||
struct ion_handle *ion_import_dma_buf(struct ion_client *client,
|
||||
struct dma_buf *dmabuf);
|
||||
int ion_map_iommu(struct ion_client *client, struct ion_handle *handle,
|
||||
struct iommu_map_format *format);
|
||||
|
||||
/**
|
||||
* ion_import_dma_buf_fd() - given a dma-buf fd from the ion exporter get handle
|
||||
* ion_unmap_iommu() - destroy a iommu mapping for a handle
|
||||
* @client: the client
|
||||
* @handle: the handle
|
||||
*/
|
||||
void ion_unmap_iommu(struct ion_client *client, struct ion_handle *handle);
|
||||
|
||||
/**
|
||||
* ion_change_flags() - change buffer flags
|
||||
* @client: the client
|
||||
* @handle: the handle
|
||||
* @flags: flags
|
||||
*/
|
||||
int ion_change_flags(struct ion_client *client,
|
||||
struct ion_handle *handle, int flags);
|
||||
|
||||
/**
|
||||
* ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle
|
||||
* @client: the client
|
||||
* @fd: the dma-buf fd
|
||||
*
|
||||
* Given an dma-buf fd that was allocated through ion via ion_share_dma_buf_fd,
|
||||
* import that fd and return a handle representing it. If a dma-buf from
|
||||
* Given an dma-buf fd that was allocated through ion via ion_share_dma_buf,
|
||||
* import that fd and return a handle representing it. If a dma-buf from
|
||||
* another exporter is passed in this function will return ERR_PTR(-EINVAL)
|
||||
*/
|
||||
struct ion_handle *ion_import_dma_buf_fd(struct ion_client *client, int fd);
|
||||
struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd);
|
||||
|
||||
#endif /* _LINUX_ION_H */
|
||||
|
||||
@@ -25,17 +25,15 @@
|
||||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
|
||||
#define ION_CARVEOUT_ALLOCATE_FAIL -1
|
||||
|
||||
struct ion_carveout_heap {
|
||||
struct ion_heap heap;
|
||||
struct gen_pool *pool;
|
||||
ion_phys_addr_t base;
|
||||
};
|
||||
|
||||
static ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
|
||||
unsigned long size,
|
||||
unsigned long align)
|
||||
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
|
||||
unsigned long size,
|
||||
unsigned long align)
|
||||
{
|
||||
struct ion_carveout_heap *carveout_heap =
|
||||
container_of(heap, struct ion_carveout_heap, heap);
|
||||
@@ -47,8 +45,8 @@ static ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
|
||||
unsigned long size)
|
||||
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
|
||||
unsigned long size)
|
||||
{
|
||||
struct ion_carveout_heap *carveout_heap =
|
||||
container_of(heap, struct ion_carveout_heap, heap);
|
||||
@@ -58,6 +56,19 @@ static void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
|
||||
gen_pool_free(carveout_heap->pool, addr, size);
|
||||
}
|
||||
|
||||
static int ion_carveout_heap_phys(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
ion_phys_addr_t *addr, size_t *len)
|
||||
{
|
||||
struct sg_table *table = buffer->priv_virt;
|
||||
struct page *page = sg_page(table->sgl);
|
||||
ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
|
||||
|
||||
*addr = paddr;
|
||||
*len = buffer->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ion_carveout_heap_allocate(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size, unsigned long align,
|
||||
@@ -70,7 +81,7 @@ static int ion_carveout_heap_allocate(struct ion_heap *heap,
|
||||
if (align > PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
table = kmalloc(sizeof(*table), GFP_KERNEL);
|
||||
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
ret = sg_alloc_table(table, 1, GFP_KERNEL);
|
||||
@@ -84,7 +95,7 @@ static int ion_carveout_heap_allocate(struct ion_heap *heap,
|
||||
}
|
||||
|
||||
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
|
||||
buffer->sg_table = table;
|
||||
buffer->priv_virt = table;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -98,7 +109,7 @@ err_free:
|
||||
static void ion_carveout_heap_free(struct ion_buffer *buffer)
|
||||
{
|
||||
struct ion_heap *heap = buffer->heap;
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
struct sg_table *table = buffer->priv_virt;
|
||||
struct page *page = sg_page(table->sgl);
|
||||
ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
|
||||
|
||||
@@ -106,19 +117,35 @@ static void ion_carveout_heap_free(struct ion_buffer *buffer)
|
||||
|
||||
if (ion_buffer_cached(buffer))
|
||||
dma_sync_sg_for_device(NULL, table->sgl, table->nents,
|
||||
DMA_BIDIRECTIONAL);
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
ion_carveout_free(heap, paddr, buffer->size);
|
||||
sg_free_table(table);
|
||||
kfree(table);
|
||||
}
|
||||
|
||||
static struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
return buffer->priv_virt;
|
||||
}
|
||||
|
||||
static void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ion_heap_ops carveout_heap_ops = {
|
||||
.allocate = ion_carveout_heap_allocate,
|
||||
.free = ion_carveout_heap_free,
|
||||
.phys = ion_carveout_heap_phys,
|
||||
.map_dma = ion_carveout_heap_map_dma,
|
||||
.unmap_dma = ion_carveout_heap_unmap_dma,
|
||||
.map_user = ion_heap_map_user,
|
||||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
.map_iommu = ion_heap_map_iommu,
|
||||
.unmap_iommu = ion_heap_unmap_iommu,
|
||||
};
|
||||
|
||||
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
|
||||
@@ -138,7 +165,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL);
|
||||
carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
|
||||
if (!carveout_heap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ struct ion_chunk_heap {
|
||||
};
|
||||
|
||||
static int ion_chunk_heap_allocate(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size, unsigned long align,
|
||||
unsigned long flags)
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size, unsigned long align,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct ion_chunk_heap *chunk_heap =
|
||||
container_of(heap, struct ion_chunk_heap, heap);
|
||||
@@ -55,7 +55,7 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
|
||||
if (allocated_size > chunk_heap->size - chunk_heap->allocated)
|
||||
return -ENOMEM;
|
||||
|
||||
table = kmalloc(sizeof(*table), GFP_KERNEL);
|
||||
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
ret = sg_alloc_table(table, num_chunks, GFP_KERNEL);
|
||||
@@ -71,11 +71,11 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
|
||||
if (!paddr)
|
||||
goto err;
|
||||
sg_set_page(sg, pfn_to_page(PFN_DOWN(paddr)),
|
||||
chunk_heap->chunk_size, 0);
|
||||
chunk_heap->chunk_size, 0);
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
buffer->sg_table = table;
|
||||
buffer->priv_virt = table;
|
||||
chunk_heap->allocated += allocated_size;
|
||||
return 0;
|
||||
err:
|
||||
@@ -95,7 +95,7 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
|
||||
struct ion_heap *heap = buffer->heap;
|
||||
struct ion_chunk_heap *chunk_heap =
|
||||
container_of(heap, struct ion_chunk_heap, heap);
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
struct sg_table *table = buffer->priv_virt;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
unsigned long allocated_size;
|
||||
@@ -106,7 +106,7 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
|
||||
|
||||
if (ion_buffer_cached(buffer))
|
||||
dma_sync_sg_for_device(NULL, table->sgl, table->nents,
|
||||
DMA_BIDIRECTIONAL);
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
for_each_sg(table->sgl, sg, table->nents, i) {
|
||||
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
|
||||
@@ -117,9 +117,22 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
|
||||
kfree(table);
|
||||
}
|
||||
|
||||
static struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
return buffer->priv_virt;
|
||||
}
|
||||
|
||||
static void ion_chunk_heap_unmap_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ion_heap_ops chunk_heap_ops = {
|
||||
.allocate = ion_chunk_heap_allocate,
|
||||
.free = ion_chunk_heap_free,
|
||||
.map_dma = ion_chunk_heap_map_dma,
|
||||
.unmap_dma = ion_chunk_heap_unmap_dma,
|
||||
.map_user = ion_heap_map_user,
|
||||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
@@ -141,7 +154,7 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
chunk_heap = kzalloc(sizeof(*chunk_heap), GFP_KERNEL);
|
||||
chunk_heap = kzalloc(sizeof(struct ion_chunk_heap), GFP_KERNEL);
|
||||
if (!chunk_heap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -161,7 +174,7 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
|
||||
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
|
||||
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
|
||||
pr_debug("%s: base %lu size %zu align %ld\n", __func__,
|
||||
chunk_heap->base, heap_data->size, heap_data->align);
|
||||
chunk_heap->base, heap_data->size, heap_data->align);
|
||||
|
||||
return &chunk_heap->heap;
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
goto free_table;
|
||||
/* keep this for memory release */
|
||||
buffer->priv_virt = info;
|
||||
buffer->sg_table = info->table;
|
||||
dev_dbg(dev, "Allocate buffer %p\n", buffer);
|
||||
return 0;
|
||||
|
||||
@@ -106,6 +105,36 @@ static void ion_cma_free(struct ion_buffer *buffer)
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
/* return physical address in addr */
|
||||
static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
ion_phys_addr_t *addr, size_t *len)
|
||||
{
|
||||
struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
|
||||
struct device *dev = cma_heap->dev;
|
||||
struct ion_cma_buffer_info *info = buffer->priv_virt;
|
||||
|
||||
dev_dbg(dev, "Return buffer %p physical address %pa\n", buffer,
|
||||
&info->handle);
|
||||
|
||||
*addr = info->handle;
|
||||
*len = buffer->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
struct ion_cma_buffer_info *info = buffer->priv_virt;
|
||||
|
||||
return info->table;
|
||||
}
|
||||
|
||||
static void ion_cma_heap_unmap_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
@@ -126,16 +155,21 @@ static void *ion_cma_map_kernel(struct ion_heap *heap,
|
||||
}
|
||||
|
||||
static void ion_cma_unmap_kernel(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ion_heap_ops ion_cma_ops = {
|
||||
.allocate = ion_cma_allocate,
|
||||
.free = ion_cma_free,
|
||||
.map_dma = ion_cma_heap_map_dma,
|
||||
.unmap_dma = ion_cma_heap_unmap_dma,
|
||||
.phys = ion_cma_phys,
|
||||
.map_user = ion_cma_mmap,
|
||||
.map_kernel = ion_cma_map_kernel,
|
||||
.unmap_kernel = ion_cma_unmap_kernel,
|
||||
.map_iommu = ion_heap_map_iommu,
|
||||
.unmap_iommu = ion_heap_unmap_iommu,
|
||||
};
|
||||
|
||||
struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)
|
||||
|
||||
@@ -68,8 +68,6 @@ static int __init ion_dummy_init(void)
|
||||
int i, err;
|
||||
|
||||
idev = ion_device_create(NULL);
|
||||
if (IS_ERR(idev))
|
||||
return PTR_ERR(idev);
|
||||
heaps = kcalloc(dummy_ion_pdata.nr, sizeof(struct ion_heap *),
|
||||
GFP_KERNEL);
|
||||
if (!heaps)
|
||||
@@ -99,7 +97,7 @@ static int __init ion_dummy_init(void)
|
||||
struct ion_platform_heap *heap_data = &dummy_ion_pdata.heaps[i];
|
||||
|
||||
if (heap_data->type == ION_HEAP_TYPE_CARVEOUT &&
|
||||
!heap_data->base)
|
||||
!heap_data->base)
|
||||
continue;
|
||||
|
||||
if (heap_data->type == ION_HEAP_TYPE_CHUNK && !heap_data->base)
|
||||
@@ -120,12 +118,12 @@ err:
|
||||
|
||||
if (carveout_ptr) {
|
||||
free_pages_exact(carveout_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
|
||||
carveout_ptr = NULL;
|
||||
}
|
||||
if (chunk_ptr) {
|
||||
free_pages_exact(chunk_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
|
||||
chunk_ptr = NULL;
|
||||
}
|
||||
return err;
|
||||
@@ -144,12 +142,12 @@ static void __exit ion_dummy_exit(void)
|
||||
|
||||
if (carveout_ptr) {
|
||||
free_pages_exact(carveout_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
|
||||
carveout_ptr = NULL;
|
||||
}
|
||||
if (chunk_ptr) {
|
||||
free_pages_exact(chunk_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
|
||||
chunk_ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/hisi/ion-iommu.h>
|
||||
|
||||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
|
||||
@@ -93,7 +96,7 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
}
|
||||
len = min(len, remainder);
|
||||
ret = remap_pfn_range(vma, addr, page_to_pfn(page), len,
|
||||
vma->vm_page_prot);
|
||||
vma->vm_page_prot);
|
||||
if (ret)
|
||||
return ret;
|
||||
addr += len;
|
||||
@@ -103,6 +106,31 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ion_heap_map_iommu(struct ion_buffer *buffer,
|
||||
struct ion_iommu_map *map_data)
|
||||
{
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
int ret;
|
||||
|
||||
ret = hisi_iommu_map_domain(table->sgl, &map_data->format);
|
||||
if (ret) {
|
||||
pr_err("%s: iommu map failed, heap: %s\n", __func__,
|
||||
buffer->heap->name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ion_heap_unmap_iommu(struct ion_iommu_map *map_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hisi_iommu_unmap_domain(&map_data->format);
|
||||
if (ret) {
|
||||
pr_err("%s: iommu unmap failed, heap: %s\n", __func__,
|
||||
map_data->buffer->heap->name);
|
||||
}
|
||||
}
|
||||
|
||||
static int ion_heap_clear_pages(struct page **pages, int num, pgprot_t pgprot)
|
||||
{
|
||||
void *addr = vm_map_ram(pages, num, -1, pgprot);
|
||||
@@ -116,7 +144,7 @@ static int ion_heap_clear_pages(struct page **pages, int num, pgprot_t pgprot)
|
||||
}
|
||||
|
||||
static int ion_heap_sglist_zero(struct scatterlist *sgl, unsigned int nents,
|
||||
pgprot_t pgprot)
|
||||
pgprot_t pgprot)
|
||||
{
|
||||
int p = 0;
|
||||
int ret = 0;
|
||||
@@ -181,7 +209,7 @@ size_t ion_heap_freelist_size(struct ion_heap *heap)
|
||||
}
|
||||
|
||||
static size_t _ion_heap_freelist_drain(struct ion_heap *heap, size_t size,
|
||||
bool skip_pools)
|
||||
bool skip_pools)
|
||||
{
|
||||
struct ion_buffer *buffer;
|
||||
size_t total_drained = 0;
|
||||
@@ -266,7 +294,7 @@ int ion_heap_init_deferred_free(struct ion_heap *heap)
|
||||
}
|
||||
|
||||
static unsigned long ion_heap_shrink_count(struct shrinker *shrinker,
|
||||
struct shrink_control *sc)
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
@@ -279,7 +307,7 @@ static unsigned long ion_heap_shrink_count(struct shrinker *shrinker,
|
||||
}
|
||||
|
||||
static unsigned long ion_heap_shrink_scan(struct shrinker *shrinker,
|
||||
struct shrink_control *sc)
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Based on work from:
|
||||
* Andrew Andrianov <andrew@ncrmnt.org>
|
||||
* Google
|
||||
* The Linux Foundation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
#include "ion_of.h"
|
||||
|
||||
static int ion_parse_dt_heap_common(struct device_node *heap_node,
|
||||
struct ion_platform_heap *heap,
|
||||
struct ion_of_heap *compatible)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; compatible[i].name; i++) {
|
||||
if (of_device_is_compatible(heap_node, compatible[i].compat))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!compatible[i].name)
|
||||
return -ENODEV;
|
||||
|
||||
heap->id = compatible[i].heap_id;
|
||||
heap->type = compatible[i].type;
|
||||
heap->name = compatible[i].name;
|
||||
heap->align = compatible[i].align;
|
||||
|
||||
/* Some kind of callback function pointer? */
|
||||
|
||||
pr_info("%s: id %d type %d name %s align %lx\n", __func__,
|
||||
heap->id, heap->type, heap->name, heap->align);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ion_setup_heap_common(struct platform_device *parent,
|
||||
struct device_node *heap_node,
|
||||
struct ion_platform_heap *heap)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (heap->type) {
|
||||
case ION_HEAP_TYPE_CARVEOUT:
|
||||
case ION_HEAP_TYPE_CHUNK:
|
||||
if (heap->base && heap->size)
|
||||
return 0;
|
||||
|
||||
ret = of_reserved_mem_device_init(heap->priv);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ion_platform_data *ion_parse_dt(struct platform_device *pdev,
|
||||
struct ion_of_heap *compatible)
|
||||
{
|
||||
int num_heaps, ret;
|
||||
const struct device_node *dt_node = pdev->dev.of_node;
|
||||
struct device_node *node;
|
||||
struct ion_platform_heap *heaps;
|
||||
struct ion_platform_data *data;
|
||||
int i = 0;
|
||||
|
||||
num_heaps = of_get_available_child_count(dt_node);
|
||||
|
||||
if (!num_heaps)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_platform_heap) * num_heaps,
|
||||
GFP_KERNEL);
|
||||
if (!heaps)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct ion_platform_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_available_child_of_node(dt_node, node) {
|
||||
struct platform_device *heap_pdev;
|
||||
|
||||
ret = ion_parse_dt_heap_common(node, &heaps[i], compatible);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
heap_pdev = of_platform_device_create(node, heaps[i].name,
|
||||
&pdev->dev);
|
||||
if (!heap_pdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
heap_pdev->dev.platform_data = &heaps[i];
|
||||
|
||||
heaps[i].priv = &heap_pdev->dev;
|
||||
|
||||
ret = ion_setup_heap_common(pdev, node, &heaps[i]);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
i++;
|
||||
}
|
||||
|
||||
data->heaps = heaps;
|
||||
data->nr = num_heaps;
|
||||
return data;
|
||||
|
||||
out_err:
|
||||
for ( ; i >= 0; i--)
|
||||
if (heaps[i].priv)
|
||||
of_device_unregister(to_platform_device(heaps[i].priv));
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void ion_destroy_platform_data(struct ion_platform_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data->nr; i++)
|
||||
if (data->heaps[i].priv)
|
||||
of_device_unregister(to_platform_device(
|
||||
data->heaps[i].priv));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_RESERVED_MEM
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
static int rmem_ion_device_init(struct reserved_mem *rmem, struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ion_platform_heap *heap = pdev->dev.platform_data;
|
||||
|
||||
heap->base = rmem->base;
|
||||
heap->base = rmem->size;
|
||||
pr_debug("%s: heap %s base %pa size %pa dev %p\n", __func__,
|
||||
heap->name, &rmem->base, &rmem->size, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmem_ion_device_release(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops rmem_dma_ops = {
|
||||
.device_init = rmem_ion_device_init,
|
||||
.device_release = rmem_ion_device_release,
|
||||
};
|
||||
|
||||
static int __init rmem_ion_setup(struct reserved_mem *rmem)
|
||||
{
|
||||
phys_addr_t size = rmem->size;
|
||||
|
||||
size = size / 1024;
|
||||
|
||||
pr_info("Ion memory setup at %pa size %pa MiB\n",
|
||||
&rmem->base, &size);
|
||||
rmem->ops = &rmem_dma_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RESERVEDMEM_OF_DECLARE(ion, "ion-region", rmem_ion_setup);
|
||||
#endif
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Based on work from:
|
||||
* Andrew Andrianov <andrew@ncrmnt.org>
|
||||
* Google
|
||||
* The Linux Foundation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _ION_OF_H
|
||||
#define _ION_OF_H
|
||||
|
||||
struct ion_of_heap {
|
||||
const char *compat;
|
||||
int heap_id;
|
||||
int type;
|
||||
const char *name;
|
||||
int align;
|
||||
};
|
||||
|
||||
#define PLATFORM_HEAP(_compat, _id, _type, _name) \
|
||||
{ \
|
||||
.compat = _compat, \
|
||||
.heap_id = _id, \
|
||||
.type = _type, \
|
||||
.name = _name, \
|
||||
.align = PAGE_SIZE, \
|
||||
}
|
||||
|
||||
struct ion_platform_data *ion_parse_dt(struct platform_device *pdev,
|
||||
struct ion_of_heap *compatible);
|
||||
|
||||
void ion_destroy_platform_data(struct ion_platform_data *data);
|
||||
|
||||
#endif
|
||||
@@ -30,15 +30,17 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
|
||||
|
||||
if (!page)
|
||||
return NULL;
|
||||
if (!pool->cached)
|
||||
ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order,
|
||||
DMA_BIDIRECTIONAL);
|
||||
ion_page_pool_alloc_set_cache_policy(pool, page);
|
||||
|
||||
ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order,
|
||||
DMA_BIDIRECTIONAL);
|
||||
return page;
|
||||
}
|
||||
|
||||
static void ion_page_pool_free_pages(struct ion_page_pool *pool,
|
||||
struct page *page)
|
||||
{
|
||||
ion_page_pool_free_set_cache_policy(pool, page);
|
||||
__free_pages(page, pool->order);
|
||||
}
|
||||
|
||||
@@ -104,6 +106,11 @@ void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
|
||||
ion_page_pool_free_pages(pool, page);
|
||||
}
|
||||
|
||||
void ion_page_pool_free_immediate(struct ion_page_pool *pool, struct page *page)
|
||||
{
|
||||
ion_page_pool_free_pages(pool, page);
|
||||
}
|
||||
|
||||
static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
|
||||
{
|
||||
int count = pool->low_count;
|
||||
@@ -115,7 +122,7 @@ static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
|
||||
}
|
||||
|
||||
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
int nr_to_scan)
|
||||
{
|
||||
int freed = 0;
|
||||
bool high;
|
||||
@@ -148,11 +155,10 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
return freed;
|
||||
}
|
||||
|
||||
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
|
||||
bool cached)
|
||||
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
|
||||
{
|
||||
struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
|
||||
|
||||
struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
|
||||
GFP_KERNEL);
|
||||
if (!pool)
|
||||
return NULL;
|
||||
pool->high_count = 0;
|
||||
@@ -163,8 +169,6 @@ struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
|
||||
pool->order = order;
|
||||
mutex_init(&pool->mutex);
|
||||
plist_node_init(&pool->list, order);
|
||||
if (cached)
|
||||
pool->cached = true;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
@@ -26,10 +26,21 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/shrinker.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/iommu.h>
|
||||
#ifdef CONFIG_ION_POOL_CACHE_POLICY
|
||||
#include <asm/cacheflush.h>
|
||||
#endif
|
||||
|
||||
#include "ion.h"
|
||||
|
||||
struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
|
||||
|
||||
struct ion_iommu_map {
|
||||
struct ion_buffer *buffer;
|
||||
struct kref ref;
|
||||
struct iommu_map_format format;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ion_buffer - metadata for a particular buffer
|
||||
* @ref: reference count
|
||||
@@ -41,6 +52,8 @@
|
||||
* @size: size of the buffer
|
||||
* @priv_virt: private data to the buffer representable as
|
||||
* a void *
|
||||
* @priv_phys: private data to the buffer representable as
|
||||
* an ion_phys_addr_t (and someday a phys_addr_t)
|
||||
* @lock: protects the buffers cnt fields
|
||||
* @kmap_cnt: number of times the buffer is mapped to the kernel
|
||||
* @vaddr: the kernel mapping if kmap_cnt is not zero
|
||||
@@ -66,7 +79,10 @@ struct ion_buffer {
|
||||
unsigned long flags;
|
||||
unsigned long private_flags;
|
||||
size_t size;
|
||||
void *priv_virt;
|
||||
union {
|
||||
void *priv_virt;
|
||||
ion_phys_addr_t priv_phys;
|
||||
};
|
||||
struct mutex lock;
|
||||
int kmap_cnt;
|
||||
void *vaddr;
|
||||
@@ -78,88 +94,21 @@ struct ion_buffer {
|
||||
int handle_count;
|
||||
char task_comm[TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
struct ion_iommu_map *iommu_map;
|
||||
/*use for sync & free when buffer alloc from cpu draw heap*/
|
||||
struct sg_table *cpudraw_sg_table;
|
||||
size_t cpu_buffer_size;
|
||||
};
|
||||
void ion_buffer_destroy(struct ion_buffer *buffer);
|
||||
|
||||
/**
|
||||
* struct ion_device - the metadata of the ion device node
|
||||
* @dev: the actual misc device
|
||||
* @buffers: an rb tree of all the existing buffers
|
||||
* @buffer_lock: lock protecting the tree of buffers
|
||||
* @lock: rwsem protecting the tree of heaps and clients
|
||||
* @heaps: list of all the heaps in the system
|
||||
* @user_clients: list of all the clients created from userspace
|
||||
*/
|
||||
struct ion_device {
|
||||
struct miscdevice dev;
|
||||
struct rb_root buffers;
|
||||
struct mutex buffer_lock;
|
||||
struct rw_semaphore lock;
|
||||
struct plist_head heaps;
|
||||
long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
struct rb_root clients;
|
||||
struct dentry *debug_root;
|
||||
struct dentry *heaps_debug_root;
|
||||
struct dentry *clients_debug_root;
|
||||
int heap_cnt;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ion_client - a process/hw block local address space
|
||||
* @node: node in the tree of all clients
|
||||
* @dev: backpointer to ion device
|
||||
* @handles: an rb tree of all the handles in this client
|
||||
* @idr: an idr space for allocating handle ids
|
||||
* @lock: lock protecting the tree of handles
|
||||
* @name: used for debugging
|
||||
* @display_name: used for debugging (unique version of @name)
|
||||
* @display_serial: used for debugging (to make display_name unique)
|
||||
* @task: used for debugging
|
||||
*
|
||||
* A client represents a list of buffers this client may access.
|
||||
* The mutex stored here is used to protect both handles tree
|
||||
* as well as the handles themselves, and should be held while modifying either.
|
||||
*/
|
||||
struct ion_client {
|
||||
struct rb_node node;
|
||||
struct ion_device *dev;
|
||||
struct rb_root handles;
|
||||
struct idr idr;
|
||||
struct mutex lock;
|
||||
const char *name;
|
||||
char *display_name;
|
||||
int display_serial;
|
||||
struct task_struct *task;
|
||||
pid_t pid;
|
||||
struct dentry *debug_root;
|
||||
};
|
||||
|
||||
/**
|
||||
* ion_handle - a client local reference to a buffer
|
||||
* @ref: reference count
|
||||
* @client: back pointer to the client the buffer resides in
|
||||
* @buffer: pointer to the buffer
|
||||
* @node: node in the client's handle rbtree
|
||||
* @kmap_cnt: count of times this client has mapped to kernel
|
||||
* @id: client-unique id allocated by client->idr
|
||||
*
|
||||
* Modifications to node, map_cnt or mapping should be protected by the
|
||||
* lock in the client. Other fields are never changed after initialization.
|
||||
*/
|
||||
struct ion_handle {
|
||||
struct kref ref;
|
||||
struct ion_client *client;
|
||||
struct ion_buffer *buffer;
|
||||
struct rb_node node;
|
||||
unsigned int kmap_cnt;
|
||||
int id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ion_heap_ops - ops to operate on a given heap
|
||||
* @allocate: allocate memory
|
||||
* @free: free memory
|
||||
* @phys get physical address of a buffer (only define on
|
||||
* physically contiguous heaps)
|
||||
* @map_dma map the memory for dma to a scatterlist
|
||||
* @unmap_dma unmap the memory for dma
|
||||
* @map_kernel map memory to the kernel
|
||||
* @unmap_kernel unmap memory to the kernel
|
||||
* @map_user map memory to userspace
|
||||
@@ -176,10 +125,19 @@ struct ion_heap_ops {
|
||||
struct ion_buffer *buffer, unsigned long len,
|
||||
unsigned long align, unsigned long flags);
|
||||
void (*free)(struct ion_buffer *buffer);
|
||||
int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
ion_phys_addr_t *addr, size_t *len);
|
||||
struct sg_table * (*map_dma)(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer);
|
||||
void (*unmap_dma)(struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
|
||||
struct vm_area_struct *vma);
|
||||
int (*map_iommu)(struct ion_buffer *buffer,
|
||||
struct ion_iommu_map *map_data);
|
||||
void (*unmap_iommu)(struct ion_iommu_map *map_data);
|
||||
void (*buffer_zero)(struct ion_buffer *buffer);
|
||||
int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan);
|
||||
};
|
||||
|
||||
@@ -291,6 +249,10 @@ void *ion_heap_map_kernel(struct ion_heap *, struct ion_buffer *);
|
||||
void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *);
|
||||
int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
|
||||
struct vm_area_struct *);
|
||||
int ion_heap_map_iommu(struct ion_buffer *buffer,
|
||||
struct ion_iommu_map *map_data);
|
||||
void ion_heap_unmap_iommu(struct ion_iommu_map *map_data);
|
||||
void ion_flush_all_cpus_caches(void);
|
||||
int ion_heap_buffer_zero(struct ion_buffer *buffer);
|
||||
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
|
||||
|
||||
@@ -387,6 +349,20 @@ void ion_chunk_heap_destroy(struct ion_heap *);
|
||||
struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *);
|
||||
void ion_cma_heap_destroy(struct ion_heap *);
|
||||
|
||||
/**
|
||||
* kernel api to allocate/free from carveout -- used when carveout is
|
||||
* used to back an architecture specific custom heap
|
||||
*/
|
||||
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size,
|
||||
unsigned long align);
|
||||
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
|
||||
unsigned long size);
|
||||
/**
|
||||
* The carveout heap returns physical addresses, since 0 may be a valid
|
||||
* physical address, this is used to indicate allocation failed
|
||||
*/
|
||||
#define ION_CARVEOUT_ALLOCATE_FAIL -1
|
||||
|
||||
/**
|
||||
* functions for creating and destroying a heap pool -- allows you
|
||||
* to keep a pool of pre allocated memory to use from your heap. Keeping
|
||||
@@ -406,7 +382,6 @@ void ion_cma_heap_destroy(struct ion_heap *);
|
||||
* @gfp_mask: gfp_mask to use from alloc
|
||||
* @order: order of pages in the pool
|
||||
* @list: plist node for list of pools
|
||||
* @cached: it's cached pool or not
|
||||
*
|
||||
* Allows you to keep a pool of pre allocated pages to use from your heap.
|
||||
* Keeping a pool of pages that is ready for dma, ie any cached mapping have
|
||||
@@ -416,7 +391,6 @@ void ion_cma_heap_destroy(struct ion_heap *);
|
||||
struct ion_page_pool {
|
||||
int high_count;
|
||||
int low_count;
|
||||
bool cached;
|
||||
struct list_head high_items;
|
||||
struct list_head low_items;
|
||||
struct mutex mutex;
|
||||
@@ -425,11 +399,41 @@ struct ion_page_pool {
|
||||
struct plist_node list;
|
||||
};
|
||||
|
||||
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
|
||||
bool cached);
|
||||
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
|
||||
void ion_page_pool_destroy(struct ion_page_pool *);
|
||||
struct page *ion_page_pool_alloc(struct ion_page_pool *);
|
||||
void ion_page_pool_free(struct ion_page_pool *, struct page *);
|
||||
void ion_page_pool_free_immediate(struct ion_page_pool *, struct page *);
|
||||
|
||||
#ifdef CONFIG_ION_POOL_CACHE_POLICY
|
||||
static inline void ion_page_pool_alloc_set_cache_policy
|
||||
(struct ion_page_pool *pool,
|
||||
struct page *page){
|
||||
void *va = page_address(page);
|
||||
|
||||
if (va)
|
||||
set_memory_wc((unsigned long)va, 1 << pool->order);
|
||||
}
|
||||
|
||||
static inline void ion_page_pool_free_set_cache_policy
|
||||
(struct ion_page_pool *pool,
|
||||
struct page *page){
|
||||
void *va = page_address(page);
|
||||
|
||||
if (va)
|
||||
set_memory_wb((unsigned long)va, 1 << pool->order);
|
||||
|
||||
}
|
||||
#else
|
||||
static inline void ion_page_pool_alloc_set_cache_policy
|
||||
(struct ion_page_pool *pool,
|
||||
struct page *page){ }
|
||||
|
||||
static inline void ion_page_pool_free_set_cache_policy
|
||||
(struct ion_page_pool *pool,
|
||||
struct page *page){ }
|
||||
#endif
|
||||
|
||||
|
||||
/** ion_page_pool_shrink - shrinks the size of the memory cached in the pool
|
||||
* @pool: the pool
|
||||
@@ -452,22 +456,4 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
void ion_pages_sync_for_device(struct device *dev, struct page *page,
|
||||
size_t size, enum dma_data_direction dir);
|
||||
|
||||
long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
|
||||
int ion_sync_for_device(struct ion_client *client, int fd);
|
||||
|
||||
struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
|
||||
int id);
|
||||
|
||||
void ion_free_nolock(struct ion_client *client, struct ion_handle *handle);
|
||||
|
||||
int ion_handle_put_nolock(struct ion_handle *handle);
|
||||
|
||||
struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
|
||||
int id);
|
||||
|
||||
int ion_handle_put(struct ion_handle *handle);
|
||||
|
||||
int ion_query_heaps(struct ion_client *client, struct ion_heap_query *query);
|
||||
|
||||
#endif /* _ION_PRIV_H */
|
||||
|
||||
@@ -26,18 +26,16 @@
|
||||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
|
||||
#define NUM_ORDERS ARRAY_SIZE(orders)
|
||||
|
||||
static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN |
|
||||
__GFP_NORETRY) & ~__GFP_RECLAIM;
|
||||
static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO);
|
||||
__GFP_NORETRY) & ~__GFP_DIRECT_RECLAIM;
|
||||
static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN);
|
||||
static const unsigned int orders[] = {8, 4, 0};
|
||||
|
||||
static const int num_orders = ARRAY_SIZE(orders);
|
||||
static int order_to_index(unsigned int order)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++)
|
||||
for (i = 0; i < num_orders; i++)
|
||||
if (order == orders[i])
|
||||
return i;
|
||||
BUG();
|
||||
@@ -51,55 +49,49 @@ static inline unsigned int order_to_size(int order)
|
||||
|
||||
struct ion_system_heap {
|
||||
struct ion_heap heap;
|
||||
struct ion_page_pool *uncached_pools[NUM_ORDERS];
|
||||
struct ion_page_pool *cached_pools[NUM_ORDERS];
|
||||
struct ion_page_pool *pools[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* The page from page-pool are all zeroed before. We need do cache
|
||||
* clean for cached buffer. The uncached buffer are always non-cached
|
||||
* since it's allocated. So no need for non-cached pages.
|
||||
*/
|
||||
static struct page *alloc_buffer_page(struct ion_system_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long order)
|
||||
{
|
||||
bool cached = ion_buffer_cached(buffer);
|
||||
struct ion_page_pool *pool;
|
||||
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
|
||||
struct page *page;
|
||||
|
||||
if (!cached)
|
||||
pool = heap->uncached_pools[order_to_index(order)];
|
||||
else
|
||||
pool = heap->cached_pools[order_to_index(order)];
|
||||
if (!cached) {
|
||||
page = ion_page_pool_alloc(pool);
|
||||
} else {
|
||||
gfp_t gfp_flags = low_order_gfp_flags;
|
||||
|
||||
page = ion_page_pool_alloc(pool);
|
||||
|
||||
if (cached)
|
||||
if (order > 4)
|
||||
gfp_flags = high_order_gfp_flags;
|
||||
page = alloc_pages(gfp_flags | __GFP_COMP, order);
|
||||
if (!page)
|
||||
return NULL;
|
||||
ion_pages_sync_for_device(NULL, page, PAGE_SIZE << order,
|
||||
DMA_BIDIRECTIONAL);
|
||||
DMA_BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static void free_buffer_page(struct ion_system_heap *heap,
|
||||
struct ion_buffer *buffer, struct page *page)
|
||||
{
|
||||
struct ion_page_pool *pool;
|
||||
unsigned int order = compound_order(page);
|
||||
bool cached = ion_buffer_cached(buffer);
|
||||
|
||||
/* go to system */
|
||||
if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) {
|
||||
if (!cached) {
|
||||
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
|
||||
if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE)
|
||||
ion_page_pool_free_immediate(pool, page);
|
||||
else
|
||||
ion_page_pool_free(pool, page);
|
||||
} else {
|
||||
__free_pages(page, order);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cached)
|
||||
pool = heap->uncached_pools[order_to_index(order)];
|
||||
else
|
||||
pool = heap->cached_pools[order_to_index(order)];
|
||||
|
||||
ion_page_pool_free(pool, page);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +103,7 @@ static struct page *alloc_largest_available(struct ion_system_heap *heap,
|
||||
struct page *page;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
if (size < order_to_size(orders[i]))
|
||||
continue;
|
||||
if (max_order < orders[i])
|
||||
@@ -128,9 +120,9 @@ static struct page *alloc_largest_available(struct ion_system_heap *heap,
|
||||
}
|
||||
|
||||
static int ion_system_heap_allocate(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size, unsigned long align,
|
||||
unsigned long flags)
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size, unsigned long align,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct ion_system_heap *sys_heap = container_of(heap,
|
||||
struct ion_system_heap,
|
||||
@@ -152,7 +144,7 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
|
||||
INIT_LIST_HEAD(&pages);
|
||||
while (size_remaining > 0) {
|
||||
page = alloc_largest_available(sys_heap, buffer, size_remaining,
|
||||
max_order);
|
||||
max_order);
|
||||
if (!page)
|
||||
goto free_pages;
|
||||
list_add_tail(&page->lru, &pages);
|
||||
@@ -174,7 +166,7 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
|
||||
list_del(&page->lru);
|
||||
}
|
||||
|
||||
buffer->sg_table = table;
|
||||
buffer->priv_virt = table;
|
||||
return 0;
|
||||
|
||||
free_table:
|
||||
@@ -191,11 +183,16 @@ static void ion_system_heap_free(struct ion_buffer *buffer)
|
||||
struct ion_system_heap,
|
||||
heap);
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
bool cached = ion_buffer_cached(buffer);
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
/* zero the buffer before goto page pool */
|
||||
if (!(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))
|
||||
/*
|
||||
* uncached pages come from the page pools, zero them before returning
|
||||
* for security purposes (other allocations are zerod at
|
||||
* alloc time
|
||||
*/
|
||||
if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))
|
||||
ion_heap_buffer_zero(buffer);
|
||||
|
||||
for_each_sg(table->sgl, sg, table->nents, i)
|
||||
@@ -204,11 +201,20 @@ static void ion_system_heap_free(struct ion_buffer *buffer)
|
||||
kfree(table);
|
||||
}
|
||||
|
||||
static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
static struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
return buffer->priv_virt;
|
||||
}
|
||||
|
||||
static void ion_system_heap_unmap_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
struct ion_page_pool *uncached_pool;
|
||||
struct ion_page_pool *cached_pool;
|
||||
struct ion_system_heap *sys_heap;
|
||||
int nr_total = 0;
|
||||
int i, nr_freed;
|
||||
@@ -219,44 +225,33 @@ static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
|
||||
if (!nr_to_scan)
|
||||
only_scan = 1;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
uncached_pool = sys_heap->uncached_pools[i];
|
||||
cached_pool = sys_heap->cached_pools[i];
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
|
||||
if (only_scan) {
|
||||
nr_total += ion_page_pool_shrink(uncached_pool,
|
||||
gfp_mask,
|
||||
nr_to_scan);
|
||||
nr_freed = ion_page_pool_shrink(pool, gfp_mask, nr_to_scan);
|
||||
nr_total += nr_freed;
|
||||
|
||||
nr_total += ion_page_pool_shrink(cached_pool,
|
||||
gfp_mask,
|
||||
nr_to_scan);
|
||||
} else {
|
||||
nr_freed = ion_page_pool_shrink(uncached_pool,
|
||||
gfp_mask,
|
||||
nr_to_scan);
|
||||
if (!only_scan) {
|
||||
nr_to_scan -= nr_freed;
|
||||
nr_total += nr_freed;
|
||||
if (nr_to_scan <= 0)
|
||||
break;
|
||||
nr_freed = ion_page_pool_shrink(cached_pool,
|
||||
gfp_mask,
|
||||
nr_to_scan);
|
||||
nr_to_scan -= nr_freed;
|
||||
nr_total += nr_freed;
|
||||
/* shrink completed */
|
||||
if (nr_to_scan <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nr_total;
|
||||
}
|
||||
|
||||
static struct ion_heap_ops system_heap_ops = {
|
||||
.allocate = ion_system_heap_allocate,
|
||||
.free = ion_system_heap_free,
|
||||
.map_dma = ion_system_heap_map_dma,
|
||||
.unmap_dma = ion_system_heap_unmap_dma,
|
||||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
.map_user = ion_heap_map_user,
|
||||
.map_iommu = ion_heap_map_iommu,
|
||||
.unmap_iommu = ion_heap_unmap_iommu,
|
||||
.shrink = ion_system_heap_shrink,
|
||||
};
|
||||
|
||||
@@ -268,89 +263,52 @@ static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
|
||||
struct ion_system_heap,
|
||||
heap);
|
||||
int i;
|
||||
struct ion_page_pool *pool;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
pool = sys_heap->uncached_pools[i];
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
|
||||
seq_printf(s, "%d order %u highmem pages uncached %lu total\n",
|
||||
seq_printf(s, "%d order %u highmem pages in pool = %lu total\n",
|
||||
pool->high_count, pool->order,
|
||||
(PAGE_SIZE << pool->order) * pool->high_count);
|
||||
seq_printf(s, "%d order %u lowmem pages uncached %lu total\n",
|
||||
pool->low_count, pool->order,
|
||||
(PAGE_SIZE << pool->order) * pool->low_count);
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
pool = sys_heap->cached_pools[i];
|
||||
|
||||
seq_printf(s, "%d order %u highmem pages cached %lu total\n",
|
||||
pool->high_count, pool->order,
|
||||
(PAGE_SIZE << pool->order) * pool->high_count);
|
||||
seq_printf(s, "%d order %u lowmem pages cached %lu total\n",
|
||||
seq_printf(s, "%d order %u lowmem pages in pool = %lu total\n",
|
||||
pool->low_count, pool->order,
|
||||
(PAGE_SIZE << pool->order) * pool->low_count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++)
|
||||
if (pools[i])
|
||||
ion_page_pool_destroy(pools[i]);
|
||||
}
|
||||
|
||||
static int ion_system_heap_create_pools(struct ion_page_pool **pools,
|
||||
bool cached)
|
||||
{
|
||||
int i;
|
||||
gfp_t gfp_flags = low_order_gfp_flags;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
struct ion_page_pool *pool;
|
||||
|
||||
if (orders[i] > 4)
|
||||
gfp_flags = high_order_gfp_flags;
|
||||
|
||||
pool = ion_page_pool_create(gfp_flags, orders[i], cached);
|
||||
if (!pool)
|
||||
goto err_create_pool;
|
||||
pools[i] = pool;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_create_pool:
|
||||
ion_system_heap_destroy_pools(pools);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
|
||||
{
|
||||
struct ion_system_heap *heap;
|
||||
int i;
|
||||
|
||||
heap = kzalloc(sizeof(*heap), GFP_KERNEL);
|
||||
heap = kzalloc(sizeof(struct ion_system_heap) +
|
||||
sizeof(struct ion_page_pool *) * num_orders,
|
||||
GFP_KERNEL);
|
||||
if (!heap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
heap->heap.ops = &system_heap_ops;
|
||||
heap->heap.type = ION_HEAP_TYPE_SYSTEM;
|
||||
heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
|
||||
|
||||
if (ion_system_heap_create_pools(heap->uncached_pools, false))
|
||||
goto free_heap;
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool;
|
||||
gfp_t gfp_flags = low_order_gfp_flags;
|
||||
|
||||
if (ion_system_heap_create_pools(heap->cached_pools, true))
|
||||
goto destroy_uncached_pools;
|
||||
if (orders[i] > 4)
|
||||
gfp_flags = high_order_gfp_flags;
|
||||
pool = ion_page_pool_create(gfp_flags, orders[i]);
|
||||
if (!pool)
|
||||
goto destroy_pools;
|
||||
heap->pools[i] = pool;
|
||||
}
|
||||
|
||||
heap->heap.debug_show = ion_system_heap_debug_show;
|
||||
return &heap->heap;
|
||||
|
||||
destroy_uncached_pools:
|
||||
ion_system_heap_destroy_pools(heap->uncached_pools);
|
||||
|
||||
free_heap:
|
||||
destroy_pools:
|
||||
while (i--)
|
||||
ion_page_pool_destroy(heap->pools[i]);
|
||||
kfree(heap);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
@@ -362,10 +320,8 @@ void ion_system_heap_destroy(struct ion_heap *heap)
|
||||
heap);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
ion_page_pool_destroy(sys_heap->uncached_pools[i]);
|
||||
ion_page_pool_destroy(sys_heap->cached_pools[i]);
|
||||
}
|
||||
for (i = 0; i < num_orders; i++)
|
||||
ion_page_pool_destroy(sys_heap->pools[i]);
|
||||
kfree(sys_heap);
|
||||
}
|
||||
|
||||
@@ -406,7 +362,7 @@ static int ion_system_contig_heap_allocate(struct ion_heap *heap,
|
||||
|
||||
sg_set_page(table->sgl, page, len, 0);
|
||||
|
||||
buffer->sg_table = table;
|
||||
buffer->priv_virt = table;
|
||||
|
||||
ion_pages_sync_for_device(NULL, page, len, DMA_BIDIRECTIONAL);
|
||||
|
||||
@@ -423,7 +379,7 @@ free_pages:
|
||||
|
||||
static void ion_system_contig_heap_free(struct ion_buffer *buffer)
|
||||
{
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
struct sg_table *table = buffer->priv_virt;
|
||||
struct page *page = sg_page(table->sgl);
|
||||
unsigned long pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT;
|
||||
unsigned long i;
|
||||
@@ -434,9 +390,34 @@ static void ion_system_contig_heap_free(struct ion_buffer *buffer)
|
||||
kfree(table);
|
||||
}
|
||||
|
||||
static int ion_system_contig_heap_phys(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
ion_phys_addr_t *addr, size_t *len)
|
||||
{
|
||||
struct sg_table *table = buffer->priv_virt;
|
||||
struct page *page = sg_page(table->sgl);
|
||||
*addr = page_to_phys(page);
|
||||
*len = buffer->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
return buffer->priv_virt;
|
||||
}
|
||||
|
||||
static void ion_system_contig_heap_unmap_dma(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ion_heap_ops kmalloc_ops = {
|
||||
.allocate = ion_system_contig_heap_allocate,
|
||||
.free = ion_system_contig_heap_free,
|
||||
.phys = ion_system_contig_heap_phys,
|
||||
.map_dma = ion_system_contig_heap_map_dma,
|
||||
.unmap_dma = ion_system_contig_heap_unmap_dma,
|
||||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
.map_user = ion_heap_map_user,
|
||||
|
||||
@@ -42,8 +42,7 @@ struct ion_test_data {
|
||||
};
|
||||
|
||||
static int ion_handle_test_dma(struct device *dev, struct dma_buf *dma_buf,
|
||||
void __user *ptr, size_t offset, size_t size,
|
||||
bool write)
|
||||
void __user *ptr, size_t offset, size_t size, bool write)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dma_buf_attachment *attach;
|
||||
@@ -99,7 +98,7 @@ err:
|
||||
}
|
||||
|
||||
static int ion_handle_test_kernel(struct dma_buf *dma_buf, void __user *ptr,
|
||||
size_t offset, size_t size, bool write)
|
||||
size_t offset, size_t size, bool write)
|
||||
{
|
||||
int ret;
|
||||
unsigned long page_offset = offset >> PAGE_SHIFT;
|
||||
@@ -110,7 +109,7 @@ static int ion_handle_test_kernel(struct dma_buf *dma_buf, void __user *ptr,
|
||||
if (offset > dma_buf->size || size > dma_buf->size - offset)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dma_buf_begin_cpu_access(dma_buf, dir);
|
||||
ret = dma_buf_begin_cpu_access(dma_buf, offset, size, dir);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -140,12 +139,12 @@ static int ion_handle_test_kernel(struct dma_buf *dma_buf, void __user *ptr,
|
||||
copy_offset = 0;
|
||||
}
|
||||
err:
|
||||
dma_buf_end_cpu_access(dma_buf, dir);
|
||||
dma_buf_end_cpu_access(dma_buf, offset, size, dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long ion_test_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
unsigned long arg)
|
||||
{
|
||||
struct ion_test_data *test_data = filp->private_data;
|
||||
int ret = 0;
|
||||
@@ -180,19 +179,17 @@ static long ion_test_ioctl(struct file *filp, unsigned int cmd,
|
||||
case ION_IOC_TEST_DMA_MAPPING:
|
||||
{
|
||||
ret = ion_handle_test_dma(test_data->dev, test_data->dma_buf,
|
||||
u64_to_uptr(data.test_rw.ptr),
|
||||
data.test_rw.offset,
|
||||
data.test_rw.size,
|
||||
data.test_rw.write);
|
||||
u64_to_uptr(data.test_rw.ptr),
|
||||
data.test_rw.offset, data.test_rw.size,
|
||||
data.test_rw.write);
|
||||
break;
|
||||
}
|
||||
case ION_IOC_TEST_KERNEL_MAPPING:
|
||||
{
|
||||
ret = ion_handle_test_kernel(test_data->dma_buf,
|
||||
u64_to_uptr(data.test_rw.ptr),
|
||||
data.test_rw.offset,
|
||||
data.test_rw.size,
|
||||
data.test_rw.write);
|
||||
u64_to_uptr(data.test_rw.ptr),
|
||||
data.test_rw.offset, data.test_rw.size,
|
||||
data.test_rw.write);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -211,7 +208,7 @@ static int ion_test_open(struct inode *inode, struct file *file)
|
||||
struct ion_test_data *data;
|
||||
struct miscdevice *miscdev = file->private_data;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
data = kzalloc(sizeof(struct ion_test_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -245,7 +242,7 @@ static int __init ion_test_probe(struct platform_device *pdev)
|
||||
struct ion_test_device *testdev;
|
||||
|
||||
testdev = devm_kzalloc(&pdev->dev, sizeof(struct ion_test_device),
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL);
|
||||
if (!testdev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -33,11 +33,12 @@ static int tegra_ion_probe(struct platform_device *pdev)
|
||||
|
||||
num_heaps = pdata->nr;
|
||||
|
||||
heaps = devm_kcalloc(&pdev->dev, pdata->nr,
|
||||
sizeof(struct ion_heap *), GFP_KERNEL);
|
||||
heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_heap *) * pdata->nr,
|
||||
GFP_KERNEL);
|
||||
|
||||
idev = ion_device_create(NULL);
|
||||
if (IS_ERR(idev))
|
||||
if (IS_ERR_OR_NULL(idev))
|
||||
return PTR_ERR(idev);
|
||||
|
||||
/* create the heaps as specified in the board file */
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/iommu.h>
|
||||
|
||||
typedef int ion_user_handle_t;
|
||||
|
||||
@@ -40,30 +41,45 @@ enum ion_heap_type {
|
||||
ION_HEAP_TYPE_CARVEOUT,
|
||||
ION_HEAP_TYPE_CHUNK,
|
||||
ION_HEAP_TYPE_DMA,
|
||||
ION_HEAP_TYPE_CUSTOM, /*
|
||||
* must be last so device specific heaps always
|
||||
* are at the end of this enum
|
||||
*/
|
||||
ION_HEAP_TYPE_DMA_POOL,
|
||||
ION_HEAP_TYPE_CPUDRAW,
|
||||
ION_HEAP_TYPE_IOMMU,
|
||||
ION_HEAP_TYPE_SECCM,
|
||||
ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
|
||||
are at the end of this enum */
|
||||
ION_HEAP_TYPE_RESERVED,
|
||||
ION_NUM_HEAPS = 16,
|
||||
};
|
||||
|
||||
#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
|
||||
#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
|
||||
#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
|
||||
#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
|
||||
#define ION_HEAP_TYPE_DMA_POOL_MASK (1 << ION_HEAP_TYPE_DMA_POOL)
|
||||
#define ION_HEAP_CPUDRAW_MASK (1 << ION_HEAP_TYPE_CPUDRAW)
|
||||
#define ION_HEAP_TYPE_IOMMU_MASK (1 << ION_HEAP_TYPE_IOMMU)
|
||||
|
||||
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
|
||||
|
||||
/**
|
||||
* allocation flags - the lower 16 bits are used by core ion, the upper 16
|
||||
* bits are reserved for use by the heaps themselves.
|
||||
*/
|
||||
#define ION_FLAG_CACHED (0x1 << 0) /* mappings of this buffer should be
|
||||
cached, ion will do cache
|
||||
maintenance when the buffer is
|
||||
mapped for dma */
|
||||
#define ION_FLAG_CACHED_NEEDS_SYNC (0x1 << 1) /* mappings of this buffer
|
||||
will created at mmap time,
|
||||
if this is set caches
|
||||
must be managed manually */
|
||||
#define ION_FLAG_NOT_ZERO_BUFFER (0x1 << 2) /* do not zero buffer*/
|
||||
|
||||
/*
|
||||
* mappings of this buffer should be cached, ion will do cache maintenance
|
||||
* when the buffer is mapped for dma
|
||||
*/
|
||||
#define ION_FLAG_CACHED 1
|
||||
#define ION_FLAG_SECURE_BUFFER (0x1 << 3)
|
||||
|
||||
/*
|
||||
* mappings of this buffer will created at mmap time, if this is set
|
||||
* caches must be managed manually
|
||||
*/
|
||||
#define ION_FLAG_CACHED_NEEDS_SYNC 2
|
||||
#define ION_FLAG_GRAPHIC_BUFFER (0x1 << 4)
|
||||
|
||||
#define ION_FLAG_GRAPHIC_GPU_BUFFER (0x1 << 5)
|
||||
|
||||
/**
|
||||
* DOC: Ion Userspace API
|
||||
@@ -128,36 +144,6 @@ struct ion_custom_data {
|
||||
unsigned long arg;
|
||||
};
|
||||
|
||||
#define MAX_HEAP_NAME 32
|
||||
|
||||
/**
|
||||
* struct ion_heap_data - data about a heap
|
||||
* @name - first 32 characters of the heap name
|
||||
* @type - heap type
|
||||
* @heap_id - heap id for the heap
|
||||
*/
|
||||
struct ion_heap_data {
|
||||
char name[MAX_HEAP_NAME];
|
||||
__u32 type;
|
||||
__u32 heap_id;
|
||||
__u32 reserved0;
|
||||
__u32 reserved1;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ion_heap_query - collection of data about all heaps
|
||||
* @cnt - total number of heaps to be copied
|
||||
* @heaps - buffer to copy heap data
|
||||
*/
|
||||
struct ion_heap_query {
|
||||
__u32 cnt; /* Total number of heaps to be copied */
|
||||
__u32 reserved0; /* align to 64bits */
|
||||
__u64 heaps; /* buffer to be populated */
|
||||
__u32 reserved1;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
#define ION_IOC_MAGIC 'I'
|
||||
|
||||
/**
|
||||
@@ -225,12 +211,33 @@ struct ion_heap_query {
|
||||
#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_HEAP_QUERY - information about available heaps
|
||||
* struct ion_map_iommu_data - metadata passed between userspace for iommu mapping
|
||||
* @handle: the handle of buffer
|
||||
* @format: the format of iommu mapping
|
||||
*
|
||||
* Takes an ion_heap_query structure and populates information about
|
||||
* available Ion heaps.
|
||||
* Provided by userspace as an argument to the ioctl
|
||||
*/
|
||||
#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, \
|
||||
struct ion_heap_query)
|
||||
struct iommu_map_format;
|
||||
struct ion_map_iommu_data {
|
||||
ion_user_handle_t handle;
|
||||
struct iommu_map_format format;
|
||||
};
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_MAP_IOMMU - map a buffr to iova
|
||||
*/
|
||||
#define ION_IOC_MAP_IOMMU _IOWR(ION_IOC_MAGIC, \
|
||||
8, struct ion_map_iommu_data)
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_UNMAP_IOMMU - destory iommu mapping of a buffer
|
||||
*/
|
||||
#define ION_IOC_UNMAP_IOMMU _IOWR(ION_IOC_MAGIC, \
|
||||
9, struct ion_map_iommu_data)
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_INV - invalidate a shared file descriptors in cache
|
||||
*/
|
||||
#define ION_IOC_INV _IOWR(ION_IOC_MAGIC, 10, struct ion_fd_data)
|
||||
|
||||
#endif /* _UAPI_LINUX_ION_H */
|
||||
|
||||
@@ -59,12 +59,12 @@ static inline phys_addr_t hisi_iommu_domain_iova_to_phys(unsigned long iova)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
unsigned int hisi_iommu_page_size (void)
|
||||
static inline unsigned int hisi_iommu_page_size (void)
|
||||
{
|
||||
return SZ_4K;
|
||||
}
|
||||
|
||||
bool hisi_iommu_off_on(void)
|
||||
static inline bool hisi_iommu_off_on(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,6 @@ enum iommu_attr {
|
||||
DOMAIN_ATTR_MAX,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HISI_IOMMU
|
||||
/* metadata for iommu mapping */
|
||||
struct iommu_map_format {
|
||||
unsigned long iova_start;
|
||||
@@ -143,7 +142,6 @@ struct tile_format {
|
||||
unsigned long phys_page_line;
|
||||
unsigned long virt_page_line;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct iommu_dm_region - descriptor for a direct mapped memory region
|
||||
|
||||
Reference in New Issue
Block a user