msm: audio: qdsp6v2: Replace PMEM with ION in LPA driver

ION is replacement for PMEM which is less complex with better
reference counting. Hence ION which is a superset of PMEM is
used to allocate memory in LPA driver.

Change-Id: I3b33597c12b654c26e6550cd50774f45ea203049
Signed-off-by: Chaithanya Krishna Bacharaju <chaithan@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
index 0887577..41a7387 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
@@ -28,8 +28,8 @@
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/earlysuspend.h>
+#include <linux/ion.h>
 #include <linux/list.h>
-#include <linux/android_pmem.h>
 #include <linux/slab.h>
 #include <asm/atomic.h>
 #include <asm/ioctls.h>
@@ -88,10 +88,10 @@
 	int event_type;
 	union msm_audio_event_payload payload;
 };
-
-struct audlpa_pmem_region {
+struct audlpa_ion_region {
 	struct list_head list;
-	struct file *file;
+	struct ion_handle *handle;
+	struct ion_client *client;
 	int fd;
 	void *vaddr;
 	unsigned long paddr;
@@ -115,12 +115,13 @@
 
 static void audlpa_post_event(struct audio *audio, int type,
 	union msm_audio_event_payload payload);
-static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
 				unsigned long len, int ref_up);
+static void audlpa_unmap_ion_region(struct audio *audio);
 static void audlpa_async_send_data(struct audio *audio, unsigned needed,
 				uint32_t token);
 static int audlpa_pause(struct audio *audio);
-static void audlpa_unmap_pmem_region(struct audio *audio);
 static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 static int audlpa_set_pcm_params(void *data);
 
@@ -422,7 +423,7 @@
 	    drv_evt->event_type == AUDIO_EVENT_READ_DONE)) {
 		pr_debug("%s: AUDIO_EVENT_WRITE_DONE completing\n", __func__);
 		mutex_lock(&audio->lock);
-		audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+		audlpa_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
 				  drv_evt->payload.aio_buf.buf_len, 0);
 		mutex_unlock(&audio->lock);
 	}
@@ -432,38 +433,41 @@
 	return rc;
 }
 
-static int audlpa_pmem_check(struct audio *audio,
-		void *vaddr, unsigned long len)
+static int audlpa_ion_check(struct audio *audio,
+			void *vaddr, unsigned long len)
 {
-	struct audlpa_pmem_region *region_elt;
-	struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len };
+	struct audlpa_ion_region *region_elt;
+	struct audlpa_ion_region t = {.vaddr = vaddr, .len = len };
 
-	list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
 		if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
 		    OVERLAPS(region_elt, &t)) {
-			pr_err("%s: region (vaddr %p len %ld)"
-				" clashes with registered region"
-				" (vaddr %p paddr %p len %ld)\n",
-				__func__, vaddr, len,
-				region_elt->vaddr,
-				(void *)region_elt->paddr,
-				region_elt->len);
+			pr_err("%s[%p]:region (vaddr %p len %ld)"
+			" clashes with registered region"
+			" (vaddr %p paddr %p len %ld)\n",
+			__func__, audio, vaddr, len,
+			region_elt->vaddr,
+			(void *)region_elt->paddr, region_elt->len);
 			return -EINVAL;
 		}
 	}
 
 	return 0;
 }
