msm: ocmem: Add support for notifications
Add support for clients to register for OCMEM notifications.
This interface will be used by the clients that make use of
asynchronous OCMEM APIs to obtain notifications on buffer
status.
Change-Id: Iebe1521265c3c927fd69ba7ab7c1a7eb92c06eb6
Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/ocmem_notifier.c b/arch/arm/mach-msm/ocmem_notifier.c
new file mode 100644
index 0000000..58ad3d9
--- /dev/null
+++ b/arch/arm/mach-msm/ocmem_notifier.c
@@ -0,0 +1,137 @@
+/* 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 <mach/ocmem_priv.h>
+#include <linux/hardirq.h>
+
+static unsigned notifier_threshold;
+
+/* Protect the notifier structure below */
+DEFINE_MUTEX(nc_lock);
+
+struct ocmem_notifier {
+ int owner;
+ struct atomic_notifier_head nc;
+ unsigned listeners;
+} notifiers[OCMEM_CLIENT_MAX];
+
+static int check_id(int id)
+{
+ return (id < OCMEM_CLIENT_MAX && id >= OCMEM_GRAPHICS);
+}
+
+int check_notifier(int id)
+{
+ int ret = 0;
+
+ if (!check_id(id))
+ return 0;
+
+ mutex_lock(&nc_lock);
+ ret = notifiers[id].listeners;
+ mutex_unlock(&nc_lock);
+ return ret;
+}
+
+int ocmem_notifier_init(void)
+{
+ int id;
+ /* Maximum notifiers for each subsystem */
+ notifier_threshold = 1;
+ mutex_lock(&nc_lock);
+ for (id = 0; id < OCMEM_CLIENT_MAX; id++) {
+ notifiers[id].owner = id;
+ ATOMIC_INIT_NOTIFIER_HEAD(¬ifiers[id].nc);
+ notifiers[id].listeners = 0;
+ }
+ mutex_unlock(&nc_lock);
+ return 0;
+}
+
+/* Broadcast a notification to listeners */
+int dispatch_notification(int id, enum ocmem_notif_type notif,
+ struct ocmem_buf *buf)
+{
+ int ret = 0;
+ struct ocmem_notifier *nc_hndl = NULL;
+ mutex_lock(&nc_lock);
+ nc_hndl = ¬ifiers[id];
+ if (nc_hndl->listeners == 0) {
+ /* Send an error so that the scheduler can clean up */
+ mutex_unlock(&nc_lock);
+ return -EINVAL;
+ }
+ ret = atomic_notifier_call_chain(¬ifiers[id].nc, notif, buf);
+ mutex_unlock(&nc_lock);
+ return ret;
+}
+
+void *ocmem_notifier_register(int client_id, struct notifier_block *nb)
+{
+
+ int ret = 0;
+ struct ocmem_notifier *nc_hndl = NULL;
+
+ if (!check_id(client_id)) {
+ pr_err("ocmem: Invalid Client id\n");
+ return NULL;
+ }
+
+ if (!nb) {
+ pr_err("ocmem: Invalid Notifier Block\n");
+ return NULL;
+ }
+
+ mutex_lock(&nc_lock);
+
+ nc_hndl = ¬ifiers[client_id];
+
+ if (nc_hndl->listeners >= notifier_threshold) {
+ pr_err("ocmem: Max notifiers already registered\n");
+ mutex_unlock(&nc_lock);
+ return NULL;
+ }
+
+ ret = atomic_notifier_chain_register(&nc_hndl->nc, nb);
+
+ if (ret < 0) {
+ mutex_unlock(&nc_lock);
+ return NULL;
+ }
+
+ nc_hndl->listeners++;
+ pr_info("ocmem: Notifier registered for %d\n", client_id);
+ mutex_unlock(&nc_lock);
+ return nc_hndl;
+}
+EXPORT_SYMBOL(ocmem_notifier_register);
+
+int ocmem_notifier_unregister(void *hndl, struct notifier_block *nb)
+{
+
+ int ret = 0;
+
+ struct ocmem_notifier *nc_hndl = (struct ocmem_notifier *) hndl;
+
+ if (!nc_hndl) {
+ pr_err("ocmem: Invalid notification handle\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&nc_lock);
+ ret = atomic_notifier_chain_unregister(&nc_hndl->nc, nb);
+ nc_hndl->listeners--;
+ mutex_unlock(&nc_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(ocmem_notifier_unregister);