/*! ***************************************************************************** * Copyright (c) Imagination Technologies Ltd. * * The contents of this file are subject to the MIT license as set out below. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 ("GPL")in which case the provisions of * GPL are applicable instead of those above. * * If you wish to allow use of your version of this file only under the terms * of GPL, and not to allow others to use your version of this file under the * terms of the MIT license, indicate your decision by deleting the provisions * above and replace them with the notice and other provisions required by GPL * as set out in the file called "GPLHEADER" included in this distribution. If * you do not delete the provisions above, a recipient may use your version of * this file under the terms of either the MIT license or GPL. * * This License is also included in this distribution in the file called * "MIT_COPYING". * *****************************************************************************/ #include #include #include #include #include #include #include #include #include "img_mem_man_priv.h" struct buffer_data { void *kptr; dma_addr_t dma_handle; /* addr returned by dma_alloc_coherent */ uint64_t *addrs; /* array of physical addresses, upcast to 64-bit */ struct device *dev; size_t size; }; static int trace_physical_pages; static int coherent_heap_alloc(struct device *device, struct heap *heap, size_t size, enum img_mem_attr attr, struct buffer *buffer) { struct buffer_data *buffer_data; phys_addr_t phys_addr; size_t pages, page; pr_debug("%s:%d buffer %d (0x%p)\n", __func__, __LINE__, buffer->id, buffer); buffer_data = kmalloc(sizeof(struct buffer_data), GFP_KERNEL); if (!buffer_data) return -ENOMEM; pages = size / PAGE_SIZE; buffer_data->addrs = kmalloc_array(pages, sizeof(uint64_t), GFP_KERNEL); if (!buffer_data->addrs) { kfree(buffer_data); return -ENOMEM; } buffer_data->dev = device; buffer_data->size = size; buffer_data->kptr = dma_alloc_coherent(device, size, &buffer_data->dma_handle, heap->options.coherent.gfp_flags); if (!buffer_data->kptr) { pr_err("%s dma_alloc_coherent failed!\n", __func__); kfree(buffer_data->addrs); kfree(buffer_data); return -ENOMEM; } buffer->kptr = (void *)buffer_data->kptr; page = 0; phys_addr = buffer_data->dma_handle; while (page < pages) { if (trace_physical_pages) pr_info("%s phys %llx\n", __func__, (unsigned long long)phys_addr); buffer_data->addrs[page++] = phys_addr; phys_addr += PAGE_SIZE; }; buffer->priv = buffer_data; pr_debug("%s buffer %d kptr %p phys %#llx size %zu\n", __func__, buffer->id, buffer->kptr, (unsigned long long)buffer_data->addrs[0], size); return 0; } static void coherent_heap_free(struct heap *heap, struct buffer *buffer) { struct buffer_data *buffer_data = buffer->priv; pr_debug("%s:%d buffer %d (0x%p)\n", __func__, __LINE__, buffer->id, buffer); dma_free_coherent(buffer_data->dev, buffer_data->size, buffer_data->kptr, buffer_data->dma_handle); kfree(buffer_data->addrs); kfree(buffer_data); } static int coherent_heap_map_um(struct heap *heap, struct buffer *buffer, struct vm_area_struct *vma) { struct buffer_data *buffer_data = buffer->priv; unsigned long pfn = *buffer_data->addrs >> PAGE_SHIFT; pr_debug("%s:%d buffer %d (0x%p)\n", __func__, __LINE__, buffer->id, buffer); pr_debug("%s:%d vm_start %#lx vm_end %#lx size %ld\n", __func__, __LINE__, vma->vm_start, vma->vm_end, vma->vm_end - vma->vm_start); vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); return remap_pfn_range(vma, vma->vm_start, pfn, vma->vm_end - vma->vm_start, vma->vm_page_prot); } static int coherent_heap_map_km(struct heap *heap, struct buffer *buffer) { pr_debug("%s:%d buffer %d (0x%p) kptr 0x%p\n", __func__, __LINE__, buffer->id, buffer, buffer->kptr); return 0; } static int coherent_heap_unmap_km(struct heap *heap, struct buffer *buffer) { pr_debug("%s:%d buffer %d (0x%p) kptr 0x%p\n", __func__, __LINE__, buffer->id, buffer, buffer->kptr); return 0; } static int coherent_heap_get_page_array(struct heap *heap, struct buffer *buffer, uint64_t **addrs) { struct buffer_data *buffer_data = buffer->priv; *addrs = buffer_data->addrs; return 0; } static void coherent_heap_destroy(struct heap *heap) { pr_debug("%s:%d\n", __func__, __LINE__); } static struct heap_ops coherent_heap_ops = { .alloc = coherent_heap_alloc, .import = NULL, .free = coherent_heap_free, .map_um = coherent_heap_map_um, .unmap_um = NULL, .map_km = coherent_heap_map_km, .unmap_km = coherent_heap_unmap_km, .get_sg_table = NULL, .get_page_array = coherent_heap_get_page_array, .sync_cpu_to_dev = NULL, .sync_dev_to_cpu = NULL, .set_offset = NULL, .destroy = coherent_heap_destroy, }; int img_mem_coherent_init(const struct heap_config *config, struct heap *heap) { pr_debug("%s gfp:%x\n", __func__, config->options.coherent.gfp_flags); heap->ops = &coherent_heap_ops; return 0; }