M7350/kernel/drivers/media/platform/msm/camera_v1/msm.c
2024-09-09 08:52:07 +00:00

1591 lines
42 KiB
C

/* Copyright (c) 2011-2012, 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/workqueue.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/ioctl.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include "msm.h"
#include "msm_cam_server.h"
#include "msm_sensor.h"
#include "msm_actuator.h"
#include "msm_camera_eeprom.h"
#define MSM_MAX_CAMERA_SENSORS 5
#ifdef CONFIG_MSM_CAMERA_DEBUG
#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
#else
#define D(fmt, args...) do {} while (0)
#endif
static unsigned msm_camera_v4l2_nr = -1;
static int vnode_count;
module_param(msm_camera_v4l2_nr, uint, 0644);
MODULE_PARM_DESC(msm_camera_v4l2_nr, "videoX start number, -1 is autodetect");
/* callback function from all subdevices of a msm_cam_v4l2_device */
static void msm_cam_v4l2_subdev_notify(struct v4l2_subdev *sd,
unsigned int notification, void *arg)
{
struct msm_cam_v4l2_device *pcam;
struct msm_cam_media_controller *pmctl;
if (sd == NULL)
return;
pcam = to_pcam(sd->v4l2_dev);
if (pcam == NULL)
return;
pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return;
}
/*
*
* implementation of v4l2_ioctl_ops
*
*/
static int msm_camera_v4l2_querycap(struct file *f, void *pctx,
struct v4l2_capability *pcaps)
{
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
/* some other day, some other time */
/*cap->version = LINUX_VERSION_CODE; */
pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
static int msm_camera_v4l2_queryctrl(struct file *f, void *pctx,
struct v4l2_queryctrl *pqctrl)
{
int rc = 0;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_q_ctrl(pcam, pqctrl);
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_private_general(struct file *f, void *pctx,
struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
{
int rc = 0;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
WARN_ON(pctx != f->private_data);
rc = msm_server_private_general(pcam, ioctl_ptr);
if (rc < 0)
pr_err("%s: Private command failed rc %d\n", __func__, rc);
return rc;
}
static int msm_camera_v4l2_private_g_ctrl(struct file *f, void *pctx,
struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
{
int rc = -EINVAL;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
switch (ioctl_ptr->id) {
case MSM_V4L2_PID_INST_HANDLE:
COPY_TO_USER(rc, (void __user *)ioctl_ptr->ioctl_ptr,
(void *)&pcam_inst->inst_handle, sizeof(uint32_t));
if (rc)
ERR_COPY_TO_USER();
break;
default:
pr_err("%s Unsupported ioctl %d ", __func__, ioctl_ptr->id);
break;
}
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_g_ctrl(struct file *f, void *pctx,
struct v4l2_control *c)
{
int rc = 0;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_g_ctrl(pcam, c);
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_private_s_ctrl(struct file *f, void *pctx,
struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
{
int rc = -EINVAL;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
switch (ioctl_ptr->id) {
case MSM_V4L2_PID_CTRL_CMD:
rc = msm_server_proc_ctrl_cmd(pcam, ioctl_ptr, 1);
break;
}
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_s_ctrl(struct file *f, void *pctx,
struct v4l2_control *ctrl)
{
int rc = 0;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
switch (ctrl->id) {
case MSM_V4L2_PID_MMAP_INST:
D("%s: mmap_inst=(0x%p, %d)\n",
__func__, pcam_inst, pcam_inst->my_index);
pcam_inst->is_mem_map_inst = 1;
break;
default:
if (ctrl->id == MSM_V4L2_PID_CAM_MODE)
pcam->op_mode = ctrl->value;
rc = msm_server_s_ctrl(pcam, ctrl);
break;
}
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_reqbufs(struct file *f, void *pctx,
struct v4l2_requestbuffers *pb)
{
int rc = 0, i, j;
struct msm_cam_v4l2_dev_inst *pcam_inst;
struct msm_cam_media_controller *pmctl;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam_inst->inst_lock);
if (!pcam_inst->vbqueue_initialized && pb->count) {
pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (pmctl == NULL) {
pr_err("%s Invalid mctl ptr", __func__);
return -EINVAL;
}
pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
pb->type);
pcam_inst->vbqueue_initialized = 1;
}
rc = vb2_reqbufs(&pcam_inst->vid_bufq, pb);
if (rc < 0) {
pr_err("%s reqbufs failed %d ", __func__, rc);
mutex_unlock(&pcam_inst->inst_lock);
return rc;
}
if (!pb->count) {
/* Deallocation. free buf_offset array */
D("%s Inst %p freeing buffer offsets array",
__func__, pcam_inst);
for (j = 0 ; j < pcam_inst->buf_count ; j++) {
kfree(pcam_inst->buf_offset[j]);
pcam_inst->buf_offset[j] = NULL;
}
kfree(pcam_inst->buf_offset);
pcam_inst->buf_offset = NULL;
/* If the userspace has deallocated all the
* buffers, then release the vb2 queue */
if (pcam_inst->vbqueue_initialized) {
vb2_queue_release(&pcam_inst->vid_bufq);
pcam_inst->vbqueue_initialized = 0;
}
} else {
D("%s Inst %p Allocating buf_offset array",
__func__, pcam_inst);
/* Allocation. allocate buf_offset array */
pcam_inst->buf_offset = (struct msm_cam_buf_offset **)
kzalloc(pb->count * sizeof(struct msm_cam_buf_offset *),
GFP_KERNEL);
if (!pcam_inst->buf_offset) {
pr_err("%s out of memory ", __func__);
mutex_unlock(&pcam_inst->inst_lock);
return -ENOMEM;
}
for (i = 0; i < pb->count; i++) {
pcam_inst->buf_offset[i] =
kzalloc(sizeof(struct msm_cam_buf_offset) *
pcam_inst->plane_info.num_planes, GFP_KERNEL);
if (!pcam_inst->buf_offset[i]) {
pr_err("%s out of memory ", __func__);
for (j = i-1 ; j >= 0; j--) {
kfree(pcam_inst->buf_offset[j]);
pcam_inst->buf_offset[j] = NULL;
}
kfree(pcam_inst->buf_offset);
pcam_inst->buf_offset = NULL;
mutex_unlock(&pcam_inst->inst_lock);
return -ENOMEM;
}
}
}
pcam_inst->buf_count = pb->count;
mutex_unlock(&pcam_inst->inst_lock);
return rc;
}
static int msm_camera_v4l2_querybuf(struct file *f, void *pctx,
struct v4l2_buffer *pb)
{
/* get the video device */
int rc = 0;
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam_inst->inst_lock);
rc = vb2_querybuf(&pcam_inst->vid_bufq, pb);
mutex_unlock(&pcam_inst->inst_lock);
return rc;
}
static int msm_camera_v4l2_qbuf(struct file *f, void *pctx,
struct v4l2_buffer *pb)
{
int rc = 0, i = 0;
/* get the camera device */
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s Inst=%p, mode=%d, idx=%d\n", __func__, pcam_inst,
pcam_inst->image_mode, pb->index);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam_inst->inst_lock);
if (!pcam_inst->buf_offset) {
pr_err("%s Buffer is already released. Returning.\n", __func__);
mutex_unlock(&pcam_inst->inst_lock);
return -EINVAL;
}
if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
/* Reject the buffer if planes array was not allocated */
if (pb->m.planes == NULL) {
pr_err("%s Planes array is null\n", __func__);
mutex_unlock(&pcam_inst->inst_lock);
return -EINVAL;
}
for (i = 0; i < pcam_inst->plane_info.num_planes; i++) {
D("%s stored offsets for plane %d as"
"addr offset %d, data offset %d\n",
__func__, i, pb->m.planes[i].reserved[0],
pb->m.planes[i].data_offset);
pcam_inst->buf_offset[pb->index][i].data_offset =
pb->m.planes[i].data_offset;
pcam_inst->buf_offset[pb->index][i].addr_offset =
pb->m.planes[i].reserved[0];
}
} else {
D("%s stored reserved info %d\n", __func__, pb->reserved);
pcam_inst->buf_offset[pb->index][0].addr_offset = pb->reserved;
}
rc = vb2_qbuf(&pcam_inst->vid_bufq, pb);
D("%s, videobuf_qbuf mode %d and idx %d returns %d\n", __func__,
pcam_inst->image_mode, pb->index, rc);
mutex_unlock(&pcam_inst->inst_lock);
return rc;
}
static int msm_camera_v4l2_dqbuf(struct file *f, void *pctx,
struct v4l2_buffer *pb)
{
int rc = 0, i = 0;
/* get the camera device */
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam_inst->inst_lock);
if (0 == pcam_inst->streamon) {
mutex_unlock(&pcam_inst->inst_lock);
return -EACCES;
}
rc = vb2_dqbuf(&pcam_inst->vid_bufq, pb, f->f_flags & O_NONBLOCK);
D("%s, videobuf_dqbuf returns %d\n", __func__, rc);
if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
/* Reject the buffer if planes array was not allocated */
if (pb->m.planes == NULL) {
pr_err("%s Planes array is null\n", __func__);
mutex_unlock(&pcam_inst->inst_lock);
return -EINVAL;
}
for (i = 0; i < pcam_inst->plane_info.num_planes; i++) {
pb->m.planes[i].data_offset =
pcam_inst->buf_offset[pb->index][i].data_offset;
pb->m.planes[i].reserved[0] =
pcam_inst->buf_offset[pb->index][i].addr_offset;
D("%s stored offsets for plane %d as "
"addr offset %d, data offset %d\n",
__func__, i, pb->m.planes[i].reserved[0],
pb->m.planes[i].data_offset);
}
} else {
D("%s stored reserved info %d\n", __func__, pb->reserved);
pb->reserved = pcam_inst->buf_offset[pb->index][0].addr_offset;
}
mutex_unlock(&pcam_inst->inst_lock);
return rc;
}
static int msm_camera_v4l2_streamon(struct file *f, void *pctx,
enum v4l2_buf_type buf_type)
{
int rc = 0;
/* get the camera device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s Inst %p\n", __func__, pcam_inst);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
mutex_lock(&pcam_inst->inst_lock);
if ((buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
(buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
pr_err("%s Invalid buffer type ", __func__);
mutex_unlock(&pcam_inst->inst_lock);
mutex_unlock(&pcam->vid_lock);
return -EINVAL;
}
D("%s Calling videobuf_streamon", __func__);
/* if HW streaming on is successful, start buffer streaming */
rc = vb2_streamon(&pcam_inst->vid_bufq, buf_type);
D("%s, videobuf_streamon returns %d\n", __func__, rc);
/* turn HW (VFE/sensor) streaming */
pcam_inst->streamon = 1;
rc = msm_server_streamon(pcam, pcam_inst->my_index);
mutex_unlock(&pcam_inst->inst_lock);
mutex_unlock(&pcam->vid_lock);
D("%s rc = %d\n", __func__, rc);
return rc;
}
static int msm_camera_v4l2_streamoff(struct file *f, void *pctx,
enum v4l2_buf_type buf_type)
{
int rc = 0;
/* get the camera device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s Inst %p\n", __func__, pcam_inst);
WARN_ON(pctx != f->private_data);
if ((buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
(buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
pr_err("%s Invalid buffer type ", __func__);
return -EINVAL;
}
/* first turn of HW (VFE/sensor) streaming so that buffers are
not in use when we free the buffers */
mutex_lock(&pcam->vid_lock);
mutex_lock(&pcam_inst->inst_lock);
pcam_inst->streamon = 0;
if (msm_server_get_usecount() > 0)
rc = msm_server_streamoff(pcam, pcam_inst->my_index);
if (rc < 0)
pr_err("%s: hw failed to stop streaming\n", __func__);
/* stop buffer streaming */
rc = vb2_streamoff(&pcam_inst->vid_bufq, buf_type);
D("%s, videobuf_streamoff returns %d\n", __func__, rc);
mutex_unlock(&pcam_inst->inst_lock);
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_enum_fmt_cap(struct file *f, void *pctx,
struct v4l2_fmtdesc *pfmtdesc)
{
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
const struct msm_isp_color_fmt *isp_fmt;
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
if ((pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
(pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
return -EINVAL;
if (pfmtdesc->index >= pcam->num_fmts)
return -EINVAL;
isp_fmt = &pcam->usr_fmts[pfmtdesc->index];
if (isp_fmt->name)
strlcpy(pfmtdesc->description, isp_fmt->name,
sizeof(pfmtdesc->description));
pfmtdesc->pixelformat = isp_fmt->fourcc;
D("%s: [%d] 0x%x, %s\n", __func__, pfmtdesc->index,
isp_fmt->fourcc, isp_fmt->name);
return 0;
}
static int msm_camera_v4l2_g_fmt_cap(struct file *f,
void *pctx, struct v4l2_format *pfmt)
{
int rc = 0;
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
rc = msm_server_get_fmt(pcam, pcam_inst->my_index, pfmt);
D("%s: current_fmt->fourcc: 0x%08x, rc = %d\n", __func__,
pfmt->fmt.pix.pixelformat, rc);
return rc;
}
static int msm_camera_v4l2_g_fmt_cap_mplane(struct file *f,
void *pctx, struct v4l2_format *pfmt)
{
int rc = 0;
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
rc = msm_server_get_fmt_mplane(pcam, pcam_inst->my_index, pfmt);
D("%s: current_fmt->fourcc: 0x%08x, rc = %d\n", __func__,
pfmt->fmt.pix_mp.pixelformat, rc);
return rc;
}
/* This function will readjust the format parameters based in HW
capabilities. Called by s_fmt_cap
*/
static int msm_camera_v4l2_try_fmt_cap(struct file *f, void *pctx,
struct v4l2_format *pfmt)
{
int rc = 0;
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_try_fmt(pcam, pfmt);
if (rc)
pr_err("Format %x not found, rc = %d\n",
pfmt->fmt.pix.pixelformat, rc);
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_try_fmt_cap_mplane(struct file *f, void *pctx,
struct v4l2_format *pfmt)
{
int rc = 0;
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_try_fmt_mplane(pcam, pfmt);
if (rc)
pr_err("Format %x not found, rc = %d\n",
pfmt->fmt.pix_mp.pixelformat, rc);
mutex_unlock(&pcam->vid_lock);
return rc;
}
/* This function will reconfig the v4l2 driver and HW device, it should be
called after the streaming is stopped.
*/
static int msm_camera_v4l2_s_fmt_cap(struct file *f, void *pctx,
struct v4l2_format *pfmt)
{
int rc;
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
D("%s, inst=0x%x,idx=%d,priv = 0x%p\n",
__func__, (u32)pcam_inst, pcam_inst->my_index,
(void *)pfmt->fmt.pix.priv);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_set_fmt(pcam, pcam_inst->my_index, pfmt);
if (rc < 0) {
pr_err("%s: msm_server_set_fmt Error: %d\n",
__func__, rc);
}
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_s_fmt_cap_mplane(struct file *f, void *pctx,
struct v4l2_format *pfmt)
{
int rc;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s Inst %p\n", __func__, pcam_inst);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_set_fmt_mplane(pcam, pcam_inst->my_index, pfmt);
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_g_jpegcomp(struct file *f, void *pctx,
struct v4l2_jpegcompression *pcomp)
{
int rc = -EINVAL;
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
return rc;
}
static int msm_camera_v4l2_s_jpegcomp(struct file *f, void *pctx,
struct v4l2_jpegcompression *pcomp)
{
int rc = -EINVAL;
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
return rc;
}
static int msm_camera_v4l2_g_crop(struct file *f, void *pctx,
struct v4l2_crop *crop)
{
int rc = -EINVAL;
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
mutex_lock(&pcam->vid_lock);
rc = msm_server_get_crop(pcam, pcam_inst->my_index, crop);
mutex_unlock(&pcam->vid_lock);
return rc;
}
static int msm_camera_v4l2_s_crop(struct file *f, void *pctx,
struct v4l2_crop *a)
{
int rc = -EINVAL;
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
return rc;
}
/* Stream type-dependent parameter ioctls */
static int msm_camera_v4l2_g_parm(struct file *f, void *pctx,
struct v4l2_streamparm *a)
{
int rc = -EINVAL;
return rc;
}
static int msm_vidbuf_get_path(u32 extendedmode)
{
switch (extendedmode) {
case MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL:
return OUTPUT_TYPE_T;
case MSM_V4L2_EXT_CAPTURE_MODE_MAIN:
return OUTPUT_TYPE_S;
case MSM_V4L2_EXT_CAPTURE_MODE_VIDEO:
return OUTPUT_TYPE_V;
case MSM_V4L2_EXT_CAPTURE_MODE_RDI:
return OUTPUT_TYPE_R;
case MSM_V4L2_EXT_CAPTURE_MODE_RDI1:
return OUTPUT_TYPE_R1;
case MSM_V4L2_EXT_CAPTURE_MODE_AEC:
return OUTPUT_TYPE_SAEC;
case MSM_V4L2_EXT_CAPTURE_MODE_AF:
return OUTPUT_TYPE_SAFC;
case MSM_V4L2_EXT_CAPTURE_MODE_AWB:
return OUTPUT_TYPE_SAWB;
case MSM_V4L2_EXT_CAPTURE_MODE_IHIST:
return OUTPUT_TYPE_IHST;
case MSM_V4L2_EXT_CAPTURE_MODE_CSTA:
return OUTPUT_TYPE_CSTA;
case MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT:
case MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW:
default:
return OUTPUT_TYPE_P;
}
}
static int msm_camera_v4l2_s_parm(struct file *f, void *pctx,
struct v4l2_streamparm *a)
{
int rc = 0;
int is_bayer_sensor = 0;
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
pcam_inst->image_mode = (a->parm.capture.extendedmode & 0x7F);
SET_DEVID_MODE(pcam_inst->inst_handle, pcam_inst->pcam->vnode_id);
SET_IMG_MODE(pcam_inst->inst_handle, pcam_inst->image_mode);
SET_VIDEO_INST_IDX(pcam_inst->inst_handle, pcam_inst->my_index);
pcam_inst->pcam->dev_inst_map[pcam_inst->image_mode] = pcam_inst;
pcam_inst->path = msm_vidbuf_get_path(pcam_inst->image_mode);
if (pcam_inst->pcam->sdata->sensor_type == BAYER_SENSOR)
is_bayer_sensor = 1;
rc = msm_cam_server_config_interface_map(pcam_inst->image_mode,
pcam_inst->pcam->mctl_handle, pcam_inst->pcam->vnode_id,
is_bayer_sensor);
D("%s path=%d, rc=%d\n", __func__,
pcam_inst->path, rc);
return rc;
}
static int msm_camera_v4l2_subscribe_event(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
int rc = 0;
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst =
(struct msm_cam_v4l2_dev_inst *)container_of(fh,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s:fh = 0x%x, type = 0x%x\n", __func__, (u32)fh, sub->type);
if (pcam_inst->my_index != 0)
return -EINVAL;
if (sub->type == V4L2_EVENT_ALL)
sub->type = V4L2_EVENT_PRIVATE_START+MSM_CAM_APP_NOTIFY_EVENT;
rc = v4l2_event_subscribe(fh, sub, 30);
if (rc < 0)
D("%s: failed for evtType = 0x%x, rc = %d\n",
__func__, sub->type, rc);
return rc;
}
static int msm_camera_v4l2_unsubscribe_event(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
int rc = 0;
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst =
(struct msm_cam_v4l2_dev_inst *)container_of(fh,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s: fh = 0x%x\n", __func__, (u32)fh);
if (pcam_inst->my_index != 0)
return -EINVAL;
rc = v4l2_event_unsubscribe(fh, sub);
D("%s: rc = %d\n", __func__, rc);
return rc;
}
static long msm_camera_v4l2_private_ioctl(struct file *file, void *fh,
bool valid_prio, int cmd,
void *arg)
{
int rc = -EINVAL;
struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
struct msm_cam_v4l2_device *pcam = video_drvdata(file);
D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
switch (cmd) {
case MSM_CAM_V4L2_IOCTL_PRIVATE_S_CTRL:
rc = msm_camera_v4l2_private_s_ctrl(file, fh, ioctl_ptr);
break;
case MSM_CAM_V4L2_IOCTL_PRIVATE_G_CTRL:
rc = msm_camera_v4l2_private_g_ctrl(file, fh, ioctl_ptr);
break;
case MSM_CAM_V4L2_IOCTL_PRIVATE_GENERAL:
rc = msm_camera_v4l2_private_general(file, fh, ioctl_ptr);
break;
case MSM_CAM_V4L2_IOCTL_GET_EVENT_PAYLOAD: {
struct msm_queue_cmd *event_cmd;
void *payload;
mutex_lock(&pcam->event_lock);
event_cmd = msm_dequeue(&pcam->eventData_q, list_eventdata);
if (!event_cmd) {
pr_err("%s: No event payload\n", __func__);
rc = -EINVAL;
mutex_unlock(&pcam->event_lock);
return rc;
}
payload = event_cmd->command;
if (event_cmd->trans_code != ioctl_ptr->trans_code) {
pr_err("%s: Events don't match\n", __func__);
kfree(payload);
kfree(event_cmd);
rc = -EINVAL;
mutex_unlock(&pcam->event_lock);
break;
}
if (ioctl_ptr->len > 0) {
if (copy_to_user(ioctl_ptr->ioctl_ptr, payload,
ioctl_ptr->len)) {
pr_err("%s Copy to user failed for cmd %d",
__func__, cmd);
kfree(payload);
kfree(event_cmd);
rc = -EINVAL;
mutex_unlock(&pcam->event_lock);
break;
}
}
kfree(payload);
kfree(event_cmd);
mutex_unlock(&pcam->event_lock);
rc = 0;
break;
}
default:
pr_err("%s Unsupported ioctl cmd %d ", __func__, cmd);
break;
}
return rc;
}
/* v4l2_ioctl_ops */
static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
.vidioc_querycap = msm_camera_v4l2_querycap,
.vidioc_s_crop = msm_camera_v4l2_s_crop,
.vidioc_g_crop = msm_camera_v4l2_g_crop,
.vidioc_queryctrl = msm_camera_v4l2_queryctrl,
.vidioc_g_ctrl = msm_camera_v4l2_g_ctrl,
.vidioc_s_ctrl = msm_camera_v4l2_s_ctrl,
.vidioc_reqbufs = msm_camera_v4l2_reqbufs,
.vidioc_querybuf = msm_camera_v4l2_querybuf,
.vidioc_qbuf = msm_camera_v4l2_qbuf,
.vidioc_dqbuf = msm_camera_v4l2_dqbuf,
.vidioc_streamon = msm_camera_v4l2_streamon,
.vidioc_streamoff = msm_camera_v4l2_streamoff,
/* format ioctls */
.vidioc_enum_fmt_vid_cap = msm_camera_v4l2_enum_fmt_cap,
.vidioc_enum_fmt_vid_cap_mplane = msm_camera_v4l2_enum_fmt_cap,
.vidioc_try_fmt_vid_cap = msm_camera_v4l2_try_fmt_cap,
.vidioc_try_fmt_vid_cap_mplane = msm_camera_v4l2_try_fmt_cap_mplane,
.vidioc_g_fmt_vid_cap = msm_camera_v4l2_g_fmt_cap,
.vidioc_g_fmt_vid_cap_mplane = msm_camera_v4l2_g_fmt_cap_mplane,
.vidioc_s_fmt_vid_cap = msm_camera_v4l2_s_fmt_cap,
.vidioc_s_fmt_vid_cap_mplane = msm_camera_v4l2_s_fmt_cap_mplane,
.vidioc_g_jpegcomp = msm_camera_v4l2_g_jpegcomp,
.vidioc_s_jpegcomp = msm_camera_v4l2_s_jpegcomp,
/* Stream type-dependent parameter ioctls */
.vidioc_g_parm = msm_camera_v4l2_g_parm,
.vidioc_s_parm = msm_camera_v4l2_s_parm,
/* event subscribe/unsubscribe */
.vidioc_subscribe_event = msm_camera_v4l2_subscribe_event,
.vidioc_unsubscribe_event = msm_camera_v4l2_unsubscribe_event,
.vidioc_default = msm_camera_v4l2_private_ioctl,
};
/* v4l2_file_operations */
static int msm_open(struct file *f)
{
int i, rc = -EINVAL;
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
int ion_client_created = 0;
#endif
int server_q_idx = 0;
/* get the video device */
struct msm_cam_v4l2_device *pcam = video_drvdata(f);
struct msm_cam_v4l2_dev_inst *pcam_inst;
struct msm_cam_media_controller *pmctl = NULL;
D("%s\n", __func__);
if (!pcam) {
pr_err("%s NULL pointer passed in!\n", __func__);
return rc;
}
if (!msm_server_get_usecount()) {
pr_err("%s: error, daemon not yet started.", __func__);
return -EINVAL;
}
mutex_lock(&pcam->vid_lock);
for (i = 0; i < MSM_DEV_INST_MAX; i++) {
if (pcam->dev_inst[i] == NULL)
break;
}
/* if no instance is available, return error */
if (i == MSM_DEV_INST_MAX) {
mutex_unlock(&pcam->vid_lock);
return rc;
}
pcam_inst = kzalloc(sizeof(struct msm_cam_v4l2_dev_inst), GFP_KERNEL);
if (!pcam_inst) {
mutex_unlock(&pcam->vid_lock);
return rc;
}
mutex_init(&pcam_inst->inst_lock);
pcam_inst->sensor_pxlcode = pcam->usr_fmts[0].pxlcode;
pcam_inst->my_index = i;
pcam_inst->pcam = pcam;
pcam->dev_inst[i] = pcam_inst;
D("%s index %d nodeid %d count %d\n", __func__,
pcam_inst->my_index,
pcam->vnode_id, pcam->use_count);
pcam->use_count++;
D("%s Inst %p use_count %d\n", __func__, pcam_inst, pcam->use_count);
if (pcam->use_count == 1) {
server_q_idx = msm_find_free_queue();
if (server_q_idx < 0)
return server_q_idx;
rc = msm_server_begin_session(pcam, server_q_idx);
if (rc < 0) {
pr_err("%s error starting server session ", __func__);
goto msm_cam_server_begin_session_failed;
}
pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s mctl ptr is null ", __func__);
goto msm_cam_server_begin_session_failed;
}
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
pmctl->client = msm_ion_client_create(-1, "camera");
kref_init(&pmctl->refcount);
ion_client_created = 1;
#endif
/* Should be set to sensor ops if any but right now its OK!! */
if (!pmctl->mctl_open) {
D("%s: media contoller is not inited\n", __func__);
rc = -ENODEV;
goto mctl_open_failed;
}
/* Now we really have to activate the camera */
D("%s: call mctl_open\n", __func__);
rc = pmctl->mctl_open(pmctl, MSM_APPS_ID_V4L2);
if (rc < 0) {
pr_err("%s: HW open failed rc = 0x%x\n", __func__, rc);
goto mctl_open_failed;
}
pmctl->pcam_ptr = pcam;
msm_setup_v4l2_event_queue(&pcam_inst->eventHandle,
pcam->pvdev);
mutex_init(&pcam->event_lock);
msm_queue_init(&pcam->eventData_q, "eventData");
}
pcam_inst->vbqueue_initialized = 0;
rc = 0;
f->private_data = &pcam_inst->eventHandle;
D("f->private_data = 0x%x, pcam = 0x%x\n",
(u32)f->private_data, (u32)pcam_inst);
if (pcam->use_count == 1) {
rc = msm_send_open_server(pcam);
if (rc < 0 && rc != -ERESTARTSYS) {
pr_err("%s: msm_send_open_server failed %d\n",
__func__, rc);
goto msm_send_open_server_failed;
}
}
mutex_unlock(&pcam->vid_lock);
D("%s: end\n", __func__);
return rc;
msm_send_open_server_failed:
msm_drain_eventq(&pcam->eventData_q);
msm_destroy_v4l2_event_queue(&pcam_inst->eventHandle);
if (pmctl->mctl_release)
pmctl->mctl_release(pmctl);
mctl_open_failed:
if (pcam->use_count == 1) {
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
if (ion_client_created) {
D("%s: destroy ion client", __func__);
kref_put(&pmctl->refcount, msm_release_ion_client);
}
#endif
if (msm_server_end_session(pcam) < 0)
pr_err("%s: msm_server_end_session failed\n",
__func__);
}
msm_cam_server_begin_session_failed:
if (pcam->use_count == 1) {
pcam->dev_inst[i] = NULL;
pcam->use_count = 0;
}
pcam->dev_inst[i] = NULL;
mutex_unlock(&pcam->vid_lock);
mutex_destroy(&pcam_inst->inst_lock);
kfree(pcam_inst);
pr_err("%s: error end", __func__);
return rc;
}
static int msm_addr_remap(struct msm_cam_v4l2_dev_inst *pcam_inst,
struct vm_area_struct *vma)
{
int phyaddr;
int retval;
unsigned long size;
int rc = 0;
struct msm_cam_media_controller *mctl;
mctl = msm_cam_server_get_mctl(pcam_inst->pcam->mctl_handle);
if (!mctl) {
pr_err("%s: invalid mctl pointer", __func__);
return -EFAULT;
}
rc = msm_pmem_region_get_phy_addr(&mctl->stats_info.pmem_stats_list,
&pcam_inst->mem_map,
&phyaddr);
if (rc) {
pr_err("%s: cannot map vaddr", __func__);
return -EFAULT;
}
size = vma->vm_end - vma->vm_start;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
retval = remap_pfn_range(vma, vma->vm_start,
phyaddr >> PAGE_SHIFT,
size, vma->vm_page_prot);
if (retval) {
pr_err("%s:mmap: remap failed with error %d. ",
__func__, retval);
memset(&pcam_inst->mem_map, 0, sizeof(pcam_inst->mem_map));
return -ENOMEM;
}
D("%s:mmap: phy_addr=0x%x: %08lx-%08lx, pgoff %08lx\n",
__func__, (uint32_t)phyaddr,
vma->vm_start, vma->vm_end, vma->vm_pgoff);
memset(&pcam_inst->mem_map, 0, sizeof(pcam_inst->mem_map));
return 0;
}
static int msm_mmap(struct file *f, struct vm_area_struct *vma)
{
int rc = 0;
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("mmap called, vma=0x%08lx\n", (unsigned long)vma);
if (pcam_inst->is_mem_map_inst &&
pcam_inst->mem_map.cookie) {
rc = msm_addr_remap(pcam_inst, vma);
D("%s: msm_addr_remap ret=%d\n", __func__, rc);
return rc;
} else
rc = vb2_mmap(&pcam_inst->vid_bufq, vma);
D("vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
rc);
return rc;
}
void msm_release_ion_client(struct kref *ref)
{
struct msm_cam_media_controller *mctl = container_of(ref,
struct msm_cam_media_controller, refcount);
pr_err("%s Calling ion_client_destroy\n", __func__);
ion_client_destroy(mctl->client);
}
static int msm_close(struct file *f)
{
int rc = 0;
struct msm_cam_v4l2_device *pcam;
struct msm_cam_v4l2_dev_inst *pcam_inst;
struct msm_cam_media_controller *pmctl;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
pcam = pcam_inst->pcam;
if (!pcam) {
pr_err("%s NULL pointer of camera device!\n", __func__);
return -EINVAL;
}
pmctl = msm_cam_server_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s NULL mctl pointer\n", __func__);
return -EINVAL;
}
mutex_lock(&pcam->vid_lock);
mutex_lock(&pcam_inst->inst_lock);
if (pcam_inst->streamon) {
/*something went wrong since instance
is closing without streamoff*/
if (pmctl->mctl_release)
pmctl->mctl_release(pmctl);
pmctl->mctl_release = NULL;/*so that it isn't closed again*/
}
pcam_inst->streamon = 0;
pcam->use_count--;
pcam->dev_inst_map[pcam_inst->image_mode] = NULL;
if (pcam_inst->vbqueue_initialized)
vb2_queue_release(&pcam_inst->vid_bufq);
D("%s Closing down instance %p ", __func__, pcam_inst);
D("%s index %d nodeid %d count %d\n", __func__, pcam_inst->my_index,
pcam->vnode_id, pcam->use_count);
pcam->dev_inst[pcam_inst->my_index] = NULL;
if (pcam_inst->my_index == 0) {
mutex_lock(&pcam->event_lock);
msm_drain_eventq(&pcam->eventData_q);
mutex_unlock(&pcam->event_lock);
mutex_destroy(&pcam->event_lock);
msm_destroy_v4l2_event_queue(&pcam_inst->eventHandle);
}
CLR_VIDEO_INST_IDX(pcam_inst->inst_handle);
CLR_IMG_MODE(pcam_inst->inst_handle);
CLR_DEVID_MODE(pcam_inst->inst_handle);
mutex_unlock(&pcam_inst->inst_lock);
mutex_destroy(&pcam_inst->inst_lock);
kfree(pcam_inst);
f->private_data = NULL;
if (pcam->use_count == 0) {
if (msm_server_get_usecount() > 0) {
rc = msm_send_close_server(pcam);
if (rc < 0)
pr_err("msm_send_close_server failed %d\n", rc);
}
if (pmctl->mctl_release)
pmctl->mctl_release(pmctl);
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
kref_put(&pmctl->refcount, msm_release_ion_client);
#endif
rc = msm_server_end_session(pcam);
if (rc < 0)
pr_err("msm_server_end_session fails %d\n", rc);
}
mutex_unlock(&pcam->vid_lock);
return rc;
}
static unsigned int msm_poll(struct file *f, struct poll_table_struct *wait)
{
int rc = 0;
struct msm_cam_v4l2_device *pcam;
struct msm_cam_v4l2_dev_inst *pcam_inst;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
pcam = pcam_inst->pcam;
D("%s\n", __func__);
if (!pcam) {
pr_err("%s NULL pointer of camera device!\n", __func__);
return -EINVAL;
}
if (pcam_inst->my_index == 0) {
poll_wait(f, &(pcam_inst->eventHandle.wait), wait);
if (v4l2_event_pending(&pcam_inst->eventHandle))
rc |= POLLPRI;
} else {
if (!pcam_inst->vid_bufq.streaming) {
D("%s vid_bufq.streaming is off, inst=0x%x\n",
__func__, (u32)pcam_inst);
return -EINVAL;
}
rc |= vb2_poll(&pcam_inst->vid_bufq, f, wait);
}
D("%s returns, rc = 0x%x\n", __func__, rc);
return rc;
}
long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
unsigned int cmd, unsigned long evt)
{
struct v4l2_event v4l2_ev;
struct v4l2_event_and_payload evt_payload;
struct msm_cam_v4l2_device *pcam = NULL;
int rc = 0;
struct msm_queue_cmd *event_qcmd;
void *payload;
if (!mctl) {
pr_err("%s: mctl is NULL\n", __func__);
return -EINVAL;
}
if (copy_from_user(&evt_payload, (void __user *)evt,
sizeof(struct v4l2_event_and_payload))) {
ERR_COPY_FROM_USER();
return -EFAULT;
}
v4l2_ev = evt_payload.evt;
v4l2_ev.id = 0;
pcam = mctl->pcam_ptr;
ktime_get_ts(&v4l2_ev.timestamp);
if (evt_payload.payload_length > 0 && evt_payload.payload != NULL) {
mutex_lock(&pcam->event_lock);
event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
if (!event_qcmd) {
pr_err("%s Insufficient memory. return", __func__);
rc = -ENOMEM;
goto event_qcmd_alloc_fail;
}
payload = kzalloc(evt_payload.payload_length, GFP_KERNEL);
if (!payload) {
pr_err("%s Insufficient memory. return", __func__);
rc = -ENOMEM;
goto payload_alloc_fail;
}
if (copy_from_user(payload,
(void __user *)evt_payload.payload,
evt_payload.payload_length)) {
ERR_COPY_FROM_USER();
rc = -EFAULT;
goto copy_from_user_failed;
}
event_qcmd->command = payload;
event_qcmd->trans_code = evt_payload.transaction_id;
msm_enqueue(&pcam->eventData_q, &event_qcmd->list_eventdata);
mutex_unlock(&pcam->event_lock);
}
v4l2_event_queue(pcam->pvdev, &v4l2_ev);
return rc;
copy_from_user_failed:
kfree(payload);
payload_alloc_fail:
kfree(event_qcmd);
event_qcmd_alloc_fail:
return rc;
}
static struct v4l2_file_operations g_msm_fops = {
.owner = THIS_MODULE,
.open = msm_open,
.poll = msm_poll,
.mmap = msm_mmap,
.release = msm_close,
.ioctl = video_ioctl2,
};
static int msm_cam_dev_init(struct msm_cam_v4l2_device *pcam)
{
int rc = -ENOMEM;
struct video_device *pvdev = NULL;
struct i2c_client *client = NULL;
struct platform_device *pdev = NULL;
D("%s\n", __func__);
/* first register the v4l2 device */
if (pcam->sensor_sdev->flags & V4L2_SUBDEV_FL_IS_I2C) {
client = v4l2_get_subdevdata(pcam->sensor_sdev);
pcam->v4l2_dev.dev = &client->dev;
pcam->media_dev.dev = &client->dev;
} else {
pdev = v4l2_get_subdevdata(pcam->sensor_sdev);
pcam->v4l2_dev.dev = &pdev->dev;
pcam->media_dev.dev = &pdev->dev;
}
rc = v4l2_device_register(pcam->v4l2_dev.dev, &pcam->v4l2_dev);
if (rc < 0)
return -EINVAL;
else
pcam->v4l2_dev.notify = msm_cam_v4l2_subdev_notify;
/* now setup video device */
pvdev = video_device_alloc();
if (pvdev == NULL) {
pr_err("%s: video_device_alloc failed\n", __func__);
return rc;
}
strlcpy(pcam->media_dev.model, QCAMERA_NAME,
sizeof(pcam->media_dev.model));
rc = media_device_register(&pcam->media_dev);
pvdev->v4l2_dev = &pcam->v4l2_dev;
pcam->v4l2_dev.mdev = &pcam->media_dev;
/* init video device's driver interface */
D("sensor name = %s, sizeof(pvdev->name)=%d\n",
pcam->sensor_sdev->name, sizeof(pvdev->name));
/* device info - strlcpy is safer than strncpy but
only if architecture supports*/
strlcpy(pvdev->name, pcam->sensor_sdev->name, sizeof(pvdev->name));
pvdev->release = video_device_release;
pvdev->fops = &g_msm_fops;
pvdev->ioctl_ops = &g_msm_ioctl_ops;
pvdev->minor = -1;
pvdev->vfl_type = VFL_TYPE_GRABBER;
media_entity_init(&pvdev->entity, 0, NULL, 0);
pvdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
pvdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
/* register v4l2 video device to kernel as /dev/videoXX */
D("video_register_device\n");
rc = video_register_device(pvdev,
VFL_TYPE_GRABBER,
msm_camera_v4l2_nr);
if (rc) {
pr_err("%s: video_register_device failed\n", __func__);
goto reg_fail;
}
pvdev->entity.name = video_device_node_name(pvdev);
D("%s: video device registered as /dev/video%d\n",
__func__, pvdev->num);
/* connect pcam and video dev to each other */
pcam->pvdev = pvdev;
video_set_drvdata(pcam->pvdev, pcam);
return rc;
reg_fail:
video_device_release(pvdev);
v4l2_device_unregister(&pcam->v4l2_dev);
pcam->v4l2_dev.dev = NULL;
return rc;
}
static struct v4l2_subdev *msm_actuator_probe(
struct msm_actuator_info *actuator_info)
{
struct v4l2_subdev *act_sdev;
struct i2c_adapter *adapter = NULL;
struct msm_actuator_ctrl_t *actrl;
void *act_client = NULL;
D("%s called\n", __func__);
if (!actuator_info || !actuator_info->board_info)
goto probe_fail;
adapter = i2c_get_adapter(actuator_info->bus_id);
if (!adapter)
goto probe_fail;
act_client = i2c_new_device(adapter, actuator_info->board_info);
if (!act_client)
goto device_fail;
act_sdev = (struct v4l2_subdev *)i2c_get_clientdata(act_client);
if (act_sdev == NULL)
goto client_fail;
if (actuator_info->vcm_enable) {
actrl = get_actrl(act_sdev);
if (actrl) {
actrl->vcm_enable = actuator_info->vcm_enable;
actrl->vcm_pwd = actuator_info->vcm_pwd;
}
}
i2c_put_adapter(adapter);
return act_sdev;
client_fail:
i2c_unregister_device(act_client);
device_fail:
i2c_put_adapter(adapter);
adapter = NULL;
probe_fail:
return NULL;
}
static struct v4l2_subdev *msm_eeprom_probe(
struct msm_eeprom_info *eeprom_info)
{
struct v4l2_subdev *eeprom_sdev;
struct i2c_adapter *adapter = NULL;
void *eeprom_client = NULL;
D("%s called\n", __func__);
if (!eeprom_info || !eeprom_info->board_info)
goto probe_fail;
adapter = i2c_get_adapter(eeprom_info->bus_id);
if (!adapter)
goto probe_fail;
eeprom_client = i2c_new_device(adapter, eeprom_info->board_info);
if (!eeprom_client)
goto device_fail;
eeprom_sdev = (struct v4l2_subdev *)i2c_get_clientdata(eeprom_client);
if (eeprom_sdev == NULL)
goto client_fail;
i2c_put_adapter(adapter);
return eeprom_sdev;
client_fail:
pr_err("%s client_fail\n", __func__);
i2c_unregister_device(eeprom_client);
device_fail:
pr_err("%s device_fail\n", __func__);
i2c_put_adapter(adapter);
adapter = NULL;
probe_fail:
pr_err("%s probe_fail\n", __func__);
return NULL;
}
static struct v4l2_subdev *msm_flash_probe(
struct msm_camera_sensor_flash_data *flash_info)
{
struct v4l2_subdev *flash_sdev = NULL;
struct i2c_adapter *adapter = NULL;
void *flash_client = NULL;
D("%s called\n", __func__);
if (!flash_info || !flash_info->board_info)
goto probe_fail;
adapter = i2c_get_adapter(flash_info->bus_id);
if (!adapter)
goto probe_fail;
flash_client = i2c_new_device(adapter, flash_info->board_info);
if (!flash_client)
goto device_fail;
flash_sdev = (struct v4l2_subdev *)i2c_get_clientdata(flash_client);
if (flash_sdev == NULL)
goto client_fail;
i2c_put_adapter(adapter);
return flash_sdev;
client_fail:
pr_err("%s client_fail\n", __func__);
i2c_unregister_device(flash_client);
device_fail:
pr_err("%s device_fail\n", __func__);
i2c_put_adapter(adapter);
adapter = NULL;
probe_fail:
pr_err("%s probe_fail\n", __func__);
return NULL;
}
/* register a msm sensor into the msm device, which will probe the
* sensor HW. if the HW exist then create a video device (/dev/videoX/)
* to represent this sensor */
int msm_sensor_register(struct v4l2_subdev *sensor_sd)
{
int rc = -EINVAL;
struct msm_camera_sensor_info *sdata;
struct msm_cam_v4l2_device *pcam;
struct msm_sensor_ctrl_t *s_ctrl;
struct msm_cam_subdev_info sd_info;
D("%s for %s\n", __func__, sensor_sd->name);
/* allocate the memory for the camera device first */
pcam = kzalloc(sizeof(*pcam), GFP_KERNEL);
if (!pcam) {
pr_err("%s: could not allocate mem for msm_cam_v4l2_device\n",
__func__);
return -ENOMEM;
}
pcam->sensor_sdev = sensor_sd;
s_ctrl = get_sctrl(sensor_sd);
sdata = (struct msm_camera_sensor_info *) s_ctrl->sensordata;
pcam->act_sdev = msm_actuator_probe(sdata->actuator_info);
pcam->eeprom_sdev = msm_eeprom_probe(sdata->eeprom_info);
pcam->flash_sdev = msm_flash_probe(sdata->flash_data);
D("%s: pcam =0x%p\n", __func__, pcam);
pcam->sdata = sdata;
/* init the user count and lock*/
pcam->use_count = 0;
mutex_init(&pcam->vid_lock);
mutex_init(&pcam->mctl_node.dev_lock);
/* Initialize the formats supported */
rc = msm_mctl_init_user_formats(pcam);
if (rc < 0)
goto failure;
rc = msm_cam_dev_init(pcam);
if (rc < 0)
goto failure;
rc = msm_setup_mctl_node(pcam);
if (rc < 0) {
pr_err("%s:failed to create mctl device: %d\n",
__func__, rc);
goto failure;
}
msm_server_update_sensor_info(pcam, sdata);
sd_info.sdev_type = SENSOR_DEV;
sd_info.sd_index = vnode_count;
sd_info.irq_num = 0;
/* register the subdevice, must be done for callbacks */
rc = msm_cam_register_subdev_node(sensor_sd, &sd_info);
if (rc < 0) {
D("%s sensor sub device register failed\n",
__func__);
goto failure;
}
if (pcam->act_sdev) {
rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
pcam->act_sdev);
if (rc < 0) {
D("%s actuator sub device register failed\n",
__func__);
goto failure;
}
}
if (pcam->eeprom_sdev) {
rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
pcam->eeprom_sdev);
if (rc < 0) {
D("%s eeprom sub device register failed\n", __func__);
goto failure;
}
}
if (pcam->flash_sdev) {
rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
pcam->flash_sdev);
if (rc < 0) {
D("%s flash sub device register failed\n", __func__);
goto failure;
}
}
pcam->vnode_id = vnode_count++;
return rc;
failure:
kzfree(pcam);
return rc;
}
EXPORT_SYMBOL(msm_sensor_register);