// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFIG_SPF_CORE #include #endif #include #include #include #include MODULE_IMPORT_NS(DMA_BUF); #define MSM_AUDIO_ION_PROBED (1 << 0) #define MSM_AUDIO_ION_PHYS_ADDR(alloc_data) \ alloc_data->table->sgl->dma_address #define MSM_AUDIO_SMMU_SID_OFFSET 32 #define TZ_PIL_PROTECT_MEM_SUBSYS_ID 0x0C #define TZ_PIL_CLEAR_PROTECT_MEM_SUBSYS_ID 0x0D #define MSM_AUDIO_ION_DRIVER_NAME "msm_audio_ion" #define MINOR_NUMBER_COUNT 1 struct msm_audio_ion_private { bool smmu_enabled; struct device *cb_dev; u8 device_status; struct list_head alloc_list; struct mutex list_mutex; u64 smmu_sid_bits; u32 smmu_version; bool is_non_hypervisor; char *driver_name; /*char dev related data */ dev_t ion_major; struct class *ion_class; struct device *chardev; struct cdev cdev; }; struct msm_audio_alloc_data { size_t len; struct iosys_map *vmap; struct dma_buf *dma_buf; struct dma_buf_attachment *attach; struct sg_table *table; struct list_head list; }; struct msm_audio_ion_fd_list_private { struct mutex list_mutex; /*list to store fd, phy. addr and handle data */ struct list_head fd_list; }; static struct msm_audio_ion_fd_list_private msm_audio_ion_fd_list = {0,}; static bool msm_audio_ion_fd_list_init = false; struct msm_audio_fd_data { int fd; size_t plen; void *handle; dma_addr_t paddr; struct device *dev; struct list_head list; bool hyp_assign; }; static void msm_audio_ion_add_allocation( struct msm_audio_ion_private *msm_audio_ion_data, struct msm_audio_alloc_data *alloc_data) { /* * Since these APIs can be invoked by multiple * clients, there is need to make sure the list * of allocations is always protected */ mutex_lock(&(msm_audio_ion_data->list_mutex)); list_add_tail(&(alloc_data->list), &(msm_audio_ion_data->alloc_list)); mutex_unlock(&(msm_audio_ion_data->list_mutex)); } /* This function is called with ion_data list mutex lock */ static int msm_audio_ion_map_kernel(struct dma_buf *dma_buf, struct msm_audio_ion_private *ion_data, struct iosys_map *iosys_vmap) { int rc = 0; struct msm_audio_alloc_data *alloc_data = NULL; rc = dma_buf_begin_cpu_access(dma_buf, DMA_BIDIRECTIONAL); if (rc) { pr_err("%s: kmap dma_buf_begin_cpu_access fail\n", __func__); goto exit; } rc = dma_buf_vmap(dma_buf, iosys_vmap); if (rc) { pr_err("%s: kernel mapping of dma_buf failed\n", __func__); dma_buf_end_cpu_access(dma_buf, DMA_BIDIRECTIONAL); goto exit; } /* * TBD: remove the below section once new API * for mapping kernel virtual address is available. */ mutex_lock(&(ion_data->list_mutex)); list_for_each_entry(alloc_data, &(ion_data->alloc_list), list) { if (alloc_data->dma_buf == dma_buf) { alloc_data->vmap = iosys_vmap; break; } } mutex_unlock(&(ion_data->list_mutex)); exit: return rc; } /* This function is called with ion_data list mutex lock */ static int msm_audio_dma_buf_map(struct dma_buf *dma_buf, dma_addr_t *addr, size_t *len, bool is_iova, struct msm_audio_ion_private *ion_data) { struct msm_audio_alloc_data *alloc_data = NULL; int rc = 0; struct iosys_map *iosys_vmap = NULL; struct device *cb_dev = ion_data->cb_dev; iosys_vmap = kzalloc(sizeof(*iosys_vmap), GFP_KERNEL); if (!iosys_vmap) return -ENOMEM; /* Data required per buffer mapping */ alloc_data = kzalloc(sizeof(*alloc_data), GFP_KERNEL); if (!alloc_data) { kfree(iosys_vmap); return -ENOMEM; } alloc_data->dma_buf = dma_buf; alloc_data->len = dma_buf->size; *len = dma_buf->size; /* Attach the dma_buf to context bank device */ alloc_data->attach = dma_buf_attach(alloc_data->dma_buf, cb_dev); if (IS_ERR(alloc_data->attach)) { rc = PTR_ERR(alloc_data->attach); dev_err(cb_dev, "%s: Fail to attach dma_buf to CB, rc = %d\n", __func__, rc); goto free_alloc_data; } /* * Get the scatter-gather list. * There is no info as this is a write buffer or * read buffer, hence the request is bi-directional * to accommodate both read and write mappings. */ alloc_data->table = dma_buf_map_attachment(alloc_data->attach, DMA_BIDIRECTIONAL); if (IS_ERR(alloc_data->table)) { rc = PTR_ERR(alloc_data->table); dev_err(cb_dev, "%s: Fail to map attachment, rc = %d\n", __func__, rc); goto detach_dma_buf; } /* physical address from mapping */ if (!is_iova) { *addr = sg_phys(alloc_data->table->sgl); rc = msm_audio_ion_map_kernel((void *)dma_buf, ion_data, iosys_vmap); if (rc) { pr_err("%s: ION memory mapping for AUDIO failed, err:%d\n", __func__, rc); rc = -ENOMEM; goto detach_dma_buf; } alloc_data->vmap = iosys_vmap; } else { *addr = MSM_AUDIO_ION_PHYS_ADDR(alloc_data); kfree(iosys_vmap); } msm_audio_ion_add_allocation(ion_data, alloc_data); return rc; detach_dma_buf: dma_buf_detach(alloc_data->dma_buf, alloc_data->attach); free_alloc_data: kfree(iosys_vmap); kfree(alloc_data); alloc_data = NULL; return rc; } static int msm_audio_dma_buf_unmap(struct dma_buf *dma_buf, struct msm_audio_ion_private *ion_data) { int rc = 0; struct msm_audio_alloc_data *alloc_data = NULL; struct list_head *ptr, *next; bool found = false; struct device *cb_dev = ion_data->cb_dev; /* * Though list_for_each_safe is delete safe, lock * should be explicitly acquired to avoid race condition * on adding elements to the list. */ list_for_each_safe(ptr, next, &(ion_data->alloc_list)) { alloc_data = list_entry(ptr, struct msm_audio_alloc_data, list); if (alloc_data->dma_buf == dma_buf) { found = true; dma_buf_unmap_attachment(alloc_data->attach, alloc_data->table, DMA_BIDIRECTIONAL); dma_buf_detach(alloc_data->dma_buf, alloc_data->attach); dma_buf_put(alloc_data->dma_buf); list_del(&(alloc_data->list)); kfree(alloc_data->vmap); kfree(alloc_data); alloc_data = NULL; break; } } if (!found) { dev_err(cb_dev, "%s: cannot find allocation, dma_buf %pK", __func__, dma_buf); rc = -EINVAL; } return rc; } static int msm_audio_ion_get_phys(struct dma_buf *dma_buf, dma_addr_t *addr, size_t *len, bool is_iova, struct msm_audio_ion_private *ion_data) { int rc = 0; rc = msm_audio_dma_buf_map(dma_buf, addr, len, is_iova, ion_data); if (rc) { pr_err("%s: failed to map DMA buf, err = %d\n", __func__, rc); goto err; } if (ion_data->smmu_enabled && is_iova) { /* Append the SMMU SID information to the IOVA address */ *addr |= ion_data->smmu_sid_bits; } pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc); err: return rc; } static int msm_audio_ion_unmap_kernel(struct dma_buf *dma_buf, struct msm_audio_ion_private *ion_data) { int rc = 0; struct iosys_map *iosys_vmap = NULL; struct msm_audio_alloc_data *alloc_data = NULL; struct device *cb_dev = ion_data->cb_dev; /* * TBD: remove the below section once new API * for unmapping kernel virtual address is available. */ list_for_each_entry(alloc_data, &(ion_data->alloc_list), list) { if (alloc_data->dma_buf == dma_buf) { iosys_vmap = alloc_data->vmap; break; } } if (!iosys_vmap) { dev_err(cb_dev, "%s: cannot find allocation for dma_buf %pK", __func__, dma_buf); rc = -EINVAL; goto err; } dma_buf_vunmap(dma_buf, iosys_vmap); rc = dma_buf_end_cpu_access(dma_buf, DMA_BIDIRECTIONAL); if (rc) { dev_err(cb_dev, "%s: kmap dma_buf_end_cpu_access fail\n", __func__); goto err; } err: return rc; } /* This function is called with ion_data list mutex lock */ static int msm_audio_ion_buf_map(struct dma_buf *dma_buf, dma_addr_t *paddr, size_t *plen, struct iosys_map *iosys_vmap, struct msm_audio_ion_private *ion_data) { int rc = 0; bool is_iova = true; if (!dma_buf || !paddr || !plen) { pr_err("%s: Invalid params\n", __func__); return -EINVAL; } rc = msm_audio_ion_get_phys(dma_buf, paddr, plen, is_iova, ion_data); if (rc) { pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", __func__, rc); dma_buf_put(dma_buf); goto err; } rc = msm_audio_ion_map_kernel(dma_buf, ion_data, iosys_vmap); if (rc) { pr_err("%s: ION memory mapping for AUDIO failed, err:%d\n", __func__, rc); rc = -ENOMEM; mutex_lock(&(ion_data->list_mutex)); msm_audio_dma_buf_unmap(dma_buf, ion_data); mutex_unlock(&(ion_data->list_mutex)); goto err; } err: return rc; } void msm_audio_fd_list_debug(void) { struct msm_audio_fd_data *msm_audio_fd_data = NULL; list_for_each_entry(msm_audio_fd_data, &msm_audio_ion_fd_list.fd_list, list) { pr_debug("%s fd %d handle %pK phy. addr %pK\n", __func__, msm_audio_fd_data->fd, msm_audio_fd_data->handle, (void *)msm_audio_fd_data->paddr); } } void msm_audio_update_fd_list(struct msm_audio_fd_data *msm_audio_fd_data) { struct msm_audio_fd_data *msm_audio_fd_data1 = NULL; mutex_lock(&(msm_audio_ion_fd_list.list_mutex)); list_for_each_entry(msm_audio_fd_data1, &msm_audio_ion_fd_list.fd_list, list) { if (msm_audio_fd_data1->fd == msm_audio_fd_data->fd) { pr_err("%s fd already present, not updating the list", __func__); mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); return; } } list_add_tail(&msm_audio_fd_data->list, &msm_audio_ion_fd_list.fd_list); mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); } void msm_audio_delete_fd_entry(void *handle) { struct msm_audio_fd_data *msm_audio_fd_data = NULL; struct list_head *ptr, *next; if (!handle) { pr_err("%s Invalid handle\n", __func__); return; } mutex_lock(&(msm_audio_ion_fd_list.list_mutex)); list_for_each_safe(ptr, next, &msm_audio_ion_fd_list.fd_list) { msm_audio_fd_data = list_entry(ptr, struct msm_audio_fd_data, list); if (msm_audio_fd_data->handle == handle) { pr_debug("%s deleting handle %pK entry from list\n", __func__, handle); list_del(&(msm_audio_fd_data->list)); kfree(msm_audio_fd_data); break; } } mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); } int msm_audio_get_phy_addr(int fd, dma_addr_t *paddr, size_t *pa_len) { struct msm_audio_fd_data *msm_audio_fd_data = NULL; int status = -EINVAL; if (!paddr) { pr_err("%s Invalid paddr param status %d\n", __func__, status); return status; } pr_debug("%s, fd %d\n", __func__, fd); mutex_lock(&(msm_audio_ion_fd_list.list_mutex)); list_for_each_entry(msm_audio_fd_data, &msm_audio_ion_fd_list.fd_list, list) { if (msm_audio_fd_data->fd == fd) { *paddr = msm_audio_fd_data->paddr; *pa_len = msm_audio_fd_data->plen; status = 0; pr_debug("%s Found fd %d paddr %pK\n", __func__, fd, paddr); mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); return status; } } mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); return status; } EXPORT_SYMBOL(msm_audio_get_phy_addr); static int msm_audio_set_hyp_assign(int fd, bool assign) { struct msm_audio_fd_data *msm_audio_fd_data = NULL; int status = -EINVAL; pr_debug("%s, fd %d\n", __func__, fd); mutex_lock(&(msm_audio_ion_fd_list.list_mutex)); list_for_each_entry(msm_audio_fd_data, &msm_audio_ion_fd_list.fd_list, list) { if (msm_audio_fd_data->fd == fd) { status = 0; pr_debug("%s Found fd %d\n", __func__, fd); msm_audio_fd_data->hyp_assign = assign; mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); return status; } } mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); return status; } void msm_audio_get_handle(int fd, void **handle) { struct msm_audio_fd_data *msm_audio_fd_data = NULL; pr_debug("%s fd %d\n", __func__, fd); mutex_lock(&(msm_audio_ion_fd_list.list_mutex)); *handle = NULL; list_for_each_entry(msm_audio_fd_data, &msm_audio_ion_fd_list.fd_list, list) { if (msm_audio_fd_data->fd == fd) { *handle = (struct dma_buf *)msm_audio_fd_data->handle; pr_debug("%s handle %pK\n", __func__, *handle); break; } } mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); } /** * msm_audio_ion_import- * Import ION buffer with given file descriptor * * @dma_buf: dma_buf for the ION memory * @fd: file descriptor for the ION memory * @ionflag: flags associated with ION buffer * @bufsz: buffer size * @paddr: Physical address to be assigned with allocated region * @plen: length of allocated region to be assigned * @iosys_vmap: Virtual mapping vmap pointer to be assigned * * Returns 0 on success or error on failure */ static int msm_audio_ion_import(struct dma_buf **dma_buf, int fd, unsigned long *ionflag, size_t bufsz, dma_addr_t *paddr, size_t *plen, struct iosys_map *iosys_vmap, struct msm_audio_ion_private *ion_data) { int rc = 0; if (!(ion_data->device_status & MSM_AUDIO_ION_PROBED)) { pr_debug("%s: probe is not done, deferred\n", __func__); return -EPROBE_DEFER; } if (!dma_buf || !paddr || !plen) { pr_err("%s: Invalid params\n", __func__); return -EINVAL; } /* bufsz should be 0 and fd shouldn't be 0 as of now */ *dma_buf = dma_buf_get(fd); pr_debug("%s: dma_buf =%pK, fd=%d\n", __func__, *dma_buf, fd); if (IS_ERR_OR_NULL((void *)(*dma_buf))) { pr_err("%s: dma_buf_get failed\n", __func__); rc = -EINVAL; goto err; } if (ionflag != NULL) { rc = dma_buf_get_flags(*dma_buf, ionflag); if (rc) { pr_err("%s: could not get flags for the dma_buf\n", __func__); goto err_ion_flag; } } if (ion_data->smmu_enabled) { rc = msm_audio_ion_buf_map(*dma_buf, paddr, plen, iosys_vmap, ion_data); if (rc) { pr_err("%s: failed to map ION buf, rc = %d\n", __func__, rc); goto err; } pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, iosys_vmap->vaddr, bufsz); } else { msm_audio_dma_buf_map(*dma_buf, paddr, plen, true, ion_data); } return 0; err_ion_flag: dma_buf_put(*dma_buf); err: *dma_buf = NULL; return rc; } /** * msm_audio_ion_free - * fress ION memory for given client and handle * * @dma_buf: dma_buf for the ION memory * * Returns 0 on success or error on failure */ /* This funtion is called with ion_data list mutex lock */ static int msm_audio_ion_free(struct dma_buf *dma_buf, struct msm_audio_ion_private *ion_data) { int ret = 0; if (!dma_buf) { pr_err("%s: dma_buf invalid\n", __func__); return -EINVAL; } mutex_lock(&(ion_data->list_mutex)); if (ion_data->smmu_enabled) { ret = msm_audio_ion_unmap_kernel(dma_buf, ion_data); if (ret) { mutex_unlock(&(ion_data->list_mutex)); return ret; } } msm_audio_dma_buf_unmap(dma_buf, ion_data); mutex_unlock(&(ion_data->list_mutex)); return 0; } static int msm_audio_hyp_unassign(struct msm_audio_fd_data *msm_audio_fd_data) { int ret = 0; u64 src_vmid_unmap_list = BIT(VMID_LPASS) | BIT(VMID_ADSP_HEAP); struct qcom_scm_vmperm dst_vmids_unmap[] = {{QCOM_SCM_VMID_HLOS, PERM_READ | PERM_WRITE | PERM_EXEC}}; if (msm_audio_fd_data->hyp_assign) { ret = qcom_scm_assign_mem(msm_audio_fd_data->paddr, msm_audio_fd_data->plen, &src_vmid_unmap_list, dst_vmids_unmap, ARRAY_SIZE(dst_vmids_unmap)); if (ret < 0) { pr_err("%s: qcom assign unmap failed result = %d addr = 0x%pK size = %d\n", __func__, ret, msm_audio_fd_data->paddr, msm_audio_fd_data->plen); } msm_audio_fd_data->hyp_assign = false; pr_debug("%s: qcom scm unmap success\n", __func__); } return ret; } /** * msm_audio_ion_crash_handler - * handles cleanup after userspace crashes. * * To be called from machine driver. */ void msm_audio_ion_crash_handler(void) { struct msm_audio_fd_data *msm_audio_fd_data = NULL; struct list_head *ptr, *next; void *handle = NULL; struct msm_audio_ion_private *ion_data = NULL; pr_debug("Inside %s\n", __func__); if(!msm_audio_ion_fd_list_init) { pr_err("%s: list not initialized yet, hence returning .... ", __func__); return; } mutex_lock(&(msm_audio_ion_fd_list.list_mutex)); list_for_each_entry(msm_audio_fd_data, &msm_audio_ion_fd_list.fd_list, list) { if(msm_audio_fd_data) { handle = msm_audio_fd_data->handle; ion_data = dev_get_drvdata(msm_audio_fd_data->dev); /* clean if CMA was used*/ if (msm_audio_fd_data->hyp_assign) msm_audio_hyp_unassign(msm_audio_fd_data); if(handle) msm_audio_ion_free(handle, ion_data); } } list_for_each_safe(ptr, next, &msm_audio_ion_fd_list.fd_list) { if(ptr) { msm_audio_fd_data = list_entry(ptr, struct msm_audio_fd_data, list); if(msm_audio_fd_data) { list_del(&(msm_audio_fd_data->list)); kfree(msm_audio_fd_data); } } } mutex_unlock(&(msm_audio_ion_fd_list.list_mutex)); } EXPORT_SYMBOL(msm_audio_ion_crash_handler); static int msm_audio_ion_open(struct inode *inode, struct file *file) { int ret = 0; struct msm_audio_ion_private *ion_data = container_of(inode->i_cdev, struct msm_audio_ion_private, cdev); struct device *dev = ion_data->chardev; pr_debug("Inside %s\n", __func__); get_device(dev); return ret; } static int msm_audio_ion_release(struct inode *inode, struct file *file) { struct msm_audio_ion_private *ion_data = container_of(inode->i_cdev, struct msm_audio_ion_private, cdev); struct device *dev = ion_data->chardev; pr_debug("Inside %s\n", __func__); put_device(dev); return 0; } static long msm_audio_ion_ioctl(struct file *file, unsigned int ioctl_num, unsigned long __user ioctl_param) { void *mem_handle; dma_addr_t paddr; size_t pa_len = 0; struct iosys_map *iosys_vmap = NULL; int ret = 0; u64 src_vmid_map_list = BIT(QCOM_SCM_VMID_HLOS); struct qcom_scm_vmperm dst_vmids_map[] = {{VMID_LPASS, PERM_READ | PERM_WRITE}, {VMID_ADSP_HEAP, PERM_READ | PERM_WRITE}}; u64 src_vmid_unmap_list = BIT(VMID_LPASS) | BIT(VMID_ADSP_HEAP); struct qcom_scm_vmperm dst_vmids_unmap[] = {{QCOM_SCM_VMID_HLOS, PERM_READ | PERM_WRITE | PERM_EXEC}}; struct msm_audio_fd_data *msm_audio_fd_data = NULL; struct msm_audio_ion_private *ion_data = container_of(file->f_inode->i_cdev, struct msm_audio_ion_private, cdev); pr_debug("%s ioctl num %u\n", __func__, ioctl_num); switch (ioctl_num) { case IOCTL_MAP_PHYS_ADDR: iosys_vmap = kzalloc(sizeof(struct msm_audio_fd_data), GFP_KERNEL); if (!iosys_vmap) return -ENOMEM; msm_audio_fd_data = kzalloc((sizeof(struct msm_audio_fd_data)), GFP_KERNEL); if (!msm_audio_fd_data) { kfree(iosys_vmap); return -ENOMEM; } ret = msm_audio_ion_import((struct dma_buf **)&mem_handle, (int)ioctl_param, NULL, 0, &paddr, &pa_len, iosys_vmap, ion_data); if (ret < 0) { pr_err("%s Memory map Failed %d\n", __func__, ret); kfree(iosys_vmap); kfree(msm_audio_fd_data); return ret; } msm_audio_fd_data->fd = (int)ioctl_param; msm_audio_fd_data->handle = mem_handle; msm_audio_fd_data->paddr = paddr; msm_audio_fd_data->plen = pa_len; msm_audio_fd_data->dev = ion_data->cb_dev; msm_audio_update_fd_list(msm_audio_fd_data); break; case IOCTL_UNMAP_PHYS_ADDR: msm_audio_get_handle((int)ioctl_param, &mem_handle); ret = msm_audio_ion_free(mem_handle, ion_data); if (ret < 0) { pr_err("%s Ion free failed %d\n", __func__, ret); return ret; } msm_audio_delete_fd_entry(mem_handle); break; case IOCTL_MAP_HYP_ASSIGN: ret = msm_audio_get_phy_addr((int)ioctl_param, &paddr, &pa_len); if (ret < 0) { pr_err("%s get phys addr failed %d\n", __func__, ret); return ret; } ret = qcom_scm_assign_mem(paddr, pa_len, &src_vmid_map_list, dst_vmids_map, ARRAY_SIZE(dst_vmids_map)); if (ret < 0) { pr_err("%s: qcom_assign failed result = %d addr = 0x%pK size = %d\n", __func__, ret, paddr, pa_len); return ret; } pr_debug("%s: qcom scm assign success\n", __func__); msm_audio_set_hyp_assign((int)ioctl_param, true); break; case IOCTL_UNMAP_HYP_ASSIGN: ret = msm_audio_get_phy_addr((int)ioctl_param, &paddr, &pa_len); if (ret < 0) { pr_err("%s get phys addr failed %d\n", __func__, ret); return ret; } ret = qcom_scm_assign_mem(paddr, pa_len, &src_vmid_unmap_list, dst_vmids_unmap, ARRAY_SIZE(dst_vmids_unmap)); if (ret < 0) { pr_err("%s: qcom scm unassign failed result = %d addr = 0x%pK size = %d\n", __func__, ret, paddr, pa_len); return ret; } pr_debug("%s: qcom scm unassign success\n", __func__); msm_audio_set_hyp_assign((int)ioctl_param, false); break; default: pr_err("%s Entered default. Invalid ioctl num %u", __func__, ioctl_num); ret = -EINVAL; break; } return ret; } /* support of 32bit userspace on 64bit platforms */ #ifdef CONFIG_COMPAT static long msm_audio_ion_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) { return msm_audio_ion_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif static const struct of_device_id msm_audio_ion_dt_match[] = { { .compatible = "qcom,msm-audio-ion" }, { .compatible = "qcom,msm-audio-ion-cma"}, { } }; MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); static const struct file_operations msm_audio_ion_fops = { .owner = THIS_MODULE, .open = msm_audio_ion_open, .release = msm_audio_ion_release, .unlocked_ioctl = msm_audio_ion_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = msm_audio_ion_ioctl_compat, #endif }; static int msm_audio_ion_reg_chrdev(struct msm_audio_ion_private *ion_data) { int ret = 0; ret = alloc_chrdev_region(&ion_data->ion_major, 0, MINOR_NUMBER_COUNT, ion_data->driver_name); if (ret < 0) { pr_err("%s alloc_chr_dev_region failed ret : %d\n", __func__, ret); return ret; } pr_debug("%s major number %d", __func__, MAJOR(ion_data->ion_major)); ion_data->ion_class = class_create(THIS_MODULE, ion_data->driver_name); if (IS_ERR(ion_data->ion_class)) { ret = PTR_ERR(ion_data->ion_class); pr_err("%s class create failed. ret : %d", __func__, ret); goto err_class; } ion_data->chardev = device_create(ion_data->ion_class, NULL, ion_data->ion_major, NULL, ion_data->driver_name); if (IS_ERR(ion_data->chardev)) { ret = PTR_ERR(ion_data->chardev); pr_err("%s device create failed ret : %d\n", __func__, ret); goto err_device; } cdev_init(&ion_data->cdev, &msm_audio_ion_fops); ret = cdev_add(&ion_data->cdev, ion_data->ion_major, 1); if (ret) { pr_err("%s cdev add failed, ret : %d\n", __func__, ret); goto err_cdev; } return ret; err_cdev: device_destroy(ion_data->ion_class, ion_data->ion_major); err_device: class_destroy(ion_data->ion_class); err_class: unregister_chrdev_region(0, MINOR_NUMBER_COUNT); return ret; } static int msm_audio_ion_unreg_chrdev(struct msm_audio_ion_private *ion_data) { cdev_del(&ion_data->cdev); device_destroy(ion_data->ion_class, ion_data->ion_major); class_destroy(ion_data->ion_class); unregister_chrdev_region(0, MINOR_NUMBER_COUNT); return 0; } static int msm_audio_ion_probe(struct platform_device *pdev) { int rc = 0; u64 smmu_sid = 0; u64 smmu_sid_mask = 0; const char *msm_audio_ion_dt = "qcom,smmu-enabled"; const char *msm_audio_ion_non_hyp = "qcom,non-hyp-assign"; const char *msm_audio_ion_smmu = "qcom,smmu-version"; const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask"; bool smmu_enabled; bool is_non_hypervisor_en; struct device *dev = &pdev->dev; struct of_phandle_args iommuspec; struct msm_audio_ion_private *msm_audio_ion_data = NULL; #ifndef CONFIG_SPF_CORE enum apr_subsys_state q6_state; #endif dev_err(dev, "%s: msm_audio_ion_probe\n", __func__); if (dev->of_node == NULL) { dev_err(dev, "%s: device tree is not found\n", __func__); return 0; } msm_audio_ion_data = devm_kzalloc(&pdev->dev, (sizeof(struct msm_audio_ion_private)), GFP_KERNEL); if (!msm_audio_ion_data) return -ENOMEM; is_non_hypervisor_en = of_property_read_bool(dev->of_node, msm_audio_ion_non_hyp); msm_audio_ion_data->is_non_hypervisor = is_non_hypervisor_en; smmu_enabled = of_property_read_bool(dev->of_node, msm_audio_ion_dt); msm_audio_ion_data->smmu_enabled = smmu_enabled; if (!smmu_enabled) dev_dbg(dev, "%s: SMMU is Disabled\n", __func__); #ifndef CONFIG_SPF_CORE q6_state = apr_get_q6_state(); if (q6_state == APR_SUBSYS_DOWN) { dev_info(dev, "defering %s, adsp_state %d\n", __func__, q6_state); return -EPROBE_DEFER; } #endif dev_dbg(dev, "%s: adsp is ready\n", __func__); if (smmu_enabled) { msm_audio_ion_data->driver_name = "msm_audio_ion"; rc = of_property_read_u32(dev->of_node, msm_audio_ion_smmu, &(msm_audio_ion_data->smmu_version)); if (rc) { dev_err(dev, "%s: qcom,smmu_version missing in DT node\n", __func__); return rc; } dev_dbg(dev, "%s: SMMU is Enabled. SMMU version is (%d)", __func__, msm_audio_ion_data->smmu_version); /* Get SMMU SID information from Devicetree */ rc = of_property_read_u64(dev->of_node, msm_audio_ion_smmu_sid_mask, &smmu_sid_mask); if (rc) { dev_err(dev, "%s: qcom,smmu-sid-mask missing in DT node, using default\n", __func__); smmu_sid_mask = 0xFFFFFFFFFFFFFFFF; } rc = of_parse_phandle_with_args(dev->of_node, "iommus", "#iommu-cells", 0, &iommuspec); if (rc) dev_err(dev, "%s: could not get smmu SID, ret = %d\n", __func__, rc); else smmu_sid = (iommuspec.args[0] & smmu_sid_mask); msm_audio_ion_data->smmu_sid_bits = smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET; } else { msm_audio_ion_data->driver_name = "msm_audio_ion_cma"; } if (!rc) msm_audio_ion_data->device_status |= MSM_AUDIO_ION_PROBED; msm_audio_ion_data->cb_dev = dev; dev_set_drvdata(dev, msm_audio_ion_data); if (!msm_audio_ion_fd_list_init) { INIT_LIST_HEAD(&msm_audio_ion_fd_list.fd_list); mutex_init(&(msm_audio_ion_fd_list.list_mutex)); msm_audio_ion_fd_list_init = true; } INIT_LIST_HEAD(&msm_audio_ion_data->alloc_list); mutex_init(&(msm_audio_ion_data->list_mutex)); rc = msm_audio_ion_reg_chrdev(msm_audio_ion_data); if (rc) { pr_err("%s register char dev failed, rc : %d", __func__, rc); return rc; } return rc; } static int msm_audio_ion_remove(struct platform_device *pdev) { struct msm_audio_ion_private *ion_data = dev_get_drvdata(&pdev->dev); ion_data->smmu_enabled = 0; ion_data->device_status = 0; msm_audio_ion_unreg_chrdev(ion_data); return 0; } static struct platform_driver msm_audio_ion_driver = { .driver = { .name = "msm-audio-ion", .owner = THIS_MODULE, .of_match_table = msm_audio_ion_dt_match, .suppress_bind_attrs = true, }, .probe = msm_audio_ion_probe, .remove = msm_audio_ion_remove, }; int __init msm_audio_ion_init(void) { pr_debug("%s: msm_audio_ion_init called \n",__func__); return platform_driver_register(&msm_audio_ion_driver); } void msm_audio_ion_exit(void) { platform_driver_unregister(&msm_audio_ion_driver); } module_init(msm_audio_ion_init); module_exit(msm_audio_ion_exit); MODULE_DESCRIPTION("MSM Audio ION module"); MODULE_LICENSE("GPL v2");