-
-static int audlpa_pmem_add(struct audio *audio,
-	struct msm_audio_pmem_info *info)
+static int audlpa_ion_add(struct audio *audio,
+			struct msm_audio_ion_info *info)
 {
-	unsigned long paddr, kvaddr, len;
-	struct file *file;
-	struct audlpa_pmem_region *region;
+	ion_phys_addr_t paddr;
+	size_t len;
+	unsigned long kvaddr;
+	struct audlpa_ion_region *region;
 	int rc = -EINVAL;
+	struct ion_handle *handle;
+	struct ion_client *client;
+	unsigned long ionflag;
+	void *temp_ptr;
 
-	pr_debug("%s:\n", __func__);
+	pr_debug("%s[%p]:\n", __func__, audio);
 	region = kmalloc(sizeof(*region), GFP_KERNEL);
 
 	if (!region) {
@@ -471,61 +475,105 @@
 		goto end;
 	}
 
-	if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
-		kfree(region);
-		goto end;
+	client = msm_ion_client_create(UINT_MAX, "Audio_LPA_Client");
+	if (IS_ERR_OR_NULL(client)) {
+		pr_err("Unable to create ION client\n");
+		goto client_error;
 	}
 
-	rc = audlpa_pmem_check(audio, info->vaddr, len);
+	handle = ion_import_fd(client, info->fd);
+	if (IS_ERR_OR_NULL(handle)) {
+		pr_err("%s: could not get handle of the given fd\n", __func__);
+		goto import_error;
+	}
+
+	rc = ion_handle_get_flags(client, handle, &ionflag);
+	if (rc) {
+		pr_err("%s: could not get flags for the handle\n", __func__);
+		goto flag_error;
+	}
+
+	temp_ptr = ion_map_kernel(client, handle, ionflag);
+	if (IS_ERR_OR_NULL(temp_ptr)) {
+		pr_err("%s: could not get virtual address\n", __func__);
+		goto map_error;
+	}
+	kvaddr = (unsigned long) temp_ptr;
+
+	rc = ion_phys(client, handle, &paddr, &len);
+	if (rc) {
+		pr_err("%s: could not get physical address\n", __func__);
+		goto ion_error;
+	}
+
+	rc = audlpa_ion_check(audio, info->vaddr, len);
 	if (rc < 0) {
-		put_pmem_file(file);
-		kfree(region);
-		goto end;
+		pr_err("%s: audlpa_ion_check failed\n", __func__);
+		goto ion_error;
 	}
 
+	region->client = client;
+	region->handle = handle;
 	region->vaddr = info->vaddr;
 	region->fd = info->fd;
 	region->paddr = paddr;
 	region->kvaddr = kvaddr;
 	region->len = len;
-	region->file = file;
 	region->ref_cnt = 0;
-	pr_debug("%s: add region paddr %lx vaddr %p, len %lu\n", __func__,
-			 region->paddr, region->vaddr,
-			 region->len);
-	list_add_tail(&region->list, &audio->pmem_region_queue);
+	pr_debug("%s[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n",
+		__func__, audio,
+		region->paddr, region->vaddr, region->len, region->kvaddr);
+	list_add_tail(&region->list, &audio->ion_region_queue);
+
 	rc = q6asm_memory_map(audio->ac, (uint32_t)paddr, IN, (uint32_t)len, 1);
-	if (rc < 0)
-		pr_err("%s: memory map failed\n", __func__);
+	if (rc < 0) {
+		pr_err("%s[%p]: memory map failed\n", __func__, audio);
+		goto ion_error;
+	} else {
+		goto end;
+	}
+
+ion_error:
+	ion_unmap_kernel(client, handle);
+map_error:
+	ion_free(client, handle);
+flag_error:
+import_error:
+	ion_client_destroy(client);
+client_error:
+	kfree(region);
 end:
 	return rc;
 }
 
-static int audlpa_pmem_remove(struct audio *audio,
-	struct msm_audio_pmem_info *info)
+static int audlpa_ion_remove(struct audio *audio,
+			struct msm_audio_ion_info *info)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	struct list_head *ptr, *next;
 	int rc = -EINVAL;
 
-	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
-		region = list_entry(ptr, struct audlpa_pmem_region, list);
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audlpa_ion_region, list);
 
-		if ((region != NULL) && (region->fd == info->fd) &&
-		    (region->vaddr == info->vaddr)) {
+		if (region != NULL && (region->fd == info->fd) &&
+			(region->vaddr == info->vaddr)) {
 			if (region->ref_cnt) {
-				pr_debug("%s: region %p in use ref_cnt %d\n",
-					__func__, region, region->ref_cnt);
+				pr_debug("%s[%p]:region %p in use ref_cnt %d\n",
+					__func__, audio, region,
+					region->ref_cnt);
 				break;
 			}
 			rc = q6asm_memory_unmap(audio->ac,
-						(uint32_t)region->paddr,
-						IN);
+				(uint32_t) region->paddr, IN);
 			if (rc < 0)
-				pr_err("%s: memory unmap failed\n", __func__);
+				pr_err("%s[%p]: memory unmap failed\n",
+					__func__, audio);
 
 			list_del(&region->list);
-			put_pmem_file(region->file);
+			ion_unmap_kernel(region->client, region->handle);
+			ion_free(region->client, region->handle);
+			ion_client_destroy(region->client);
 			kfree(region);
 			rc = 0;
 			break;
@@ -535,24 +583,21 @@
 	return rc;
 }
 
