| // 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 <ion/ion.h> |
| #include "dev_ion.h" |
| |
| MODULE_DESCRIPTION("AMLOGIC ION driver"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Amlogic SH"); |
| |
| #define DION_ERROR(fmt, args ...) pr_err("ion_dev: " fmt, ## args) |
| #define DION_INFO(fmt, args ...) pr_info("ion_dev: " fmt, ## args) |
| #define DION_DEBUG(fmt, args ...) pr_debug("ion_dev: " fmt, ## args) |
| |
| static bool is_private_pool; |
| static const char *ion_rmem_name; |
| static unsigned int ion_heap_id; |
| struct device *ion_dev; |
| |
| 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; |
| } |
| } |
| |
| 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; |
| } |
| |
| /*return:1:match,0:unmatch*/ |
| int meson_ion_cma_heap_match(const char *name) |
| { |
| int ret; |
| |
| if (!ion_rmem_name) { |
| ret = 0; |
| DION_INFO("%s, ion_rmem_name is NULL!!\n", __func__); |
| } else if (strcmp(name, ion_rmem_name)) { |
| ret = 0; |
| DION_INFO("%s, ion_rmem.name(%s) unmatch input(%s)\n", |
| __func__, ion_rmem_name, name); |
| } else { |
| ret = 1; |
| DION_INFO("%s, ion_rmem.name(%s) match input(%s)\n", |
| __func__, ion_rmem_name, name); |
| } |
| return ret; |
| } |
| EXPORT_SYMBOL(meson_ion_cma_heap_match); |
| |
| void meson_ion_cma_heap_id_set(unsigned int id) |
| { |
| ion_heap_id = id; |
| } |
| EXPORT_SYMBOL(meson_ion_cma_heap_id_set); |
| |
| unsigned int meson_ion_cma_heap_id_get(void) |
| { |
| return ion_heap_id; |
| } |
| |
| s32 init_ionmem_device(void); |
| s32 uninit_ionmem_device(void); |
| int dev_ion_probe(struct platform_device *pdev) |
| { |
| int err = 0; |
| struct device_node *mem_node; |
| struct reserved_mem *rmem = NULL; |
| |
| is_private_pool = false; |
| /* init reserved memory */ |
| err = of_reserved_mem_device_init(&pdev->dev); |
| if (err != 0) { |
| DION_INFO("failed get reserved memory\n"); |
| return err; |
| } |
| ion_dev = &pdev->dev; |
| mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); |
| if (mem_node) |
| rmem = of_reserved_mem_lookup(mem_node); |
| of_node_put(mem_node); |
| |
| if (rmem) { |
| ion_rmem_name = rmem->name; |
| DION_INFO("%s, create [%s] heap done (%s)\n", __func__, |
| rmem->name, is_private_pool ? "privated" : "shared"); |
| } else { |
| ion_rmem_name = NULL; |
| DION_ERROR("%s, done with NULL rmem!!\n", __func__); |
| } |
| |
| if(init_ionmem_device() != 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int dev_ion_remove(struct platform_device *pdev) |
| { |
| uninit_ionmem_device(); |
| of_reserved_mem_device_release(&pdev->dev); |
| 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 |
| } |
| }; |
| |
| static int __init ion_init(void) |
| { |
| return platform_driver_register(&ion_driver); |
| } |
| |
| static void __exit ion_exit(void) |
| { |
| platform_driver_unregister(&ion_driver); |
| } |
| |
| extern size_t ion_info_dbg(char *buf); |
| static ssize_t ionmem_status_show(struct class *cla, |
| struct class_attribute *attr, char *buf) |
| { |
| return ion_info_dbg(buf); |
| } |
| |
| static CLASS_ATTR_RO(ionmem_status); |
| static struct attribute *iondev_class_attrs[] = { |
| &class_attr_ionmem_status.attr, |
| NULL |
| }; |
| |
| ATTRIBUTE_GROUPS(iondev_class); |
| |
| static struct class iondev_class = { |
| .name = "dev_ion", |
| .class_groups = iondev_class_groups, |
| }; |
| |
| static s32 s_register_flag; |
| s32 init_ionmem_device(void) |
| { |
| s32 r = 0; |
| |
| r = class_register(&iondev_class); |
| if (r < 0) { |
| DION_ERROR("error create dev_ion class.\n"); |
| return -1; |
| } |
| s_register_flag = 1; |
| |
| return 0; |
| } |
| |
| s32 uninit_ionmem_device(void) |
| { |
| if (s_register_flag) |
| class_destroy(&iondev_class); |
| s_register_flag = 0; |
| |
| return 0; |
| } |
| |
| extern bool ion_private_pool_init(unsigned long addr, unsigned long size); |
| extern bool ion_private_pool_exit(void); |
| static s32 ion_mem_device_init( |
| struct reserved_mem *rmem, struct device *dev) |
| { |
| |
| if (!rmem) { |
| DION_ERROR("will allocate multienc buffer!\n"); |
| return -EFAULT; |
| } |
| |
| if (!rmem->base) { |
| DION_ERROR( |
| "%s: memory range error, 0x%lx - 0x%lx\n", |
| __func__, |
| (ulong)rmem->base, (ulong)rmem->size); |
| return -EFAULT; |
| } |
| |
| // Init pool |
| is_private_pool = true; |
| ion_private_pool_init(rmem->base, rmem->size); |
| return 0; |
| } |
| |
| static void ion_mem_device_release( |
| struct reserved_mem *rmem, struct device *dev) |
| { |
| |
| if (!rmem) { |
| DION_ERROR("will free multienc buffer!\n"); |
| return; |
| } |
| |
| if (!rmem->base) { |
| DION_ERROR( |
| "%s: memory range error, 0x%lx - 0x%lx\n", |
| __func__, |
| (ulong)rmem->base, (ulong)rmem->size); |
| return; |
| } |
| |
| // Destroy pool |
| ion_private_pool_exit(); |
| return; |
| } |
| |
| bool iondev_mem_is_private(void) |
| { |
| return is_private_pool; |
| } |
| EXPORT_SYMBOL(iondev_mem_is_private); |
| |
| static const struct reserved_mem_ops rmem_ion_ops = { |
| .device_init = ion_mem_device_init, |
| .device_release = ion_mem_device_release, |
| }; |
| |
| static s32 __init ion_mem_setup(struct reserved_mem *rmem) |
| { |
| rmem->ops = &rmem_ion_ops; |
| DION_DEBUG("ION private memory setup.\n"); |
| return 0; |
| } |
| |
| RESERVEDMEM_OF_DECLARE(amlogic_ion_dev, "ion, private-mem", ion_mem_setup); |
| fs_initcall(ion_init); |
| module_exit(ion_exit); |