From: Benjamin Gaignard benjamin.gaignard@linaro.org
The goal of those patches is to allow ION clients (drivers or userland applications) to use Contiguous Memory Allocator (CMA).
To get more info about CMA: http://lists.linaro.org/pipermail/linaro-mm-sig/2012-February/001328.html
patches version 4: - add ION_HEAP_TYPE_DMA heap type in ion_heap_type enum. - CMA heap is now a "native" ION heap. - add ion_heap_create_full function to keep backward compatibilty. - clean up included files in CMA heap - ux500-ion is using ion_heap_create_full instead of ion_heap_create
patches version 3: - add a private field in ion_heap structure instead of expose ion_device structure to all heaps - ion_cma_heap is no more a platform driver - ion_cma_heap use ion_heap private field to store the device pointer and make the link with reserved CMA regions - provide ux500-ion driver and configuration file for snowball board to give an example of how use CMA heaps
patches version 2: - fix comments done by Andy Green
Benjamin Gaignard (4): fix ion_platform_data definition add private field in ion_heap structure add CMA heap add test/example driver for ux500 platform
arch/arm/mach-ux500/board-mop500.c | 77 +++++++++++++++++++ drivers/gpu/ion/Kconfig | 6 ++ drivers/gpu/ion/Makefile | 4 +- drivers/gpu/ion/ion_cma_heap.c | 129 ++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 14 +++- drivers/gpu/ion/ion_priv.h | 11 +++ drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 142 ++++++++++++++++++++++++++++++++++++ include/linux/ion.h | 5 +- 9 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/ion/ion_cma_heap.c create mode 100644 drivers/gpu/ion/ux500/Makefile create mode 100644 drivers/gpu/ion/ux500/ux500_ion.c
From: Benjamin Gaignard benjamin.gaignard@linaro.org
fix ion_platform_heap to make is use an usual way in board configuration file.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- include/linux/ion.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/linux/ion.h b/include/linux/ion.h index aed8349..1468cb3 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -83,7 +83,7 @@ struct ion_platform_heap { */ struct ion_platform_data { int nr; - struct ion_platform_heap heaps[]; + struct ion_platform_heap *heaps; };
/**
From: Benjamin Gaignard benjamin.gaignard@linaro.org
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- drivers/gpu/ion/ion_priv.h | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index 3323954..bf5c75e 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -111,6 +111,7 @@ struct ion_heap_ops { * allocating. These are specified by platform data and * MUST be unique * @name: used for debugging + * @priv: private heap data * * Represents a pool of memory from which buffers can be made. In some * systems the only heap is regular system memory allocated via vmalloc. @@ -124,6 +125,7 @@ struct ion_heap { struct ion_heap_ops *ops; int id; const char *name; + void *priv; };
/**
From: Benjamin Gaignard benjamin.gaignard@linaro.org
New heap type ION_HEAP_TYPE_DMA where allocation is done with dma_alloc_coherent API. device coherent_dma_mask must be set to DMA_BIT_MASK(32). Add ion_heap_create_full function to avoid breaking backward compatibilty.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- drivers/gpu/ion/Makefile | 3 +- drivers/gpu/ion/ion_cma_heap.c | 129 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 14 ++++- drivers/gpu/ion/ion_priv.h | 9 +++ include/linux/ion.h | 3 + 5 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/ion/ion_cma_heap.c
diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index 73fe3fa..ddcedb5 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,2 +1,3 @@ -obj-$(CONFIG_ION) += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o +ion-driver-objs += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o ion_cma_heap.o +obj-$(CONFIG_ION) += ion-driver.o obj-$(CONFIG_ION_TEGRA) += tegra/ diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c new file mode 100644 index 0000000..0dee6f0 --- /dev/null +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -0,0 +1,129 @@ +/* + * drivers/gpu/ion/ion_cma_heap.c + * + * Copyright (C) Linaro 2012 + * Author: benjamin.gaignard@linaro.org for ST-Ericsson. + * + * 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/device.h> +#include <linux/ion.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/dma-mapping.h> + +/* for ion_heap_ops structure */ +#include "ion_priv.h" + +#define ION_CMA_ALLOCATE_FAILED -1 + +struct ion_cma_buffer_info { + void *cpu_addr; + dma_addr_t handle; +}; + +/* ION CMA heap operations functions */ +static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, + unsigned long len, unsigned long align, + unsigned long flags) +{ + struct device *dev = heap->priv; + struct ion_cma_buffer_info *info; + + dev_dbg(dev, "Request buffer allocation len %ld\n", len); + + info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate buffer info\n"); + return ION_CMA_ALLOCATE_FAILED; + } + + info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0); + + if (!info->cpu_addr) { + dev_err(dev, "Fail to allocate buffer\n"); + kfree(info); + return ION_CMA_ALLOCATE_FAILED; + } + + /* keep this for memory release */ + buffer->priv_virt = info; + dev_dbg(dev, "Allocate buffer %p\n", buffer); + return 0; +} + +static void ion_cma_free(struct ion_buffer *buffer) +{ + struct device *dev = buffer->heap->priv; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Release buffer %p\n", buffer); + dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); + 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 device *dev = heap->priv; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer, + virt_to_phys(info->cpu_addr)); + + *addr = virt_to_phys(info->cpu_addr); + *len = buffer->size; + + return 0; +} + +static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + struct device *dev = buffer->heap->priv; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle, + buffer->size); +} + +static struct ion_heap_ops ion_cma_ops = { + .allocate = ion_cma_allocate, + .free = ion_cma_free, + .phys = ion_cma_phys, + .map_user = ion_cma_mmap, +}; + +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data, + struct device *dev) +{ + struct ion_heap *heap; + + heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); + + if (!heap) + return ERR_PTR(-ENOMEM); + + heap->ops = &ion_cma_ops; + /* set device as private heaps data, later it will be + * used to make the link with reserved CMA memory */ + heap->priv = dev; + heap->type = ION_HEAP_TYPE_DMA; + return heap; +} + +void ion_cma_heap_destroy(struct ion_heap *heap) +{ + kfree(heap); +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 8ce3c19..7f5f325 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -18,7 +18,8 @@ #include <linux/ion.h> #include "ion_priv.h"
-struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) +struct ion_heap *ion_heap_create_full(struct ion_platform_heap *heap_data, + struct device *dev) { struct ion_heap *heap = NULL;
@@ -32,6 +33,9 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) case ION_HEAP_TYPE_CARVEOUT: heap = ion_carveout_heap_create(heap_data); break; + case ION_HEAP_TYPE_DMA: + heap = ion_cma_heap_create(heap_data, dev); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap_data->type); @@ -50,6 +54,11 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) return heap; }
+struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) +{ + return ion_heap_create_full(heap_data, NULL); +} + void ion_heap_destroy(struct ion_heap *heap) { if (!heap) @@ -65,6 +74,9 @@ void ion_heap_destroy(struct ion_heap *heap) case ION_HEAP_TYPE_CARVEOUT: ion_carveout_heap_destroy(heap); break; + case ION_HEAP_TYPE_DMA: + ion_cma_heap_destroy(heap); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap->type); diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index bf5c75e..7607b26 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -22,6 +22,7 @@ #include <linux/mutex.h> #include <linux/rbtree.h> #include <linux/ion.h> +#include <linux/device.h>
struct ion_mapping;
@@ -159,6 +160,9 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap); */
struct ion_heap *ion_heap_create(struct ion_platform_heap *); +struct ion_heap *ion_heap_create_full(struct ion_platform_heap *, + struct device *); + void ion_heap_destroy(struct ion_heap *);
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *); @@ -177,6 +181,11 @@ 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); + +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *, + struct device *); +void ion_cma_heap_destroy(struct ion_heap *); + /** * The carveout heap returns physical addresses, since 0 may be a valid * physical address, this is used to indicate allocation failed diff --git a/include/linux/ion.h b/include/linux/ion.h index 1468cb3..5104e64 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -27,12 +27,14 @@ struct ion_handle; * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved * carveout heap, allocations are physically * contiguous + * @ION_HEAP_TYPE_DMA: memory allocated via DMA API * @ION_HEAP_END: helper for iterating over heaps */ enum ion_heap_type { ION_HEAP_TYPE_SYSTEM, ION_HEAP_TYPE_SYSTEM_CONTIG, ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_DMA, ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always are at the end of this enum */ ION_NUM_HEAPS, @@ -41,6 +43,7 @@ enum ion_heap_type { #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)
#ifdef __KERNEL__ struct ion_device;
From: Benjamin Gaignard benjamin.gaignard@linaro.org
DO NOT MERGE ux500-ion driver is provided as example. Define 2 CMA heaps, one on a specific CMA region reserved at boot time the other will use the default CMA region. Since we can have multiple instances of this driver while only one ion device can be instanciated, we need to take care of ion_heap structure reallocation.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- arch/arm/mach-ux500/board-mop500.c | 77 +++++++++++++++++++ drivers/gpu/ion/Kconfig | 6 ++ drivers/gpu/ion/Makefile | 1 + drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 142 ++++++++++++++++++++++++++++++++++++ 5 files changed, 227 insertions(+), 0 deletions(-) create mode 100644 drivers/gpu/ion/ux500/Makefile create mode 100644 drivers/gpu/ion/ux500/ux500_ion.c
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index bdd7b80..d9dbee5 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -30,6 +30,11 @@ #include <linux/gpio_keys.h> #include <linux/delay.h>
+#ifdef CONFIG_ION +#include <linux/ion.h> +#include <linux/dma-contiguous.h> +#endif + #include <linux/leds.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -50,6 +55,62 @@ #include "board-mop500.h" #include "board-mop500-regulators.h"
+#ifdef CONFIG_ION +static u64 snowball_dmamask = DMA_BIT_MASK(32); + +static struct ion_platform_heap snowball_ion_heap1[] = { + [0] = { + .type = ION_HEAP_TYPE_DMA, + .id = 1, + .name = "ion-cma-heap-1", + .base = 0, + .size = (16 * SZ_1M), + }, +}; + +static struct ion_platform_data snowball_ion_data1 = { + .heaps = snowball_ion_heap1, + .nr = ARRAY_SIZE(snowball_ion_heap1), +}; + +static struct platform_device snowball_ion_device1 = { + .name = "ion-ux500", + .id = 1, + .dev = { + .dma_mask = &snowball_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &snowball_ion_data1, + }, + .num_resources = 0, +}; + +static struct ion_platform_heap snowball_ion_heap2[] = { + [0] = { + .type = ION_HEAP_TYPE_DMA, + .id = 2, + .name = "ion-cma-heap-2", + .base = 0, + .size = (16 * SZ_1M), + }, +}; + +static struct ion_platform_data snowball_ion_data2 = { + .heaps = snowball_ion_heap2, + .nr = ARRAY_SIZE(snowball_ion_heap2), +}; + +static struct platform_device snowball_ion_device2 = { + .name = "ion-ux500", + .id = 2, + .dev = { + .dma_mask = &snowball_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &snowball_ion_data2, + }, + .num_resources = 0, +}; +#endif + static struct gpio_led snowball_led_array[] = { { .name = "user_led", @@ -598,8 +659,21 @@ static struct platform_device *snowball_platform_devs[] __initdata = { &snowball_key_dev, &snowball_sbnet_dev, &ab8500_device, +#ifdef CONFIG_ION + &snowball_ion_device1, + &snowball_ion_device2, +#endif };
+#ifdef CONFIG_ION +static void __init mop500_reserve(void) +{ + dma_declare_contiguous(&snowball_ion_device1.dev, + snowball_ion_heap1[0].size, + snowball_ion_heap1[0].base, 0); +} +#endif + static void __init mop500_init_machine(void) { int i2c0_devs; @@ -713,4 +787,7 @@ MACHINE_START(SNOWBALL, "Calao Systems Snowball platform") /* we re-use nomadik timer here */ .timer = &ux500_timer, .init_machine = snowball_init_machine, +#ifdef CONFIG_ION + .reserve = mop500_reserve, +#endif MACHINE_END diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig index 0e87dab..9352020 100644 --- a/drivers/gpu/ion/Kconfig +++ b/drivers/gpu/ion/Kconfig @@ -9,3 +9,9 @@ config ION_TEGRA depends on ARCH_TEGRA && ION help Choose this option if you wish to use ion on an nVidia Tegra. + +config ION_UX500 + tristate "Ion for ux500" + depends on ARCH_U8500 && ION + help + Choose this option if you wish to use ion on ux500 platforms. diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index ddcedb5..ed38775 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,3 +1,4 @@ ion-driver-objs += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o ion_cma_heap.o obj-$(CONFIG_ION) += ion-driver.o obj-$(CONFIG_ION_TEGRA) += tegra/ +obj-$(CONFIG_ION_UX500) += ux500/ diff --git a/drivers/gpu/ion/ux500/Makefile b/drivers/gpu/ion/ux500/Makefile new file mode 100644 index 0000000..691c600 --- /dev/null +++ b/drivers/gpu/ion/ux500/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ION_UX500) += ux500_ion.o diff --git a/drivers/gpu/ion/ux500/ux500_ion.c b/drivers/gpu/ion/ux500/ux500_ion.c new file mode 100644 index 0000000..c7560c7 --- /dev/null +++ b/drivers/gpu/ion/ux500/ux500_ion.c @@ -0,0 +1,142 @@ +/* + * drivers/gpu/ion/ux500/ux500_ion.c + * + * Copyright (C) Linaro 2012 + * Author: benjamin.gaignard@linaro.org for ST-Ericsson. + * + * 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/err.h> +#include <linux/ion.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include "../ion_priv.h" + +struct ion_device *ux500_ion_device; +int num_heaps; +struct ion_heap **ux500_ion_heaps; + +int ux500_ion_probe(struct platform_device *pdev) +{ + struct ion_platform_data *pdata = pdev->dev.platform_data; + int err; + int i, previous_heaps_count = 0; + + /* test if it is the first time we try to create ions heaps */ + if (num_heaps == 0) { + num_heaps = pdata->nr; + + ux500_ion_heaps = + kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL); + memset(ux500_ion_heaps, 0, + sizeof(struct ion_heap *) * pdata->nr); + + dev_dbg(&pdev->dev, "create ion device\n"); + ux500_ion_device = ion_device_create(NULL); + if (IS_ERR_OR_NULL(ux500_ion_device)) { + kfree(ux500_ion_heaps); + num_heaps = 0; + dev_err(&pdev->dev, "failed to create ion device\n"); + return PTR_ERR(ux500_ion_device); + } + } else { + struct ion_heap **new_ux500_ion_heaps; + + previous_heaps_count = num_heaps; + num_heaps += pdata->nr; + + /* allocate a bigger array of ion_heap */ + new_ux500_ion_heaps = + kzalloc(sizeof(struct ion_heap *) * num_heaps, GFP_KERNEL); + memset(new_ux500_ion_heaps, 0, + sizeof(struct ion_heap *) * num_heaps); + + dev_dbg(&pdev->dev, "realloc ion heap\n"); + /* copy old heap array info into the new one */ + for (i = 0; i < previous_heaps_count; i++) + new_ux500_ion_heaps[i] = ux500_ion_heaps[i]; + + /* free old heap array and swap it with the new one */ + kfree(ux500_ion_heaps); + ux500_ion_heaps = new_ux500_ion_heaps; + } + + /* create the heaps as specified in the board file */ + for (i = previous_heaps_count; i < num_heaps; i++) { + struct ion_platform_heap *heap_data = + &pdata->heaps[i - previous_heaps_count]; + + ux500_ion_heaps[i] = + ion_heap_create_full(heap_data, &pdev->dev); + + if (IS_ERR_OR_NULL(ux500_ion_heaps[i])) { + err = PTR_ERR(ux500_ion_heaps[i]); + ux500_ion_heaps[i] = NULL; + dev_err(&pdev->dev, + "failed to create heap type %d id %d\n", + heap_data->type, heap_data->id); + goto err; + } + ion_device_add_heap(ux500_ion_device, ux500_ion_heaps[i]); + } + + platform_set_drvdata(pdev, ux500_ion_device); + + return 0; +err: + for (i = 0; i < num_heaps; i++) { + if (ux500_ion_heaps[i]) + ion_heap_destroy(ux500_ion_heaps[i]); + } + kfree(ux500_ion_heaps); + return err; +} + +int ux500_ion_remove(struct platform_device *pdev) +{ + struct ion_device *idev = platform_get_drvdata(pdev); + int i; + + ion_device_destroy(idev); + for (i = 0; i < num_heaps; i++) + ion_heap_destroy(ux500_ion_heaps[i]); + kfree(ux500_ion_heaps); + return 0; +} + +static struct platform_driver ux500_ion_driver = { + .probe = ux500_ion_probe, + .remove = ux500_ion_remove, + .driver = { + .name = "ion-ux500", + } +}; + +static int __init ux500_ion_init(void) +{ + ux500_ion_device = NULL; + num_heaps = 0; + ux500_ion_heaps = NULL; + + return platform_driver_register(&ux500_ion_driver); +} + +static void __exit ux500_ion_exit(void) +{ + if (ux500_ion_device) + ion_device_destroy(ux500_ion_device); + + platform_driver_unregister(&ux500_ion_driver); +} + +module_init(ux500_ion_init); +module_exit(ux500_ion_exit);
linaro-mm-sig@lists.linaro.org