diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/tegra/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/video/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/avp.c | 453 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/avp_svc.c | 2 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/nvavp.h | 53 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/tegra_rpc.c | 6 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/trpc_local.c | 224 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/trpc_sema.c | 86 | ||||
-rw-r--r-- | drivers/media/video/tegra/avp/trpc_sema.h | 6 | ||||
-rw-r--r-- | drivers/media/video/tegra/mediaserver/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/video/tegra/mediaserver/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/tegra/mediaserver/tegra_mediaserver.c | 554 |
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"); + |