android_kernel_samsung_msm8976/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c

690 lines
19 KiB
C

/* Copyright (c) 2013-2015, 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_get_buf(struct msm_buf_mngr_device *dev,
void __user *argp)
{
unsigned long flags;
struct msm_buf_mngr_user_buf_cont_info *cbuf, *cont_save;
unsigned int i;
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)) {
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);
mutex_unlock(&dev->cont_mutex);
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;
}
}
} else {
pr_err("Nothing mapped in user buf for %d,%d\n",
buf_info->session_id, buf_info->stream_id);
mutex_unlock(&dev->cont_mutex);
return -EINVAL;
}
mutex_unlock(&dev->cont_mutex);
}
return 0;
}
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_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 =%pK, session_id=%u, bufs->ses_id=%d, str_id=%d, idx=%d\n",
__func__, (void *)bufs, session->session,
bufs->session_id, bufs->stream_id,
bufs->vb2_buf->v4l2_buf.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)) {
uint32_t cnt = 0, tstream = -1;
int32_t found = -1;
struct msm_buf_mngr_user_buf_cont_info *cont_bufs, *cont_save;
do {
found = msm_buf_mngr_find_cont_stream(dev, &cnt,
&tstream, session);
if (found == -1)
break;
list_for_each_entry_safe(cont_bufs,
cont_save, &dev->cont_qhead, entry) {
if ((cont_bufs->sessid ==
session->session) &&
(cont_bufs->strid == tstream)) {
if (cnt == 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);
} while (found == 0);
}
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;
unsigned long 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 %lu\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) {
int cnt = cont_cmd->cnt;
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->main_fd == cont_cmd->cont_fd) &&
(bufs->cnt == cont_cmd->cnt) &&
(bufs->strid == cont_cmd->stream_id)) {
if (cnt == 1) {
ion_unmap_kernel(
dev->ion_client,
bufs->ion_handle);
ion_free(dev->ion_client,
bufs->ion_handle);
}
list_del_init(&bufs->entry);
kfree(bufs);
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)) {
list_for_each_entry_safe(bufs,
save, &dev->cont_qhead, entry) {
if ((bufs->sessid == cont_cmd->session_id) &&
(bufs->main_fd == cont_cmd->cont_fd) &&
(bufs->cnt == cont_cmd->cnt) &&
(bufs->strid == cont_cmd->stream_id)) {
list_del_init(&bufs->entry);
kfree(bufs);
i--;
if (i == 0)
break;
}
}
}
}
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_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_buf_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
msm_buf_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
msm_buf_v4l2_subdev_fops.unlocked_ioctl = msm_buf_subdev_fops_ioctl;
msm_buf_v4l2_subdev_fops.release = v4l2_subdev_fops.release;
msm_buf_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll;
#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");