msm: ocmem: Add support for OCMEM APIs

Add support for OCMEM APIs that provide required buffer
management routines for OCMEM Clients.

Change-Id: I989472c0079524376481832d8147c209f2877166
Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index e6c3bad..eb9dc0a 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -331,7 +331,7 @@
 obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60-vcm.o
 endif
 obj-$(CONFIG_MSM_OCMEM) += ocmem.o ocmem_allocator.o ocmem_notifier.o
-obj-$(CONFIG_MSM_OCMEM) += ocmem_sched.o
+obj-$(CONFIG_MSM_OCMEM) += ocmem_sched.o ocmem_api.o
 
 obj-$(CONFIG_ARCH_MSM7X27) += gpiomux-7x27.o gpiomux-v1.o gpiomux.o
 obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-7x30.o gpiomux-v1.o gpiomux.o
diff --git a/arch/arm/mach-msm/include/mach/ocmem.h b/arch/arm/mach-msm/include/mach/ocmem.h
index bf7c338..b0475ed 100644
--- a/arch/arm/mach-msm/include/mach/ocmem.h
+++ b/arch/arm/mach-msm/include/mach/ocmem.h
@@ -41,7 +41,7 @@
 };
 
 struct ocmem_map_list {
-	int num_chunks;
+	unsigned num_chunks;
 	struct ocmem_chunk chunks[OCMEM_MAX_CHUNKS];
 };
 
@@ -84,9 +84,14 @@
 
 int ocmem_notifier_unregister(void *notif_hndl, struct notifier_block *nb);
 
+/* Obtain the maximum quota for the client */
+unsigned long get_max_quota(int client_id);
+
 /* Allocation APIs */
 struct ocmem_buf *ocmem_allocate(int client_id, unsigned long size);
 
+struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size);
+
 struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size);
 
 struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min,
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
new file mode 100644
index 0000000..bed13de
--- /dev/null
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 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
+ * 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/slab.h>
+#include <mach/ocmem_priv.h>
+
+static inline int check_id(int id)
+{
+	return (id < OCMEM_CLIENT_MAX && id >= OCMEM_GRAPHICS);
+}
+
+static struct ocmem_handle *generate_handle(void)
+{
+	struct ocmem_handle *handle = NULL;
+
+	handle = kzalloc(sizeof(struct ocmem_handle), GFP_KERNEL);
+	if (!handle) {
+		pr_err("ocmem: Unable to generate buffer handle\n");
+		return NULL;
+	}
+	mutex_init(&handle->handle_mutex);
+	return handle;
+}
+
+static int free_handle(struct ocmem_handle *handle)
+{
+	if (!handle)
+		return -EINVAL;
+
+	mutex_destroy(&handle->handle_mutex);
+	kfree(handle);
+	handle = NULL;
+	return 0;
+}
+
+static int __ocmem_free(int id, struct ocmem_buf *buf)
+{
+	int ret = 0;
+	struct ocmem_handle *handle = buffer_to_handle(buf);
+
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_mutex);
+	ret = process_free(id, handle);
+	mutex_unlock(&handle->handle_mutex);
+
+	if (ret)
+		return -EINVAL;
+
+	free_handle(handle);
+	return 0;
+}
+
+static struct ocmem_buf *__ocmem_allocate_range(int id, unsigned long min,
+		unsigned long max, unsigned long step, bool block, bool wait)
+{
+	struct ocmem_handle *handle = NULL;
+	int ret = 0;
+
+	handle = generate_handle();
+	if (!handle) {
+		pr_err("ocmem: Unable to generate handle\n");
+		return NULL;
+	}
+
+	mutex_lock(&handle->handle_mutex);
+	ret = process_allocate(id, handle, min, max, step, block, wait);
+	mutex_unlock(&handle->handle_mutex);
+	if (ret) {
+		pr_err("ocmem allocation failed\n");
+		free_handle(handle);
+		return NULL;
+	} else
+		return handle_to_buffer(handle);
+}
+
+struct ocmem_buf *ocmem_allocate(int client_id, unsigned long size)
+{
+	bool can_block = false;
+	bool can_wait = true;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return NULL;
+	}
+
+	if (size < OCMEM_MIN_ALLOC) {
+		pr_err("ocmem: requested size %lx must be at least %x\n",
+				size, OCMEM_MIN_ALLOC);
+		return NULL;
+	}
+
+	if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
+		pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
+				OCMEM_MIN_ALIGN);
+		return NULL;
+	}
+
+	return __ocmem_allocate_range(client_id, size, size,
+					size, can_block, can_wait);
+}
+
+struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size)
+{
+	bool can_block = false;
+	bool can_wait = false;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return NULL;
+	}
+
+	if (size < OCMEM_MIN_ALLOC) {
+		pr_err("ocmem: requested size %lx must be at least %x\n",
+				size, OCMEM_MIN_ALLOC);
+		return NULL;
+	}
+
+	if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
+		pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
+				OCMEM_MIN_ALIGN);
+		return NULL;
+	}
+	return __ocmem_allocate_range(client_id, size, size,
+					size, can_block, can_wait);
+}
+
+struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min,
+		unsigned long goal, unsigned long step)
+{
+	bool can_block = true;
+	bool can_wait = false;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return NULL;
+	}
+
+	/* Asynchronous API requires notifier registration */
+	if (!check_notifier(client_id)) {
+		pr_err("ocmem: No notifier registered for client %d\n",
+				client_id);
+		return NULL;
+	}
+
+	if (min < OCMEM_MIN_ALLOC) {
+		pr_err("ocmem: requested min size %lx must be at least %x\n",
+				min, OCMEM_MIN_ALLOC);
+		return NULL;
+	}
+
+	if (!IS_ALIGNED(min | goal | step, OCMEM_MIN_ALIGN)) {
+		pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
+				OCMEM_MIN_ALIGN);
+		return NULL;
+	}
+
+	return __ocmem_allocate_range(client_id, min, goal,
+				step, can_block, can_wait);
+}
+
+struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size)
+{
+	bool can_block = true;
+	bool can_wait = false;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return NULL;
+	}
+
+	/* Asynchronous API requires notifier registration */
+	if (!check_notifier(client_id)) {
+		pr_err("ocmem: No notifier registered for client %d\n",
+				client_id);
+		return NULL;
+	}
+
+	if (size < OCMEM_MIN_ALLOC) {
+		pr_err("ocmem: requested size %lx must be at least %x\n",
+				size, OCMEM_MIN_ALLOC);
+		return NULL;
+	}
+
+	if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
+		pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
+				OCMEM_MIN_ALIGN);
+		return NULL;
+	}
+
+	return __ocmem_allocate_range(client_id, 0, size, size,
+						can_block, can_wait);
+
+}
+
+int ocmem_free(int client_id, struct ocmem_buf *buffer)
+{
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return -EINVAL;
+	}
+
+	if (!buffer) {
+		pr_err("ocmem: Invalid buffer\n");
+		return -EINVAL;
+	}
+
+	return __ocmem_free(client_id, buffer);
+}
+
+int pre_validate_chunk_list(struct ocmem_map_list *list)
+{
+	int i = 0;
+	struct ocmem_chunk *chunks;
+
+	if (!list)
+		return -EINVAL;
+
+	if (list->num_chunks > OCMEM_MAX_CHUNKS || list->num_chunks == 0)
+		return -EINVAL;
+
+	chunks = list->chunks;
+
+	if (!chunks)
+		return -EINVAL;
+
+	for (i = 0; i < list->num_chunks; i++) {
+		if (!chunks[i].ddr_paddr ||
+			chunks[i].size < MIN_CHUNK_SIZE)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+int ocmem_map(int client_id, struct ocmem_buf *buffer,
+			struct ocmem_map_list *list)
+{
+	int ret = 0;
+	struct ocmem_handle *handle = NULL;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return -EINVAL;
+	}
+
+	/* Asynchronous API requires notifier registration */
+	if (!check_notifier(client_id)) {
+		pr_err("ocmem: No notifier registered for client %d\n",
+				client_id);
+		return -EINVAL;
+	}
+
+	if (!buffer) {
+		pr_err("ocmem: Invalid buffer\n");
+		return -EINVAL;
+	}
+
+	if (!pre_validate_chunk_list(list))
+		return -EINVAL;
+
+	handle = buffer_to_handle(buffer);
+
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_mutex);
+	ret = process_xfer(client_id, handle, list, TO_OCMEM);
+	mutex_unlock(&handle->handle_mutex);
+	return ret;
+}
+
+int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
+			struct ocmem_map_list *list)
+{
+
+	int ret = 0;
+	struct ocmem_handle *handle = NULL;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return -EINVAL;
+	}
+
+	/* Asynchronous API requires notifier registration */
+	if (!check_notifier(client_id)) {
+		pr_err("ocmem: No notifier registered for client %d\n",
+				client_id);
+		return -EINVAL;
+	}
+
+	if (!buffer) {
+		pr_err("ocmem: Invalid buffer\n");
+		return -EINVAL;
+	}
+
+	if (!pre_validate_chunk_list(list))
+		return -EINVAL;
+
+	handle = buffer_to_handle(buffer);
+
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_mutex);
+	ret = process_xfer(client_id, handle, list, TO_DDR);
+	mutex_unlock(&handle->handle_mutex);
+	return ret;
+}
+
+unsigned long get_max_quota(int client_id)
+{
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return 0x0;
+	}
+	return process_quota(client_id);
+}