From bd4aec9190440d3294df045d1b45c30095ef67c9 Mon Sep 17 00:00:00 2001 From: Li Huang Date: Thu, 22 Sep 2022 16:17:01 +0800 Subject: [PATCH] dma-buf: heaps: sram_heap: init sram-heap depend on sram driver Signed-off-by: Li Huang Change-Id: Ide523ee2691407afb6bdc18423afaa095dafbb9f --- drivers/dma-buf/heaps/Kconfig | 7 + drivers/dma-buf/heaps/Makefile | 1 + drivers/dma-buf/heaps/sram_heap.c | 437 ++++++++++++++++++++++++++++++ include/linux/sram_heap.h | 49 ++++ 4 files changed, 494 insertions(+) create mode 100644 drivers/dma-buf/heaps/sram_heap.c create mode 100644 include/linux/sram_heap.h diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig index ff52efa83f39..75f4e3e349c1 100644 --- a/drivers/dma-buf/heaps/Kconfig +++ b/drivers/dma-buf/heaps/Kconfig @@ -22,3 +22,10 @@ config DMABUF_HEAPS_CMA Choose this option to enable dma-buf CMA heap. This heap is backed by the Contiguous Memory Allocator (CMA). If your system has these regions, you should say Y here. + +config DMABUF_HEAPS_SRAM + tristate "Export on-chip SRAM pools using DMA-Heaps" + depends on DMABUF_HEAPS && SRAM + help + This driver allows the export of on-chip SRAM marked as exportable + to userspace using the DMA-Heaps interface. diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile index 4e134a221a06..07ba63081b0f 100644 --- a/drivers/dma-buf/heaps/Makefile +++ b/drivers/dma-buf/heaps/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_DMABUF_HEAPS_DEFERRED_FREE) += deferred-free-helper.o obj-$(CONFIG_DMABUF_HEAPS_PAGE_POOL) += page_pool.o obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += rk_system_heap.o obj-$(CONFIG_DMABUF_HEAPS_CMA) += rk_cma_heap.o +obj-$(CONFIG_DMABUF_HEAPS_SRAM) += sram_heap.o diff --git a/drivers/dma-buf/heaps/sram_heap.c b/drivers/dma-buf/heaps/sram_heap.c new file mode 100644 index 000000000000..d9a9b70a79d4 --- /dev/null +++ b/drivers/dma-buf/heaps/sram_heap.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SRAM DMA-Heap exporter && support alloc page and dmabuf on kernel + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * + * Author: Andrew F. Davis + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ +#define pr_fmt(fmt) "sram_heap: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define RK3588_SRAM_BASE 0xff001000 + +struct sram_dma_heap { + struct dma_heap *heap; + struct gen_pool *pool; +}; + +struct sram_dma_heap_buffer { + struct gen_pool *pool; + struct list_head attachments; + struct mutex attachments_lock; + unsigned long len; + void *vaddr; + phys_addr_t paddr; +}; + +struct dma_heap_attachment { + struct device *dev; + struct sg_table *table; + struct list_head list; +}; + +static int dma_heap_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a; + struct sg_table *table; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) + goto table_alloc_failed; + + if (sg_alloc_table(table, 1, GFP_KERNEL)) + goto sg_alloc_failed; + + /* + * The referenced pfn and page are for setting the sram address to the + * sgtable, and cannot be used for other purposes, and cannot be accessed + * directly or indirectly. + * + * And not sure if there is a problem with the 32-bit system. + * + * page cannot support kmap func. + */ + sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(buffer->paddr)), buffer->len, 0); + + a->table = table; + a->dev = attachment->dev; + INIT_LIST_HEAD(&a->list); + + attachment->priv = a; + + mutex_lock(&buffer->attachments_lock); + list_add(&a->list, &buffer->attachments); + mutex_unlock(&buffer->attachments_lock); + + return 0; + +sg_alloc_failed: + kfree(table); +table_alloc_failed: + kfree(a); + return -ENOMEM; +} + +static void dma_heap_detatch(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a = attachment->priv; + + mutex_lock(&buffer->attachments_lock); + list_del(&a->list); + mutex_unlock(&buffer->attachments_lock); + + sg_free_table(a->table); + kfree(a->table); + kfree(a); +} + +static struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct dma_heap_attachment *a = attachment->priv; + struct sg_table *table = a->table; + int ret = 0; + + ret = dma_map_sgtable(attachment->dev, table, direction, DMA_ATTR_SKIP_CPU_SYNC); + if (ret) + return ERR_PTR(-ENOMEM); + + return table; +} + +static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + dma_unmap_sgtable(attachment->dev, table, direction, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void dma_heap_dma_buf_release(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); + kfree(buffer); +} + +static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + int ret; + + /* SRAM mappings are not cached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret = vm_iomap_memory(vma, buffer->paddr, buffer->len); + if (ret) + pr_err("Could not map buffer to userspace\n"); + + return ret; +} + +static void *dma_heap_vmap(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->vaddr; +} + +static const struct dma_buf_ops sram_dma_heap_buf_ops = { + .attach = dma_heap_attach, + .detach = dma_heap_detatch, + .map_dma_buf = dma_heap_map_dma_buf, + .unmap_dma_buf = dma_heap_unmap_dma_buf, + .release = dma_heap_dma_buf_release, + .mmap = dma_heap_mmap, + .vmap = dma_heap_vmap, +}; + +static struct dma_buf *sram_dma_heap_allocate(struct dma_heap *heap, + unsigned long len, + unsigned long fd_flags, + unsigned long heap_flags) +{ + struct sram_dma_heap *sram_dma_heap = dma_heap_get_drvdata(heap); + struct sram_dma_heap_buffer *buffer; + + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dmabuf; + int ret = -ENOMEM; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + buffer->pool = sram_dma_heap->pool; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->attachments_lock); + buffer->len = len; + + buffer->vaddr = (void *)gen_pool_alloc(buffer->pool, buffer->len); + if (!buffer->vaddr) { + ret = -ENOMEM; + goto free_buffer; + } + + buffer->paddr = gen_pool_virt_to_phys(buffer->pool, (unsigned long)buffer->vaddr); + if (buffer->paddr == -1) { + ret = -ENOMEM; + goto free_pool; + } + + /* create the dmabuf */ + exp_info.ops = &sram_dma_heap_buf_ops; + exp_info.size = buffer->len; + exp_info.flags = fd_flags; + exp_info.priv = buffer; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto free_pool; + } + + return dmabuf; + +free_pool: + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); +free_buffer: + kfree(buffer); + + return ERR_PTR(ret); +} + +static struct dma_heap_ops sram_dma_heap_ops = { + .allocate = sram_dma_heap_allocate, +}; + +static struct sram_dma_heap *sram_dma_heap_global; + +static int sram_dma_heap_export(const char *name, + struct gen_pool *sram_gp) +{ + struct sram_dma_heap *sram_dma_heap; + struct dma_heap_export_info exp_info; + + pr_info("Exporting SRAM pool '%s'\n", name); + + sram_dma_heap = kzalloc(sizeof(*sram_dma_heap), GFP_KERNEL); + if (!sram_dma_heap) + return -ENOMEM; + sram_dma_heap->pool = sram_gp; + + exp_info.name = "sram_dma_heap"; + exp_info.ops = &sram_dma_heap_ops; + exp_info.priv = sram_dma_heap; + + sram_dma_heap_global = sram_dma_heap; + + sram_dma_heap->heap = dma_heap_add(&exp_info); + if (IS_ERR(sram_dma_heap->heap)) { + int ret = PTR_ERR(sram_dma_heap->heap); + + kfree(sram_dma_heap); + return ret; + } + + return 0; +} + +struct dma_buf *sram_heap_alloc_dma_buf(size_t size) +{ + struct sram_dma_heap *sram_dma_heap = sram_dma_heap_global; + struct sram_dma_heap_buffer *buffer; + + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dmabuf; + int ret = -ENOMEM; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + buffer->pool = sram_dma_heap->pool; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->attachments_lock); + buffer->len = size; + + buffer->vaddr = (void *)gen_pool_alloc(buffer->pool, buffer->len); + if (!buffer->vaddr) { + ret = -ENOMEM; + goto free_buffer; + } + + buffer->paddr = gen_pool_virt_to_phys(buffer->pool, (unsigned long)buffer->vaddr); + if (buffer->paddr == -1) { + ret = -ENOMEM; + goto free_pool; + } + + /* create the dmabuf */ + exp_info.ops = &sram_dma_heap_buf_ops; + exp_info.size = buffer->len; + exp_info.priv = buffer; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto free_pool; + } + + return dmabuf; + +free_pool: + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); +free_buffer: + kfree(buffer); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(sram_heap_alloc_dma_buf); + +struct page *sram_heap_alloc_pages(size_t size) +{ + struct sram_dma_heap *sram_dma_heap = sram_dma_heap_global; + + void *vaddr; + phys_addr_t paddr; + struct page *p; + + int ret = -ENOMEM; + + vaddr = (void *)gen_pool_alloc(sram_dma_heap->pool, size); + if (!vaddr) { + ret = -ENOMEM; + pr_err("no memory"); + goto failed; + } + + paddr = gen_pool_virt_to_phys(sram_dma_heap->pool, (unsigned long)vaddr); + if (paddr == -1) { + ret = -ENOMEM; + pr_err("gen_pool_virt_to_phys failed"); + goto free_pool; + } + + p = pfn_to_page(PFN_DOWN(paddr)); + + return p; + +free_pool: + gen_pool_free(sram_dma_heap->pool, (unsigned long)vaddr, size); +failed: + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(sram_heap_alloc_pages); + +static u64 gen_pool_phys_to_virt(struct gen_pool *pool, phys_addr_t paddr) +{ + struct gen_pool_chunk *chunk; + u64 vaddr = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { + /* TODO: only suit for simple chunk now */ + vaddr = chunk->start_addr + (paddr - chunk->phys_addr); + } + rcu_read_unlock(); + + return vaddr; +} + +void sram_heap_free_pages(struct page *p) +{ + struct sram_dma_heap *sram_dma_heap = sram_dma_heap_global; + void *vaddr; + + vaddr = (void *)gen_pool_phys_to_virt(sram_dma_heap->pool, page_to_phys(p)); + + gen_pool_free(sram_dma_heap->pool, (unsigned long)vaddr, PAGE_SIZE); +} +EXPORT_SYMBOL_GPL(sram_heap_free_pages); + +void sram_heap_free_dma_buf(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); + kfree(buffer); +} +EXPORT_SYMBOL_GPL(sram_heap_free_dma_buf); + +void *sram_heap_get_vaddr(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->vaddr; +} +EXPORT_SYMBOL_GPL(sram_heap_get_vaddr); + +phys_addr_t sram_heap_get_paddr(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->paddr; +} +EXPORT_SYMBOL_GPL(sram_heap_get_paddr); + +static int rk_add_default_sram_heap(void) +{ + struct device_node *np = NULL; + struct gen_pool *sram_gp = NULL; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "rockchip,sram-heap"); + if (!np) { + pr_info("failed to get device node of sram-heap\n"); + return -ENODEV; + } + + if (!of_device_is_available(np)) { + of_node_put(np); + return ret; + } + + sram_gp = of_gen_pool_get(np, "rockchip,sram", 0); + /* release node */ + of_node_put(np); + if (sram_gp == NULL) { + pr_err("sram gen pool is NULL"); + return -ENOMEM; + } + + ret = sram_dma_heap_export("sram-heap", sram_gp); + + return ret; +} +module_init(rk_add_default_sram_heap); +MODULE_DESCRIPTION("Rockchip DMA-BUF SRAM Heap"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/sram_heap.h b/include/linux/sram_heap.h new file mode 100644 index 000000000000..341f501b9153 --- /dev/null +++ b/include/linux/sram_heap.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SRAM DMA-Heap exporter && support alloc page and dmabuf on kernel + * + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ +#ifndef _LINUX_SRAM_HEAP_H +#define _LINUX_SRAM_HEAP_H + +#include +#include +#include + +#if IS_REACHABLE(CONFIG_DMABUF_HEAPS_SRAM) +struct dma_buf *sram_heap_alloc_dma_buf(size_t size); +struct page *sram_heap_alloc_pages(size_t size); +void sram_heap_free_pages(struct page *p); +void sram_heap_free_dma_buf(struct dma_buf *dmabuf); +void *sram_heap_get_vaddr(struct dma_buf *dmabuf); +phys_addr_t sram_heap_get_paddr(struct dma_buf *dmabuf); + +#else +static inline struct dma_buf *sram_heap_alloc_dma_buf(size_t size) +{ + return NULL; +} + +static inline struct page *sram_heap_alloc_pages(size_t size) +{ + return NULL; +} + +static inline void sram_heap_free_pages(struct page *p) {} +static inline void sram_heap_free_dma_buf(struct dma_buf *dmabuf) {} + +static inline void *sram_heap_get_vaddr(struct dma_buf *dmabuf) +{ + return NULL; +} + +static inline phys_addr_t sram_heap_get_paddr(struct dma_buf *dmabuf) +{ + return 0; +} +#endif + +#endif