-static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr,
-		     unsigned long len, struct audlpa_pmem_region **region)
+static int audlpa_ion_lookup_vaddr(struct audio *audio, void *addr,
+			unsigned long len, struct audlpa_ion_region **region)
 {
-	struct audlpa_pmem_region *region_elt;
-
+	struct audlpa_ion_region *region_elt;
 	int match_count = 0;
-
 	*region = NULL;
 
 	/* returns physical address or zero */
-	list_for_each_entry(region_elt, &audio->pmem_region_queue,
-		list) {
+	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
 		if (addr >= region_elt->vaddr &&
-		    addr < region_elt->vaddr + region_elt->len &&
-		    addr + len <= region_elt->vaddr + region_elt->len) {
+			addr < region_elt->vaddr + region_elt->len &&
+			addr + len <= region_elt->vaddr + region_elt->len) {
 			/* offset since we could pass vaddr inside a registerd
-			 * pmem buffer
-			 */
+			* ion buffer
+			*/
 
 			match_count++;
 			if (!*region)
@@ -561,32 +606,33 @@
 	}
 
 	if (match_count > 1) {
-		pr_err("%s: multiple hits for vaddr %p, len %ld\n", __func__,
-			   addr, len);
-		list_for_each_entry(region_elt,
-		  &audio->pmem_region_queue, list) {
-			if (addr >= region_elt->vaddr &&
-			    addr < region_elt->vaddr + region_elt->len &&
-			    addr + len <= region_elt->vaddr + region_elt->len)
-				pr_err("%s: \t%p, %ld --> %p\n", __func__,
-					   region_elt->vaddr, region_elt->len,
-					   (void *)region_elt->paddr);
+		pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+			 __func__, audio, addr, len);
+		list_for_each_entry(region_elt, &audio->ion_region_queue,
+					list) {
+		if (addr >= region_elt->vaddr &&
+			addr < region_elt->vaddr + region_elt->len &&
+			addr + len <= region_elt->vaddr + region_elt->len)
+			pr_err("\t%s[%p]:%p, %ld --> %p\n",
+				__func__, audio,
+					region_elt->vaddr,
+					region_elt->len,
+					(void *)region_elt->paddr);
 		}
 	}
-
 	return *region ? 0 : -1;
 }
-
-unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
-		    unsigned long len, int ref_up)
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
+			unsigned long len, int ref_up)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	unsigned long paddr;
 	int ret;
 
-	ret = audlpa_pmem_lookup_vaddr(audio, addr, len, &region);
+	ret = audlpa_ion_lookup_vaddr(audio, addr, len, &region);
 	if (ret) {
-		pr_err("%s: lookup (%p, %ld) failed\n", __func__, addr, len);
+		pr_err("%s[%p]:lookup (%p, %ld) failed\n",
+			__func__, audio, addr, len);
 		return 0;
 	}
 	if (ref_up)
@@ -613,7 +659,7 @@
 		return -EFAULT;
 	}
 
-	buf_node->paddr = audlpa_pmem_fixup(
+	buf_node->paddr = audlpa_ion_fixup(
 		audio, buf_node->buf.buf_addr,
 		buf_node->buf.buf_len, 1);
 	if (dir) {
@@ -921,25 +967,26 @@
 		}
 		break;
 
-	case AUDIO_REGISTER_PMEM: {
-			struct msm_audio_pmem_info info;
-			pr_debug("%s: AUDIO_REGISTER_PMEM\n", __func__);
-			if (copy_from_user(&info, (void *) arg, sizeof(info)))
-				rc = -EFAULT;
-			else
-				rc = audlpa_pmem_add(audio, &info);
-			break;
-		}
+	case AUDIO_REGISTER_ION: {
+		struct msm_audio_ion_info info;
+		pr_debug("%s: AUDIO_REGISTER_ION\n", __func__);
+		if (copy_from_user(&info, (void *)arg, sizeof(info)))
+			rc = -EFAULT;
+		else
+			rc = audlpa_ion_add(audio, &info);
+		break;
+	}
 
-	case AUDIO_DEREGISTER_PMEM: {
-			struct msm_audio_pmem_info info;
-			pr_debug("%s: AUDIO_DEREGISTER_PMEM\n", __func__);
-			if (copy_from_user(&info, (void *) arg, sizeof(info)))
-				rc = -EFAULT;
-			else
-				rc = audlpa_pmem_remove(audio, &info);
-			break;
-		}
+	case AUDIO_DEREGISTER_ION: {
+		struct msm_audio_ion_info info;
+		pr_debug("%s: AUDIO_DEREGISTER_ION\n", __func__);
+		if (copy_from_user(&info, (void *)arg, sizeof(info)))
+			rc = -EFAULT;
+		else
+			rc = audlpa_ion_remove(audio, &info);
+		break;
+	}
+
 	case AUDIO_ASYNC_WRITE:
 		pr_debug("%s: AUDIO_ASYNC_WRITE\n", __func__);
 		if (audio->drv_status & ADRV_STATUS_FSYNC)
@@ -1038,34 +1085,37 @@
 	return audlpa_async_fsync(audio);
 }
 
