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 5: - port patches on android kernel 3.4 where ION use dmabuf - add ion_cma_heap_map_dma and ion_cma_heap_unmap_dma functions
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 | 5 ++ drivers/gpu/ion/Makefile | 5 +- drivers/gpu/ion/ion_cma_heap.c | 175 ++++++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 18 +++- drivers/gpu/ion/ion_priv.h | 13 +++ drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 142 +++++++++++++++++++++++++++++ include/linux/ion.h | 5 +- 9 files changed, 438 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 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/ion.h b/include/linux/ion.h index d44ce69..f6bb4df 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 file changed, 2 insertions(+)
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index cf4a960..58945ab 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -99,6 +99,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. @@ -112,6 +113,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 | 4 +- drivers/gpu/ion/ion_cma_heap.c | 175 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 18 ++++- drivers/gpu/ion/ion_priv.h | 11 +++ include/linux/ion.h | 3 + 5 files changed, 209 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..32d3385 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,2 +1,4 @@ -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 +obj-$(CONFIG_ION) += ion-driver.o +obj-$(CONFIG_CMA) += ion_cma_heap.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..1569e62 --- /dev/null +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -0,0 +1,175 @@ +/* + * 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; + struct sg_table *table; +}; + +/* 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; + int n_pages, i; + struct scatterlist *sg; + + 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"); + goto err; + } + + info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!info->table) { + dev_err(dev, "Fail to allocate sg table\n"); + goto err; + } + + n_pages = PAGE_ALIGN(len) >> PAGE_SHIFT; + + /* CMA allocate one big chunk of memory + * so we will only have one entry in sg table */ + i = sg_alloc_table(info->table, 1, GFP_KERNEL); + if (i) { + kfree(info->table); + goto err; + } + for_each_sg(info->table->sgl, sg, info->table->nents, i) { + /* we will this loop only one time */ + struct page *page = pfn_to_page(dma_to_pfn(dev, info->handle)); + sg_set_page(sg, page, PAGE_SIZE * n_pages, 0); + } + + /* keep this for memory release */ + buffer->priv_virt = info; + dev_dbg(dev, "Allocate buffer %p\n", buffer); + return 0; + +err: + kfree(info); + return ION_CMA_ALLOCATE_FAILED; +} + +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); + /* release memory */ + dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); + /* release sg table */ + kfree(info->table); + 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; +} + +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; +} + +void ion_cma_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return; +} + +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, + .map_dma = ion_cma_heap_map_dma, + .unmap_dma = ion_cma_heap_unmap_dma, + .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..2791259 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,11 @@ 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; +#ifdef CONFIG_CMA + case ION_HEAP_TYPE_DMA: + heap = ion_cma_heap_create(heap_data, dev); + break; +#endif default: pr_err("%s: Invalid heap type %d\n", __func__, heap_data->type); @@ -50,6 +56,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 +76,11 @@ void ion_heap_destroy(struct ion_heap *heap) case ION_HEAP_TYPE_CARVEOUT: ion_carveout_heap_destroy(heap); break; +#ifdef CONFIG_CMA + case ION_HEAP_TYPE_DMA: + ion_cma_heap_destroy(heap); + break; +#endif 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 58945ab..36409c2 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_buffer *ion_handle_buffer(struct ion_handle *handle);
@@ -147,6 +148,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 *); @@ -165,6 +169,13 @@ 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); + +#ifdef CONFIG_CMA +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *, + struct device *); +void ion_cma_heap_destroy(struct ion_heap *); +#endif + /** * 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 f6bb4df..9ed9e52 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 | 5 ++ drivers/gpu/ion/Makefile | 1 + drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 142 ++++++++++++++++++++++++++++++++++++ 5 files changed, 226 insertions(+) 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 77d03c1..e4e75ac 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/of.h> #include <linux/of_platform.h>
@@ -54,6 +59,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", @@ -607,8 +668,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) { struct device *parent = NULL; @@ -741,6 +815,9 @@ MACHINE_START(SNOWBALL, "Calao Systems Snowball platform") .timer = &ux500_timer, .handle_irq = gic_handle_irq, .init_machine = snowball_init_machine, +#ifdef CONFIG_ION + .reserve = mop500_reserve, +#endif MACHINE_END
#ifdef CONFIG_MACH_UX500_DT diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig index b5bfdb4..bfe572d 100644 --- a/drivers/gpu/ion/Kconfig +++ b/drivers/gpu/ion/Kconfig @@ -11,3 +11,8 @@ config ION_TEGRA 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 32d3385..a7ea570 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -2,3 +2,4 @@ ion-driver-objs += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o obj-$(CONFIG_ION) += ion-driver.o obj-$(CONFIG_CMA) += ion_cma_heap.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);
Hi,
Can we modify the struct ion_platform_heap to add a priv field which can be used for passing the device? 1. The ion_heap_create can be retained as such instead of having a ion_heap_create_full newly defined. 2. We can have multiple cma heaps within the same ion device. If a single cma heap id is used per ion device, board file can initialize the ion_platform_heap priv member to ion platform_device dev as is being done in the current set of patches. Else, we could declare dummy devices in board file and pass them as priv member of ion_platform heap. Usecase: Define multiple cma heaps for different SDRAM banks and userspace could use same ion device to try allocating from a specific heap for avoid RW switching overhead or any of the cma heaps.
- Nishanth Peethambaran
On Tue, Jun 12, 2012 at 8:19 AM, benjamin.gaignard@stericsson.com wrote:
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 | 5 ++ drivers/gpu/ion/Makefile | 1 + drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 142 ++++++++++++++++++++++++++++++++++++ 5 files changed, 226 insertions(+) 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 77d03c1..e4e75ac 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/of.h> #include <linux/of_platform.h>
@@ -54,6 +59,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", @@ -607,8 +668,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) { struct device *parent = NULL; @@ -741,6 +815,9 @@ MACHINE_START(SNOWBALL, "Calao Systems Snowball platform") .timer = &ux500_timer, .handle_irq = gic_handle_irq, .init_machine = snowball_init_machine, +#ifdef CONFIG_ION
- .reserve = mop500_reserve,
+#endif MACHINE_END
#ifdef CONFIG_MACH_UX500_DT diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig index b5bfdb4..bfe572d 100644 --- a/drivers/gpu/ion/Kconfig +++ b/drivers/gpu/ion/Kconfig @@ -11,3 +11,8 @@ config ION_TEGRA 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 32d3385..a7ea570 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -2,3 +2,4 @@ ion-driver-objs += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o obj-$(CONFIG_ION) += ion-driver.o obj-$(CONFIG_CMA) += ion_cma_heap.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);
1.7.10
Linaro-mm-sig mailing list Linaro-mm-sig@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-mm-sig
Hello,
The addition of a private field in ion_platform_heap has been discussed in the previous versions of the patches: http://lists.linaro.org/pipermail/linaro-mm-sig/2012-March/001431.html
Each CMA area need to be linked with one device, so we need to have multiple ion-driver device beacuse there is only one ION device. If we don't link CMA to one device driver, all calls to dma_alloc_coherent will go to the default CMA area.
I'm not sure about your use case, but if you mean that you want to define multiple heaps and let the ION allocate the memory in one of them, it is already what ION does in ion_alloc function by checking heap_mask and flags.
Benjamin
Hi,
I missed out that you are stil creating only one ION miscdevice though multiple ion-driver devices are created.
Isn't it better to add a priv field in the platform heap than modifying the heap creation API? The priv field can be then used for extended purposes for custom heaps (board file and heap implementation needs to know what is being passed in the priv field).
- Nishanth Peethambaran
On Wed, Jun 13, 2012 at 9:05 AM, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
Hello,
The addition of a private field in ion_platform_heap has been discussed in the previous versions of the patches: http://lists.linaro.org/pipermail/linaro-mm-sig/2012-March/001431.html
Each CMA area need to be linked with one device, so we need to have multiple ion-driver device beacuse there is only one ION device. If we don't link CMA to one device driver, all calls to dma_alloc_coherent will go to the default CMA area.
I'm not sure about your use case, but if you mean that you want to define multiple heaps and let the ION allocate the memory in one of them, it is already what ION does in ion_alloc function by checking heap_mask and flags.
Benjamin
Hi,
I agree with Nishanth here. Internally, we've added a private field to the platform_heap_data to pass generic board specific data to heaps. Similar to carveout heap:
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), .priv = &snowball_ion_device1.dev }, };
----
struct ion_cma_heap { struct ion_heap heap; struct device *dev; }
struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) { struct ion_cma_heap *heap;
heap = kzalloc(sizeof(*heap), GFP_KERNEL); if (!heap) return ERR_PTR(-ENOMEM);
heap->dev = data->priv; heap->heap.ops = &ion_cma_ops; heap->heap.type = ION_HEAP_TYPE_DMA;
return &heap->heap; }
And all other accesses become container_of to get the heap. This makes it easier to pass in other data that may not be directly linked to the platform device.
Laura
On 6/13/2012 7:31 PM, Nishanth Peethambaran wrote:
Hi,
I missed out that you are stil creating only one ION miscdevice though multiple ion-driver devices are created.
Isn't it better to add a priv field in the platform heap than modifying the heap creation API? The priv field can be then used for extended purposes for custom heaps (board file and heap implementation needs to know what is being passed in the priv field).
- Nishanth Peethambaran
On Wed, Jun 13, 2012 at 9:05 AM, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
Hello,
The addition of a private field in ion_platform_heap has been discussed in the previous versions of the patches: http://lists.linaro.org/pipermail/linaro-mm-sig/2012-March/001431.html
Each CMA area need to be linked with one device, so we need to have multiple ion-driver device beacuse there is only one ION device. If we don't link CMA to one device driver, all calls to dma_alloc_coherent will go to the default CMA area.
I'm not sure about your use case, but if you mean that you want to define multiple heaps and let the ION allocate the memory in one of them, it is already what ION does in ion_alloc function by checking heap_mask and flags.
Benjamin
Linaro-mm-sig mailing list Linaro-mm-sig@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-mm-sig
Hi,
I have try to minimize the change in ion_heap_platform structure, that why I haven't add a private field.
If we put the device in a private field we have to declare it before the heap but in sametime we need put the heap in device platform_data. I don't have spend lot of time on it but for me it looks like a infinite loop... maybe I'm wrong.
Rebecca, do you have some guidelines/recommandations about this problem ?
Benjamin
Hi,
The priv field could be initialized before board initialization gets called also.
- Nishanth Peethambaran +91-9448074166
On Fri, Jun 15, 2012 at 1:42 AM, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
Hi,
I have try to minimize the change in ion_heap_platform structure, that why I haven't add a private field.
If we put the device in a private field we have to declare it before the heap but in sametime we need put the heap in device platform_data. I don't have spend lot of time on it but for me it looks like a infinite loop... maybe I'm wrong.
Rebecca, do you have some guidelines/recommandations about this problem ?
Benjamin
Linaro-mm-sig mailing list Linaro-mm-sig@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-mm-sig
Hi,
The priv field could be initialized before board initialization gets called also.
- Nishanth Peethambaran
On Fri, Jun 15, 2012 at 1:42 AM, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
Hi,
I have try to minimize the change in ion_heap_platform structure, that why I haven't add a private field.
If we put the device in a private field we have to declare it before the heap but in sametime we need put the heap in device platform_data. I don't have spend lot of time on it but for me it looks like a infinite loop... maybe I'm wrong.
Rebecca, do you have some guidelines/recommandations about this problem ?
Benjamin
linaro-mm-sig@lists.linaro.org