829 lines
20 KiB
C
829 lines
20 KiB
C
/*
|
|
* 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);
|