audio-lnx: Initial change for techpack of audio drivers.

Add snapshot for audio drivers for SDM targets. The code is
migrated from msm-4.9 kernel at the below cutoff -

(74ff856e8d6: "net: ipc_router: Add dynamic enable/disable
wakeup source feature")

This changes are done for new techpack addition
for audio kernel. Migrate all audio kernel drivers
to this techpack.

Change-Id: I33d580af3ba86a5cb777583efc5d4cdaf2882d93
Signed-off-by: Asish Bhattacharya <asishb@codeaurora.org>
diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
new file mode 100644
index 0000000..2f14c74
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/of_device.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/export.h>
+#include <asm/dma-iommu.h>
+
+#define MSM_AUDIO_ION_PROBED (1 << 0)
+
+#define MSM_AUDIO_ION_PHYS_ADDR(alloc_data) \
+	alloc_data->table->sgl->dma_address
+
+#define MSM_AUDIO_ION_VA_START 0x10000000
+#define MSM_AUDIO_ION_VA_LEN 0x0FFFFFFF
+
+#define MSM_AUDIO_SMMU_SID_OFFSET 32
+
+struct msm_audio_ion_private {
+	bool smmu_enabled;
+	bool audioheap_enabled;
+	struct device *cb_dev;
+	struct dma_iommu_mapping *mapping;
+	u8 device_status;
+	struct list_head alloc_list;
+	struct mutex list_mutex;
+	u64 smmu_sid_bits;
+	u32 smmu_version;
+};
+
+struct msm_audio_alloc_data {
+	struct ion_client *client;
+	struct ion_handle *handle;
+	size_t len;
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attach;
+	struct sg_table *table;
+	struct list_head list;
+};
+
+static struct msm_audio_ion_private msm_audio_ion_data = {0,};
+
+static int msm_audio_ion_get_phys(struct ion_client *client,
+				  struct ion_handle *handle,
+				  ion_phys_addr_t *addr, size_t *len);
+
+static int msm_audio_dma_buf_map(struct ion_client *client,
+				  struct ion_handle *handle,
+				  ion_phys_addr_t *addr, size_t *len);
+
+static int msm_audio_dma_buf_unmap(struct ion_client *client,
+				   struct ion_handle *handle);
+
+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));
+}
+
+int msm_audio_ion_alloc(const char *name, struct ion_client **client,
+			struct ion_handle **handle, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+	int rc = -EINVAL;
+	unsigned long err_ion_ptr = 0;
+
+	if ((msm_audio_ion_data.smmu_enabled == true) &&
+	    !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+		pr_debug("%s:probe is not done, deferred\n", __func__);
+		return -EPROBE_DEFER;
+	}
+	if (!name || !client || !handle || !paddr || !vaddr
+		|| !bufsz || !pa_len) {
+		pr_err("%s: Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	*client = msm_audio_ion_client_create(name);
+	if (IS_ERR_OR_NULL((void *)(*client))) {
+		pr_err("%s: ION create client for AUDIO failed\n", __func__);
+		goto err;
+	}
+
+	*handle = ion_alloc(*client, bufsz, SZ_4K,
+			ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL((void *) (*handle))) {
+		if (msm_audio_ion_data.smmu_enabled == true) {
+			pr_debug("system heap is used");
+			msm_audio_ion_data.audioheap_enabled = 0;
+			*handle = ion_alloc(*client, bufsz, SZ_4K,
+					ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+		}
+		if (IS_ERR_OR_NULL((void *) (*handle))) {
+			if (IS_ERR((void *)(*handle)))
+				err_ion_ptr = PTR_ERR((int *)(*handle));
+			pr_err("%s:ION alloc fail err ptr=%ld, smmu_enabled=%d\n",
+			__func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled);
+			rc = -ENOMEM;
+			goto err_ion_client;
+		}
+	} else {
+		pr_debug("audio heap is used");
+		msm_audio_ion_data.audioheap_enabled = 1;
+	}
+
+	rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
+	if (rc) {
+		pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+			__func__, rc);
+		goto err_ion_handle;
+	}
+
+	*vaddr = ion_map_kernel(*client, *handle);
+	if (IS_ERR_OR_NULL((void *)*vaddr)) {
+		pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+		goto err_ion_handle;
+	}
+	pr_debug("%s: mapped address = %pK, size=%zd\n", __func__,
+		*vaddr, bufsz);
+
+	if (bufsz != 0) {
+		pr_debug("%s: memset to 0 %pK %zd\n", __func__, *vaddr, bufsz);
+		memset((void *)*vaddr, 0, bufsz);
+	}
+
+	return rc;
+
+err_ion_handle:
+	ion_free(*client, *handle);
+err_ion_client:
+	msm_audio_ion_client_destroy(*client);
+	*handle = NULL;
+	*client = NULL;
+err:
+	return rc;
+}
+EXPORT_SYMBOL(msm_audio_ion_alloc);
+
+int msm_audio_ion_import(const char *name, struct ion_client **client,
+			struct ion_handle **handle, int fd,
+			unsigned long *ionflag, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+	int rc = 0;
+
+	if ((msm_audio_ion_data.smmu_enabled == true) &&
+	    !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+		pr_debug("%s:probe is not done, deferred\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
+		pr_err("%s: Invalid params\n", __func__);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	*client = msm_audio_ion_client_create(name);
+	if (IS_ERR_OR_NULL((void *)(*client))) {
+		pr_err("%s: ION create client for AUDIO failed\n", __func__);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* name should be audio_acdb_client or Audio_Dec_Client,
+	 * bufsz should be 0 and fd shouldn't be 0 as of now
+	 */
+	*handle = ion_import_dma_buf_fd(*client, fd);
+	pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__,
+							name, fd, *handle);
+	if (IS_ERR_OR_NULL((void *) (*handle))) {
+		pr_err("%s: ion import dma buffer failed\n",
+				__func__);
+		rc = -EINVAL;
+		goto err_destroy_client;
+	}
+
+	if (ionflag != NULL) {
+		rc = ion_handle_get_flags(*client, *handle, ionflag);
+		if (rc) {
+			pr_err("%s: could not get flags for the handle\n",
+				__func__);
+			goto err_ion_handle;
+		}
+	}
+
+	rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
+	if (rc) {
+		pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+				__func__, rc);
+		goto err_ion_handle;
+	}
+
+	*vaddr = ion_map_kernel(*client, *handle);
+	if (IS_ERR_OR_NULL((void *)*vaddr)) {
+		pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+		rc = -ENOMEM;
+		goto err_ion_handle;
+	}
+	pr_debug("%s: mapped address = %pK, size=%zd\n", __func__,
+		*vaddr, bufsz);
+
+	return 0;
+
+err_ion_handle:
+	ion_free(*client, *handle);
+err_destroy_client:
+	msm_audio_ion_client_destroy(*client);
+	*client = NULL;
+	*handle = NULL;
+err:
+	return rc;
+}
+
+int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle)
+{
+	if (!client || !handle) {
+		pr_err("%s Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	if (msm_audio_ion_data.smmu_enabled)
+		msm_audio_dma_buf_unmap(client, handle);
+
+	ion_unmap_kernel(client, handle);
+
+	ion_free(client, handle);
+	msm_audio_ion_client_destroy(client);
+	return 0;
+}
+EXPORT_SYMBOL(msm_audio_ion_free);
+
+int msm_audio_ion_mmap(struct audio_buffer *ab,
+		       struct vm_area_struct *vma)
+{
+	struct sg_table *table;
+	unsigned long addr = vma->vm_start;
+	unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+	struct scatterlist *sg;
+	unsigned int i;
+	struct page *page;
+	int ret;
+
+	pr_debug("%s\n", __func__);
+
+	table = ion_sg_table(ab->client, ab->handle);
+
+	if (IS_ERR(table)) {
+		pr_err("%s: Unable to get sg_table from ion: %ld\n",
+			__func__, PTR_ERR(table));
+		return PTR_ERR(table);
+	} else if (!table) {
+		pr_err("%s: sg_list is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/* uncached */
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	/* We need to check if a page is associated with this sg list because:
+	 * If the allocation came from a carveout we currently don't have
+	 * pages associated with carved out memory. This might change in the
+	 * future and we can remove this check and the else statement.
+	 */
+	page = sg_page(table->sgl);
+	if (page) {
+		pr_debug("%s: page is NOT null\n", __func__);
+		for_each_sg(table->sgl, sg, table->nents, i) {
+			unsigned long remainder = vma->vm_end - addr;
+			unsigned long len = sg->length;
+
+			page = sg_page(sg);
+
+			if (offset >= len) {
+				offset -= len;
+				continue;
+			} else if (offset) {
+				page += offset / PAGE_SIZE;
+				len -= offset;
+				offset = 0;
+			}
+			len = min(len, remainder);
+			pr_debug("vma=%pK, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%lu\n",
+				vma, (unsigned int)addr, len,
+				(unsigned int)vma->vm_start,
+				(unsigned int)vma->vm_end,
+				(unsigned long)vma->vm_page_prot.pgprot);
+			remap_pfn_range(vma, addr, page_to_pfn(page), len,
+					vma->vm_page_prot);
+			addr += len;
+			if (addr >= vma->vm_end)
+				return 0;
+		}
+	} else {
+		ion_phys_addr_t phys_addr;
+		size_t phys_len;
+		size_t va_len = 0;
+
+		pr_debug("%s: page is NULL\n", __func__);
+		ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len);
+		if (ret) {
+			pr_err("%s: Unable to get phys address from ION buffer: %d\n"
+				, __func__, ret);
+			return ret;
+		}
+		pr_debug("phys=%pKK len=%zd\n", &phys_addr, phys_len);
+		pr_debug("vma=%pK, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%lu\n",
+			vma, (unsigned int)vma->vm_start,
+			(unsigned int)vma->vm_end, vma->vm_pgoff,
+			(unsigned long)vma->vm_page_prot.pgprot);
+		va_len = vma->vm_end - vma->vm_start;
+		if ((offset > phys_len) || (va_len > phys_len-offset)) {
+			pr_err("wrong offset size %ld, lens= %zd, va_len=%zd\n",
+				offset, phys_len, va_len);
+			return -EINVAL;
+		}
+		ret =  remap_pfn_range(vma, vma->vm_start,
+				__phys_to_pfn(phys_addr) + vma->vm_pgoff,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot);
+	}
+	return 0;
+}
+
+
+bool msm_audio_ion_is_smmu_available(void)
+{
+	return msm_audio_ion_data.smmu_enabled;
+}
+
+/* move to static section again */
+struct ion_client *msm_audio_ion_client_create(const char *name)
+{
+	struct ion_client *pclient = NULL;
+
+	pclient = msm_ion_client_create(name);
+	return pclient;
+}
+
+
+void msm_audio_ion_client_destroy(struct ion_client *client)
+{
+	pr_debug("%s: client = %pK smmu_enabled = %d\n", __func__,
+		client, msm_audio_ion_data.smmu_enabled);
+
+	ion_client_destroy(client);
+}
+
+int msm_audio_ion_import_legacy(const char *name, struct ion_client *client,
+			struct ion_handle **handle, int fd,
+			unsigned long *ionflag, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+	int rc = 0;
+
+	if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
+		pr_err("%s: Invalid params\n", __func__);
+		rc = -EINVAL;
+		goto err;
+	}
+	/* client is already created for legacy and given
+	 * name should be audio_acdb_client or Audio_Dec_Client,
+	 * bufsz should be 0 and fd shouldn't be 0 as of now
+	 */
+	*handle = ion_import_dma_buf_fd(client, fd);
+	pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__,
+							name, fd, *handle);
+	if (IS_ERR_OR_NULL((void *)(*handle))) {
+		pr_err("%s: ion import dma buffer failed\n",
+			__func__);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	if (ionflag != NULL) {
+		rc = ion_handle_get_flags(client, *handle, ionflag);
+		if (rc) {
+			pr_err("%s: could not get flags for the handle\n",
+							__func__);
+			rc = -EINVAL;
+			goto err_ion_handle;
+		}
+	}
+
+	rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len);
+	if (rc) {
+		pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+			__func__, rc);
+		rc = -EINVAL;
+		goto err_ion_handle;
+	}
+
+	/*Need to add condition SMMU enable or not */
+	*vaddr = ion_map_kernel(client, *handle);
+	if (IS_ERR_OR_NULL((void *)*vaddr)) {
+		pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+		rc = -EINVAL;
+		goto err_ion_handle;
+	}
+
+	if (bufsz != 0)
+		memset((void *)*vaddr, 0, bufsz);
+
+	return 0;
+
+err_ion_handle:
+	ion_free(client, *handle);
+err:
+	return rc;
+}
+
+int msm_audio_ion_free_legacy(struct ion_client *client,
+			      struct ion_handle *handle)
+{
+	if (msm_audio_ion_data.smmu_enabled)
+		msm_audio_dma_buf_unmap(client, handle);
+
+	ion_unmap_kernel(client, handle);
+
+	ion_free(client, handle);
+	/* no client_destrody in legacy*/
+	return 0;
+}
+
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op)
+{
+	unsigned long ionflag = 0;
+	int rc = 0;
+	int msm_cache_ops = 0;
+
+	if (!abuff) {
+		pr_err("%s: Invalid params: %pK\n", __func__, abuff);
+		return -EINVAL;
+	}
+	rc = ion_handle_get_flags(abuff->client, abuff->handle,
+		&ionflag);
+	if (rc) {
+		pr_err("ion_handle_get_flags failed: %d\n", rc);
+		goto cache_op_failed;
+	}
+
+	/* has to be CACHED */
+	if (ION_IS_CACHED(ionflag)) {
+		/* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */
+		msm_cache_ops = cache_op;
+		rc = msm_ion_do_cache_op(abuff->client,
+				abuff->handle,
+				(unsigned long *) abuff->data,
+				(unsigned long)abuff->size,
+				msm_cache_ops);
+		if (rc) {
+			pr_err("cache operation failed %d\n", rc);
+			goto cache_op_failed;
+		}
+	}
+cache_op_failed:
+	return rc;
+}
+
+
+static int msm_audio_dma_buf_map(struct ion_client *client,
+		struct ion_handle *handle,
+		ion_phys_addr_t *addr, size_t *len)
+{
+
+	struct msm_audio_alloc_data *alloc_data;
+	struct device *cb_dev;
+	int rc = 0;
+
+	cb_dev = msm_audio_ion_data.cb_dev;
+
+	/* Data required per buffer mapping */
+	alloc_data = kzalloc(sizeof(*alloc_data), GFP_KERNEL);
+	if (!alloc_data)
+		return -ENOMEM;
+
+	/* Get the ION handle size */
+	ion_handle_get_size(client, handle, len);
+
+	alloc_data->client = client;
+	alloc_data->handle = handle;
+	alloc_data->len = *len;
+
+	/* Get the dma_buf handle from ion_handle */
+	alloc_data->dma_buf = ion_share_dma_buf(client, handle);
+	if (IS_ERR(alloc_data->dma_buf)) {
+		rc = PTR_ERR(alloc_data->dma_buf);
+		dev_err(cb_dev,
+			"%s: Fail to get dma_buf handle, rc = %d\n",
+			__func__, rc);
+		goto err_dma_buf;
+	}
+
+	/* 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 err_attach;
+	}
+
+	/*
+	 * 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 err_map_attach;
+	}
+
+	rc = dma_map_sg(cb_dev, alloc_data->table->sgl,
+			alloc_data->table->nents,
+			DMA_BIDIRECTIONAL);
+	if (rc != alloc_data->table->nents) {
+		dev_err(cb_dev,
+			"%s: Fail to map SG, rc = %d, nents = %d\n",
+			__func__, rc, alloc_data->table->nents);
+		goto err_map_sg;
+	}
+	/* Make sure not to return rc from dma_map_sg, as it can be nonzero */
+	rc = 0;
+
+	/* physical address from mapping */
+	*addr = MSM_AUDIO_ION_PHYS_ADDR(alloc_data);
+
+	msm_audio_ion_add_allocation(&msm_audio_ion_data,
+				     alloc_data);
+	return rc;
+
+err_map_sg:
+	dma_buf_unmap_attachment(alloc_data->attach,
+				 alloc_data->table,
+				 DMA_BIDIRECTIONAL);
+err_map_attach:
+	dma_buf_detach(alloc_data->dma_buf,
+		       alloc_data->attach);
+err_attach:
+	dma_buf_put(alloc_data->dma_buf);
+
+err_dma_buf:
+	kfree(alloc_data);
+
+	return rc;
+}
+
+static int msm_audio_dma_buf_unmap(struct ion_client *client,
+				   struct ion_handle *handle)
+{
+	int rc = 0;
+	struct msm_audio_alloc_data *alloc_data = NULL;
+	struct list_head *ptr, *next;
+	struct device *cb_dev = msm_audio_ion_data.cb_dev;
+	bool found = false;
+
+	/*
+	 * Though list_for_each_safe is delete safe, lock
+	 * should be explicitly acquired to avoid race condition
+	 * on adding elements to the list.
+	 */
+	mutex_lock(&(msm_audio_ion_data.list_mutex));
+	list_for_each_safe(ptr, next,
+			    &(msm_audio_ion_data.alloc_list)) {
+
+		alloc_data = list_entry(ptr, struct msm_audio_alloc_data,
+					list);
+
+		if (alloc_data->handle == handle &&
+		    alloc_data->client == client) {
+			found = true;
+			dma_unmap_sg(cb_dev,
+				    alloc_data->table->sgl,
+				    alloc_data->table->nents,
+				    DMA_BIDIRECTIONAL);
+
+			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);
+			break;
+		}
+	}
+	mutex_unlock(&(msm_audio_ion_data.list_mutex));
+
+	if (!found) {
+		dev_err(cb_dev,
+			"%s: cannot find allocation, ion_handle %pK, ion_client %pK",
+			__func__, handle, client);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int msm_audio_ion_get_phys(struct ion_client *client,
+		struct ion_handle *handle,
+		ion_phys_addr_t *addr, size_t *len)
+{
+	int rc = 0;
+
+	pr_debug("%s: smmu_enabled = %d\n", __func__,
+		msm_audio_ion_data.smmu_enabled);
+
+	if (msm_audio_ion_data.smmu_enabled) {
+		rc = msm_audio_dma_buf_map(client, handle, addr, len);
+		if (rc) {
+			pr_err("%s: failed to map DMA buf, err = %d\n",
+				__func__, rc);
+			goto err;
+		}
+		/* Append the SMMU SID information to the IOVA address */
+		*addr |= msm_audio_ion_data.smmu_sid_bits;
+	} else {
+		rc = ion_phys(client, handle, addr, len);
+	}
+
+	pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc);
+err:
+	return rc;
+}
+
+static int msm_audio_smmu_init(struct device *dev)
+{
+	struct dma_iommu_mapping *mapping;
+	int ret;
+
+	mapping = arm_iommu_create_mapping(&platform_bus_type,
+					   MSM_AUDIO_ION_VA_START,
+					   MSM_AUDIO_ION_VA_LEN);
+	if (IS_ERR(mapping))
+		return PTR_ERR(mapping);
+
+	ret = arm_iommu_attach_device(dev, mapping);
+	if (ret) {
+		dev_err(dev, "%s: Attach failed, err = %d\n",
+			__func__, ret);
+		goto fail_attach;
+	}
+
+	msm_audio_ion_data.cb_dev = dev;
+	msm_audio_ion_data.mapping = mapping;
+	INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list);
+	mutex_init(&(msm_audio_ion_data.list_mutex));
+
+	return 0;
+
+fail_attach:
+	arm_iommu_release_mapping(mapping);
+	return ret;
+}
+
+static const struct of_device_id msm_audio_ion_dt_match[] = {
+	{ .compatible = "qcom,msm-audio-ion" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match);
+
+
+u32 msm_audio_ion_get_smmu_sid_mode32(void)
+{
+	if (msm_audio_ion_data.smmu_enabled)
+		return upper_32_bits(msm_audio_ion_data.smmu_sid_bits);
+	else
+		return 0;
+}
+
+u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa)
+{
+	if (sizeof(ion_phys_addr_t) == sizeof(u32))
+		return msm_audio_ion_get_smmu_sid_mode32();
+	else
+		return upper_32_bits(pa);
+}
+
+static int msm_audio_ion_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	const char *msm_audio_ion_dt = "qcom,smmu-enabled";
+	const char *msm_audio_ion_smmu = "qcom,smmu-version";
+	const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask";
+	bool smmu_enabled;
+	enum apr_subsys_state q6_state;
+	struct device *dev = &pdev->dev;
+
+	if (dev->of_node == NULL) {
+		dev_err(dev,
+			"%s: device tree is not found\n",
+			__func__);
+		msm_audio_ion_data.smmu_enabled = 0;
+		return 0;
+	}
+
+	smmu_enabled = of_property_read_bool(dev->of_node,
+					     msm_audio_ion_dt);
+	msm_audio_ion_data.smmu_enabled = smmu_enabled;
+
+	if (smmu_enabled) {
+		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 version is (%d)", __func__,
+				msm_audio_ion_data.smmu_version);
+		q6_state = apr_get_q6_state();
+		if (q6_state == APR_SUBSYS_DOWN) {
+			dev_dbg(dev,
+				"defering %s, adsp_state %d\n",
+				__func__, q6_state);
+			return -EPROBE_DEFER;
+		}
+		dev_dbg(dev, "%s: adsp is ready\n", __func__);
+	}
+
+	dev_dbg(dev, "%s: SMMU is %s\n", __func__,
+		(smmu_enabled) ? "Enabled" : "Disabled");
+
+	if (smmu_enabled) {
+		u64 smmu_sid = 0;
+		u64 smmu_sid_mask = 0;
+		struct of_phandle_args iommuspec;
+
+		/* 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;
+
+		if (msm_audio_ion_data.smmu_version == 0x2) {
+			rc = msm_audio_smmu_init(dev);
+		} else {
+			dev_err(dev, "%s: smmu version invalid %d\n",
+				__func__, msm_audio_ion_data.smmu_version);
+			rc = -EINVAL;
+		}
+		if (rc)
+			dev_err(dev, "%s: smmu init failed, err = %d\n",
+				__func__, rc);
+	}
+
+	if (!rc)
+		msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED;
+
+	return rc;
+}
+
+static int msm_audio_ion_remove(struct platform_device *pdev)
+{
+	struct dma_iommu_mapping *mapping;
+	struct device *audio_cb_dev;
+
+	mapping = msm_audio_ion_data.mapping;
+	audio_cb_dev = msm_audio_ion_data.cb_dev;
+
+	if (audio_cb_dev && mapping) {
+		arm_iommu_detach_device(audio_cb_dev);
+		arm_iommu_release_mapping(mapping);
+	}
+
+	msm_audio_ion_data.smmu_enabled = 0;
+	msm_audio_ion_data.device_status = 0;
+	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,
+	},
+	.probe = msm_audio_ion_probe,
+	.remove = msm_audio_ion_remove,
+};
+
+static int __init msm_audio_ion_init(void)
+{
+	return platform_driver_register(&msm_audio_ion_driver);
+}
+module_init(msm_audio_ion_init);
+
+static void __exit msm_audio_ion_exit(void)
+{
+	platform_driver_unregister(&msm_audio_ion_driver);
+}
+module_exit(msm_audio_ion_exit);
+
+MODULE_DESCRIPTION("MSM Audio ION module");
+MODULE_LICENSE("GPL v2");