| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/err.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/of_fdt.h> |
| #include <linux/of_reserved_mem.h> |
| #include <linux/uaccess.h> |
| #include <linux/scatterlist.h> |
| #include <linux/dma-buf.h> |
| #include <linux/cma.h> |
| #include <linux/ion.h> |
| #include <linux/amlogic/tee.h> |
| #include "dev_ion.h" |
| |
| //MODULE_DESCRIPTION("AMLOGIC ION driver"); |
| //MODULE_LICENSE("GPL v2"); |
| //MODULE_AUTHOR("Amlogic SH"); |
| |
| #define MESON_MAX_ION_HEAP 8 |
| |
| static struct heap_type_desc { |
| char *name; |
| int heap_type; |
| struct ion_heap_ops *ops; |
| unsigned long flags; |
| } meson_heap_descs[] = { |
| { |
| .name = "codec_mm_cma", |
| .heap_type = ION_HEAP_TYPE_CUSTOM, |
| .ops = &codec_mm_heap_ops, |
| .flags = ION_HEAP_FLAG_DEFER_FREE, |
| }, |
| { |
| .name = "ion-dev", |
| .heap_type = ION_HEAP_TYPE_DMA, |
| .ops = &ion_cma_ops, |
| }, |
| { |
| .name = "ion-fb", |
| .heap_type = ION_HEAP_TYPE_DMA, |
| .ops = &ion_cma_ops, |
| }, |
| }; |
| |
| struct device *ion_dev; |
| static int num_heaps; |
| static struct ion_cma_heap *heaps[MESON_MAX_ION_HEAP]; |
| static struct ion_heap *secure_heap; |
| |
| struct device *meson_ion_get_dev(void) |
| { |
| return ion_dev; |
| } |
| EXPORT_SYMBOL(meson_ion_get_dev); |
| |
| void meson_ion_buffer_to_phys(struct ion_buffer *buffer, |
| phys_addr_t *addr, size_t *len) |
| { |
| struct sg_table *sg_table; |
| struct page *page; |
| |
| if (buffer) { |
| sg_table = buffer->sg_table; |
| page = sg_page(sg_table->sgl); |
| *addr = PFN_PHYS(page_to_pfn(page)); |
| *len = buffer->size; |
| } |
| } |
| EXPORT_SYMBOL(meson_ion_buffer_to_phys); |
| |
| int meson_ion_share_fd_to_phys(int fd, phys_addr_t *addr, size_t *len) |
| { |
| struct dma_buf *dmabuf; |
| struct ion_buffer *buffer; |
| |
| dmabuf = dma_buf_get(fd); |
| if (IS_ERR_OR_NULL(dmabuf)) |
| return PTR_ERR(dmabuf); |
| |
| buffer = (struct ion_buffer *)dmabuf->priv; |
| meson_ion_buffer_to_phys(buffer, addr, len); |
| dma_buf_put(dmabuf); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(meson_ion_share_fd_to_phys); |
| |
| static unsigned int meson_ion_heap_id_get(char *heap_name) |
| { |
| int i; |
| struct ion_cma_heap *heap; |
| |
| for (i = 0; i < num_heaps; i++) { |
| heap = heaps[i]; |
| if (!strcmp(heap->heap.name, heap_name)) |
| return heap->heap.id; |
| } |
| |
| return 0; |
| } |
| |
| unsigned int meson_ion_cma_heap_id_get(void) |
| { |
| return meson_ion_heap_id_get("ion-dev"); |
| } |
| EXPORT_SYMBOL(meson_ion_cma_heap_id_get); |
| |
| unsigned int meson_ion_fb_heap_id_get(void) |
| { |
| return meson_ion_heap_id_get("ion-fb"); |
| } |
| EXPORT_SYMBOL(meson_ion_fb_heap_id_get); |
| |
| unsigned int meson_ion_codecmm_heap_id_get(void) |
| { |
| return meson_ion_heap_id_get("codec_mm_cma"); |
| } |
| EXPORT_SYMBOL(meson_ion_codecmm_heap_id_get); |
| |
| unsigned int meson_ion_secure_heap_id_get(void) |
| { |
| unsigned int id = 0; |
| |
| if (secure_heap) |
| id = secure_heap->id; |
| return id; |
| } |
| EXPORT_SYMBOL(meson_ion_secure_heap_id_get); |
| |
| static int __meson_ion_add_heap(struct ion_heap *heap, |
| struct heap_type_desc *desc) |
| { |
| int ret; |
| |
| heap->ops = desc->ops; |
| heap->type = desc->heap_type; |
| heap->flags = desc->flags; |
| heap->name = desc->name; |
| |
| ret = ion_device_add_heap(heap); |
| if (ret) |
| DION_ERROR("%s fail\n", __func__); |
| else |
| DION_INFO("%s,heap->name=%s heap->id=%d\n", |
| __func__, heap->name, heap->id); |
| |
| return ret; |
| } |
| |
| static int meson_ion_add_heap(struct cma *cma, void *data) |
| { |
| int i, ret, heap_type; |
| bool need_add = false; |
| struct heap_type_desc *desc; |
| struct ion_cma_heap *heap; |
| int *cma_nr = data; |
| const char *cma_name = cma_get_name(cma); |
| |
| for (i = 0; i < ARRAY_SIZE(meson_heap_descs); i++) { |
| desc = &meson_heap_descs[i]; |
| |
| if (strstr(cma_name, desc->name)) { |
| heap_type = desc->heap_type; |
| need_add = true; |
| break; |
| } |
| } |
| |
| if (need_add) { |
| heap = kzalloc(sizeof(*heap), GFP_KERNEL); |
| if (!heap) |
| return -ENOMEM; |
| ret = __meson_ion_add_heap(&heap->heap, desc); |
| if (!ret) |
| heap->is_added = true; |
| |
| heap->cma = cma; |
| mutex_init(&heap->mutex); |
| heaps[num_heaps++] = heap; |
| *cma_nr = num_heaps; |
| } |
| |
| return 0; |
| } |
| |
| static int ion_secure_mem_init(struct reserved_mem *rmem, struct device *dev) |
| { |
| #ifdef CONFIG_AMLOGIC_TEE |
| u32 secure_heap_handle; |
| #endif |
| int ret; |
| |
| secure_heap = ion_secure_heap_create(rmem->base, rmem->size); |
| ret = ion_device_add_heap(secure_heap); |
| if (ret) |
| DION_ERROR("%s fail\n", __func__); |
| else |
| DION_INFO("%s,secure_heap->name=%s secure_heap->id=%d\n", |
| __func__, secure_heap->name, secure_heap->id); |
| #ifdef CONFIG_AMLOGIC_TEE |
| ret = tee_protect_mem_by_type(TEE_MEM_TYPE_GPU, |
| (u32)rmem->base, |
| (u32)rmem->size, |
| &secure_heap_handle); |
| if (ret) |
| DION_ERROR("tee protect gpu mem fail!\n"); |
| else |
| DION_INFO("tee protect gpu mem done\n"); |
| #endif |
| DION_INFO("ion secure_mem_init size=0x%pa, paddr=0x%pa\n", |
| &rmem->size, &rmem->base); |
| |
| return 0; |
| } |
| |
| static const struct reserved_mem_ops rmem_ion_secure_ops = { |
| .device_init = ion_secure_mem_init, |
| }; |
| |
| static int __init rmem_ion_secure_setup(struct reserved_mem *rmem) |
| { |
| rmem->ops = &rmem_ion_secure_ops; |
| return 0; |
| } |
| |
| static int dev_ion_probe(struct platform_device *pdev) |
| { |
| int ret; |
| int nr = 0; |
| int secure_flag = 0; |
| int secure_region_index = 0; |
| struct device_node *search_target = NULL; |
| #ifdef MODULE |
| struct device_node *target = NULL; |
| struct reserved_mem *mem = NULL; |
| #endif |
| |
| while (1) { |
| search_target = of_parse_phandle(pdev->dev.of_node, |
| "memory-region", |
| secure_region_index); |
| if (!search_target) |
| break; |
| if (!strcmp(search_target->name, "linux,ion-secure")) { |
| secure_flag = 1; |
| break; |
| } |
| secure_region_index++; |
| } |
| |
| if (secure_flag) { |
| #ifdef MODULE |
| target = of_parse_phandle(pdev->dev.of_node, |
| "memory-region", |
| secure_region_index); |
| if (target) |
| mem = of_reserved_mem_lookup(target); |
| if (mem) { |
| ret = rmem_ion_secure_setup(mem); |
| if (ret != 0) |
| DION_ERROR("failed to ion_secure_mem_init\n"); |
| else |
| DION_INFO("ion_secure_mem_init succeed\n"); |
| } |
| #endif |
| |
| ret = of_reserved_mem_device_init_by_idx(&pdev->dev, |
| pdev->dev.of_node, secure_region_index); |
| if (ret != 0) |
| DION_ERROR("failed get secure memory\n"); |
| } |
| |
| ret = cma_for_each_area(meson_ion_add_heap, &nr); |
| if (ret) { |
| for (nr = 0; nr < num_heaps && heaps[nr]; nr++) { |
| if (heaps[nr]->is_added) |
| ion_device_remove_heap(&heaps[nr]->heap); |
| kfree(heaps[nr]); |
| } |
| } |
| ion_dev = &pdev->dev; |
| |
| return ret; |
| } |
| |
| static int dev_ion_remove(struct platform_device *pdev) |
| { |
| if (secure_heap) |
| ion_secure_heap_destroy(secure_heap); |
| return 0; |
| } |
| |
| static const struct of_device_id amlogic_ion_dev_dt_match[] = { |
| { .compatible = "amlogic, ion_dev", }, |
| { }, |
| }; |
| |
| static struct platform_driver ion_driver = { |
| .probe = dev_ion_probe, |
| .remove = dev_ion_remove, |
| .driver = { |
| .name = "ion_dev", |
| .owner = THIS_MODULE, |
| .of_match_table = amlogic_ion_dev_dt_match |
| } |
| }; |
| |
| RESERVEDMEM_OF_DECLARE(ion_secure_mem, |
| "amlogic, ion-secure-mem", |
| rmem_ion_secure_setup); |
| |
| int __init ion_init(void) |
| { |
| return platform_driver_register(&ion_driver); |
| } |
| |
| void __exit ion_exit(void) |
| { |
| platform_driver_unregister(&ion_driver); |
| } |
| |