summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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
12 files changed, 1106 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");
+