summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDima Zavin <dima@android.com>2010-11-01 14:20:17 -0700
committerDima Zavin <dima@android.com>2010-11-07 22:37:40 -0800
commit44729054a5c38c702ebada8ba31184d8f938d0b0 (patch)
tree68cc844b297f2a8e8c63108b7dcde1ce28a74165 /drivers
parent23e9aed0c0911ff3bd3ee33df1c88f9c1db83b17 (diff)
media: video: tegra: Add Tegra RPC support for tegra multimedia framework
Change-Id: I9233c5d7c678f6a9ba1c23af686137bf4d6a4291 Signed-off-by: Dima Zavin <dima@android.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/tegra/Kconfig3
-rw-r--r--drivers/media/video/tegra/Makefile1
-rw-r--r--drivers/media/video/tegra/avp/Kconfig15
-rw-r--r--drivers/media/video/tegra/avp/Makefile3
-rw-r--r--drivers/media/video/tegra/avp/tegra_rpc.c738
-rw-r--r--drivers/media/video/tegra/avp/trpc.h77
-rw-r--r--drivers/media/video/tegra/avp/trpc_local.c333
-rw-r--r--drivers/media/video/tegra/avp/trpc_sema.c220
-rw-r--r--drivers/media/video/tegra/avp/trpc_sema.h28
9 files changed, 1418 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig
index 86c6aa2c466b..ce31c4ecbe97 100644
--- a/drivers/media/video/tegra/Kconfig
+++ b/drivers/media/video/tegra/Kconfig
@@ -6,3 +6,6 @@ config TEGRA_CAMERA
Enables support for the Tegra camera interface
If unsure, say Y
+
+
+source "drivers/media/video/tegra/avp/Kconfig"
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index e5a074ea6608..4cb8e3c78485 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o
+obj-y += avp/
diff --git a/drivers/media/video/tegra/avp/Kconfig b/drivers/media/video/tegra/avp/Kconfig
new file mode 100644
index 000000000000..dca58da0862c
--- /dev/null
+++ b/drivers/media/video/tegra/avp/Kconfig
@@ -0,0 +1,15 @@
+config TEGRA_RPC
+ bool "Enable support for Tegra RPC"
+ depends on ARCH_TEGRA
+ default y
+ help
+ Enables support for the RPC mechanism necessary for the Tegra
+ multimedia framework. It is both used to communicate locally on the
+ CPU between multiple multimedia components as well as to communicate
+ with the AVP for offloading media decode.
+
+ Exports the local tegra RPC interface on device node
+ /dev/tegra_rpc. Also provides tegra fd based semaphores needed by
+ the tegra multimedia framework.
+
+ If unsure, say Y
diff --git a/drivers/media/video/tegra/avp/Makefile b/drivers/media/video/tegra/avp/Makefile
new file mode 100644
index 000000000000..438cbb78d8cd
--- /dev/null
+++ b/drivers/media/video/tegra/avp/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_TEGRA_RPC) += tegra_rpc.o
+obj-$(CONFIG_TEGRA_RPC) += trpc_local.o
+obj-$(CONFIG_TEGRA_RPC) += trpc_sema.o
diff --git a/drivers/media/video/tegra/avp/tegra_rpc.c b/drivers/media/video/tegra/avp/tegra_rpc.c
new file mode 100644
index 000000000000..c4e707e9a89e
--- /dev/null
+++ b/drivers/media/video/tegra/avp/tegra_rpc.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Dima Zavin <dima@android.com>
+ *
+ * Based on original NVRM code from NVIDIA, and a partial rewrite by:
+ * Gary King <gking@nvidia.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/delay.h>
+#include <linux/err.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/tegra_rpc.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "trpc.h"
+
+struct trpc_port;
+struct trpc_endpoint {
+ struct list_head msg_list;
+ wait_queue_head_t msg_waitq;
+
+ struct trpc_endpoint *out;
+ struct trpc_port *port;
+
+ struct trpc_node *owner;
+
+ struct completion *connect_done;
+ bool ready;
+ struct trpc_ep_ops *ops;
+ void *priv;
+};
+
+struct trpc_port {
+ char name[TEGRA_RPC_MAX_NAME_LEN];
+
+ /* protects peer and closed state */
+ spinlock_t lock;
+ struct trpc_endpoint peers[2];
+ bool closed;
+
+ /* private */
+ struct kref ref;
+ struct rb_node rb_node;
+};
+
+enum {
+ TRPC_TRACE_MSG = 1U << 0,
+ TRPC_TRACE_CONN = 1U << 1,
+ TRPC_TRACE_PORT = 1U << 2,
+};
+
+static u32 trpc_debug_mask = TRPC_TRACE_CONN | TRPC_TRACE_PORT;
+module_param_named(debug_mask, trpc_debug_mask, uint, S_IWUSR | S_IRUGO);
+
+#define DBG(flag, args...) \
+ do { if (trpc_debug_mask & (flag)) pr_info(args); } while (0)
+
+struct tegra_rpc_info {
+ struct kmem_cache *msg_cache;
+
+ spinlock_t ports_lock;
+ struct rb_root ports;
+
+ struct list_head node_list;
+ struct mutex node_lock;
+};
+
+struct trpc_msg {
+ struct list_head list;
+
+ size_t len;
+ u8 payload[TEGRA_RPC_MAX_MSG_LEN];
+};
+
+static struct tegra_rpc_info *tegra_rpc;
+
+static struct trpc_msg *dequeue_msg_locked(struct trpc_endpoint *ep);
+
+/* a few accessors for the outside world to keep the trpc_endpoint struct
+ * definition private to this module */
+void *trpc_priv(struct trpc_endpoint *ep)
+{
+ return ep->priv;
+}
+
+struct trpc_endpoint *trpc_peer(struct trpc_endpoint *ep)
+{
+ return ep->out;
+}
+
+const char *trpc_name(struct trpc_endpoint *ep)
+{
+ return ep->port->name;
+}
+
+static inline bool is_connected(struct trpc_port *port)
+{
+ return port->peers[0].ready && port->peers[1].ready;
+}
+
+static inline bool is_closed(struct trpc_port *port)
+{
+ return port->closed;
+}
+
+static void rpc_port_free(struct tegra_rpc_info *info, struct trpc_port *port)
+{
+ struct trpc_msg *msg;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ struct list_head *list = &port->peers[i].msg_list;
+ while (!list_empty(list)) {
+ msg = list_first_entry(list, struct trpc_msg, list);
+ list_del(&msg->list);
+ kmem_cache_free(info->msg_cache, msg);
+ }
+ }
+ kfree(port);
+}
+
+static void _rpc_port_release(struct kref *kref)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+ struct trpc_port *port = container_of(kref, struct trpc_port, ref);
+ unsigned long flags;
+
+ DBG(TRPC_TRACE_PORT, "%s: releasing port '%s' (%p)\n", __func__,
+ port->name, port);
+ spin_lock_irqsave(&info->ports_lock, flags);
+ rb_erase(&port->rb_node, &info->ports);
+ spin_unlock_irqrestore(&info->ports_lock, flags);
+ rpc_port_free(info, port);
+}
+
+/* note that the refcount is actually on the port and not on the endpoint */
+void trpc_put(struct trpc_endpoint *ep)
+{
+ kref_put(&ep->port->ref, _rpc_port_release);
+}
+
+void trpc_get(struct trpc_endpoint *ep)
+{
+ kref_get(&ep->port->ref);
+}
+
+/* Searches the rb_tree for a port with the provided name. If one is not found,
+ * the new port in inserted. Otherwise, the existing port is returned.
+ * Must be called with the ports_lock held */
+static struct trpc_port *rpc_port_find_insert(struct tegra_rpc_info *info,
+ struct trpc_port *port)
+{
+ struct rb_node **p;
+ struct rb_node *parent;
+ struct trpc_port *tmp;
+ int ret = 0;
+
+ p = &info->ports.rb_node;
+ parent = NULL;
+ while (*p) {
+ parent = *p;
+ tmp = rb_entry(parent, struct trpc_port, rb_node);
+
+ ret = strncmp(port->name, tmp->name, TEGRA_RPC_MAX_NAME_LEN);
+ if (ret < 0)
+ p = &(*p)->rb_left;
+ else if (ret > 0)
+ p = &(*p)->rb_right;
+ else
+ return tmp;
+ }
+ rb_link_node(&port->rb_node, parent, p);
+ rb_insert_color(&port->rb_node, &info->ports);
+ DBG(TRPC_TRACE_PORT, "%s: inserted port '%s' (%p)\n", __func__,
+ port->name, port);
+ return port;
+}
+
+static int nodes_try_connect(struct tegra_rpc_info *info,
+ struct trpc_node *src,
+ struct trpc_endpoint *from)
+{
+ struct trpc_node *node;
+ int ret;
+
+ mutex_lock(&info->node_lock);
+ list_for_each_entry(node, &info->node_list, list) {
+ if (!node->try_connect)
+ continue;
+ ret = node->try_connect(node, src, from);
+ if (!ret) {
+ mutex_unlock(&info->node_lock);
+ return 0;
+ }
+ }
+ mutex_unlock(&info->node_lock);
+ return -ECONNREFUSED;
+}
+
+static struct trpc_port *rpc_port_alloc(const char *name)
+{
+ struct trpc_port *port;
+ int i;
+
+ port = kzalloc(sizeof(struct trpc_port), GFP_KERNEL);
+ if (!port) {
+ pr_err("%s: can't alloc rpc_port\n", __func__);
+ return NULL;
+ }
+ BUILD_BUG_ON(2 != ARRAY_SIZE(port->peers));
+
+ spin_lock_init(&port->lock);
+ kref_init(&port->ref);
+ strlcpy(port->name, name, TEGRA_RPC_MAX_NAME_LEN);
+ for (i = 0; i < 2; i++) {
+ struct trpc_endpoint *ep = port->peers + i;
+ INIT_LIST_HEAD(&ep->msg_list);
+ init_waitqueue_head(&ep->msg_waitq);
+ ep->port = port;
+ }
+ port->peers[0].out = &port->peers[1];
+ port->peers[1].out = &port->peers[0];
+
+ return port;
+}
+
+/* must be holding the ports lock */
+static inline void handle_port_connected(struct trpc_port *port)
+{
+ int i;
+
+ DBG(TRPC_TRACE_CONN, "tegra_rpc: port '%s' connected\n", port->name);
+
+ for (i = 0; i < 2; i++)
+ if (port->peers[i].connect_done)
+ complete(port->peers[i].connect_done);
+}
+
+static inline void _ready_ep(struct trpc_endpoint *ep,
+ struct trpc_node *owner,
+ struct trpc_ep_ops *ops,
+ void *priv)
+{
+ ep->ready = true;
+ ep->owner = owner;
+ ep->ops = ops;
+ ep->priv = priv;
+}
+
+/* this keeps a reference on the port */
+static struct trpc_endpoint *_create_peer(struct tegra_rpc_info *info,
+ struct trpc_node *owner,
+ struct trpc_endpoint *ep,
+ struct trpc_ep_ops *ops,
+ void *priv)
+{
+ struct trpc_port *port = ep->port;
+ struct trpc_endpoint *peer = ep->out;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ BUG_ON(port->closed);
+ if (peer->ready || !ep->ready) {
+ peer = NULL;
+ goto out;
+ }
+ _ready_ep(peer, owner, ops, priv);
+ if (WARN_ON(!is_connected(port)))
+ pr_warning("%s: created peer but no connection established?!\n",
+ __func__);
+ else
+ handle_port_connected(port);
+ trpc_get(peer);
+out:
+ spin_unlock_irqrestore(&port->lock, flags);
+ return peer;
+}
+
+/* Exported code. This is out interface to the outside world */
+struct trpc_endpoint *trpc_create(struct trpc_node *owner, const char *name,
+ struct trpc_ep_ops *ops, void *priv)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+ struct trpc_endpoint *ep;
+ struct trpc_port *new_port;
+ struct trpc_port *port;
+ unsigned long flags;
+
+ BUG_ON(!owner);
+
+ /* we always allocate a new port even if one already might exist. This
+ * is slightly inefficient, but it allows us to do the allocation
+ * without holding our ports_lock spinlock. */
+ new_port = rpc_port_alloc(name);
+ if (!new_port) {
+ pr_err("%s: can't allocate memory for '%s'\n", __func__, name);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ spin_lock_irqsave(&info->ports_lock, flags);
+ port = rpc_port_find_insert(info, new_port);
+ if (port != new_port) {
+ rpc_port_free(info, new_port);
+ /* There was already a port by that name in the rb_tree,
+ * so just try to create its peer[1], i.e. peer for peer[0]
+ */
+ ep = _create_peer(info, owner, &port->peers[0], ops, priv);
+ if (!ep) {
+ pr_err("%s: port '%s' is not in a connectable state\n",
+ __func__, port->name);
+ ep = ERR_PTR(-EINVAL);
+ }
+ goto out;
+ }
+ /* don't need to grab the individual port lock here since we must be
+ * holding the ports_lock to add the new element, and never dropped
+ * it, and thus noone could have gotten a reference to this port
+ * and thus the state couldn't have been touched */
+ ep = &port->peers[0];
+ _ready_ep(ep, owner, ops, priv);
+out:
+ spin_unlock_irqrestore(&info->ports_lock, flags);
+ return ep;
+}
+
+struct trpc_endpoint *trpc_create_peer(struct trpc_node *owner,
+ struct trpc_endpoint *ep,
+ struct trpc_ep_ops *ops,
+ void *priv)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+ struct trpc_endpoint *peer;
+ unsigned long flags;
+
+ BUG_ON(!owner);
+
+ spin_lock_irqsave(&info->ports_lock, flags);
+ peer = _create_peer(info, owner, ep, ops, priv);
+ spin_unlock_irqrestore(&info->ports_lock, flags);
+ return peer;
+}
+
+/* timeout == -1, waits forever
+ * timeout == 0, return immediately
+ */
+int trpc_connect(struct trpc_endpoint *from, long timeout)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+ struct trpc_port *port = from->port;
+ struct trpc_node *src = from->owner;
+ int ret;
+ bool no_retry = !timeout;
+ unsigned long endtime = jiffies + msecs_to_jiffies(timeout);
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* XXX: add state for connections and ports to prevent invalid
+ * states like multiple connections, etc. ? */
+ if (unlikely(is_closed(port))) {
+ ret = -ECONNRESET;
+ pr_err("%s: can't connect to %s, closed\n", __func__,
+ port->name);
+ goto out;
+ } else if (is_connected(port)) {
+ ret = 0;
+ goto out;
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ do {
+ ret = nodes_try_connect(info, src, from);
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (is_connected(port)) {
+ ret = 0;
+ goto out;
+ } else if (no_retry) {
+ goto out;
+ } else if (signal_pending(current)) {
+ ret = -EINTR;
+ goto out;
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+ usleep_range(5000, 20000);
+ } while (timeout < 0 || time_before(jiffies, endtime));
+
+ return -ETIMEDOUT;
+
+out:
+ spin_unlock_irqrestore(&port->lock, flags);
+ return ret;
+}
+
+/* convenience function for doing this common pattern in a single call */
+struct trpc_endpoint *trpc_create_connect(struct trpc_node *src,
+ char *name,
+ struct trpc_ep_ops *ops,
+ void *priv,
+ long timeout)
+{
+ struct trpc_endpoint *ep;
+ int ret;
+
+ ep = trpc_create(src, name, ops, priv);
+ if (IS_ERR(ep))
+ return ep;
+
+ ret = trpc_connect(ep, timeout);
+ if (ret) {
+ trpc_close(ep);
+ return ERR_PTR(ret);
+ }
+
+ return ep;
+}
+
+void trpc_close(struct trpc_endpoint *ep)
+{
+ struct trpc_port *port = ep->port;
+ struct trpc_endpoint *peer = ep->out;
+ bool need_close_op = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ BUG_ON(!ep->ready);
+ ep->ready = false;
+ port->closed = true;
+ if (peer->ready) {
+ need_close_op = true;
+ /* the peer may be waiting for a message */
+ wake_up_all(&peer->msg_waitq);
+ if (peer->connect_done)
+ complete(peer->connect_done);
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+ if (need_close_op && peer->ops && peer->ops->close)
+ peer->ops->close(peer);
+ trpc_put(ep);
+}
+
+int trpc_wait_peer(struct trpc_endpoint *ep, long timeout)
+{
+ struct trpc_port *port = ep->port;
+ DECLARE_COMPLETION_ONSTACK(event);
+ int ret;
+ unsigned long flags;
+
+ if (timeout < 0)
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ else if (timeout > 0)
+ timeout = msecs_to_jiffies(timeout);
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (ep->connect_done) {
+ ret = -EBUSY;
+ goto done;
+ } else if (is_connected(port)) {
+ ret = 0;
+ goto done;
+ } else if (is_closed(port)) {
+ ret = -ECONNRESET;
+ goto done;
+ } else if (!timeout) {
+ ret = -EAGAIN;
+ goto done;
+ }
+ ep->connect_done = &event;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ ret = wait_for_completion_interruptible_timeout(&event, timeout);
+
+ spin_lock_irqsave(&port->lock, flags);
+ ep->connect_done = NULL;
+
+ if (is_connected(port)) {
+ ret = 0;
+ } else {
+ if (is_closed(port))
+ ret = -ECONNRESET;
+ else if (ret == -ERESTARTSYS)
+ ret = -EINTR;
+ else if (!ret)
+ ret = -ETIMEDOUT;
+ }
+
+done:
+ spin_unlock_irqrestore(&port->lock, flags);
+ return ret;
+}
+
+static inline int _ep_id(struct trpc_endpoint *ep)
+{
+ return ep - ep->port->peers;
+}
+
+static int queue_msg(struct trpc_node *src, struct trpc_endpoint *from,
+ void *buf, size_t len, gfp_t gfp_flags)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+ struct trpc_endpoint *peer = from->out;
+ struct trpc_port *port = from->port;
+ struct trpc_msg *msg;
+ unsigned long flags;
+ int ret;
+
+ BUG_ON(len > TEGRA_RPC_MAX_MSG_LEN);
+ /* shouldn't be enqueueing to the endpoint */
+ BUG_ON(peer->ops && peer->ops->send);
+
+ DBG(TRPC_TRACE_MSG, "%s: queueing message for %s.%d\n", __func__,
+ port->name, _ep_id(peer));
+
+ msg = kmem_cache_alloc(info->msg_cache, gfp_flags);
+ if (!msg) {
+ pr_err("%s: can't alloc memory for msg\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(msg->payload, buf, len);
+ msg->len = len;
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (is_closed(port)) {
+ pr_err("%s: cannot send message for closed port %s.%d\n",
+ __func__, port->name, _ep_id(peer));
+ ret = -ECONNRESET;
+ goto err;
+ } else if (!is_connected(port)) {
+ pr_err("%s: cannot send message for unconnected port %s.%d\n",
+ __func__, port->name, _ep_id(peer));
+ ret = -ENOTCONN;
+ goto err;
+ }
+
+ list_add_tail(&msg->list, &peer->msg_list);
+ if (peer->ops && peer->ops->notify_recv)
+ peer->ops->notify_recv(peer);
+ wake_up_all(&peer->msg_waitq);
+ spin_unlock_irqrestore(&port->lock, flags);
+ return 0;
+
+err:
+ spin_unlock_irqrestore(&port->lock, flags);
+ kmem_cache_free(info->msg_cache, msg);
+ return ret;
+}
+
+/* Returns -ENOMEM if failed to allocate memory for the message. */
+int trpc_send_msg(struct trpc_node *src, struct trpc_endpoint *from,
+ void *buf, size_t len, gfp_t gfp_flags)
+{
+ struct trpc_endpoint *peer = from->out;
+ struct trpc_port *port = from->port;
+
+ BUG_ON(len > TEGRA_RPC_MAX_MSG_LEN);
+
+ DBG(TRPC_TRACE_MSG, "%s: sending message from %s.%d to %s.%d\n",
+ __func__, port->name, _ep_id(from), port->name, _ep_id(peer));
+
+ if (peer->ops && peer->ops->send) {
+ might_sleep();
+ return peer->ops->send(peer, buf, len);
+ } else {
+ might_sleep_if(gfp_flags & __GFP_WAIT);
+ return queue_msg(src, from, buf, len, gfp_flags);
+ }
+}
+
+static inline struct trpc_msg *dequeue_msg_locked(struct trpc_endpoint *ep)
+{
+ struct trpc_msg *msg = NULL;
+
+ if (!list_empty(&ep->msg_list)) {
+ msg = list_first_entry(&ep->msg_list, struct trpc_msg, list);
+ list_del_init(&msg->list);
+ }
+
+ return msg;
+}
+
+static bool __should_wake(struct trpc_endpoint *ep)
+{
+ struct trpc_port *port = ep->port;
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&port->lock, flags);
+ ret = !list_empty(&ep->msg_list) || is_closed(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ return ret;
+}
+
+int trpc_recv_msg(struct trpc_node *src, struct trpc_endpoint *ep,
+ void *buf, size_t buf_len, long timeout)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+ struct trpc_port *port = ep->port;
+ struct trpc_msg *msg;
+ size_t len;
+ long ret;
+ unsigned long flags;
+
+ BUG_ON(buf_len > TEGRA_RPC_MAX_MSG_LEN);
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* we allow closed ports to finish receiving already-queued messages */
+ msg = dequeue_msg_locked(ep);
+ if (msg) {
+ goto got_msg;
+ } else if (is_closed(port)) {
+ ret = -ECONNRESET;
+ goto out;
+ } else if (!is_connected(port)) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ if (timeout == 0) {
+ ret = 0;
+ goto out;
+ } else if (timeout < 0) {
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ } else {
+ timeout = msecs_to_jiffies(timeout);
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+ DBG(TRPC_TRACE_MSG, "%s: waiting for message for %s.%d\n", __func__,
+ port->name, _ep_id(ep));
+
+ ret = wait_event_interruptible_timeout(ep->msg_waitq, __should_wake(ep),
+ timeout);
+
+ DBG(TRPC_TRACE_MSG, "%s: woke up for %s\n", __func__, port->name);
+ spin_lock_irqsave(&port->lock, flags);
+ msg = dequeue_msg_locked(ep);
+ if (!msg) {
+ if (is_closed(port))
+ ret = -ECONNRESET;
+ else if (!ret)
+ ret = -ETIMEDOUT;
+ else if (ret == -ERESTARTSYS)
+ ret = -EINTR;
+ else
+ pr_err("%s: error (%d) while receiving msg for '%s'\n",
+ __func__, (int)ret, port->name);
+ goto out;
+ }
+
+got_msg:
+ spin_unlock_irqrestore(&port->lock, flags);
+ len = min(buf_len, msg->len);
+ memcpy(buf, msg->payload, len);
+ kmem_cache_free(info->msg_cache, msg);
+ return len;
+
+out:
+ spin_unlock_irqrestore(&port->lock, flags);
+ return ret;
+}
+
+int trpc_node_register(struct trpc_node *node)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+
+ if (!info)
+ return -ENOMEM;
+
+ pr_info("%s: Adding '%s' to node list\n", __func__, node->name);
+
+ mutex_lock(&info->node_lock);
+ if (node->type == TRPC_NODE_LOCAL)
+ list_add(&node->list, &info->node_list);
+ else
+ list_add_tail(&node->list, &info->node_list);
+ mutex_unlock(&info->node_lock);
+ return 0;
+}
+
+void trpc_node_unregister(struct trpc_node *node)
+{
+ struct tegra_rpc_info *info = tegra_rpc;
+
+ mutex_lock(&info->node_lock);
+ list_del(&node->list);
+ mutex_unlock(&info->node_lock);
+}
+
+static int __init tegra_rpc_init(void)
+{
+ struct tegra_rpc_info *rpc_info;
+ int ret;
+
+ rpc_info = kzalloc(sizeof(struct tegra_rpc_info), GFP_KERNEL);
+ if (!rpc_info) {
+ pr_err("%s: error allocating rpc_info\n", __func__);
+ return -ENOMEM;
+ }
+
+ rpc_info->ports = RB_ROOT;
+ spin_lock_init(&rpc_info->ports_lock);
+ INIT_LIST_HEAD(&rpc_info->node_list);
+ mutex_init(&rpc_info->node_lock);
+
+ rpc_info->msg_cache = KMEM_CACHE(trpc_msg, 0);
+ if (!rpc_info->msg_cache) {
+ pr_err("%s: unable to create message cache\n", __func__);
+ ret = -ENOMEM;
+ goto err_kmem_cache;
+ }
+ tegra_rpc = rpc_info;
+
+ return 0;
+
+err_kmem_cache:
+ kfree(rpc_info);
+ return ret;
+}
+
+subsys_initcall(tegra_rpc_init);
diff --git a/drivers/media/video/tegra/avp/trpc.h b/drivers/media/video/tegra/avp/trpc.h
new file mode 100644
index 000000000000..859c94e7460c
--- /dev/null
+++ b/drivers/media/video/tegra/avp/trpc.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARM_MACH_TEGRA_RPC_H
+#define __ARM_MACH_TEGRA_RPC_H
+
+#include <linux/list.h>
+#include <linux/tegra_rpc.h>
+
+struct trpc_endpoint;
+struct trpc_ep_ops {
+ /* send is allowed to sleep */
+ int (*send)(struct trpc_endpoint *ep, void *buf, size_t len);
+ /* notify_recv is NOT allowed to sleep */
+ void (*notify_recv)(struct trpc_endpoint *ep);
+ /* close is allowed to sleep */
+ void (*close)(struct trpc_endpoint *ep);
+};
+
+enum {
+ TRPC_NODE_LOCAL,
+ TRPC_NODE_REMOTE,
+};
+
+struct trpc_node {
+ struct list_head list;
+ const char *name;
+ int type;
+ void *priv;
+
+ int (*try_connect)(struct trpc_node *node,
+ struct trpc_node *src,
+ struct trpc_endpoint *from);
+};
+
+struct trpc_endpoint *trpc_peer(struct trpc_endpoint *ep);
+void *trpc_priv(struct trpc_endpoint *ep);
+const char *trpc_name(struct trpc_endpoint *ep);
+
+void trpc_put(struct trpc_endpoint *ep);
+void trpc_get(struct trpc_endpoint *ep);
+
+int trpc_send_msg(struct trpc_node *src, struct trpc_endpoint *ep, void *buf,
+ size_t len, gfp_t gfp_flags);
+int trpc_recv_msg(struct trpc_node *src, struct trpc_endpoint *ep,
+ void *buf, size_t len, long timeout);
+struct trpc_endpoint *trpc_create(struct trpc_node *owner, const char *name,
+ struct trpc_ep_ops *ops, void *priv);
+struct trpc_endpoint *trpc_create_connect(struct trpc_node *src, char *name,
+ struct trpc_ep_ops *ops, void *priv,
+ long timeout);
+int trpc_connect(struct trpc_endpoint *from, long timeout);
+struct trpc_endpoint *trpc_create_peer(struct trpc_node *owner,
+ struct trpc_endpoint *ep,
+ struct trpc_ep_ops *ops,
+ void *priv);
+void trpc_close(struct trpc_endpoint *ep);
+int trpc_wait_peer(struct trpc_endpoint *ep, long timeout);
+
+int trpc_node_register(struct trpc_node *node);
+void trpc_node_unregister(struct trpc_node *node);
+
+#endif
diff --git a/drivers/media/video/tegra/avp/trpc_local.c b/drivers/media/video/tegra/avp/trpc_local.c
new file mode 100644
index 000000000000..5a941a78fc40
--- /dev/null
+++ b/drivers/media/video/tegra/avp/trpc_local.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Dima Zavin <dima@android.com>
+ *
+ * Based on original NVRM code from NVIDIA, and a partial rewrite by
+ * Gary King <gking@nvidia.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/err.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tegra_rpc.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include "trpc.h"
+#include "trpc_sema.h"
+
+struct rpc_info {
+ struct trpc_endpoint *rpc_ep;
+ struct file *sema_file;
+};
+
+/* ports names reserved for system functions, i.e. communicating with the
+ * AVP */
+static const char reserved_ports[][TEGRA_RPC_MAX_NAME_LEN] = {
+ "RPC_AVP_PORT",
+ "RPC_CPU_PORT",
+};
+static int num_reserved_ports = ARRAY_SIZE(reserved_ports);
+
+static void rpc_notify_recv(struct trpc_endpoint *ep);
+
+/* TODO: do we need to do anything when port is closed from the other side? */
+static struct trpc_ep_ops ep_ops = {
+ .notify_recv = rpc_notify_recv,
+};
+
+static struct trpc_node rpc_node = {
+ .name = "local",
+ .type = TRPC_NODE_LOCAL,
+};
+
+static void rpc_notify_recv(struct trpc_endpoint *ep)
+{
+ struct rpc_info *info = trpc_priv(ep);
+
+ if (WARN_ON(!info))
+ return;
+ if (info->sema_file)
+ trpc_sema_signal(info->sema_file);
+}
+
+static int local_rpc_open(struct inode *inode, struct file *file)
+{
+ struct rpc_info *info;
+
+ info = kzalloc(sizeof(struct rpc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ nonseekable_open(inode, file);
+ file->private_data = info;
+ return 0;
+}
+
+static int local_rpc_release(struct inode *inode, struct file *file)
+{
+ struct rpc_info *info = file->private_data;
+
+ if (info->rpc_ep)
+ trpc_close(info->rpc_ep);
+ if (info->sema_file)
+ fput(info->sema_file);
+ kfree(info);
+ file->private_data = NULL;
+ return 0;
+}
+
+static int __get_port_desc(struct tegra_rpc_port_desc *desc,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int size = _IOC_SIZE(cmd);
+
+ if (size != sizeof(struct tegra_rpc_port_desc))
+ return -EINVAL;
+ if (copy_from_user(desc, (void __user *)arg, sizeof(*desc)))
+ return -EFAULT;
+
+ desc->name[TEGRA_RPC_MAX_NAME_LEN - 1] = '\0';
+ return 0;
+}
+
+static char uniq_name[] = "aaaaaaaa+";
+static const int uniq_len = sizeof(uniq_name) - 1;
+static DEFINE_MUTEX(uniq_lock);
+
+static void _gen_port_name(char *new_name)
+{
+ int i;
+
+ mutex_lock(&uniq_lock);
+ for (i = 0; i < uniq_len - 1; i++) {
+ ++uniq_name[i];
+ if (uniq_name[i] != 'z')
+ break;
+ uniq_name[i] = 'a';
+ }
+ strlcpy(new_name, uniq_name, TEGRA_RPC_MAX_NAME_LEN);
+ mutex_unlock(&uniq_lock);
+}
+
+static int _validate_port_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_reserved_ports; i++)
+ if (!strncmp(name, reserved_ports[i], TEGRA_RPC_MAX_NAME_LEN))
+ return -EINVAL;
+ return 0;
+}
+
+static long local_rpc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct rpc_info *info = file->private_data;
+ struct tegra_rpc_port_desc desc;
+ struct trpc_endpoint *ep;
+ int ret = 0;
+
+ if (_IOC_TYPE(cmd) != TEGRA_RPC_IOCTL_MAGIC ||
+ _IOC_NR(cmd) < TEGRA_RPC_IOCTL_MIN_NR ||
+ _IOC_NR(cmd) > TEGRA_RPC_IOCTL_MAX_NR) {
+ ret = -ENOTTY;
+ goto err;
+ }
+
+ switch (cmd) {
+ case TEGRA_RPC_IOCTL_PORT_CREATE:
+ if (info->rpc_ep) {
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = __get_port_desc(&desc, cmd, arg);
+ if (ret)
+ goto err;
+ if (desc.name[0]) {
+ ret = _validate_port_name(desc.name);
+ if (ret)
+ goto err;
+ } else {
+ _gen_port_name(desc.name);
+ }
+ if (desc.notify_fd != -1) {
+ /* grab a reference to the trpc_sema fd */
+ info->sema_file = trpc_sema_get_from_fd(desc.notify_fd);
+ if (IS_ERR(info->sema_file)) {
+ ret = PTR_ERR(info->sema_file);
+ info->sema_file = NULL;
+ goto err;
+ }
+ }
+ ep = trpc_create(&rpc_node, desc.name, &ep_ops, info);
+ if (IS_ERR(ep)) {
+ ret = PTR_ERR(ep);
+ if (info->sema_file)
+ fput(info->sema_file);
+ info->sema_file = NULL;
+ goto err;
+ }
+ info->rpc_ep = ep;
+ break;
+ case TEGRA_RPC_IOCTL_PORT_GET_NAME:
+ if (!info->rpc_ep) {
+ ret = -EINVAL;
+ goto err;
+ }
+ if (copy_to_user((void __user *)arg,
+ trpc_name(info->rpc_ep),
+ TEGRA_RPC_MAX_NAME_LEN)) {
+ ret = -EFAULT;
+ goto err;
+ }
+ break;
+ case TEGRA_RPC_IOCTL_PORT_CONNECT:
+ if (!info->rpc_ep) {
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = trpc_connect(info->rpc_ep, (long)arg);
+ if (ret) {
+ pr_err("%s: can't connect to '%s' (%d)\n", __func__,
+ trpc_name(info->rpc_ep), ret);
+ goto err;
+ }
+ break;
+ case TEGRA_RPC_IOCTL_PORT_LISTEN:
+ if (!info->rpc_ep) {
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = trpc_wait_peer(info->rpc_ep, (long)arg);
+ if (ret) {
+ pr_err("%s: error waiting for peer for '%s' (%d)\n",
+ __func__, trpc_name(info->rpc_ep), ret);
+ goto err;
+ }
+ break;
+ default:
+ pr_err("%s: unknown cmd %d\n", __func__, _IOC_NR(cmd));
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (ret && ret != -ERESTARTSYS)
+ pr_err("tegra_rpc: pid=%d ioctl=%x/%lx (%x) ret=%d\n",
+ current->pid, cmd, arg, _IOC_NR(cmd), ret);
+ return (long)ret;
+}
+
+static ssize_t local_rpc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct rpc_info *info = file->private_data;
+ u8 data[TEGRA_RPC_MAX_MSG_LEN];
+ int ret;
+
+ if (!info)
+ return -EINVAL;
+ else if (count > TEGRA_RPC_MAX_MSG_LEN)
+ return -EINVAL;
+
+ if (copy_from_user(data, buf, count))
+ return -EFAULT;
+
+ ret = trpc_send_msg(&rpc_node, info->rpc_ep, data, count,
+ GFP_KERNEL);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t local_rpc_read(struct file *file, char __user *buf, size_t max,
+ loff_t *ppos)
+{
+ struct rpc_info *info = file->private_data;
+ int ret;
+ u8 data[TEGRA_RPC_MAX_MSG_LEN];
+
+ if (max > TEGRA_RPC_MAX_MSG_LEN)
+ return -EINVAL;
+
+ ret = trpc_recv_msg(&rpc_node, info->rpc_ep, data,
+ TEGRA_RPC_MAX_MSG_LEN, 0);
+ if (ret == 0)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else if (ret > max)
+ return -ENOSPC;
+ else if (copy_to_user(buf, data, ret))
+ return -EFAULT;
+
+ return ret;
+}
+
+static const struct file_operations local_rpc_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = local_rpc_open,
+ .release = local_rpc_release,
+ .unlocked_ioctl = local_rpc_ioctl,
+ .write = local_rpc_write,
+ .read = local_rpc_read,
+};
+
+static struct miscdevice local_rpc_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tegra_rpc",
+ .fops = &local_rpc_misc_fops,
+};
+
+int __init rpc_local_init(void)
+{
+ int ret;
+
+ ret = trpc_sema_init();
+ if (ret) {
+ pr_err("%s: error in trpc_sema_init\n", __func__);
+ goto err_sema_init;
+ }
+
+ ret = misc_register(&local_rpc_misc_device);
+ if (ret) {
+ pr_err("%s: can't register misc device\n", __func__);
+ goto err_misc;
+ }
+
+ ret = trpc_node_register(&rpc_node);
+ if (ret) {
+ pr_err("%s: can't register rpc node\n", __func__);
+ goto err_node_reg;
+ }
+ return 0;
+
+err_node_reg:
+ misc_deregister(&local_rpc_misc_device);
+err_misc:
+err_sema_init:
+ return ret;
+}
+
+module_init(rpc_local_init);
diff --git a/drivers/media/video/tegra/avp/trpc_sema.c b/drivers/media/video/tegra/avp/trpc_sema.c
new file mode 100644
index 000000000000..b8772573d956
--- /dev/null
+++ b/drivers/media/video/tegra/avp/trpc_sema.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/err.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tegra_sema.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include "trpc_sema.h"
+
+struct trpc_sema {
+ wait_queue_head_t wq;
+ spinlock_t lock;
+ int count;
+};
+
+static int rpc_sema_minor = -1;
+
+static inline bool is_trpc_sema_file(struct file *file)
+{
+ dev_t rdev = file->f_dentry->d_inode->i_rdev;
+
+ if (MAJOR(rdev) == MISC_MAJOR && MINOR(rdev) == rpc_sema_minor)
+ return true;
+ return false;
+}
+
+struct file *trpc_sema_get_from_fd(int fd)
+{
+ struct file *file;
+
+ file = fget(fd);
+ if (unlikely(file == NULL)) {
+ pr_err("%s: fd %d is invalid\n", __func__, fd);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!is_trpc_sema_file(file)) {
+ pr_err("%s: fd (%d) is not a trpc_sema file\n", __func__, fd);
+ fput(file);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return file;
+}
+
+int trpc_sema_signal(struct file *file)
+{
+ struct trpc_sema *info = file->private_data;
+ unsigned long flags;
+
+ if (!info)
+ return -EINVAL;
+
+ spin_lock_irqsave(&info->lock, flags);
+ info->count++;
+ wake_up_interruptible_all(&info->wq);
+ spin_unlock_irqrestore(&info->lock, flags);
+ return 0;
+}
+
+static int trpc_sema_wait(struct trpc_sema *info, long *timeleft)
+{
+ unsigned long flags;
+ int ret = 0;
+ unsigned long endtime;
+ long timeout = *timeleft;
+
+ *timeleft = 0;
+ if (timeout < 0) {
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ } else if (timeout > 0) {
+ timeout = msecs_to_jiffies(timeout);
+ endtime = jiffies + timeout;
+ }
+
+again:
+ if (timeout)
+ ret = wait_event_interruptible_timeout(info->wq,
+ info->count > 0,
+ timeout);
+ spin_lock_irqsave(&info->lock, flags);
+ if (info->count > 0) {
+ info->count--;
+ ret = 0;
+ } else if (ret == 0 || timeout == 0) {
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ ret = -EINTR;
+ if (timeout != MAX_SCHEDULE_TIMEOUT &&
+ time_before(jiffies, endtime))
+ *timeleft = jiffies_to_msecs(endtime - jiffies);
+ else
+ *timeleft = 0;
+ } else {
+ /* we woke up but someone else got the semaphore and we have
+ * time left, try again */
+ timeout = ret;
+ spin_unlock_irqrestore(&info->lock, flags);
+ goto again;
+ }
+ spin_unlock_irqrestore(&info->lock, flags);
+ return ret;
+}
+
+static int trpc_sema_open(struct inode *inode, struct file *file)
+{
+ struct trpc_sema *info;
+
+ info = kzalloc(sizeof(struct trpc_sema), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ nonseekable_open(inode, file);
+ init_waitqueue_head(&info->wq);
+ spin_lock_init(&info->lock);
+ file->private_data = info;
+ return 0;
+}
+
+static int trpc_sema_release(struct inode *inode, struct file *file)
+{
+ struct trpc_sema *info = file->private_data;
+
+ file->private_data = NULL;
+ kfree(info);
+ return 0;
+}
+
+static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct trpc_sema *info = file->private_data;
+ int ret;
+ long timeout;
+
+ if (_IOC_TYPE(cmd) != TEGRA_SEMA_IOCTL_MAGIC ||
+ _IOC_NR(cmd) < TEGRA_SEMA_IOCTL_MIN_NR ||
+ _IOC_NR(cmd) > TEGRA_SEMA_IOCTL_MAX_NR)
+ return -ENOTTY;
+ else if (!info)
+ return -EINVAL;
+
+ switch (cmd) {
+ case TEGRA_SEMA_IOCTL_WAIT:
+ if (copy_from_user(&timeout, (void __user *)arg, sizeof(long)))
+ return -EFAULT;
+ ret = trpc_sema_wait(info, &timeout);
+ if (ret != -EINTR)
+ break;
+ if (copy_to_user((void __user *)arg, &timeout, sizeof(long)))
+ ret = -EFAULT;
+ break;
+ case TEGRA_SEMA_IOCTL_SIGNAL:
+ ret = trpc_sema_signal(file);
+ break;
+ default:
+ pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__,
+ _IOC_NR(cmd));
+ ret = -ENOTTY;
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations trpc_sema_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = trpc_sema_open,
+ .release = trpc_sema_release,
+ .unlocked_ioctl = trpc_sema_ioctl,
+};
+
+static struct miscdevice trpc_sema_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tegra_sema",
+ .fops = &trpc_sema_misc_fops,
+};
+
+int __init trpc_sema_init(void)
+{
+ int ret;
+
+ if (rpc_sema_minor >= 0) {
+ pr_err("%s: trpc_sema already registered\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = misc_register(&trpc_sema_misc_device);
+ if (ret) {
+ pr_err("%s: can't register misc device\n", __func__);
+ return ret;
+ }
+
+ rpc_sema_minor = trpc_sema_misc_device.minor;
+ pr_info("%s: registered misc dev %d:%d\n", __func__, MISC_MAJOR,
+ rpc_sema_minor);
+
+ return 0;
+}
diff --git a/drivers/media/video/tegra/avp/trpc_sema.h b/drivers/media/video/tegra/avp/trpc_sema.h
new file mode 100644
index 000000000000..566bbdbe739e
--- /dev/null
+++ b/drivers/media/video/tegra/avp/trpc_sema.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARM_MACH_TEGRA_RPC_SEMA_H
+#define __ARM_MACH_TEGRA_RPC_SEMA_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+struct file *trpc_sema_get_from_fd(int fd);
+int trpc_sema_signal(struct file *file);
+int __init trpc_sema_init(void);
+
+#endif