-static void audlpa_reset_pmem_region(struct audio *audio)
+void audlpa_reset_ion_region(struct audio *audio)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	struct list_head *ptr, *next;
 
-	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
-		region = list_entry(ptr, struct audlpa_pmem_region, list);
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audlpa_ion_region, list);
 		list_del(&region->list);
-		put_pmem_file(region->file);
+		ion_unmap_kernel(region->client, region->handle);
+		ion_free(region->client, region->handle);
+		ion_client_destroy(region->client);
 		kfree(region);
 	}
 
 	return;
 }
 
-static void audlpa_unmap_pmem_region(struct audio *audio)
+static void audlpa_unmap_ion_region(struct audio *audio)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	struct list_head *ptr, *next;
 	int rc = -EINVAL;
 
-	pr_debug("%s:\n", __func__);
-	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
-		region = list_entry(ptr, struct audlpa_pmem_region, list);
-		pr_debug("%s: phy_address = 0x%lx\n", __func__, region->paddr);
+	pr_debug("%s[%p]:\n", __func__, audio);
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audlpa_ion_region, list);
+		pr_debug("%s[%p]: phy_address = 0x%lx\n",
+			__func__, audio, region->paddr);
 		if (region != NULL) {
 			rc = q6asm_memory_unmap(audio->ac,
-						(uint32_t)region->paddr, IN);
+					(uint32_t)region->paddr, IN);
 			if (rc < 0)
 				pr_err("%s: memory unmap failed\n", __func__);
 		}
@@ -1084,12 +1134,12 @@
 	if (audio->out_enabled)
 		audlpa_async_flush(audio);
 	audio->wflush = 0;
-	audlpa_unmap_pmem_region(audio);
+	audlpa_unmap_ion_region(audio);
 	audio_disable(audio);
 	msm_clear_session_id(audio->ac->session);
 	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
 	q6asm_audio_client_free(audio->ac);
-	audlpa_reset_pmem_region(audio);
+	audlpa_reset_ion_region(audio);
 #ifdef CONFIG_HAS_EARLYSUSPEND
 	unregister_early_suspend(&audio->suspend_ctl.node);
 #endif
@@ -1099,7 +1149,6 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audlpa_reset_event_queue(audio);
-	pmem_kfree(audio->phys);
 	if (audio->stopped == 0)
 		audlpa_allow_sleep(audio);
 	wake_lock_destroy(&audio->wakelock);
@@ -1277,7 +1326,7 @@
 	spin_lock_init(&audio->dsp_lock);
 	init_waitqueue_head(&audio->write_wait);
 	INIT_LIST_HEAD(&audio->out_queue);
-	INIT_LIST_HEAD(&audio->pmem_region_queue);
+	INIT_LIST_HEAD(&audio->ion_region_queue);
 	INIT_LIST_HEAD(&audio->free_event_queue);
 	INIT_LIST_HEAD(&audio->event_queue);
 	init_waitqueue_head(&audio->wait);
@@ -1342,7 +1391,6 @@
 	return rc;
 err:
 	q6asm_audio_client_free(audio->ac);
-	pmem_kfree(audio->phys);
 	kfree(audio);
 	return rc;
 }
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.h b/arch/arm/mach-msm/qdsp6v2/audio_lpa.h
index ffc27ad..34b53f2 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_lpa.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -88,7 +88,7 @@
 
 	uint32_t device_events;
 
-	struct list_head pmem_region_queue; /* protected by lock */
+	struct list_head ion_region_queue; /* protected by lock */
 
 	int eq_enable;
 	int eq_needs_commit;