M7350/kernel/drivers/video/msm/ba/msm_ba.c

829 lines
20 KiB
C
Raw Permalink Normal View History

2024-09-09 08:57:42 +00:00
/*
* Copyright (c) 2012-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.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/msm_ba.h>
#include "msm_ba_internal.h"
#include "msm_ba_debug.h"
#include "msm_ba_common.h"
#define MSM_BA_DEV_NAME "msm_ba_8064"
#define MSM_BA_MAX_EVENTS 10
int msm_ba_poll(void *instance, struct file *filp,
struct poll_table_struct *wait)
{
struct msm_ba_inst *inst = instance;
int rc = 0;
if (!inst)
return -EINVAL;
poll_wait(filp, &inst->event_handler.wait, wait);
if (v4l2_event_pending(&inst->event_handler))
rc |= POLLPRI;
return rc;
}
EXPORT_SYMBOL(msm_ba_poll);
int msm_ba_querycap(void *instance, struct v4l2_capability *cap)
{
struct msm_ba_inst *inst = instance;
if (!inst || !cap) {
dprintk(BA_ERR,
"Invalid input, inst = 0x%p, cap = 0x%p", inst, cap);
return -EINVAL;
}
strlcpy(cap->driver, MSM_BA_DRV_NAME, sizeof(cap->driver));
strlcpy(cap->card, MSM_BA_DEV_NAME, sizeof(cap->card));
cap->bus_info[0] = 0;
cap->version = MSM_BA_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
memset(cap->reserved, 0x00, sizeof(cap->reserved));
return 0;
}
EXPORT_SYMBOL(msm_ba_querycap);
int msm_ba_g_priority(void *instance, enum v4l2_priority *prio)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_input *ba_input = NULL;
int rc = 0;
if (!inst || !prio) {
dprintk(BA_ERR,
"Invalid prio, inst = 0x%p, prio = 0x%p", inst, prio);
return -EINVAL;
}
ba_input = msm_ba_find_input(inst->sd_input.index);
if (!ba_input) {
dprintk(BA_ERR, "Could not find input index: %d",
inst->sd_input.index);
return -EINVAL;
}
*prio = ba_input->prio;
return rc;
}
EXPORT_SYMBOL(msm_ba_g_priority);
int msm_ba_s_priority(void *instance, enum v4l2_priority prio)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_input *ba_input = NULL;
int rc = 0;
if (!inst)
return -EINVAL;
ba_input = msm_ba_find_input(inst->sd_input.index);
if (!ba_input) {
dprintk(BA_ERR, "Could not find input index: %d",
inst->sd_input.index);
return -EINVAL;
}
ba_input->prio = prio;
inst->input_prio = prio;
return rc;
}
EXPORT_SYMBOL(msm_ba_s_priority);
int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a)
{
struct msm_ba_inst *inst = instance;
if (!inst || !a)
return -EINVAL;
return -EINVAL;
}
EXPORT_SYMBOL(msm_ba_s_parm);
int msm_ba_enum_input(void *instance, struct v4l2_input *input)
{
struct msm_ba_input *ba_input = NULL;
struct msm_ba_inst *inst = instance;
int status = 0;
int rc = 0;
if (!inst || !input)
return -EINVAL;
if (input->index >= inst->dev_ctxt->num_inputs)
return -EINVAL;
ba_input = msm_ba_find_input(input->index);
if (ba_input) {
input->type = V4L2_INPUT_TYPE_CAMERA;
input->std = V4L2_STD_ALL;
strlcpy(input->name, ba_input->name, sizeof(input->name));
if (BA_INPUT_HDMI == ba_input->inputType ||
BA_INPUT_MHL == ba_input->inputType)
input->capabilities = V4L2_IN_CAP_CUSTOM_TIMINGS;
else
input->capabilities = V4L2_IN_CAP_STD;
dprintk(BA_DBG, "msm_ba_find_input: name %s", input->name);
/* get current signal status */
rc = v4l2_subdev_call(
ba_input->sd, video, g_input_status, &status);
if (rc) {
dprintk(BA_ERR, "g_input_status failed (%d) for sd: %s",
rc, ba_input->sd->name);
} else {
input->status = status;
ba_input->signal_status = status;
}
}
return rc;
}
EXPORT_SYMBOL(msm_ba_enum_input);
int msm_ba_g_input(void *instance, unsigned int *index)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_input *ba_input = NULL;
int rc = 0;
if (!inst || !index)
return -EINVAL;
/* First find current input */
ba_input = msm_ba_find_input(inst->sd_input.index);
if (ba_input) {
if (V4L2_PRIORITY_RECORD == ba_input->prio &&
inst->input_prio != ba_input->prio) {
inst->sd_input.index++;
}
}
*index = inst->sd_input.index;
return rc;
}
EXPORT_SYMBOL(msm_ba_g_input);
int msm_ba_s_input(void *instance, unsigned int index)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_input *ba_input = NULL;
int rc = 0;
int rc_sig = 0;
if (!inst)
return -EINVAL;
if (index > inst->dev_ctxt->num_inputs)
return -EINVAL;
/* Find requested input */
ba_input = msm_ba_find_input(index);
if (!ba_input) {
dprintk(BA_ERR, "Could not find input index: %d", index);
return -EINVAL;
}
if (!ba_input->sd) {
dprintk(BA_ERR, "No sd registered");
return -EINVAL;
}
if (ba_input->in_use &&
ba_input->prio == V4L2_PRIORITY_RECORD &&
ba_input->prio != inst->input_prio) {
dprintk(BA_WARN, "Input %d in use", index);
return -EBUSY;
}
if (ba_input->ba_out_in_use) {
if (inst->ext_ops) {
if (inst->restore) {
dprintk(BA_DBG, "Stream off in set input: %d",
ba_input->bridge_chip_ip);
rc_sig = v4l2_subdev_call(ba_input->sd,
video, s_stream, 0);
}
} else {
dprintk(BA_WARN, "Sd %d in use", ba_input->ba_out);
return -EBUSY;
}
}
rc = v4l2_subdev_call(ba_input->sd, video, s_routing,
ba_input->bridge_chip_ip, 0, 0);
if (rc) {
dprintk(BA_ERR, "Error: %d setting input: %d",
rc, ba_input->bridge_chip_ip);
return rc;
}
msm_ba_reset_ip_in_use_from_sd(ba_input->sd);
inst->sd_input.index = index;
strlcpy(inst->sd_input.name, ba_input->name,
sizeof(inst->sd_input.name));
inst->sd = ba_input->sd;
ba_input->in_use = 1;
/* get current signal status */
rc_sig = v4l2_subdev_call(
ba_input->sd, video, g_input_status, &ba_input->signal_status);
dprintk(BA_DBG, "Set input %s : %d - signal status: %d",
ba_input->name, index, ba_input->signal_status);
if (!rc_sig && !ba_input->signal_status) {
struct v4l2_event sd_event = {
.id = 0,
.type = V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK};
int *ptr = (int *)sd_event.u.data;
ptr[0] = index;
ptr[1] = ba_input->signal_status;
msm_ba_queue_v4l2_event(inst, &sd_event);
}
return rc;
}
EXPORT_SYMBOL(msm_ba_s_input);
int msm_ba_enum_output(void *instance, struct v4l2_output *output)
{
struct msm_ba_input *ba_input = NULL;
struct msm_ba_inst *inst = instance;
int rc = 0;
if (!inst || !output)
return -EINVAL;
ba_input = msm_ba_find_output(output->index);
if (!ba_input)
return -EINVAL;
output->type = V4L2_OUTPUT_TYPE_ANALOG;
output->std = V4L2_STD_ALL;
strlcpy(output->name, ba_input->sd->name, sizeof(output->name));
output->capabilities = V4L2_OUT_CAP_STD;
return rc;
}
EXPORT_SYMBOL(msm_ba_enum_output);
int msm_ba_g_output(void *instance, unsigned int *index)
{
struct msm_ba_inst *inst = instance;
int rc = 0;
if (!inst || !index)
return -EINVAL;
*index = inst->sd_output.index;
return rc;
}
EXPORT_SYMBOL(msm_ba_g_output);
int msm_ba_s_output(void *instance, unsigned int index)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_input *ba_input = NULL;
int rc = 0;
if (!inst)
return -EINVAL;
ba_input = msm_ba_find_output(index);
if (ba_input) {
if (!ba_input->sd) {
dprintk(BA_ERR, "No sd registered");
return -EINVAL;
}
ba_input->ba_node_addr = index;
ba_input->ba_out = index;
inst->sd_output.index = index;
inst->sd = ba_input->sd;
inst->sd_input.index = ba_input->ba_ip_idx;
} else {
dprintk(BA_ERR, "Could not find output index: %d", index);
rc = -EINVAL;
}
return rc;
}
EXPORT_SYMBOL(msm_ba_s_output);
int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
{
struct msm_ba_inst *inst = instance;
if (!inst || !f)
return -EINVAL;
return -EINVAL;
}
EXPORT_SYMBOL(msm_ba_enum_fmt);
int msm_ba_s_fmt(void *instance, struct v4l2_format *f)
{
struct msm_ba_inst *inst = instance;
if (!inst || !f)
return -EINVAL;
return -EINVAL;
}
EXPORT_SYMBOL(msm_ba_s_fmt);
int msm_ba_g_fmt(void *instance, struct v4l2_format *f)
{
struct msm_ba_inst *inst = instance;
struct v4l2_subdev *sd = NULL;
struct msm_ba_input *ba_input = NULL;
v4l2_std_id new_std = V4L2_STD_UNKNOWN;
struct v4l2_dv_timings sd_dv_timings;
struct v4l2_mbus_framefmt sd_mbus_fmt;
int rc = 0;
if (!inst || !f)
return -EINVAL;
sd = inst->sd;
if (!sd) {
dprintk(BA_ERR, "No sd registered");
return -EINVAL;
}
ba_input = msm_ba_find_input(inst->sd_input.index);
if (!ba_input) {
dprintk(BA_ERR, "Could not find input index: %d",
inst->sd_input.index);
return -EINVAL;
}
if (BA_INPUT_HDMI != ba_input->inputType) {
rc = v4l2_subdev_call(sd, video, querystd, &new_std);
if (rc) {
dprintk(BA_ERR, "querystd failed %d for sd: %s",
rc, sd->name);
return -EINVAL;
}
inst->sd_input.std = new_std;
} else {
rc = v4l2_subdev_call(sd, video, g_dv_timings, &sd_dv_timings);
if (rc)
dprintk(BA_ERR, "g_dv_timings failed %d for sd: %s",
rc, sd->name);
}
rc = v4l2_subdev_call(sd, video, g_mbus_fmt, &sd_mbus_fmt);
if (rc) {
dprintk(BA_ERR, "g_mbus_fmt failed %d for sd: %s",
rc, sd->name);
} else {
f->fmt.pix.height = sd_mbus_fmt.height;
f->fmt.pix.width = sd_mbus_fmt.width;
switch (sd_mbus_fmt.code) {
case V4L2_MBUS_FMT_YUYV8_2X8:
f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
break;
case V4L2_MBUS_FMT_YVYU8_2X8:
f->fmt.pix.pixelformat = V4L2_PIX_FMT_YVYU;
break;
case V4L2_MBUS_FMT_VYUY8_2X8:
f->fmt.pix.pixelformat = V4L2_PIX_FMT_VYUY;
break;
case V4L2_MBUS_FMT_UYVY8_2X8:
f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
break;
default:
dprintk(BA_ERR, "Unknown sd_mbus_fmt.code 0x%x",
sd_mbus_fmt.code);
f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
break;
}
}
return rc;
}
EXPORT_SYMBOL(msm_ba_g_fmt);
int msm_ba_s_ctrl(void *instance, struct v4l2_control *control)
{
struct msm_ba_inst *inst = instance;
if (!inst || !control)
return -EINVAL;
return v4l2_s_ctrl(NULL, &inst->ctrl_handler, control);
}
EXPORT_SYMBOL(msm_ba_s_ctrl);
int msm_ba_g_ctrl(void *instance, struct v4l2_control *control)
{
struct msm_ba_inst *inst = instance;
if (!inst || !control)
return -EINVAL;
return v4l2_g_ctrl(&inst->ctrl_handler, control);
}
EXPORT_SYMBOL(msm_ba_g_ctrl);
int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
{
struct msm_ba_inst *inst = instance;
if (!inst || !control)
return -EINVAL;
return -EINVAL;
}
EXPORT_SYMBOL(msm_ba_s_ext_ctrl);
int msm_ba_streamon(void *instance, enum v4l2_buf_type i)
{
struct msm_ba_inst *inst = instance;
struct v4l2_subdev *sd = NULL;
int rc = 0;
if (!inst)
return -EINVAL;
sd = inst->sd;
if (!sd) {
dprintk(BA_ERR, "No sd registered");
return -EINVAL;
}
rc = v4l2_subdev_call(sd, video, s_stream, 1);
if (rc)
dprintk(BA_ERR, "Stream on failed on input: %d",
inst->sd_input.index);
else
msm_ba_set_out_in_use(sd, 1);
dprintk(BA_DBG, "Stream on: %s : %d",
inst->sd_input.name, inst->sd_input.index);
return rc;
}
EXPORT_SYMBOL(msm_ba_streamon);
int msm_ba_streamoff(void *instance, enum v4l2_buf_type i)
{
struct msm_ba_inst *inst = instance;
struct v4l2_subdev *sd = NULL;
int rc = 0;
if (!inst)
return -EINVAL;
sd = inst->sd;
if (!sd) {
dprintk(BA_ERR, "No sd registered");
return -EINVAL;
}
rc = v4l2_subdev_call(sd, video, s_stream, 0);
if (rc)
dprintk(BA_ERR, "Stream off failed on input: %d",
inst->sd_input.index);
dprintk(BA_DBG, "Stream off: %s : %d",
inst->sd_input.name, inst->sd_input.index);
msm_ba_set_out_in_use(sd, 0);
return rc;
}
EXPORT_SYMBOL(msm_ba_streamoff);
int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_input *ba_input = NULL;
int rc = 0;
if (!inst)
return -EINVAL;
if (BA_SR_RESTORE_IP == sr &&
inst->restore) {
dprintk(BA_DBG, "Restoring input: %d",
inst->saved_input);
rc = v4l2_subdev_call(inst->sd, video, s_routing,
inst->saved_input, 0, 0);
if (rc)
dprintk(BA_ERR, "Failed to restore input: %d",
inst->saved_input);
msm_ba_reset_ip_in_use_from_sd(inst->sd);
ba_input = msm_ba_find_input_from_sd(inst->sd,
inst->saved_input);
if (ba_input)
ba_input->in_use = 1;
else
dprintk(BA_WARN, "Could not find input %d from sd: %s",
inst->saved_input, inst->sd->name);
inst->restore = 0;
inst->saved_input = BA_IP_MAX;
dprintk(BA_DBG, "Stream on from save restore");
rc = msm_ba_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
} else if (BA_SR_SAVE_IP == sr) {
ba_input = msm_ba_find_input(inst->sd_input.index);
if (ba_input && ba_input->ba_out_in_use) {
inst->restore = 1;
inst->saved_input =
msm_ba_find_ip_in_use_from_sd(inst->sd);
if (inst->saved_input == BA_IP_MAX) {
dprintk(BA_ERR, "Could not find input to save");
inst->restore = 0;
}
dprintk(BA_DBG, "Saving input: %d",
inst->saved_input);
rc = -EBUSY;
}
if (!ba_input)
dprintk(BA_WARN, "Couldn't find input idx %d to save",
inst->sd_input.index);
} else {
dprintk(BA_DBG, "Nothing to do in save and restore");
}
return rc;
}
EXPORT_SYMBOL(msm_ba_save_restore_input);
void msm_ba_release_subdev_node(struct video_device *vdev)
{
struct v4l2_subdev *sd = video_get_drvdata(vdev);
sd->devnode = NULL;
kfree(vdev);
}
static int msm_ba_register_v4l2_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
struct video_device *vdev;
int rc = 0;
dprintk(BA_DBG, "Enter %s: v4l2_dev 0x%p, v4l2_subdev 0x%p",
__func__, v4l2_dev, sd);
if (NULL == v4l2_dev || NULL == sd || !sd->name[0]) {
dprintk(BA_ERR, "Invalid input");
return -EINVAL;
}
rc = v4l2_device_register_subdev(v4l2_dev, sd);
if (rc < 0) {
dprintk(BA_ERR,
"%s(%d), V4L2 subdev register failed for %s rc: %d",
__func__, __LINE__, sd->name, rc);
return rc;
}
if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) {
vdev = video_device_alloc();
if (NULL == vdev) {
dprintk(BA_ERR, "%s Not enough memory", __func__);
return -ENOMEM;
}
video_set_drvdata(vdev, sd);
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
vdev->release = msm_ba_release_subdev_node;
rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
if (rc < 0) {
dprintk(BA_ERR, "%s Error registering video device %s",
__func__, sd->name);
kfree(vdev);
} else {
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.info.v4l.major = VIDEO_MAJOR;
sd->entity.info.v4l.minor = vdev->minor;
sd->entity.name = video_device_node_name(vdev);
#endif
sd->devnode = vdev;
}
}
dprintk(BA_DBG, "Exit %s with rc: %d", __func__, rc);
return rc;
}
int msm_ba_register_subdev_node(struct v4l2_subdev *sd)
{
struct ba_ctxt *ba_ctxt;
int rc = 0;
ba_ctxt = msm_ba_get_ba_context();
rc = msm_ba_register_v4l2_subdev(&ba_ctxt->dev_ctxt->v4l2_dev, sd);
if (!rc) {
ba_ctxt->dev_ctxt->num_ba_subdevs++;
msm_ba_add_inputs(sd);
}
return rc;
}
EXPORT_SYMBOL(msm_ba_register_subdev_node);
static void __msm_ba_sd_unregister(struct v4l2_subdev *sub_dev)
{
struct ba_ctxt *ba_ctxt;
ba_ctxt = msm_ba_get_ba_context();
mutex_lock(&ba_ctxt->ba_cs);
v4l2_device_unregister_subdev(sub_dev);
ba_ctxt->dev_ctxt->num_ba_subdevs--;
msm_ba_del_inputs(sub_dev);
dprintk(BA_DBG, "%s(%d), BA Unreg Sub Device : num ba devices %d : %s",
__func__, __LINE__,
ba_ctxt->dev_ctxt->num_ba_subdevs, sub_dev->name);
mutex_unlock(&ba_ctxt->ba_cs);
}
int msm_ba_unregister_subdev_node(struct v4l2_subdev *sub_dev)
{
struct ba_ctxt *ba_ctxt;
ba_ctxt = msm_ba_get_ba_context();
if (!ba_ctxt || !ba_ctxt->dev_ctxt)
return -ENODEV;
if (!sub_dev)
return -EINVAL;
__msm_ba_sd_unregister(sub_dev);
return 0;
}
EXPORT_SYMBOL(msm_ba_unregister_subdev_node);
static int msm_ba_setup_event_queue(void *inst,
struct video_device *pvdev)
{
int rc = 0;
struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
v4l2_fh_init(&ba_inst->event_handler, pvdev);
v4l2_fh_add(&ba_inst->event_handler);
return rc;
}
int msm_ba_subscribe_event(void *inst,
const struct v4l2_event_subscription *sub)
{
int rc = 0;
struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
if (!inst || !sub)
return -EINVAL;
rc = v4l2_event_subscribe(&ba_inst->event_handler, sub,
MSM_BA_MAX_EVENTS, NULL);
return rc;
}
EXPORT_SYMBOL(msm_ba_subscribe_event);
int msm_ba_unsubscribe_event(void *inst,
const struct v4l2_event_subscription *sub)
{
int rc = 0;
struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
if (!inst || !sub)
return -EINVAL;
rc = v4l2_event_unsubscribe(&ba_inst->event_handler, sub);
return rc;
}
EXPORT_SYMBOL(msm_ba_unsubscribe_event);
void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
unsigned int notification, void *arg)
{
struct msm_ba_dev *dev_ctxt = NULL;
struct msm_ba_input *ba_input;
struct msm_ba_sd_event *ba_sd_event;
int bridge_chip_ip;
if (!sd || !arg) {
dprintk(BA_ERR, "%s null v4l2 subdev or arg", __func__);
return;
}
bridge_chip_ip = ((int *)((struct v4l2_event *)arg)->u.data)[0];
ba_input = msm_ba_find_input_from_sd(sd, bridge_chip_ip);
if (!ba_input) {
dprintk(BA_WARN, "Could not find input %d from sd: %s",
bridge_chip_ip, sd->name);
return;
}
ba_sd_event = kzalloc(sizeof(*ba_sd_event), GFP_KERNEL);
if (!ba_sd_event) {
dprintk(BA_ERR, "%s out of memory", __func__);
return;
}
dev_ctxt = get_ba_dev();
ba_sd_event->sd_event = *(struct v4l2_event *)arg;
((int *)ba_sd_event->sd_event.u.data)[0] = ba_input->ba_ip_idx;
mutex_lock(&dev_ctxt->dev_cs);
list_add_tail(&ba_sd_event->list, &dev_ctxt->sd_events);
mutex_unlock(&dev_ctxt->dev_cs);
schedule_delayed_work(&dev_ctxt->sd_events_work, 0);
}
void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops)
{
struct msm_ba_inst *inst = NULL;
struct msm_ba_dev *dev_ctxt = NULL;
int rc = 0;
dev_ctxt = get_ba_dev();
inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst) {
dprintk(BA_ERR, "Failed to allocate memory");
return NULL;
}
mutex_init(&inst->inst_cs);
init_waitqueue_head(&inst->kernel_event_queue);
inst->state = MSM_BA_DEV_UNINIT_DONE;
inst->dev_ctxt = dev_ctxt;
rc = msm_ba_ctrl_init(inst);
if (rc) {
dprintk(BA_WARN, "Failed to initialize controls: %d", rc);
msm_ba_ctrl_deinit(inst);
}
if (!list_empty(&(inst->dev_ctxt->v4l2_dev.subdevs)))
inst->sd = list_first_entry(&(inst->dev_ctxt->v4l2_dev.subdevs),
struct v4l2_subdev, list);
mutex_lock(&dev_ctxt->dev_cs);
list_add_tail(&inst->list, &dev_ctxt->instances);
mutex_unlock(&dev_ctxt->dev_cs);
dev_ctxt->state = BA_DEV_INIT;
dev_ctxt->state = BA_DEV_INIT_DONE;
inst->state = MSM_BA_DEV_INIT_DONE;
inst->sd_input.index = 0;
inst->input_prio = V4L2_PRIORITY_DEFAULT;
inst->debugfs_root =
msm_ba_debugfs_init_inst(inst, dev_ctxt->debugfs_root);
inst->ext_ops = ext_ops;
msm_ba_setup_event_queue(inst, dev_ctxt->vdev);
return inst;
}
EXPORT_SYMBOL(msm_ba_open);
int msm_ba_close(void *instance)
{
struct msm_ba_inst *inst = instance;
struct msm_ba_inst *temp;
struct msm_ba_dev *dev_ctxt;
struct list_head *ptr;
struct list_head *next;
int rc = 0;
if (!inst)
return -EINVAL;
v4l2_fh_del(&inst->event_handler);
dev_ctxt = inst->dev_ctxt;
mutex_lock(&dev_ctxt->dev_cs);
list_for_each_safe(ptr, next, &dev_ctxt->instances) {
temp = list_entry(ptr, struct msm_ba_inst, list);
if (temp == inst)
list_del(&inst->list);
}
mutex_unlock(&dev_ctxt->dev_cs);
msm_ba_ctrl_deinit(inst);
debugfs_remove_recursive(inst->debugfs_root);
dprintk(BA_DBG, "Closed BA instance: %p", inst);
kfree(inst);
return rc;
}
EXPORT_SYMBOL(msm_ba_close);