aboutsummaryrefslogtreecommitdiff
path: root/camera/msm_buf_mgr/msm_generic_buf_mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/msm_buf_mgr/msm_generic_buf_mgr.c')
-rw-r--r--camera/msm_buf_mgr/msm_generic_buf_mgr.c671
1 files changed, 671 insertions, 0 deletions
diff --git a/camera/msm_buf_mgr/msm_generic_buf_mgr.c b/camera/msm_buf_mgr/msm_generic_buf_mgr.c
new file mode 100644
index 00000000..4c35615c
--- /dev/null
+++ b/camera/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -0,0 +1,671 @@
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "CAM-BUFMGR %s:%d " fmt, __func__, __LINE__
+
+#include "msm_generic_buf_mgr.h"
+
+static struct msm_buf_mngr_device *msm_buf_mngr_dev;
+
+struct v4l2_subdev *msm_buf_mngr_get_subdev(void)
+{
+ return &msm_buf_mngr_dev->subdev.sd;
+}
+
+static int32_t msm_buf_mngr_hdl_cont_get_buf(struct msm_buf_mngr_device *dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned int i;
+ struct msm_buf_mngr_user_buf_cont_info *cbuf, *cont_save;
+
+ list_for_each_entry_safe(cbuf, cont_save, &dev->cont_qhead, entry) {
+ if ((cbuf->sessid == buf_info->session_id) &&
+ (cbuf->index == buf_info->index) &&
+ (cbuf->strid == buf_info->stream_id)) {
+ buf_info->user_buf.buf_cnt = cbuf->paddr->buf_cnt;
+ if (buf_info->user_buf.buf_cnt >
+ MSM_CAMERA_MAX_USER_BUFF_CNT) {
+ pr_err("Invalid cnt%d,%d,%d\n",
+ cbuf->paddr->buf_cnt,
+ buf_info->session_id,
+ buf_info->stream_id);
+ return -EINVAL;
+ }
+ for (i = 0 ; i < buf_info->user_buf.buf_cnt; i++) {
+ buf_info->user_buf.buf_idx[i] =
+ cbuf->paddr->buf_idx[i];
+ }
+ break;
+ }
+ }
+ return 0;
+}
+static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev,
+ void __user *argp)
+{
+ unsigned long flags;
+ int32_t rc = 0;
+ struct msm_buf_mngr_info *buf_info =
+ (struct msm_buf_mngr_info *)argp;
+ struct msm_get_bufs *new_entry =
+ kzalloc(sizeof(struct msm_get_bufs), GFP_KERNEL);
+
+ if (!new_entry) {
+ pr_err("%s:No mem\n", __func__);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&new_entry->entry);
+ new_entry->vb2_buf = dev->vb2_ops.get_buf(buf_info->session_id,
+ buf_info->stream_id);
+ if (!new_entry->vb2_buf) {
+ pr_debug("%s:Get buf is null\n", __func__);
+ kfree(new_entry);
+ return -EINVAL;
+ }
+ new_entry->session_id = buf_info->session_id;
+ new_entry->stream_id = buf_info->stream_id;
+ new_entry->index = new_entry->vb2_buf->v4l2_buf.index;
+ spin_lock_irqsave(&dev->buf_q_spinlock, flags);
+ list_add_tail(&new_entry->entry, &dev->buf_qhead);
+ spin_unlock_irqrestore(&dev->buf_q_spinlock, flags);
+ buf_info->index = new_entry->vb2_buf->v4l2_buf.index;
+ if (buf_info->type == MSM_CAMERA_BUF_MNGR_BUF_USER) {
+ mutex_lock(&dev->cont_mutex);
+ if (!list_empty(&dev->cont_qhead)) {
+ rc = msm_buf_mngr_hdl_cont_get_buf(dev, buf_info);
+ } else {
+ pr_err("Nothing mapped in user buf for %d,%d\n",
+ buf_info->session_id, buf_info->stream_id);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&dev->cont_mutex);
+ }
+ return rc;
+}
+
+static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+ int32_t ret = -EINVAL;
+
+ spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
+ list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
+ if ((bufs->session_id == buf_info->session_id) &&
+ (bufs->stream_id == buf_info->stream_id) &&
+ (bufs->index == buf_info->index)) {
+ ret = buf_mngr_dev->vb2_ops.buf_done
+ (bufs->vb2_buf,
+ buf_info->session_id,
+ buf_info->stream_id,
+ buf_info->frame_id,
+ &buf_info->timestamp,
+ buf_info->reserved);
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
+ return ret;
+}
+
+
+static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+ int32_t ret = -EINVAL;
+
+ spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
+ list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
+ if ((bufs->session_id == buf_info->session_id) &&
+ (bufs->stream_id == buf_info->stream_id) &&
+ (bufs->index == buf_info->index)) {
+ ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_buf,
+ buf_info->session_id, buf_info->stream_id);
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
+ return ret;
+}
+
+static int32_t msm_generic_buf_mngr_flush(
+ struct msm_buf_mngr_device *buf_mngr_dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+ int32_t ret = -EINVAL;
+ struct timeval ts;
+
+ spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
+ /*
+ * Sanity check on client buf list, remove buf mgr
+ * queue entries in case any
+ */
+ list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
+ if ((bufs->session_id == buf_info->session_id) &&
+ (bufs->stream_id == buf_info->stream_id)) {
+ ret = buf_mngr_dev->vb2_ops.buf_done(bufs->vb2_buf,
+ buf_info->session_id,
+ buf_info->stream_id, 0, &ts, 0);
+ pr_err("Bufs not flushed: str_id = %d buf_index = %d ret = %d\n",
+ buf_info->stream_id, bufs->vb2_buf->v4l2_buf.index,
+ ret);
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ }
+ }
+ spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
+ /* Flush the remaining vb2 buffers in stream list */
+ ret = buf_mngr_dev->vb2_ops.flush_buf(buf_info->session_id,
+ buf_info->stream_id);
+ return ret;
+}
+
+static int32_t msm_buf_mngr_find_cont_stream(struct msm_buf_mngr_device *dev,
+ uint32_t *cnt, uint32_t *tstream,
+ struct msm_sd_close_ioctl *session)
+{
+ struct msm_buf_mngr_user_buf_cont_info *cont_bufs, *cont_save;
+ int32_t ret = -1;
+
+ list_for_each_entry_safe(cont_bufs,
+ cont_save, &dev->cont_qhead, entry) {
+ if (cont_bufs->sessid == session->session) {
+ *cnt = cont_bufs->cnt;
+ *tstream = cont_bufs->strid;
+ return 0;
+ }
+ }
+ return ret;
+}
+
+static void msm_buf_mngr_contq_listdel(struct msm_buf_mngr_device *dev,
+ uint32_t session, int32_t stream,
+ bool unmap, uint32_t cnt)
+{
+ struct msm_buf_mngr_user_buf_cont_info *cont_bufs, *cont_save;
+
+ list_for_each_entry_safe(cont_bufs,
+ cont_save, &dev->cont_qhead, entry) {
+ if ((cont_bufs->sessid == session) &&
+ (cont_bufs->strid == stream)) {
+ if (cnt == 1 && unmap == 1) {
+ ion_unmap_kernel(dev->ion_client,
+ cont_bufs->ion_handle);
+ ion_free(dev->ion_client,
+ cont_bufs->ion_handle);
+ }
+ list_del_init(&cont_bufs->entry);
+ kfree(cont_bufs);
+ cnt--;
+ }
+ }
+ if (cnt != 0)
+ pr_err("Buffers pending cnt = %d\n", cnt);
+}
+
+static void msm_buf_mngr_contq_cleanup(struct msm_buf_mngr_device *dev,
+ struct msm_sd_close_ioctl *session)
+{
+ int32_t stream = -1, found = -1;
+ uint32_t cnt = 0;
+
+ do {
+ found = msm_buf_mngr_find_cont_stream(dev, &cnt,
+ &stream, session);
+ if (found == -1)
+ break;
+ msm_buf_mngr_contq_listdel(dev, session->session,
+ stream, 1, cnt);
+ } while (found == 0);
+}
+
+static void msm_buf_mngr_sd_shutdown(struct msm_buf_mngr_device *dev,
+ struct msm_sd_close_ioctl *session)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+
+ BUG_ON(!dev);
+ BUG_ON(!session);
+
+ spin_lock_irqsave(&dev->buf_q_spinlock, flags);
+ if (!list_empty(&dev->buf_qhead)) {
+ list_for_each_entry_safe(bufs,
+ save, &dev->buf_qhead, entry) {
+ pr_info("%s: Delete invalid bufs =%lx, session_id=%u, bufs->ses_id=%d, str_id=%d, idx=%d\n",
+ __func__, (unsigned long)bufs, session->session,
+ bufs->session_id, bufs->stream_id,
+ bufs->index);
+ if (session->session == bufs->session_id) {
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dev->buf_q_spinlock, flags);
+ mutex_lock(&dev->cont_mutex);
+ if (!list_empty(&dev->cont_qhead))
+ msm_buf_mngr_contq_cleanup(dev, session);
+ mutex_unlock(&dev->cont_mutex);
+}
+
+static int msm_buf_mngr_handle_cont_cmd(struct msm_buf_mngr_device *dev,
+ struct msm_buf_mngr_main_cont_info
+ *cont_cmd)
+{
+ int rc = 0, i = 0;
+ struct ion_handle *ion_handle = NULL;
+ struct msm_camera_user_buf_cont_t *iaddr, *temp_addr;
+ struct msm_buf_mngr_user_buf_cont_info *new_entry, *bufs, *save;
+ size_t size;
+
+ if ((cont_cmd->cmd >= MSM_CAMERA_BUF_MNGR_CONT_MAX) ||
+ (cont_cmd->cmd < 0) ||
+ (cont_cmd->cnt > VB2_MAX_FRAME) ||
+ (cont_cmd->cont_fd < 0)) {
+ pr_debug("Invalid arg passed Cmd:%d, cnt:%d, fd:%d\n",
+ cont_cmd->cmd, cont_cmd->cnt,
+ cont_cmd->cont_fd);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->cont_mutex);
+
+ if (cont_cmd->cmd == MSM_CAMERA_BUF_MNGR_CONT_MAP) {
+ if (!list_empty(&dev->cont_qhead)) {
+ list_for_each_entry_safe(bufs,
+ save, &dev->cont_qhead, entry) {
+ if ((bufs->sessid == cont_cmd->session_id) &&
+ (bufs->strid == cont_cmd->stream_id)) {
+ pr_err("Map exist %d,%d unmap first\n",
+ cont_cmd->session_id,
+ cont_cmd->stream_id);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+ }
+ ion_handle = ion_import_dma_buf(dev->ion_client,
+ cont_cmd->cont_fd);
+ if (IS_ERR_OR_NULL(ion_handle)) {
+ pr_err("Failed to create ion handle for fd %d\n",
+ cont_cmd->cont_fd);
+ rc = -EINVAL;
+ goto end;
+ }
+ if (ion_handle_get_size(dev->ion_client,
+ ion_handle, &size) < 0) {
+ pr_err("Get ion size failed\n");
+ rc = -EINVAL;
+ goto free_ion_handle;
+ }
+ if ((size == 0) || (size <
+ (sizeof(struct msm_camera_user_buf_cont_t) *
+ cont_cmd->cnt))) {
+ pr_err("Invalid or zero size ION buffer %zu\n", size);
+ rc = -EINVAL;
+ goto free_ion_handle;
+ }
+ iaddr = ion_map_kernel(dev->ion_client, ion_handle);
+ if (IS_ERR_OR_NULL(iaddr)) {
+ pr_err("Mapping cont buff failed\n");
+ rc = -EINVAL;
+ goto free_ion_handle;
+ }
+ for (i = 0; i < cont_cmd->cnt; i++) {
+ temp_addr = iaddr + i;
+ if (temp_addr->buf_cnt >
+ MSM_CAMERA_MAX_USER_BUFF_CNT) {
+ pr_err("%s:Invalid buf_cnt:%d for cont:%d\n",
+ __func__, temp_addr->buf_cnt, i);
+ rc = -EINVAL;
+ goto free_list;
+ }
+ new_entry = kzalloc(sizeof(
+ struct msm_buf_mngr_user_buf_cont_info),
+ GFP_KERNEL);
+ if (!new_entry) {
+ pr_err("%s:No mem\n", __func__);
+ rc = -ENOMEM;
+ goto free_list;
+ }
+ INIT_LIST_HEAD(&new_entry->entry);
+ new_entry->sessid = cont_cmd->session_id;
+ new_entry->strid = cont_cmd->stream_id;
+ new_entry->index = i;
+ new_entry->main_fd = cont_cmd->cont_fd;
+ new_entry->ion_handle = ion_handle;
+ new_entry->cnt = cont_cmd->cnt;
+ new_entry->paddr = temp_addr;
+ list_add_tail(&new_entry->entry, &dev->cont_qhead);
+ }
+ goto end;
+ } else if (cont_cmd->cmd == MSM_CAMERA_BUF_MNGR_CONT_UNMAP) {
+ if (!list_empty(&dev->cont_qhead)) {
+ msm_buf_mngr_contq_listdel(dev, cont_cmd->session_id,
+ cont_cmd->stream_id, 1, cont_cmd->cnt);
+ } else {
+ pr_err("Nothing mapped for %d,%d\n",
+ cont_cmd->session_id, cont_cmd->stream_id);
+ rc = -EINVAL;
+ }
+ goto end;
+ }
+
+free_list:
+ if (i != 0) {
+ if (!list_empty(&dev->cont_qhead)) {
+ msm_buf_mngr_contq_listdel(dev, cont_cmd->session_id,
+ cont_cmd->stream_id, 0, i);
+ }
+ }
+ ion_unmap_kernel(dev->ion_client, ion_handle);
+free_ion_handle:
+ ion_free(dev->ion_client, ion_handle);
+end:
+ mutex_unlock(&dev->cont_mutex);
+ return rc;
+}
+
+static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+ return rc;
+}
+
+static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+ return rc;
+}
+
+static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+ void __user *argp = (void __user *)arg;
+
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ rc = msm_buf_mngr_get_buf(buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ rc = msm_buf_mngr_buf_done(buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF:
+ rc = msm_buf_mngr_put_buf(buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_INIT:
+ rc = msm_generic_buf_mngr_open(sd, NULL);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_DEINIT:
+ rc = msm_generic_buf_mngr_close(sd, NULL);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case VIDIOC_MSM_BUF_MNGR_FLUSH:
+ rc = msm_generic_buf_mngr_flush(buf_mngr_dev, argp);
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ case MSM_SD_SHUTDOWN:
+ msm_buf_mngr_sd_shutdown(buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_CONT_CMD:
+ rc = msm_buf_mngr_handle_cont_cmd(buf_mngr_dev, argp);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ int32_t rc = 0;
+
+ void __user *up = (void __user *)arg;
+
+ /* Convert 32 bit IOCTL ID's to 64 bit IOCTL ID's
+ * except VIDIOC_MSM_CPP_CFG32, which needs special
+ * processing
+ */
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF32:
+ cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE32:
+ cmd = VIDIOC_MSM_BUF_MNGR_BUF_DONE;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF32:
+ cmd = VIDIOC_MSM_BUF_MNGR_PUT_BUF;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_CONT_CMD:
+ cmd = VIDIOC_MSM_BUF_MNGR_CONT_CMD;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_FLUSH32:
+ cmd = VIDIOC_MSM_BUF_MNGR_FLUSH;
+ break;
+ default:
+ pr_debug("%s : unsupported compat type", __func__);
+ return -ENOIOCTLCMD;
+ }
+
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ case VIDIOC_MSM_BUF_MNGR_FLUSH:
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF: {
+ struct msm_buf_mngr_info32_t buf_info32;
+ struct msm_buf_mngr_info buf_info;
+
+ if (copy_from_user(&buf_info32, (void __user *)up,
+ sizeof(struct msm_buf_mngr_info32_t)))
+ return -EFAULT;
+
+ buf_info.session_id = buf_info32.session_id;
+ buf_info.stream_id = buf_info32.stream_id;
+ buf_info.frame_id = buf_info32.frame_id;
+ buf_info.index = buf_info32.index;
+ buf_info.timestamp.tv_sec = (long) buf_info32.timestamp.tv_sec;
+ buf_info.timestamp.tv_usec = (long) buf_info32.
+ timestamp.tv_usec;
+ buf_info.reserved = buf_info32.reserved;
+ buf_info.type = buf_info32.type;
+
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, &buf_info);
+ if (rc < 0) {
+ pr_debug("%s : Subdev cmd %d fail", __func__, cmd);
+ return rc;
+ }
+
+ buf_info32.session_id = buf_info.session_id;
+ buf_info32.stream_id = buf_info.stream_id;
+ buf_info32.index = buf_info.index;
+ buf_info32.timestamp.tv_sec = (int32_t) buf_info.
+ timestamp.tv_sec;
+ buf_info32.timestamp.tv_usec = (int32_t) buf_info.timestamp.
+ tv_usec;
+ buf_info32.reserved = buf_info.reserved;
+ buf_info32.type = buf_info.type;
+ buf_info32.user_buf.buf_cnt = buf_info.user_buf.buf_cnt;
+ memcpy(&buf_info32.user_buf.buf_idx,
+ &buf_info.user_buf.buf_idx,
+ sizeof(buf_info.user_buf.buf_idx));
+ if (copy_to_user((void __user *)up, &buf_info32,
+ sizeof(struct msm_buf_mngr_info32_t)))
+ return -EFAULT;
+ }
+ break;
+ case VIDIOC_MSM_BUF_MNGR_CONT_CMD: {
+ struct msm_buf_mngr_main_cont_info cont_cmd;
+
+ if (copy_from_user(&cont_cmd, (void __user *)up,
+ sizeof(struct msm_buf_mngr_main_cont_info)))
+ return -EFAULT;
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, &cont_cmd);
+ if (rc < 0) {
+ pr_debug("%s : Subdev cmd %d fail", __func__, cmd);
+ return rc;
+ }
+ }
+ break;
+ default:
+ pr_debug("%s : unsupported compat type", __func__);
+ return -ENOIOCTLCMD;
+ break;
+ }
+
+
+
+ return 0;
+}
+#endif
+
+static struct v4l2_subdev_core_ops msm_buf_mngr_subdev_core_ops = {
+ .ioctl = msm_buf_mngr_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_internal_ops
+ msm_generic_buf_mngr_subdev_internal_ops = {
+ .open = msm_generic_buf_mngr_open,
+ .close = msm_generic_buf_mngr_close,
+};
+
+static const struct v4l2_subdev_ops msm_buf_mngr_subdev_ops = {
+ .core = &msm_buf_mngr_subdev_core_ops,
+};
+
+static const struct of_device_id msm_buf_mngr_dt_match[] = {
+ {.compatible = "qcom,msm_buf_mngr"},
+ {}
+};
+
+static struct v4l2_file_operations msm_buf_v4l2_subdev_fops;
+
+static long msm_bmgr_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+}
+
+
+static long msm_buf_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_bmgr_subdev_do_ioctl);
+}
+
+static int32_t __init msm_buf_mngr_init(void)
+{
+ int32_t rc = 0;
+ msm_buf_mngr_dev = kzalloc(sizeof(*msm_buf_mngr_dev),
+ GFP_KERNEL);
+ if (WARN_ON(!msm_buf_mngr_dev)) {
+ pr_err("%s: not enough memory", __func__);
+ return -ENOMEM;
+ }
+ /* Sub-dev */
+ v4l2_subdev_init(&msm_buf_mngr_dev->subdev.sd,
+ &msm_buf_mngr_subdev_ops);
+ msm_cam_copy_v4l2_subdev_fops(&msm_buf_v4l2_subdev_fops);
+ msm_buf_v4l2_subdev_fops.unlocked_ioctl = msm_buf_subdev_fops_ioctl;
+#ifdef CONFIG_COMPAT
+ msm_buf_v4l2_subdev_fops.compat_ioctl32 =
+ msm_bmgr_subdev_fops_compat_ioctl;
+#endif
+ snprintf(msm_buf_mngr_dev->subdev.sd.name,
+ ARRAY_SIZE(msm_buf_mngr_dev->subdev.sd.name), "msm_buf_mngr");
+ msm_buf_mngr_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ v4l2_set_subdevdata(&msm_buf_mngr_dev->subdev.sd, msm_buf_mngr_dev);
+
+ media_entity_init(&msm_buf_mngr_dev->subdev.sd.entity, 0, NULL, 0);
+ msm_buf_mngr_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ msm_buf_mngr_dev->subdev.sd.entity.group_id =
+ MSM_CAMERA_SUBDEV_BUF_MNGR;
+ msm_buf_mngr_dev->subdev.sd.internal_ops =
+ &msm_generic_buf_mngr_subdev_internal_ops;
+ msm_buf_mngr_dev->subdev.close_seq = MSM_SD_CLOSE_4TH_CATEGORY;
+ rc = msm_sd_register(&msm_buf_mngr_dev->subdev);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto end;
+ }
+
+ msm_buf_mngr_dev->subdev.sd.devnode->fops = &msm_buf_v4l2_subdev_fops;
+
+ v4l2_subdev_notify(&msm_buf_mngr_dev->subdev.sd, MSM_SD_NOTIFY_REQ_CB,
+ &msm_buf_mngr_dev->vb2_ops);
+
+ INIT_LIST_HEAD(&msm_buf_mngr_dev->buf_qhead);
+ spin_lock_init(&msm_buf_mngr_dev->buf_q_spinlock);
+
+ mutex_init(&msm_buf_mngr_dev->cont_mutex);
+ INIT_LIST_HEAD(&msm_buf_mngr_dev->cont_qhead);
+ msm_buf_mngr_dev->ion_client =
+ msm_ion_client_create("msm_cam_generic_buf_mgr");
+ if (!msm_buf_mngr_dev->ion_client) {
+ pr_err("%s: Failed to create ion client\n", __func__);
+ rc = -EBADFD;
+ }
+
+end:
+ return rc;
+}
+
+static void __exit msm_buf_mngr_exit(void)
+{
+ msm_sd_unregister(&msm_buf_mngr_dev->subdev);
+ mutex_destroy(&msm_buf_mngr_dev->cont_mutex);
+ kfree(msm_buf_mngr_dev);
+}
+
+module_init(msm_buf_mngr_init);
+module_exit(msm_buf_mngr_exit);
+MODULE_DESCRIPTION("MSM Buffer Manager");
+MODULE_LICENSE("GPL v2");