summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorS. Holmes <sholmes@nvidia.com>2011-03-07 16:08:18 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-04-14 21:23:10 -0700
commit0c7714c9efe4d017bc73ebc2b2c119bb8a342735 (patch)
tree88cc91c49794fa6397d17113f2b35b1402d0f3a2
parent8de40995f4ee35073c99c7f07b631450bc8824d6 (diff)
tegra_mediaserver: Adding kernel media driver
This kernel driver will replace the functionality that was previously in the NVRM daemon root process and allow multiple instances of OpenMAX to function and AVP resource tracking and cleanup. Bug 772210 Change-Id: Ia5eb559cde4644bae5fd8b23a1de57cd65f3fdd8 Reviewed-on: http://git-master/r/25115 Tested-by: Stephen Holmes <sholmes@nvidia.com> Reviewed-by: Stephen Holmes <sholmes@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--drivers/media/video/tegra/Kconfig1
-rw-r--r--drivers/media/video/tegra/Makefile1
-rw-r--r--drivers/media/video/tegra/avp/avp.c453
-rw-r--r--drivers/media/video/tegra/avp/avp_svc.c2
-rw-r--r--drivers/media/video/tegra/avp/nvavp.h53
-rw-r--r--drivers/media/video/tegra/avp/tegra_rpc.c6
-rw-r--r--drivers/media/video/tegra/avp/trpc_local.c224
-rw-r--r--drivers/media/video/tegra/avp/trpc_sema.c86
-rw-r--r--drivers/media/video/tegra/avp/trpc_sema.h6
-rw-r--r--drivers/media/video/tegra/mediaserver/Kconfig10
-rw-r--r--drivers/media/video/tegra/mediaserver/Makefile2
-rw-r--r--drivers/media/video/tegra/mediaserver/tegra_mediaserver.c554
-rw-r--r--include/linux/tegra_mediaserver.h112
13 files changed, 1218 insertions, 292 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig
index dcf41a9afedc..3d5467c26edf 100644
--- a/drivers/media/video/tegra/Kconfig
+++ b/drivers/media/video/tegra/Kconfig
@@ -1,4 +1,5 @@
source "drivers/media/video/tegra/avp/Kconfig"
+source "drivers/media/video/tegra/mediaserver/Kconfig"
config TEGRA_CAMERA
bool "Enable support for tegra camera/isp hardware"
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index ce0cb388f8b5..45f77e00b155 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -2,6 +2,7 @@
# Makefile for the video capture/playback device drivers.
#
obj-y += avp/
+obj-$(CONFIG_TEGRA_MEDIASERVER) += mediaserver/
obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o
obj-$(CONFIG_VIDEO_OV5650) += ov5650.o
obj-$(CONFIG_VIDEO_OV2710) += ov2710.o
diff --git a/drivers/media/video/tegra/avp/avp.c b/drivers/media/video/tegra/avp/avp.c
index 29cbfc01e44f..384fd5af3890 100644
--- a/drivers/media/video/tegra/avp/avp.c
+++ b/drivers/media/video/tegra/avp/avp.c
@@ -48,6 +48,7 @@
#include "avp_msg.h"
#include "trpc.h"
#include "avp.h"
+#include "nvavp.h"
enum {
AVP_DBG_TRACE_XPC = 1U << 0,
@@ -59,7 +60,7 @@ enum {
AVP_DBG_TRACE_LIB = 1U << 6,
};
-static u32 avp_debug_mask = 0;
+static u32 avp_debug_mask;
module_param_named(debug_mask, avp_debug_mask, uint, S_IWUSR | S_IRUGO);
#define DBG(flag, args...) \
@@ -69,7 +70,7 @@ module_param_named(debug_mask, avp_debug_mask, uint, S_IWUSR | S_IRUGO);
#define TEGRA_AVP_KERNEL_FW "nvrm_avp.bin"
-#define TEGRA_AVP_RESET_VECTOR_ADDR \
+#define TEGRA_AVP_RESET_VECTOR_ADDR \
(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x200)
#define TEGRA_AVP_RESUME_ADDR IO_ADDRESS(TEGRA_IRAM_BASE)
@@ -82,94 +83,94 @@ module_param_named(debug_mask, avp_debug_mask, uint, S_IWUSR | S_IRUGO);
#define MBOX_TO_AVP IO_ADDRESS(TEGRA_RES_SEMA_BASE + 0x20)
/* Layout of the mailbox registers:
- * bit 31 - pending message interrupt enable (mailbox full, i.e. valid=1)
- * bit 30 - message cleared interrupt enable (mailbox empty, i.e. valid=0)
- * bit 29 - message valid. peer clears this bit after reading msg
- * bits 27:0 - message data
+ * bit 31 - pending message interrupt enable (mailbox full, i.e. valid=1)
+ * bit 30 - message cleared interrupt enable (mailbox empty, i.e. valid=0)
+ * bit 29 - message valid. peer clears this bit after reading msg
+ * bits 27:0 - message data
*/
#define MBOX_MSG_PENDING_INT_EN (1 << 31)
#define MBOX_MSG_READ_INT_EN (1 << 30)
#define MBOX_MSG_VALID (1 << 29)
-#define AVP_MSG_MAX_CMD_LEN 16
+#define AVP_MSG_MAX_CMD_LEN 16
#define AVP_MSG_AREA_SIZE (AVP_MSG_MAX_CMD_LEN + TEGRA_RPC_MAX_MSG_LEN)
-struct avp_info {
- struct clk *cop_clk;
+struct tegra_avp_info {
+ struct clk *cop_clk;
- int mbox_from_avp_pend_irq;
+ int mbox_from_avp_pend_irq;
- dma_addr_t msg_area_addr;
- u32 msg;
- void *msg_to_avp;
- void *msg_from_avp;
- struct mutex to_avp_lock;
- struct mutex from_avp_lock;
+ dma_addr_t msg_area_addr;
+ u32 msg;
+ void *msg_to_avp;
+ void *msg_from_avp;
+ struct mutex to_avp_lock;
+ struct mutex from_avp_lock;
- struct work_struct recv_work;
- struct workqueue_struct *recv_wq;
+ struct work_struct recv_work;
+ struct workqueue_struct *recv_wq;
- struct trpc_node *rpc_node;
- struct miscdevice misc_dev;
- int refcount;
- struct mutex open_lock;
+ struct trpc_node *rpc_node;
+ struct miscdevice misc_dev;
+ int refcount;
+ struct mutex open_lock;
- spinlock_t state_lock;
- bool initialized;
- bool shutdown;
- bool suspending;
- bool defer_remote;
+ spinlock_t state_lock;
+ bool initialized;
+ bool shutdown;
+ bool suspending;
+ bool defer_remote;
- struct mutex libs_lock;
- struct list_head libs;
- struct nvmap_client *nvmap_libs;
+ struct mutex libs_lock;
+ struct list_head libs;
+ struct nvmap_client *nvmap_libs;
/* client for driver allocations, persistent */
- struct nvmap_client *nvmap_drv;
- struct nvmap_handle_ref *kernel_handle;
- void *kernel_data;
- unsigned long kernel_phys;
+ struct nvmap_client *nvmap_drv;
+ struct nvmap_handle_ref *kernel_handle;
+ void *kernel_data;
+ unsigned long kernel_phys;
- struct nvmap_handle_ref *iram_backup_handle;
- void *iram_backup_data;
- unsigned long iram_backup_phys;
- unsigned long resume_addr;
+ struct nvmap_handle_ref *iram_backup_handle;
+ void *iram_backup_data;
+ unsigned long iram_backup_phys;
+ unsigned long resume_addr;
- struct trpc_endpoint *avp_ep;
- struct rb_root endpoints;
+ struct trpc_endpoint *avp_ep;
+ struct rb_root endpoints;
- struct avp_svc_info *avp_svc;
+ struct avp_svc_info *avp_svc;
};
struct remote_info {
- u32 loc_id;
- u32 rem_id;
- struct kref ref;
+ u32 loc_id;
+ u32 rem_id;
+ struct kref ref;
- struct trpc_endpoint *trpc_ep;
- struct rb_node rb_node;
+ struct trpc_endpoint *trpc_ep;
+ struct rb_node rb_node;
};
struct lib_item {
- struct list_head list;
- u32 handle;
- char name[TEGRA_AVP_LIB_MAX_NAME];
+ struct list_head list;
+ u32 handle;
+ char name[TEGRA_AVP_LIB_MAX_NAME];
};
-static struct avp_info *tegra_avp;
+static struct tegra_avp_info *tegra_avp;
static int avp_trpc_send(struct trpc_endpoint *ep, void *buf, size_t len);
static void avp_trpc_close(struct trpc_endpoint *ep);
static void avp_trpc_show(struct seq_file *s, struct trpc_endpoint *ep);
-static void libs_cleanup(struct avp_info *avp);
+static void libs_cleanup(struct tegra_avp_info *avp);
static struct trpc_ep_ops remote_ep_ops = {
- .send = avp_trpc_send,
- .close = avp_trpc_close,
- .show = avp_trpc_show,
+ .send = avp_trpc_send,
+ .close = avp_trpc_close,
+ .show = avp_trpc_show,
};
-static struct remote_info *rinfo_alloc(struct avp_info *avp)
+static struct remote_info *rinfo_alloc(struct tegra_avp_info *avp)
{
struct remote_info *rinfo;
@@ -196,7 +197,7 @@ static inline void rinfo_put(struct remote_info *rinfo)
kref_put(&rinfo->ref, _rinfo_release);
}
-static int remote_insert(struct avp_info *avp, struct remote_info *rinfo)
+static int remote_insert(struct tegra_avp_info *avp, struct remote_info *rinfo)
{
struct rb_node **p;
struct rb_node *parent;
@@ -225,7 +226,7 @@ static int remote_insert(struct avp_info *avp, struct remote_info *rinfo)
return 0;
}
-static struct remote_info *remote_find(struct avp_info *avp, u32 local_id)
+static struct remote_info *remote_find(struct tegra_avp_info *avp, u32 local_id)
{
struct rb_node *n = avp->endpoints.rb_node;
struct remote_info *rinfo;
@@ -243,7 +244,7 @@ static struct remote_info *remote_find(struct avp_info *avp, u32 local_id)
return NULL;
}
-static void remote_remove(struct avp_info *avp, struct remote_info *rinfo)
+static void remote_remove(struct tegra_avp_info *avp, struct remote_info *rinfo)
{
rb_erase(&rinfo->rb_node, &avp->endpoints);
rinfo_put(rinfo);
@@ -251,8 +252,8 @@ static void remote_remove(struct avp_info *avp, struct remote_info *rinfo)
/* test whether or not the trpc endpoint provided is a valid AVP node
* endpoint */
-static struct remote_info *validate_trpc_ep(struct avp_info *avp,
- struct trpc_endpoint *ep)
+static struct remote_info *validate_trpc_ep(struct tegra_avp_info *avp,
+ struct trpc_endpoint *ep)
{
struct remote_info *tmp = trpc_priv(ep);
struct remote_info *rinfo;
@@ -267,7 +268,7 @@ static struct remote_info *validate_trpc_ep(struct avp_info *avp,
static void avp_trpc_show(struct seq_file *s, struct trpc_endpoint *ep)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
struct remote_info *rinfo;
unsigned long flags;
@@ -277,7 +278,7 @@ static void avp_trpc_show(struct seq_file *s, struct trpc_endpoint *ep)
seq_printf(s, " <unknown>\n");
goto out;
}
- seq_printf(s, " loc_id:0x%x\n rem_id:0x%x\n",
+ seq_printf(s, " loc_id:0x%x\n rem_id:0x%x\n",
rinfo->loc_id, rinfo->rem_id);
out:
spin_unlock_irqrestore(&avp->state_lock, flags);
@@ -293,7 +294,7 @@ static inline u32 mbox_readl(void __iomem *mbox)
return readl(mbox);
}
-static inline void msg_ack_remote(struct avp_info *avp, u32 cmd, u32 arg)
+static inline void msg_ack_remote(struct tegra_avp_info *avp, u32 cmd, u32 arg)
{
struct msg_ack *ack = avp->msg_from_avp;
@@ -304,15 +305,15 @@ static inline void msg_ack_remote(struct avp_info *avp, u32 cmd, u32 arg)
wmb();
}
-static inline u32 msg_recv_get_cmd(struct avp_info *avp)
+static inline u32 msg_recv_get_cmd(struct tegra_avp_info *avp)
{
volatile u32 *cmd = avp->msg_from_avp;
rmb();
return *cmd;
}
-static inline int __msg_write(struct avp_info *avp, void *hdr, size_t hdr_len,
- void *buf, size_t len)
+static inline int __msg_write(struct tegra_avp_info *avp, void *hdr,
+ size_t hdr_len, void *buf, size_t len)
{
memcpy(avp->msg_to_avp, hdr, hdr_len);
if (buf && len)
@@ -321,8 +322,8 @@ static inline int __msg_write(struct avp_info *avp, void *hdr, size_t hdr_len,
return 0;
}
-static inline int msg_write(struct avp_info *avp, void *hdr, size_t hdr_len,
- void *buf, size_t len)
+static inline int msg_write(struct tegra_avp_info *avp, void *hdr,
+ size_t hdr_len, void *buf, size_t len)
{
/* rem_ack is a pointer into shared memory that the AVP modifies */
volatile u32 *rem_ack = avp->msg_to_avp;
@@ -341,7 +342,7 @@ static inline int msg_write(struct avp_info *avp, void *hdr, size_t hdr_len,
return 0;
}
-static inline int msg_check_ack(struct avp_info *avp, u32 cmd, u32 *arg)
+static inline int msg_check_ack(struct tegra_avp_info *avp, u32 cmd, u32 *arg)
{
struct msg_ack ack;
@@ -355,7 +356,7 @@ static inline int msg_check_ack(struct avp_info *avp, u32 cmd, u32 *arg)
}
/* XXX: add timeout */
-static int msg_wait_ack_locked(struct avp_info *avp, u32 cmd, u32 *arg)
+static int msg_wait_ack_locked(struct tegra_avp_info *avp, u32 cmd, u32 *arg)
{
/* rem_ack is a pointer into shared memory that the AVP modifies */
volatile u32 *rem_ack = avp->msg_to_avp;
@@ -379,14 +380,14 @@ static int msg_wait_ack_locked(struct avp_info *avp, u32 cmd, u32 *arg)
static int avp_trpc_send(struct trpc_endpoint *ep, void *buf, size_t len)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
struct remote_info *rinfo;
struct msg_port_data msg;
int ret;
unsigned long flags;
DBG(AVP_DBG_TRACE_TRPC_MSG, "%s: ep=%p priv=%p buf=%p len=%d\n",
- __func__, ep, trpc_priv(ep), buf, len);
+ __func__, ep, trpc_priv(ep), buf, len);
spin_lock_irqsave(&avp->state_lock, flags);
if (unlikely(avp->suspending && trpc_peer(ep) != avp->avp_ep)) {
@@ -413,7 +414,7 @@ static int avp_trpc_send(struct trpc_endpoint *ep, void *buf, size_t len)
mutex_unlock(&avp->to_avp_lock);
DBG(AVP_DBG_TRACE_TRPC_MSG, "%s: msg sent for %s (%x->%x) (%d)\n",
- __func__, trpc_name(ep), rinfo->loc_id, rinfo->rem_id, ret);
+ __func__, trpc_name(ep), rinfo->loc_id, rinfo->rem_id, ret);
rinfo_put(rinfo);
return ret;
@@ -422,7 +423,7 @@ err_state_locked:
return ret;
}
-static int _send_disconnect(struct avp_info *avp, u32 port_id)
+static int _send_disconnect(struct tegra_avp_info *avp, u32 port_id)
{
struct msg_disconnect msg;
int ret;
@@ -434,19 +435,19 @@ static int _send_disconnect(struct avp_info *avp, u32 port_id)
ret = msg_write(avp, &msg, sizeof(msg), NULL, 0);
if (ret) {
pr_err("%s: remote has not acked last message (%x)\n", __func__,
- port_id);
+ port_id);
goto err_msg_write;
}
ret = msg_wait_ack_locked(avp, CMD_ACK, NULL);
if (ret) {
pr_err("%s: remote end won't respond for %x\n", __func__,
- port_id);
+ port_id);
goto err_wait_ack;
}
DBG(AVP_DBG_TRACE_XPC_CONN, "%s: sent disconnect msg for %x\n",
- __func__, port_id);
+ __func__, port_id);
err_wait_ack:
err_msg_write:
@@ -471,7 +472,7 @@ static inline void remote_close(struct remote_info *rinfo)
static void avp_trpc_close(struct trpc_endpoint *ep)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
struct remote_info *rinfo;
unsigned long flags;
int ret;
@@ -485,7 +486,7 @@ static void avp_trpc_close(struct trpc_endpoint *ep)
rinfo = validate_trpc_ep(avp, ep);
if (!rinfo) {
pr_err("%s: tried to close invalid port '%s' endpoint (%p)\n",
- __func__, trpc_name(ep), ep);
+ __func__, trpc_name(ep), ep);
spin_unlock_irqrestore(&avp->state_lock, flags);
return;
}
@@ -494,18 +495,18 @@ static void avp_trpc_close(struct trpc_endpoint *ep)
spin_unlock_irqrestore(&avp->state_lock, flags);
DBG(AVP_DBG_TRACE_TRPC_CONN, "%s: closing '%s' (%x)\n", __func__,
- trpc_name(ep), rinfo->rem_id);
+ trpc_name(ep), rinfo->rem_id);
ret = _send_disconnect(avp, rinfo->rem_id);
if (ret)
pr_err("%s: error while closing remote port '%s' (%x)\n",
- __func__, trpc_name(ep), rinfo->rem_id);
+ __func__, trpc_name(ep), rinfo->rem_id);
remote_close(rinfo);
rinfo_put(rinfo);
}
/* takes and holds avp->from_avp_lock */
-static void recv_msg_lock(struct avp_info *avp)
+static void recv_msg_lock(struct tegra_avp_info *avp)
{
unsigned long flags;
@@ -516,7 +517,7 @@ static void recv_msg_lock(struct avp_info *avp)
}
/* MUST be called with avp->from_avp_lock held */
-static void recv_msg_unlock(struct avp_info *avp)
+static void recv_msg_unlock(struct tegra_avp_info *avp)
{
unsigned long flags;
@@ -530,7 +531,7 @@ static int avp_node_try_connect(struct trpc_node *node,
struct trpc_node *src_node,
struct trpc_endpoint *from)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
const char *port_name = trpc_name(from);
struct remote_info *rinfo;
struct msg_connect msg;
@@ -539,7 +540,7 @@ static int avp_node_try_connect(struct trpc_node *node,
int len;
DBG(AVP_DBG_TRACE_TRPC_CONN, "%s: trying connect from %s\n", __func__,
- port_name);
+ port_name);
if (node != avp->rpc_node || node->priv != avp)
return -ENODEV;
@@ -595,7 +596,7 @@ static int avp_node_try_connect(struct trpc_node *node,
ret = msg_write(avp, &msg, sizeof(msg), NULL, 0);
if (ret) {
pr_err("%s: remote has not acked last message (%s)\n", __func__,
- port_name);
+ port_name);
mutex_unlock(&avp->to_avp_lock);
goto err_msg_write;
}
@@ -604,7 +605,7 @@ static int avp_node_try_connect(struct trpc_node *node,
if (ret) {
pr_err("%s: remote end won't respond for '%s'\n", __func__,
- port_name);
+ port_name);
goto err_wait_ack;
}
if (!rinfo->rem_id) {
@@ -614,10 +615,10 @@ static int avp_node_try_connect(struct trpc_node *node,
}
DBG(AVP_DBG_TRACE_TRPC_CONN, "%s: got conn ack '%s' (%x <-> %x)\n",
- __func__, port_name, rinfo->loc_id, rinfo->rem_id);
+ __func__, port_name, rinfo->loc_id, rinfo->rem_id);
rinfo->trpc_ep = trpc_create_peer(node, from, &remote_ep_ops,
- rinfo);
+ rinfo);
if (!rinfo->trpc_ep) {
pr_err("%s: cannot create peer for %s\n", __func__, port_name);
ret = -EINVAL;
@@ -646,19 +647,19 @@ err_alloc_rinfo:
return ret;
}
-static void process_disconnect_locked(struct avp_info *avp,
- struct msg_data *raw_msg)
+static void process_disconnect_locked(struct tegra_avp_info *avp,
+ struct msg_data *raw_msg)
{
struct msg_disconnect *disconn_msg = (struct msg_disconnect *)raw_msg;
unsigned long flags;
struct remote_info *rinfo;
DBG(AVP_DBG_TRACE_XPC_CONN, "%s: got disconnect (%x)\n", __func__,
- disconn_msg->port_id);
+ disconn_msg->port_id);
if (avp_debug_mask & AVP_DBG_TRACE_XPC_MSG)
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, disconn_msg,
- sizeof(struct msg_disconnect));
+ sizeof(struct msg_disconnect));
spin_lock_irqsave(&avp->state_lock, flags);
rinfo = remote_find(avp, disconn_msg->port_id);
@@ -678,7 +679,7 @@ ack:
msg_ack_remote(avp, CMD_ACK, 0);
}
-static void process_connect_locked(struct avp_info *avp,
+static void process_connect_locked(struct tegra_avp_info *avp,
struct msg_data *raw_msg)
{
struct msg_connect *conn_msg = (struct msg_connect *)raw_msg;
@@ -690,10 +691,10 @@ static void process_connect_locked(struct avp_info *avp,
unsigned long flags;
DBG(AVP_DBG_TRACE_XPC_CONN, "%s: got connect (%x)\n", __func__,
- conn_msg->port_id);
+ conn_msg->port_id);
if (avp_debug_mask & AVP_DBG_TRACE_XPC_MSG)
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
- conn_msg, sizeof(struct msg_connect));
+ conn_msg, sizeof(struct msg_connect));
rinfo = rinfo_alloc(avp);
if (!rinfo) {
@@ -707,10 +708,10 @@ static void process_connect_locked(struct avp_info *avp,
memcpy(name, conn_msg->name, XPC_PORT_NAME_LEN);
name[XPC_PORT_NAME_LEN] = '\0';
trpc_ep = trpc_create_connect(avp->rpc_node, name, &remote_ep_ops,
- rinfo, 0);
+ rinfo, 0);
if (IS_ERR(trpc_ep)) {
pr_err("%s: remote requested unknown port '%s' (%d)\n",
- __func__, name, (int)PTR_ERR(trpc_ep));
+ __func__, name, (int)PTR_ERR(trpc_ep));
goto nack;
}
rinfo->trpc_ep = trpc_ep;
@@ -733,8 +734,8 @@ ack:
msg_ack_remote(avp, CMD_RESPONSE, local_port_id);
}
-static int process_message(struct avp_info *avp, struct msg_data *raw_msg,
- gfp_t gfp_flags)
+static int process_message(struct tegra_avp_info *avp, struct msg_data *raw_msg,
+ gfp_t gfp_flags)
{
struct msg_port_data *port_msg = (struct msg_port_data *)raw_msg;
struct remote_info *rinfo;
@@ -748,12 +749,12 @@ static int process_message(struct avp_info *avp, struct msg_data *raw_msg,
pr_info("%s: got message cmd=%x port=%x len=%d\n", __func__,
port_msg->cmd, port_msg->port_id, port_msg->msg_len);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, port_msg,
- sizeof(struct msg_port_data) + len);
+ sizeof(struct msg_port_data) + len);
}
if (len != port_msg->msg_len)
pr_err("%s: message sent is too long (%d bytes)\n", __func__,
- port_msg->msg_len);
+ port_msg->msg_len);
spin_lock_irqsave(&avp->state_lock, flags);
rinfo = remote_find(avp, port_msg->port_id);
@@ -776,8 +777,8 @@ static int process_message(struct avp_info *avp, struct msg_data *raw_msg,
goto no_ack;
} else if (ret) {
pr_err("%s: cannot queue message for port %s/%x (%d)\n",
- __func__, trpc_name(rinfo->trpc_ep), rinfo->loc_id,
- ret);
+ __func__, trpc_name(rinfo->trpc_ep), rinfo->loc_id,
+ ret);
} else {
DBG(AVP_DBG_TRACE_XPC_MSG, "%s: msg queued\n", __func__);
}
@@ -792,7 +793,8 @@ no_ack:
static void process_avp_message(struct work_struct *work)
{
- struct avp_info *avp = container_of(work, struct avp_info, recv_work);
+ struct tegra_avp_info *avp = container_of(work, struct tegra_avp_info,
+ recv_work);
struct msg_data *msg = avp->msg_from_avp;
mutex_lock(&avp->from_avp_lock);
@@ -816,7 +818,7 @@ static void process_avp_message(struct work_struct *work)
static irqreturn_t avp_mbox_pending_isr(int irq, void *data)
{
- struct avp_info *avp = data;
+ struct tegra_avp_info *avp = data;
struct msg_data *msg = avp->msg_from_avp;
u32 mbox_msg;
unsigned long flags;
@@ -862,7 +864,7 @@ done:
return IRQ_HANDLED;
}
-static int avp_reset(struct avp_info *avp, unsigned long reset_addr)
+static int avp_reset(struct tegra_avp_info *avp, unsigned long reset_addr)
{
unsigned long stub_code_phys = virt_to_phys(_tegra_avp_boot_stub);
dma_addr_t stub_data_phys;
@@ -903,7 +905,7 @@ static int avp_reset(struct avp_info *avp, unsigned long reset_addr)
return ret;
}
-static void avp_halt(struct avp_info *avp)
+static void avp_halt(struct tegra_avp_info *avp)
{
/* ensure the AVP is halted */
writel(FLOW_MODE_STOP, FLOW_CTRL_HALT_COP_EVENTS);
@@ -922,7 +924,7 @@ static void avp_halt(struct avp_info *avp)
* of the char dev for receiving replies for managing remote
* libraries/modules. */
-static int avp_init(struct avp_info *avp, const char *fw_file)
+static int avp_init(struct tegra_avp_info *avp, const char *fw_file)
{
const struct firmware *avp_fw;
int ret;
@@ -967,7 +969,7 @@ static int avp_init(struct avp_info *avp, const char *fw_file)
goto err_avp_svc_start;
ep = trpc_create_connect(avp->rpc_node, "RPC_AVP_PORT", NULL,
- NULL, -1);
+ NULL, -1);
if (IS_ERR(ep)) {
pr_err("%s: can't connect to RPC_AVP_PORT server\n", __func__);
ret = PTR_ERR(ep);
@@ -993,7 +995,7 @@ err_nvmap_create_libs_client:
return ret;
}
-static void avp_uninit(struct avp_info *avp)
+static void avp_uninit(struct tegra_avp_info *avp)
{
unsigned long flags;
struct rb_node *n;
@@ -1038,7 +1040,8 @@ static void avp_uninit(struct avp_info *avp)
}
/* returns the remote lib handle in lib->handle */
-static int _load_lib(struct avp_info *avp, struct tegra_avp_lib *lib)
+static int _load_lib(struct tegra_avp_info *avp, struct tegra_avp_lib *lib,
+ bool from_user)
{
struct svc_lib_attach svc;
struct svc_lib_attach_resp resp;
@@ -1057,7 +1060,10 @@ static int _load_lib(struct avp_info *avp, struct tegra_avp_lib *lib)
lib->args_len);
return -ENOMEM;
}
- if (copy_from_user(args, lib->args, lib->args_len)) {
+
+ if (!from_user)
+ memcpy(args, lib->args, lib->args_len);
+ else if (copy_from_user(args, lib->args, lib->args_len)) {
pr_err("avp_lib: can't copy lib args\n");
ret = -EFAULT;
goto err_cp_args;
@@ -1115,15 +1121,18 @@ static int _load_lib(struct avp_info *avp, struct tegra_avp_lib *lib)
goto err_recv_msg;
} else if (resp.err) {
pr_err("avp_lib: got remote error (%d) while loading lib %s\n",
- resp.err, lib->name);
+ resp.err, lib->name);
ret = -EPROTO;
goto err_recv_msg;
}
lib->handle = resp.lib_id;
ret = 0;
DBG(AVP_DBG_TRACE_LIB,
- "avp_lib: Successfully loaded library %s (lib_id=%x)\n",
- lib->name, resp.lib_id);
+ "avp_lib: Successfully loaded library %s (lib_id=%x)\n",
+ lib->name, resp.lib_id);
+
+ pr_info("avp_lib: Successfully loaded library %s (lib_id=%x)\n",
+ lib->name, resp.lib_id);
/* We free the memory here because by this point the AVP has already
* requested memory for the library for all the sections since it does
@@ -1146,8 +1155,8 @@ err_cp_args:
return ret;
}
-static int send_unload_lib_msg(struct avp_info *avp, u32 handle,
- const char *name)
+static int send_unload_lib_msg(struct tegra_avp_info *avp, u32 handle,
+ const char *name)
{
struct svc_lib_detach svc;
struct svc_lib_detach_resp resp;
@@ -1161,26 +1170,33 @@ static int send_unload_lib_msg(struct avp_info *avp, u32 handle,
GFP_KERNEL);
if (ret) {
pr_err("avp_lib: can't send unload message to avp for '%s'\n",
- name);
+ name);
goto err;
}
+ /* Give it a few extra moments to unload. */
+ msleep(20);
+
ret = trpc_recv_msg(avp->rpc_node, avp->avp_ep, &resp,
sizeof(resp), -1);
if (ret != sizeof(resp)) {
pr_err("avp_lib: Couldn't get unload reply for '%s' (%d)\n",
- name, ret);
+ name, ret);
} else if (resp.err) {
pr_err("avp_lib: remote error (%d) while unloading lib %s\n",
- resp.err, name);
+ resp.err, name);
ret = -EPROTO;
- } else
+ } else {
+ pr_info("avp_lib: Successfully unloaded '%s'\n",
+ name);
ret = 0;
+ }
+
err:
return ret;
}
-static struct lib_item *_find_lib_locked(struct avp_info *avp, u32 handle)
+static struct lib_item *_find_lib_locked(struct tegra_avp_info *avp, u32 handle)
{
struct lib_item *item;
@@ -1191,7 +1207,8 @@ static struct lib_item *_find_lib_locked(struct avp_info *avp, u32 handle)
return NULL;
}
-static int _insert_lib_locked(struct avp_info *avp, u32 handle, char *name)
+static int _insert_lib_locked(struct tegra_avp_info *avp, u32 handle,
+ char *name)
{
struct lib_item *item;
@@ -1204,13 +1221,14 @@ static int _insert_lib_locked(struct avp_info *avp, u32 handle, char *name)
return 0;
}
-static void _delete_lib_locked(struct avp_info *avp, struct lib_item *item)
+static void _delete_lib_locked(struct tegra_avp_info *avp,
+ struct lib_item *item)
{
list_del(&item->list);
kfree(item);
}
-static int handle_load_lib_ioctl(struct avp_info *avp, unsigned long arg)
+static int handle_load_lib_ioctl(struct tegra_avp_info *avp, unsigned long arg)
{
struct tegra_avp_lib lib;
int ret;
@@ -1226,7 +1244,7 @@ static int handle_load_lib_ioctl(struct avp_info *avp, unsigned long arg)
}
mutex_lock(&avp->libs_lock);
- ret = _load_lib(avp, &lib);
+ ret = _load_lib(avp, &lib, true);
if (ret)
goto err_load_lib;
@@ -1253,33 +1271,7 @@ err_load_lib:
return ret;
}
-static int handle_unload_lib_ioctl(struct avp_info *avp, unsigned long arg)
-{
- struct lib_item *item;
- int ret;
-
- mutex_lock(&avp->libs_lock);
- item = _find_lib_locked(avp, (u32)arg);
- if (!item) {
- pr_err("avp_lib: avp lib with handle 0x%x not found\n",
- (u32)arg);
- ret = -ENOENT;
- goto err_find;
- }
- ret = send_unload_lib_msg(avp, item->handle, item->name);
- if (!ret)
- DBG(AVP_DBG_TRACE_LIB, "avp_lib: unloaded '%s'\n", item->name);
- else
- pr_err("avp_lib: can't unload lib '%s'/0x%x (%d)\n", item->name,
- item->handle, ret);
- _delete_lib_locked(avp, item);
-
-err_find:
- mutex_unlock(&avp->libs_lock);
- return ret;
-}
-
-static void libs_cleanup(struct avp_info *avp)
+static void libs_cleanup(struct tegra_avp_info *avp)
{
struct lib_item *lib;
struct lib_item *lib_tmp;
@@ -1295,14 +1287,14 @@ static void libs_cleanup(struct avp_info *avp)
}
static long tegra_avp_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
int ret;
if (_IOC_TYPE(cmd) != TEGRA_AVP_IOCTL_MAGIC ||
- _IOC_NR(cmd) < TEGRA_AVP_IOCTL_MIN_NR ||
- _IOC_NR(cmd) > TEGRA_AVP_IOCTL_MAX_NR)
+ _IOC_NR(cmd) < TEGRA_AVP_IOCTL_MIN_NR ||
+ _IOC_NR(cmd) > TEGRA_AVP_IOCTL_MAX_NR)
return -ENOTTY;
switch (cmd) {
@@ -1310,7 +1302,7 @@ static long tegra_avp_ioctl(struct file *file, unsigned int cmd,
ret = handle_load_lib_ioctl(avp, arg);
break;
case TEGRA_AVP_IOCTL_UNLOAD_LIB:
- ret = handle_unload_lib_ioctl(avp, arg);
+ ret = tegra_avp_unload_lib(avp, arg);
break;
default:
pr_err("avp_lib: Unknown tegra_avp ioctl 0x%x\n", _IOC_NR(cmd));
@@ -1320,31 +1312,41 @@ static long tegra_avp_ioctl(struct file *file, unsigned int cmd,
return ret;
}
-static int tegra_avp_open(struct inode *inode, struct file *file)
+int tegra_avp_open(struct tegra_avp_info **avp)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *new_avp = tegra_avp;
int ret = 0;
- nonseekable_open(inode, file);
+ mutex_lock(&new_avp->open_lock);
- mutex_lock(&avp->open_lock);
+ if (!new_avp->refcount)
+ ret = avp_init(new_avp, TEGRA_AVP_KERNEL_FW);
- if (!avp->refcount)
- ret = avp_init(avp, TEGRA_AVP_KERNEL_FW);
+ if (ret < 0) {
+ new_avp = 0;
+ goto out;
+ }
- if (!ret)
- avp->refcount++;
+ new_avp->refcount++;
- mutex_unlock(&avp->open_lock);
+out:
+ mutex_unlock(&new_avp->open_lock);
+ *avp = new_avp;
return ret;
}
-static int tegra_avp_release(struct inode *inode, struct file *file)
+static int tegra_avp_open_fops(struct inode *inode, struct file *file)
+{
+ struct tegra_avp_info *avp;
+
+ nonseekable_open(inode, file);
+ return tegra_avp_open(&avp);
+}
+
+int tegra_avp_release(struct tegra_avp_info *avp)
{
- struct avp_info *avp = tegra_avp;
int ret = 0;
- pr_info("%s: release\n", __func__);
mutex_lock(&avp->open_lock);
if (!avp->refcount) {
pr_err("%s: releasing while in invalid state\n", __func__);
@@ -1361,7 +1363,13 @@ out:
return ret;
}
-static int avp_enter_lp0(struct avp_info *avp)
+static int tegra_avp_release_fops(struct inode *inode, struct file *file)
+{
+ struct tegra_avp_info *avp = tegra_avp;
+ return tegra_avp_release(avp);
+}
+
+static int avp_enter_lp0(struct tegra_avp_info *avp)
{
volatile u32 *avp_suspend_done =
avp->iram_backup_data + TEGRA_IRAM_SIZE;
@@ -1406,7 +1414,7 @@ err:
static int tegra_avp_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
unsigned long flags;
int ret;
@@ -1448,7 +1456,7 @@ err:
static int tegra_avp_resume(struct platform_device *pdev)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
int ret = 0;
pr_info("%s()+\n", __func__);
@@ -1472,9 +1480,9 @@ out:
static const struct file_operations tegra_avp_fops = {
.owner = THIS_MODULE,
- .open = tegra_avp_open,
- .release = tegra_avp_release,
- .unlocked_ioctl = tegra_avp_ioctl,
+ .open = tegra_avp_open_fops,
+ .release = tegra_avp_release_fops,
+ .unlocked_ioctl = tegra_avp_ioctl,
};
static struct trpc_node avp_trpc_node = {
@@ -1486,7 +1494,7 @@ static struct trpc_node avp_trpc_node = {
static int tegra_avp_probe(struct platform_device *pdev)
{
void *msg_area;
- struct avp_info *avp;
+ struct tegra_avp_info *avp;
int ret = 0;
int irq;
@@ -1496,9 +1504,9 @@ static int tegra_avp_probe(struct platform_device *pdev)
return -EINVAL;
}
- avp = kzalloc(sizeof(struct avp_info), GFP_KERNEL);
+ avp = kzalloc(sizeof(struct tegra_avp_info), GFP_KERNEL);
if (!avp) {
- pr_err("%s: cannot allocate avp_info\n", __func__);
+ pr_err("%s: cannot allocate tegra_avp_info\n", __func__);
return -ENOMEM;
}
@@ -1536,7 +1544,7 @@ static int tegra_avp_probe(struct platform_device *pdev)
*/
avp->iram_backup_handle =
nvmap_alloc(avp->nvmap_drv, TEGRA_IRAM_SIZE + 4,
- L1_CACHE_BYTES, NVMAP_HANDLE_WRITE_COMBINE);
+ L1_CACHE_BYTES, NVMAP_HANDLE_WRITE_COMBINE);
if (IS_ERR(avp->iram_backup_handle)) {
pr_err("%s: cannot create handle for iram backup\n", __func__);
ret = PTR_ERR(avp->iram_backup_handle);
@@ -1568,7 +1576,7 @@ static int tegra_avp_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&avp->libs);
avp->recv_wq = alloc_workqueue("avp-msg-recv",
- WQ_NON_REENTRANT | WQ_HIGHPRI, 1);
+ WQ_NON_REENTRANT | WQ_HIGHPRI, 1);
if (!avp->recv_wq) {
pr_err("%s: can't create recve workqueue\n", __func__);
ret = -ENOMEM;
@@ -1583,7 +1591,7 @@ static int tegra_avp_probe(struct platform_device *pdev)
}
msg_area = dma_alloc_coherent(&pdev->dev, AVP_MSG_AREA_SIZE * 2,
- &avp->msg_area_addr, GFP_KERNEL);
+ &avp->msg_area_addr, GFP_KERNEL);
if (!msg_area) {
pr_err("%s: cannot allocate msg_area\n", __func__);
ret = -ENOMEM;
@@ -1673,17 +1681,17 @@ err_nvmap_create_drv_client:
static int tegra_avp_remove(struct platform_device *pdev)
{
- struct avp_info *avp = tegra_avp;
+ struct tegra_avp_info *avp = tegra_avp;
if (!avp)
return 0;
mutex_lock(&avp->open_lock);
+ /* ensure that noone can open while we tear down */
if (avp->refcount) {
mutex_unlock(&avp->open_lock);
return -EBUSY;
}
- /* ensure that noone can open while we tear down */
mutex_unlock(&avp->open_lock);
misc_deregister(&avp->misc_dev);
@@ -1708,12 +1716,85 @@ static int tegra_avp_remove(struct platform_device *pdev)
return 0;
}
+int tegra_avp_load_lib(struct tegra_avp_info *avp, struct tegra_avp_lib *lib)
+{
+ int ret;
+
+ if (!avp)
+ return -ENODEV;
+
+ if (!lib)
+ return -EFAULT;
+
+ lib->name[TEGRA_AVP_LIB_MAX_NAME - 1] = '\0';
+
+ if (lib->args_len > TEGRA_AVP_LIB_MAX_ARGS) {
+ pr_err("%s: library args too long (%d)\n", __func__,
+ lib->args_len);
+ return -E2BIG;
+ }
+
+ mutex_lock(&avp->libs_lock);
+ ret = _load_lib(avp, lib, false);
+ if (ret)
+ goto err_load_lib;
+
+ ret = _insert_lib_locked(avp, lib->handle, lib->name);
+ if (ret) {
+ pr_err("%s: can't insert lib (%d)\n", __func__, ret);
+ goto err_insert_lib;
+ }
+
+ mutex_unlock(&avp->libs_lock);
+ return 0;
+
+err_insert_lib:
+ ret = send_unload_lib_msg(avp, lib->handle, lib->name);
+ if (!ret)
+ DBG(AVP_DBG_TRACE_LIB, "avp_lib: unloaded '%s'\n", lib->name);
+ else
+ pr_err("avp_lib: can't unload lib '%s' (%d)\n", lib->name, ret);
+ lib->handle = 0;
+err_load_lib:
+ mutex_unlock(&avp->libs_lock);
+ return ret;
+}
+
+int tegra_avp_unload_lib(struct tegra_avp_info *avp, unsigned long handle)
+{
+ struct lib_item *item;
+ int ret;
+
+ if (!avp)
+ return -ENODEV;
+
+ mutex_lock(&avp->libs_lock);
+ item = _find_lib_locked(avp, handle);
+ if (!item) {
+ pr_err("avp_lib: avp lib with handle 0x%x not found\n",
+ (u32)handle);
+ ret = -ENOENT;
+ goto err_find;
+ }
+ ret = send_unload_lib_msg(avp, item->handle, item->name);
+ if (!ret)
+ DBG(AVP_DBG_TRACE_LIB, "avp_lib: unloaded '%s'\n", item->name);
+ else
+ pr_err("avp_lib: can't unload lib '%s'/0x%x (%d)\n", item->name,
+ item->handle, ret);
+ _delete_lib_locked(avp, item);
+
+err_find:
+ mutex_unlock(&avp->libs_lock);
+ return ret;
+}
+
static struct platform_driver tegra_avp_driver = {
.probe = tegra_avp_probe,
.remove = tegra_avp_remove,
.suspend = tegra_avp_suspend,
.resume = tegra_avp_resume,
- .driver = {
+ .driver = {
.name = TEGRA_AVP_NAME,
.owner = THIS_MODULE,
},
diff --git a/drivers/media/video/tegra/avp/avp_svc.c b/drivers/media/video/tegra/avp/avp_svc.c
index 2eed2891e556..208ff8c4fdc2 100644
--- a/drivers/media/video/tegra/avp/avp_svc.c
+++ b/drivers/media/video/tegra/avp/avp_svc.c
@@ -38,7 +38,7 @@ enum {
AVP_DBG_TRACE_SVC = 1U << 0,
};
-static u32 debug_mask = 0;
+static u32 debug_mask;
module_param_named(debug_mask, debug_mask, uint, S_IWUSR | S_IRUGO);
#define DBG(flag, args...) \
diff --git a/drivers/media/video/tegra/avp/nvavp.h b/drivers/media/video/tegra/avp/nvavp.h
new file mode 100644
index 000000000000..dbc62b485882
--- /dev/null
+++ b/drivers/media/video/tegra/avp/nvavp.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Nvidia Corp
+ *
+ * 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 __MEDIA_VIDEO_TEGRA_NVAVP_H
+#define __MEDIA_VIDEO_TEGRA_NVAVP_H
+
+#include <linux/tegra_avp.h>
+
+struct tegra_avp_info;
+
+int tegra_avp_open(struct tegra_avp_info **avp);
+int tegra_avp_release(struct tegra_avp_info *avp);
+int tegra_avp_load_lib(struct tegra_avp_info *avp, struct tegra_avp_lib *lib);
+int tegra_avp_unload_lib(struct tegra_avp_info *avp, unsigned long handle);
+
+
+#include <linux/tegra_sema.h>
+
+struct tegra_sema_info;
+
+int tegra_sema_open(struct tegra_sema_info **sema);
+int tegra_sema_release(struct tegra_sema_info *sema);
+int tegra_sema_wait(struct tegra_sema_info *sema, long* timeout);
+int tegra_sema_signal(struct tegra_sema_info *sema);
+
+
+#include <linux/tegra_rpc.h>
+
+struct tegra_rpc_info;
+
+int tegra_rpc_open(struct tegra_rpc_info **rpc);
+int tegra_rpc_release(struct tegra_rpc_info *rpc);
+int tegra_rpc_port_create(struct tegra_rpc_info *rpc, char *name,
+ struct tegra_sema_info *sema);
+int tegra_rpc_get_name(struct tegra_rpc_info *rpc, char* name);
+int tegra_rpc_port_connect(struct tegra_rpc_info *rpc, long timeout);
+int tegra_rpc_port_listen(struct tegra_rpc_info *rpc, long timeout);
+int tegra_rpc_write(struct tegra_rpc_info *rpc, u8* buf, size_t size);
+int tegra_rpc_read(struct tegra_rpc_info *rpc, u8 *buf, size_t max);
+
+
+#endif
diff --git a/drivers/media/video/tegra/avp/tegra_rpc.c b/drivers/media/video/tegra/avp/tegra_rpc.c
index 6110d0bd066c..a0fd1dc999f4 100644
--- a/drivers/media/video/tegra/avp/tegra_rpc.c
+++ b/drivers/media/video/tegra/avp/tegra_rpc.c
@@ -70,7 +70,7 @@ enum {
TRPC_TRACE_PORT = 1U << 2,
};
-static u32 trpc_debug_mask = 0;
+static u32 trpc_debug_mask;
module_param_named(debug_mask, trpc_debug_mask, uint, S_IWUSR | S_IRUGO);
#define DBG(flag, args...) \
@@ -724,7 +724,7 @@ static int trpc_debug_ports_show(struct seq_file *s, void *data)
for (i = 0; i < ARRAY_SIZE(port->peers); i++) {
struct trpc_endpoint *ep = &port->peers[i];
seq_printf(s, " peer%d: %s\n ready:%s\n", i,
- ep->owner ? ep->owner->name: "<none>",
+ ep->owner ? ep->owner->name : "<none>",
ep->ready ? "yes" : "no");
if (ep->ops && ep->ops->show)
ep->ops->show(s, ep);
@@ -741,7 +741,7 @@ static int trpc_debug_ports_open(struct inode *inode, struct file *file)
return single_open(file, trpc_debug_ports_show, inode->i_private);
}
-static struct file_operations trpc_debug_ports_fops = {
+static const struct file_operations trpc_debug_ports_fops = {
.open = trpc_debug_ports_open,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/drivers/media/video/tegra/avp/trpc_local.c b/drivers/media/video/tegra/avp/trpc_local.c
index 5a941a78fc40..77692e094385 100644
--- a/drivers/media/video/tegra/avp/trpc_local.c
+++ b/drivers/media/video/tegra/avp/trpc_local.c
@@ -33,10 +33,11 @@
#include "trpc.h"
#include "trpc_sema.h"
+#include "nvavp.h"
-struct rpc_info {
+struct tegra_rpc_info {
struct trpc_endpoint *rpc_ep;
- struct file *sema_file;
+ struct tegra_sema_info *sema;
};
/* ports names reserved for system functions, i.e. communicating with the
@@ -55,26 +56,39 @@ static struct trpc_ep_ops ep_ops = {
};
static struct trpc_node rpc_node = {
- .name = "local",
- .type = TRPC_NODE_LOCAL,
+ .name = "local",
+ .type = TRPC_NODE_LOCAL,
};
static void rpc_notify_recv(struct trpc_endpoint *ep)
{
- struct rpc_info *info = trpc_priv(ep);
+ struct tegra_rpc_info *info = trpc_priv(ep);
if (WARN_ON(!info))
return;
- if (info->sema_file)
- trpc_sema_signal(info->sema_file);
+ if (info->sema)
+ tegra_sema_signal(info->sema);
+}
+
+int tegra_rpc_open(struct tegra_rpc_info **info)
+{
+ struct tegra_rpc_info *new_info;
+
+ new_info = kzalloc(sizeof(struct tegra_rpc_info), GFP_KERNEL);
+ if (!new_info)
+ return -ENOMEM;
+
+ *info = new_info;
+ return 0;
}
static int local_rpc_open(struct inode *inode, struct file *file)
{
- struct rpc_info *info;
+ struct tegra_rpc_info *info;
+ int ret = 0;
- info = kzalloc(sizeof(struct rpc_info), GFP_KERNEL);
- if (!info)
+ ret = tegra_rpc_open(&info);
+ if (ret < 0)
return -ENOMEM;
nonseekable_open(inode, file);
@@ -82,30 +96,23 @@ static int local_rpc_open(struct inode *inode, struct file *file)
return 0;
}
-static int local_rpc_release(struct inode *inode, struct file *file)
+int tegra_rpc_release(struct tegra_rpc_info *info)
{
- struct rpc_info *info = file->private_data;
-
if (info->rpc_ep)
trpc_close(info->rpc_ep);
- if (info->sema_file)
- fput(info->sema_file);
+ if (info->sema)
+ trpc_sema_put(info->sema);
kfree(info);
- file->private_data = NULL;
return 0;
}
+EXPORT_SYMBOL(tegra_rpc_release);
-static int __get_port_desc(struct tegra_rpc_port_desc *desc,
- unsigned int cmd, unsigned long arg)
+static int local_rpc_release(struct inode *inode, struct file *file)
{
- 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;
+ struct tegra_rpc_info *info = file->private_data;
- desc->name[TEGRA_RPC_MAX_NAME_LEN - 1] = '\0';
+ tegra_rpc_release(info);
+ file->private_data = NULL;
return 0;
}
@@ -138,55 +145,98 @@ static int _validate_port_name(const char *name)
return 0;
}
+int tegra_rpc_port_create(struct tegra_rpc_info *info, char *name,
+ struct tegra_sema_info *sema)
+{
+ struct trpc_endpoint *ep;
+ int ret = 0;
+
+ if (info->rpc_ep) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ name[TEGRA_RPC_MAX_NAME_LEN - 1] = '\0';
+ if (name[0]) {
+ ret = _validate_port_name(name);
+ if (ret)
+ goto err;
+ } else {
+ _gen_port_name(name);
+ }
+ ep = trpc_create(&rpc_node, name, &ep_ops, info);
+ if (IS_ERR(ep)) {
+ ret = PTR_ERR(ep);
+ goto err;
+ }
+ info->rpc_ep = ep;
+ info->sema = sema;
+ return 0;
+
+err:
+ return ret;
+}
+
+int tegra_rpc_get_name(struct tegra_rpc_info *info, char* name)
+{
+ if (!info->rpc_ep)
+ return -EINVAL;
+
+ strcpy(name, trpc_name(info->rpc_ep));
+ return 0;
+}
+
+int tegra_rpc_port_connect(struct tegra_rpc_info *info, long timeout)
+{
+ if (!info->rpc_ep)
+ return -EINVAL;
+
+ return trpc_connect(info->rpc_ep, timeout);
+
+}
+
+int tegra_rpc_port_listen(struct tegra_rpc_info *info, long timeout)
+{
+ if (!info->rpc_ep)
+ return -EINVAL;
+
+ return trpc_wait_peer(info->rpc_ep, timeout);
+}
+
static long local_rpc_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg)
{
- struct rpc_info *info = file->private_data;
+ struct tegra_rpc_info *info = file->private_data;
struct tegra_rpc_port_desc desc;
- struct trpc_endpoint *ep;
+ struct tegra_sema_info *sema = NULL;
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) {
+ _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 (_IOC_SIZE(cmd) != sizeof(struct tegra_rpc_port_desc))
+ return -EINVAL;
+ if (copy_from_user(&desc, (void __user *)arg, sizeof(desc)))
+ return -EFAULT;
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;
+ sema = trpc_sema_get_from_fd(desc.notify_fd);
+ if (IS_ERR(sema)) {
+ ret = PTR_ERR(sema);
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;
+
+ ret = tegra_rpc_port_create(info, desc.name, sema);
+ if (ret < 0)
goto err;
- }
- info->rpc_ep = ep;
+
break;
case TEGRA_RPC_IOCTL_PORT_GET_NAME:
if (!info->rpc_ep) {
@@ -208,7 +258,7 @@ static long local_rpc_ioctl(struct file *file, unsigned int cmd,
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);
+ trpc_name(info->rpc_ep), ret);
goto err;
}
break;
@@ -220,7 +270,7 @@ static long local_rpc_ioctl(struct file *file, unsigned int cmd,
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);
+ __func__, trpc_name(info->rpc_ep), ret);
goto err;
}
break;
@@ -235,14 +285,31 @@ static long local_rpc_ioctl(struct file *file, unsigned int cmd,
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);
+ current->pid, cmd, arg, _IOC_NR(cmd), ret);
return (long)ret;
}
+int tegra_rpc_write(struct tegra_rpc_info *info, u8* buf, size_t size)
+{
+ int ret;
+
+ if (!info->rpc_ep)
+ return -EINVAL;
+
+ if (TEGRA_RPC_MAX_MSG_LEN < size)
+ return -EINVAL;
+
+ ret = trpc_send_msg(&rpc_node, info->rpc_ep, buf, size,
+ GFP_KERNEL);
+ if (ret)
+ return ret;
+ return size;
+}
+
static ssize_t local_rpc_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
- struct rpc_info *info = file->private_data;
+ struct tegra_rpc_info *info = file->private_data;
u8 data[TEGRA_RPC_MAX_MSG_LEN];
int ret;
@@ -255,16 +322,35 @@ static ssize_t local_rpc_write(struct file *file, const char __user *buf,
return -EFAULT;
ret = trpc_send_msg(&rpc_node, info->rpc_ep, data, count,
- GFP_KERNEL);
+ GFP_KERNEL);
if (ret)
return ret;
return count;
}
+int tegra_rpc_read(struct tegra_rpc_info *info, u8 *buf, size_t max)
+{
+ int ret;
+
+ if (max > TEGRA_RPC_MAX_MSG_LEN)
+ return -EINVAL;
+
+ ret = trpc_recv_msg(&rpc_node, info->rpc_ep, buf,
+ TEGRA_RPC_MAX_MSG_LEN, 0);
+ if (ret == 0)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else if (ret > max)
+ return -ENOSPC;
+
+ return ret;
+}
+
static ssize_t local_rpc_read(struct file *file, char __user *buf, size_t max,
- loff_t *ppos)
+ loff_t *ppos)
{
- struct rpc_info *info = file->private_data;
+ struct tegra_rpc_info *info = file->private_data;
int ret;
u8 data[TEGRA_RPC_MAX_MSG_LEN];
@@ -272,7 +358,7 @@ static ssize_t local_rpc_read(struct file *file, char __user *buf, size_t max,
return -EINVAL;
ret = trpc_recv_msg(&rpc_node, info->rpc_ep, data,
- TEGRA_RPC_MAX_MSG_LEN, 0);
+ TEGRA_RPC_MAX_MSG_LEN, 0);
if (ret == 0)
return 0;
else if (ret < 0)
@@ -295,9 +381,9 @@ static const struct file_operations local_rpc_misc_fops = {
};
static struct miscdevice local_rpc_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "tegra_rpc",
- .fops = &local_rpc_misc_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tegra_rpc",
+ .fops = &local_rpc_misc_fops,
};
int __init rpc_local_init(void)
diff --git a/drivers/media/video/tegra/avp/trpc_sema.c b/drivers/media/video/tegra/avp/trpc_sema.c
index b8772573d956..cd717a1a0ca3 100644
--- a/drivers/media/video/tegra/avp/trpc_sema.c
+++ b/drivers/media/video/tegra/avp/trpc_sema.c
@@ -29,7 +29,8 @@
#include "trpc_sema.h"
-struct trpc_sema {
+struct tegra_sema_info {
+ struct file *file;
wait_queue_head_t wq;
spinlock_t lock;
int count;
@@ -46,7 +47,7 @@ static inline bool is_trpc_sema_file(struct file *file)
return false;
}
-struct file *trpc_sema_get_from_fd(int fd)
+struct tegra_sema_info *trpc_sema_get_from_fd(int fd)
{
struct file *file;
@@ -62,12 +63,17 @@ struct file *trpc_sema_get_from_fd(int fd)
return ERR_PTR(-EINVAL);
}
- return file;
+ return file->private_data;
}
-int trpc_sema_signal(struct file *file)
+void trpc_sema_put(struct tegra_sema_info *info)
+{
+ if (info->file)
+ fput(info->file);
+}
+
+int tegra_sema_signal(struct tegra_sema_info *info)
{
- struct trpc_sema *info = file->private_data;
unsigned long flags;
if (!info)
@@ -80,26 +86,25 @@ int trpc_sema_signal(struct file *file)
return 0;
}
-static int trpc_sema_wait(struct trpc_sema *info, long *timeleft)
+int tegra_sema_wait(struct tegra_sema_info *info, long *timeout)
{
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;
- }
+ long timeleft = *timeout;
+
+ *timeout = 0;
+ if (timeleft < 0)
+ timeleft = MAX_SCHEDULE_TIMEOUT;
+
+ timeleft = msecs_to_jiffies(timeleft);
+ endtime = jiffies + timeleft;
again:
- if (timeout)
+ if (timeleft)
ret = wait_event_interruptible_timeout(info->wq,
info->count > 0,
- timeout);
+ timeleft);
spin_lock_irqsave(&info->lock, flags);
if (info->count > 0) {
info->count--;
@@ -108,15 +113,15 @@ again:
ret = -ETIMEDOUT;
} else if (ret < 0) {
ret = -EINTR;
- if (timeout != MAX_SCHEDULE_TIMEOUT &&
+ if (timeleft != MAX_SCHEDULE_TIMEOUT &&
time_before(jiffies, endtime))
- *timeleft = jiffies_to_msecs(endtime - jiffies);
+ *timeout = jiffies_to_msecs(endtime - jiffies);
else
- *timeleft = 0;
+ *timeout = 0;
} else {
/* we woke up but someone else got the semaphore and we have
* time left, try again */
- timeout = ret;
+ timeleft = ret;
spin_unlock_irqrestore(&info->lock, flags);
goto again;
}
@@ -124,34 +129,53 @@ again:
return ret;
}
-static int trpc_sema_open(struct inode *inode, struct file *file)
+int tegra_sema_open(struct tegra_sema_info **sema)
{
- struct trpc_sema *info;
-
- info = kzalloc(sizeof(struct trpc_sema), GFP_KERNEL);
+ struct tegra_sema_info *info;
+ info = kzalloc(sizeof(struct tegra_sema_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- nonseekable_open(inode, file);
init_waitqueue_head(&info->wq);
spin_lock_init(&info->lock);
+ *sema = info;
+ return 0;
+}
+
+static int trpc_sema_open(struct inode *inode, struct file *file)
+{
+ struct tegra_sema_info *info;
+ int ret;
+
+ ret = tegra_sema_open(&info);
+ if (ret < 0)
+ return ret;
+
+ info->file = file;
+ nonseekable_open(inode, file);
file->private_data = info;
return 0;
}
+int tegra_sema_release(struct tegra_sema_info *sema)
+{
+ kfree(sema);
+ return 0;
+}
+
static int trpc_sema_release(struct inode *inode, struct file *file)
{
- struct trpc_sema *info = file->private_data;
+ struct tegra_sema_info *info = file->private_data;
file->private_data = NULL;
- kfree(info);
+ tegra_sema_release(info);
return 0;
}
static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct trpc_sema *info = file->private_data;
+ struct tegra_sema_info *info = file->private_data;
int ret;
long timeout;
@@ -166,14 +190,14 @@ static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
case TEGRA_SEMA_IOCTL_WAIT:
if (copy_from_user(&timeout, (void __user *)arg, sizeof(long)))
return -EFAULT;
- ret = trpc_sema_wait(info, &timeout);
+ ret = tegra_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);
+ ret = tegra_sema_signal(info);
break;
default:
pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__,
diff --git a/drivers/media/video/tegra/avp/trpc_sema.h b/drivers/media/video/tegra/avp/trpc_sema.h
index 566bbdbe739e..2a7c42245b7f 100644
--- a/drivers/media/video/tegra/avp/trpc_sema.h
+++ b/drivers/media/video/tegra/avp/trpc_sema.h
@@ -21,8 +21,10 @@
#include <linux/types.h>
#include <linux/fs.h>
-struct file *trpc_sema_get_from_fd(int fd);
-int trpc_sema_signal(struct file *file);
+struct tegra_sema_info;
+
+struct tegra_sema_info *trpc_sema_get_from_fd(int fd);
+void trpc_sema_put(struct tegra_sema_info *sema);
int __init trpc_sema_init(void);
#endif
diff --git a/drivers/media/video/tegra/mediaserver/Kconfig b/drivers/media/video/tegra/mediaserver/Kconfig
new file mode 100644
index 000000000000..9e60a5b49cd3
--- /dev/null
+++ b/drivers/media/video/tegra/mediaserver/Kconfig
@@ -0,0 +1,10 @@
+config TEGRA_MEDIASERVER
+bool "Tegra Media Server support"
+depends on ARCH_TEGRA && TEGRA_RPC
+default y
+help
+ Enables support for the multiple OpenMAX clients. Exports the
+ interface on the device node /dev/tegra_mediaserver.
+
+ If unsure, say Y
+
diff --git a/drivers/media/video/tegra/mediaserver/Makefile b/drivers/media/video/tegra/mediaserver/Makefile
new file mode 100644
index 000000000000..82e056f5faf5
--- /dev/null
+++ b/drivers/media/video/tegra/mediaserver/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_TEGRA_MEDIASERVER) += tegra_mediaserver.o
+
diff --git a/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c
new file mode 100644
index 000000000000..e25e1926d99e
--- /dev/null
+++ b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2011 NVIDIA Corp.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+
+#include <linux/tegra_mediaserver.h>
+#include "../avp/nvavp.h"
+#include "../../../../video/tegra/nvmap/nvmap.h"
+
+#define CHECK_STATUS(e, tag) \
+ do { if (e < 0) goto tag; } while (0)
+
+#define CHECK_NULL(ptr, tag) \
+ do { if (!ptr) goto tag; } while (0)
+
+#define CHECK_CONDITION(c, tag) \
+ do { if (c) goto tag; } while (0)
+
+struct tegra_mediasrv_block {
+ struct list_head entry;
+ struct tegra_mediaserver_block_info block;
+};
+
+struct tegra_mediasrv_iram {
+ struct list_head entry;
+ struct tegra_mediaserver_iram_info iram;
+};
+
+struct tegra_mediasrv_node {
+ struct tegra_mediasrv_info *mediasrv;
+ struct list_head blocks;
+ int nr_iram_shared;
+};
+
+struct tegra_mediasrv_manager {
+ struct tegra_avp_lib lib;
+ struct tegra_rpc_info *rpc;
+ struct tegra_sema_info *sema;
+};
+
+struct tegra_mediasrv_info {
+ int minor;
+ struct mutex lock;
+ struct nvmap_client *nvmap;
+ struct tegra_avp_info *avp;
+ struct tegra_mediasrv_manager manager;
+ int nr_nodes;
+ int nr_blocks;
+ struct tegra_mediaserver_iram_info iram; /* only one supported */
+ int nr_iram_shared;
+};
+
+static struct tegra_mediasrv_info *mediasrv_info;
+
+
+/*
+ * File entry points
+ */
+static int mediasrv_open(struct inode *inode, struct file *file)
+{
+ struct tegra_mediasrv_info *mediasrv = mediasrv_info;
+ struct tegra_mediasrv_node *node = NULL;
+ struct tegra_mediasrv_manager *manager = &mediasrv->manager;
+ struct tegra_avp_lib *lib = &manager->lib;
+ int e;
+
+ node = kzalloc(sizeof(struct tegra_mediasrv_node), GFP_KERNEL);
+ CHECK_NULL(node, node_alloc_fail);
+ INIT_LIST_HEAD(&node->blocks);
+ node->mediasrv = mediasrv;
+
+ mutex_lock(&mediasrv->lock);
+ nonseekable_open(inode, file);
+
+ if (!mediasrv->nr_nodes) {
+ e = tegra_sema_open(&manager->sema);
+ CHECK_STATUS(e, fail);
+
+ e = tegra_rpc_open(&manager->rpc);
+ CHECK_STATUS(e, fail);
+
+ e = tegra_rpc_port_create(manager->rpc, "NVMM_MANAGER_SRV",
+ manager->sema);
+ CHECK_STATUS(e, fail);
+
+ e = tegra_avp_open(&mediasrv->avp);
+ CHECK_STATUS(e, fail);
+
+ memcpy(lib->name, "nvmm_manager.axf\0",
+ strlen("nvmm_manager.axf") + 1);
+ lib->args = &mediasrv;
+ lib->args_len = sizeof(unsigned long);
+ e = tegra_avp_load_lib(mediasrv->avp, lib);
+ CHECK_STATUS(e, fail);
+
+ e = tegra_rpc_port_connect(manager->rpc, 50000);
+ CHECK_STATUS(e, fail);
+ }
+
+ mediasrv->nr_nodes++;
+ try_module_get(THIS_MODULE);
+
+ mutex_unlock(&mediasrv->lock);
+
+ file->private_data = node;
+
+ return 0;
+
+fail:
+ if (lib->handle) {
+ tegra_avp_unload_lib(mediasrv->avp, lib->handle);
+ lib->handle = 0;
+ }
+
+ if (mediasrv->avp) {
+ tegra_avp_release(mediasrv->avp);
+ mediasrv->avp = NULL;
+ }
+
+ if (manager->rpc) {
+ tegra_rpc_release(manager->rpc);
+ manager->rpc = NULL;
+ }
+ if (manager->sema) {
+ tegra_sema_release(manager->sema);
+ manager->sema = NULL;
+ }
+
+ kfree(node);
+
+ mutex_unlock(&mediasrv->lock);
+ return e;
+
+node_alloc_fail:
+ e = -ENOMEM;
+ return e;
+}
+
+static int mediasrv_release(struct inode *inode, struct file *file)
+{
+ struct tegra_mediasrv_info *mediasrv = mediasrv_info;
+ struct tegra_mediasrv_node *node = file->private_data;
+ struct tegra_mediasrv_block *block;
+ struct list_head *entry;
+ struct list_head *temp;
+ u32 message[2];
+ int e;
+
+ mutex_lock(&mediasrv->lock);
+
+ list_for_each_safe(entry, temp, &node->blocks) {
+ block = list_entry(entry, struct tegra_mediasrv_block, entry);
+
+ pr_info("Improperly closed block found!");
+ pr_info(" NVMM Block Handle: 0x%08x\n",
+ block->block.nvmm_block_handle);
+ pr_info(" AVP Block Handle: 0x%08x\n",
+ block->block.avp_block_handle);
+
+ message[0] = 1; /* NvmmManagerMsgType_AbnormalTerm */
+ message[1] = block->block.avp_block_handle;
+
+ e = tegra_rpc_write(mediasrv->manager.rpc, (u8 *)message,
+ sizeof(u32) * 2);
+ pr_info("Abnormal termination message result: %d\n", e);
+
+ if (block->block.avp_block_library_handle) {
+ e = tegra_avp_unload_lib(mediasrv->avp,
+ block->block.avp_block_library_handle);
+ pr_info("Unload block (0x%08x) result: %d\n",
+ block->block.avp_block_library_handle, e);
+ }
+
+ if (block->block.service_library_handle) {
+ e = tegra_avp_unload_lib(mediasrv->avp,
+ block->block.service_library_handle);
+ pr_info("Unload service (0x%08x) result: %d\n",
+ block->block.service_library_handle, e);
+ }
+
+ mediasrv->nr_blocks--;
+ list_del(entry);
+ kfree(block);
+ }
+
+ mediasrv->nr_iram_shared -= node->nr_iram_shared;
+ if (mediasrv->iram.rm_handle && !mediasrv->nr_iram_shared) {
+ pr_info("Improperly freed shared iram found!");
+ nvmap_unpin_ids(mediasrv->nvmap, 1, &mediasrv->iram.rm_handle);
+ nvmap_free_handle_id(mediasrv->nvmap, mediasrv->iram.rm_handle);
+ mediasrv->iram.rm_handle = 0;
+ mediasrv->iram.physical_address = 0;
+ }
+
+ kfree(node);
+ mediasrv->nr_nodes--;
+ if (!mediasrv->nr_nodes) {
+ struct tegra_mediasrv_manager *manager = &mediasrv->manager;
+
+ tegra_avp_unload_lib(mediasrv->avp, manager->lib.handle);
+ manager->lib.handle = 0;
+
+ tegra_avp_release(mediasrv->avp);
+ mediasrv->avp = NULL;
+
+ tegra_rpc_release(manager->rpc);
+ manager->rpc = NULL;
+
+ tegra_sema_release(manager->sema);
+ manager->sema = NULL;
+ }
+
+ mutex_unlock(&mediasrv->lock);
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static int mediasrv_alloc(struct tegra_mediasrv_node *node,
+ union tegra_mediaserver_alloc_info *in,
+ union tegra_mediaserver_alloc_info *out)
+{
+ struct tegra_mediasrv_info *mediasrv = node->mediasrv;
+ int e;
+
+ switch (in->in.tegra_mediaserver_resource_type) {
+ case TEGRA_MEDIASERVER_RESOURCE_BLOCK:
+ {
+ struct tegra_mediasrv_block *block;
+
+ block = kzalloc(sizeof(struct tegra_mediasrv_node),
+ GFP_KERNEL);
+ CHECK_NULL(block, block_alloc_fail);
+
+ block->block = in->in.u.block;
+ list_add(&block->entry, &node->blocks);
+ goto block_done;
+
+block_alloc_fail:
+ e = -ENOMEM;
+ goto fail;
+
+block_done:
+ mediasrv->nr_blocks++;
+ out->out.u.block.count = mediasrv->nr_blocks;
+ }
+ break;
+
+ case TEGRA_MEDIASERVER_RESOURCE_IRAM:
+ {
+ if (in->in.u.iram.tegra_mediaserver_iram_type ==
+ TEGRA_MEDIASERVER_IRAM_SHARED) {
+ if (!mediasrv->nr_iram_shared) {
+ size_t align, size;
+ struct nvmap_handle_ref *r = NULL;
+ unsigned long id, physical_address;
+
+ size = PAGE_ALIGN(in->in.u.iram.size);
+ r = nvmap_create_handle(mediasrv->nvmap, size);
+ CHECK_CONDITION((r < 0),
+ iram_shared_handle_fail);
+
+ id = nvmap_ref_to_id(r);
+
+ align = max_t(size_t, in->in.u.iram.alignment,
+ PAGE_SIZE);
+ e = nvmap_alloc_handle_id(mediasrv->nvmap, id,
+ NVMAP_HEAP_CARVEOUT_IRAM, align,
+ NVMAP_HANDLE_WRITE_COMBINE);
+ CHECK_STATUS(e, iram_shared_alloc_fail);
+
+ physical_address =
+ nvmap_pin_ids(mediasrv->nvmap, 1, &id);
+ CHECK_CONDITION((physical_address < 0),
+ iram_shared_pin_fail);
+
+ mediasrv->iram.rm_handle = id;
+ mediasrv->iram.physical_address =
+ physical_address;
+ goto iram_shared_done;
+
+iram_shared_pin_fail:
+ e = physical_address;
+iram_shared_alloc_fail:
+ nvmap_free_handle_id(mediasrv->nvmap, id);
+iram_shared_handle_fail:
+ goto fail;
+ }
+
+iram_shared_done:
+ out->out.u.iram.rm_handle = mediasrv->iram.rm_handle;
+ out->out.u.iram.physical_address =
+ mediasrv->iram.physical_address;
+ mediasrv->nr_iram_shared++;
+ node->nr_iram_shared++;
+ } else if (in->in.u.iram.tegra_mediaserver_iram_type ==
+ TEGRA_MEDIASERVER_IRAM_SCRATCH) {
+ e = -EINVAL;
+ goto fail;
+ }
+ }
+ break;
+
+ default:
+ {
+ e = -EINVAL;
+ goto fail;
+ }
+ break;
+ }
+
+ return 0;
+
+fail:
+ return e;
+}
+
+static void mediasrv_free(struct tegra_mediasrv_node *node,
+ union tegra_mediaserver_free_info *in)
+{
+ struct tegra_mediasrv_info *mediasrv = node->mediasrv;
+
+ switch (in->in.tegra_mediaserver_resource_type) {
+ case TEGRA_MEDIASERVER_RESOURCE_BLOCK:
+ {
+ struct tegra_mediasrv_block *block;
+ struct tegra_mediasrv_block *temp;
+ struct list_head *entry;
+
+ list_for_each(entry, &node->blocks) {
+ temp = list_entry(entry, struct tegra_mediasrv_block,
+ entry);
+ if (temp->block.nvmm_block_handle !=
+ in->in.u.nvmm_block_handle)
+ continue;
+
+ block = temp;
+ break;
+ }
+
+ CHECK_NULL(block, done);
+ list_del(&block->entry);
+ kfree(block);
+ }
+ break;
+
+ case TEGRA_MEDIASERVER_RESOURCE_IRAM:
+ {
+ if (in->in.u.iram_rm_handle == mediasrv->iram.rm_handle &&
+ node->nr_iram_shared) {
+ node->nr_iram_shared--;
+ mediasrv->nr_iram_shared--;
+
+ if (!mediasrv->nr_iram_shared) {
+ nvmap_unpin_ids(mediasrv->nvmap, 1,
+ &mediasrv->iram.rm_handle);
+ nvmap_free_handle_id(mediasrv->nvmap,
+ mediasrv->iram.rm_handle);
+ mediasrv->iram.rm_handle = 0;
+ mediasrv->iram.physical_address = 0;
+ }
+ }
+
+ else
+ goto done;
+ }
+ break;
+ }
+
+done:
+ return;
+}
+
+static int mediasrv_update_block_info(
+ struct tegra_mediasrv_node *node,
+ union tegra_mediaserver_update_block_info *in
+)
+{
+ struct tegra_mediasrv_block *entry;
+ struct tegra_mediasrv_block *block;
+ int e;
+
+ list_for_each_entry(entry, &node->blocks, entry) {
+ if (entry->block.nvmm_block_handle != in->in.nvmm_block_handle)
+ continue;
+
+ block = entry;
+ break;
+ }
+
+ CHECK_NULL(block, fail);
+
+ block->block = in->in;
+ return 0;
+
+fail:
+ e = -EINVAL;
+ return e;
+}
+
+static long mediasrv_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct tegra_mediasrv_info *mediasrv = mediasrv_info;
+ struct tegra_mediasrv_node *node = file->private_data;
+ int e = -ENODEV;
+
+ mutex_lock(&mediasrv->lock);
+
+ switch (cmd) {
+ case TEGRA_MEDIASERVER_IOCTL_ALLOC:
+ {
+ union tegra_mediaserver_alloc_info in, out;
+ e = copy_from_user(&in, (void __user *)arg, sizeof(in));
+ CHECK_CONDITION(e, copy_fail);
+ e = mediasrv_alloc(node, &in, &out);
+ CHECK_STATUS(e, fail);
+ e = copy_to_user((void __user *)arg, &out, sizeof(out));
+ CHECK_CONDITION(e, copy_fail);
+ }
+ break;
+
+ case TEGRA_MEDIASERVER_IOCTL_FREE:
+ {
+ union tegra_mediaserver_free_info in;
+ e = copy_from_user(&in, (void __user *)arg, sizeof(in));
+ CHECK_CONDITION(e, copy_fail);
+ mediasrv_free(node, &in);
+ }
+ break;
+
+ case TEGRA_MEDIASERVER_IOCTL_UPDATE_BLOCK_INFO:
+ {
+ union tegra_mediaserver_update_block_info in;
+ e = copy_from_user(&in, (void __user *)arg, sizeof(in));
+ CHECK_CONDITION(e, copy_fail);
+ e = mediasrv_update_block_info(node, &in);
+ CHECK_CONDITION(e, fail);
+ }
+ break;
+
+ default:
+ {
+ e = -ENODEV;
+ goto fail;
+ }
+ break;
+ }
+
+ mutex_unlock(&mediasrv->lock);
+ return 0;
+
+copy_fail:
+ e = -EFAULT;
+fail:
+ return e;
+}
+
+/*
+ * Kernel structures and entry points
+ */
+static const struct file_operations mediaserver_fops = {
+ .owner = THIS_MODULE,
+ .open = mediasrv_open,
+ .release = mediasrv_release,
+ .unlocked_ioctl = mediasrv_unlocked_ioctl,
+};
+
+static struct miscdevice mediaserver_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tegra_mediaserver",
+ .fops = &mediaserver_fops,
+};
+
+static int __init tegra_mediaserver_init(void)
+{
+ struct tegra_mediasrv_info *mediasrv;
+ int e = 0;
+
+ CHECK_NULL(!mediasrv_info, busy);
+
+ mediasrv = kzalloc(sizeof(struct tegra_mediasrv_info), GFP_KERNEL);
+ CHECK_NULL(mediasrv, alloc_fail);
+
+ mediasrv->nvmap = nvmap_create_client(nvmap_dev, "tegra_mediaserver");
+ CHECK_NULL(mediasrv, nvmap_create_fail);
+
+ e = misc_register(&mediaserver_misc_device);
+ CHECK_STATUS(e, register_fail);
+
+ mediasrv->nr_nodes = 0;
+ mutex_init(&mediasrv->lock);
+
+ mediasrv_info = mediasrv;
+ goto done;
+
+nvmap_create_fail:
+ e = -ENOMEM;
+ kfree(mediasrv);
+ goto done;
+
+register_fail:
+ nvmap_client_put(mediasrv->nvmap);
+ kfree(mediasrv);
+ goto done;
+
+alloc_fail:
+ e = -ENOMEM;
+ goto done;
+
+busy:
+ e = -EBUSY;
+ goto done;
+
+done:
+ return e;
+}
+
+void __exit tegra_mediaserver_cleanup(void)
+{
+ struct tegra_mediasrv_info *mediasrv = mediasrv_info;
+ int e;
+
+ e = misc_deregister(&mediaserver_misc_device);
+ CHECK_STATUS(e, fail);
+
+ nvmap_client_put(mediasrv->nvmap);
+ kfree(mediasrv);
+ mediasrv_info = NULL;
+
+fail:
+ return;
+}
+
+module_init(tegra_mediaserver_init);
+module_exit(tegra_mediaserver_cleanup);
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/tegra_mediaserver.h b/include/linux/tegra_mediaserver.h
new file mode 100644
index 000000000000..f28473baf63e
--- /dev/null
+++ b/include/linux/tegra_mediaserver.h
@@ -0,0 +1,112 @@
+/* include/linux/tegra_mediaserver.h
+ *
+ * Media Server driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef _TEGRA_MEDIASERVER_H
+#define _TEGRA_MEDIASERVER_H
+
+#include <linux/ioctl.h>
+
+#define TEGRA_MEDIASERVER_MAGIC 'm'
+#define TEGRA_MEDIASERVER_IOCTL_ALLOC \
+ _IOWR(TEGRA_MEDIASERVER_MAGIC, 0x40, \
+ union tegra_mediaserver_alloc_info)
+
+enum tegra_mediaserver_resource_type {
+ TEGRA_MEDIASERVER_RESOURCE_BLOCK = 0,
+ TEGRA_MEDIASERVER_RESOURCE_IRAM,
+};
+
+enum tegra_mediaserver_block_type {
+ TEGRA_MEDIASERVER_BLOCK_AUDDEC = 0,
+ TEGRA_MEDIASERVER_BLOCK_VIDDEC,
+};
+
+enum tegra_mediaserver_iram_type {
+ TEGRA_MEDIASERVER_IRAM_SCRATCH = 0,
+ TEGRA_MEDIASERVER_IRAM_SHARED,
+};
+
+
+struct tegra_mediaserver_block_info {
+ int nvmm_block_handle;
+ int avp_block_handle;
+ int avp_block_library_handle;
+ int service_handle;
+ int service_library_handle;
+};
+
+struct tegra_mediaserver_iram_info {
+ unsigned long rm_handle;
+ int physical_address;
+};
+
+union tegra_mediaserver_alloc_info {
+ struct {
+ int tegra_mediaserver_resource_type;
+
+ union {
+ struct tegra_mediaserver_block_info block;
+
+ struct {
+ int tegra_mediaserver_iram_type;
+ int alignment;
+ size_t size;
+ } iram;
+ } u;
+ } in;
+
+ struct {
+ union {
+ struct {
+ int count;
+ } block;
+
+ struct tegra_mediaserver_iram_info iram;
+ } u;
+ } out;
+};
+
+
+#define TEGRA_MEDIASERVER_IOCTL_FREE \
+ _IOR(TEGRA_MEDIASERVER_MAGIC, 0x41, union tegra_mediaserver_free_info)
+
+union tegra_mediaserver_free_info {
+ struct {
+ int tegra_mediaserver_resource_type;
+
+ union {
+ int nvmm_block_handle;
+ int iram_rm_handle;
+ } u;
+ } in;
+};
+
+
+#define TEGRA_MEDIASERVER_IOCTL_UPDATE_BLOCK_INFO \
+ _IOR(TEGRA_MEDIASERVER_MAGIC, 0x45, \
+ union tegra_mediaserver_update_block_info)
+
+union tegra_mediaserver_update_block_info {
+ struct tegra_mediaserver_block_info in;
+};
+#endif
+