blob: 12a0f37dfca5627d8d7c45aa7571aa3c292cb925 [file] [log] [blame]
/* Copyright (c) 2011-2012, Code Aurora Forum. 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/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_csid.h"
#include "msm_csic.h"
#include "msm_csiphy.h"
#include "msm_ispif.h"
#include "msm_sensor.h"
#include "msm_actuator.h"
#include "msm_vfe32.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 struct msm_cam_server_dev g_server_dev;
static struct class *msm_class;
static dev_t msm_devno;
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");
static void msm_queue_init(struct msm_device_queue *queue, const char *name)
{
D("%s\n", __func__);
spin_lock_init(&queue->lock);
queue->len = 0;
queue->max = 0;
queue->name = name;
INIT_LIST_HEAD(&queue->list);
init_waitqueue_head(&queue->wait);
}
static void msm_enqueue(struct msm_device_queue *queue,
struct list_head *entry)
{
unsigned long flags;
spin_lock_irqsave(&queue->lock, flags);
queue->len++;
if (queue->len > queue->max) {
queue->max = queue->len;
pr_info("%s: queue %s new max is %d\n", __func__,
queue->name, queue->max);
}
list_add_tail(entry, &queue->list);
wake_up(&queue->wait);
D("%s: woke up %s\n", __func__, queue->name);
spin_unlock_irqrestore(&queue->lock, flags);
}
static void msm_drain_eventq(struct msm_device_queue *queue)
{
unsigned long flags;
struct msm_queue_cmd *qcmd;
spin_lock_irqsave(&queue->lock, flags);
while (!list_empty(&queue->list)) {
qcmd = list_first_entry(&queue->list,
struct msm_queue_cmd, list_eventdata);
list_del_init(&qcmd->list_eventdata);
kfree(qcmd->command);
free_qcmd(qcmd);
}
spin_unlock_irqrestore(&queue->lock, flags);
}
static int32_t msm_find_free_queue(void)
{
int i;
for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
struct msm_cam_server_queue *queue;
queue = &g_server_dev.server_queue[i];
if (!queue->queue_active)
return i;
}
return -EINVAL;
}
uint32_t msm_camera_get_mctl_handle(void)
{
uint32_t i;
if ((g_server_dev.mctl_handle_cnt << 8) == 0)
g_server_dev.mctl_handle_cnt++;
for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
if (g_server_dev.mctl[i].handle == 0) {
g_server_dev.mctl[i].handle =
(++g_server_dev.mctl_handle_cnt) << 8 | i;
memset(&g_server_dev.mctl[i].mctl,
0, sizeof(g_server_dev.mctl[i].mctl));
return g_server_dev.mctl[i].handle;
}
}
return 0;
}
struct msm_cam_media_controller *msm_camera_get_mctl(uint32_t handle)
{
uint32_t mctl_index;
mctl_index = handle & 0xff;
if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
(g_server_dev.mctl[mctl_index].handle == handle))
return &g_server_dev.mctl[mctl_index].mctl;
return NULL;
}
void msm_camera_free_mctl(uint32_t handle)
{
uint32_t mctl_index;
mctl_index = handle & 0xff;
if ((mctl_index < MAX_NUM_ACTIVE_CAMERA) &&
(g_server_dev.mctl[mctl_index].handle == handle))
g_server_dev.mctl[mctl_index].handle = 0;
else
pr_err("%s: invalid free handle\n", __func__);
}
/* 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_camera_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return;
}
static int msm_ctrl_cmd_done(void *arg)
{
void __user *uptr;
struct msm_queue_cmd *qcmd;
struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
struct msm_ctrl_cmd *command =
kzalloc(sizeof(struct msm_ctrl_cmd), GFP_KERNEL);
if (!command) {
pr_err("%s Insufficient memory. return", __func__);
return -ENOMEM;
}
D("%s\n", __func__);
if (copy_from_user(command, (void __user *)ioctl_ptr->ioctl_ptr,
sizeof(struct msm_ctrl_cmd))) {
pr_err("%s: copy_from_user failed, size=%d\n",
__func__, sizeof(struct msm_ctrl_cmd));
return -EINVAL;
}
g_server_dev.server_queue[command->queue_idx].ctrl = command;
if (command->evt_id !=
g_server_dev.server_queue[command->queue_idx].evt_id) {
pr_err("Invalid event id from userspace\n");
return -EINVAL;
}
mutex_lock(&g_server_dev.server_queue_lock);
qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
atomic_set(&qcmd->on_heap, 1);
uptr = command->value;
qcmd->command = command;
if (command->length > 0) {
command->value =
g_server_dev.server_queue[command->queue_idx].ctrl_data;
if (command->length > max_control_command_size) {
pr_err("%s: user data %d is too big (max %d)\n",
__func__, command->length,
max_control_command_size);
free_qcmd(qcmd);
return -EINVAL;
}
if (copy_from_user(command->value, uptr, command->length)) {
free_qcmd(qcmd);
return -EINVAL;
}
}
msm_enqueue(&g_server_dev.server_queue
[command->queue_idx].ctrl_q, &qcmd->list_control);
mutex_unlock(&g_server_dev.server_queue_lock);
return 0;
}
/* send control command to config and wait for results*/
static int msm_server_control(struct msm_cam_server_dev *server_dev,
struct msm_ctrl_cmd *out)
{
int rc = 0;
void *value;
struct msm_queue_cmd *rcmd;
struct msm_queue_cmd *event_qcmd;
struct msm_ctrl_cmd *ctrlcmd;
struct msm_device_queue *queue =
&server_dev->server_queue[out->queue_idx].ctrl_q;
struct v4l2_event v4l2_evt;
struct msm_isp_event_ctrl *isp_event;
isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
if (!isp_event) {
pr_err("%s Insufficient memory. return", __func__);
return -ENOMEM;
}
event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
if (!event_qcmd) {
pr_err("%s Insufficient memory. return", __func__);
return -ENOMEM;
}
D("%s\n", __func__);
mutex_lock(&server_dev->server_queue_lock);
if (++server_dev->server_evt_id == 0)
server_dev->server_evt_id++;
server_dev->server_queue[out->queue_idx].evt_id =
server_dev->server_evt_id;
v4l2_evt.type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2;
v4l2_evt.u.data[0] = out->queue_idx;
/* setup event object to transfer the command; */
isp_event->resptype = MSM_CAM_RESP_V4L2;
isp_event->isp_data.ctrl = *out;
isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
atomic_set(&event_qcmd->on_heap, 1);
event_qcmd->command = isp_event;
msm_enqueue(&server_dev->server_queue[out->queue_idx].eventData_q,
&event_qcmd->list_eventdata);
/* now send command to config thread in userspace,
* and wait for results */
v4l2_event_queue(server_dev->server_command_queue.pvdev,
&v4l2_evt);
D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
mutex_unlock(&server_dev->server_queue_lock);
/* wait for config return status */
D("Waiting for config status\n");
rc = wait_event_interruptible_timeout(queue->wait,
!list_empty_careful(&queue->list),
msecs_to_jiffies(out->timeout_ms));
D("Waiting is over for config status\n");
if (list_empty_careful(&queue->list)) {
if (!rc)
rc = -ETIMEDOUT;
if (rc < 0) {
kfree(isp_event);
pr_err("%s: wait_event error %d\n", __func__, rc);
return rc;
}
}
rcmd = msm_dequeue(queue, list_control);
BUG_ON(!rcmd);
D("%s Finished servicing ioctl\n", __func__);
ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
value = out->value;
if (ctrlcmd->length > 0)
memcpy(value, ctrlcmd->value, ctrlcmd->length);
memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
out->value = value;
kfree(ctrlcmd);
server_dev->server_queue[out->queue_idx].ctrl = NULL;
free_qcmd(rcmd);
kfree(isp_event);
D("%s: rc %d\n", __func__, rc);
/* rc is the time elapsed. */
if (rc >= 0) {
/* TODO: Refactor msm_ctrl_cmd::status field */
if (out->status == 0)
rc = -1;
else if (out->status == 1 || out->status == 4)
rc = 0;
else
rc = -EINVAL;
}
return rc;
}
/*send open command to server*/
static int msm_send_open_server(struct msm_cam_v4l2_device *pcam)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
D("%s\n", __func__);
ctrlcmd.type = MSM_V4L2_OPEN;
ctrlcmd.timeout_ms = 10000;
ctrlcmd.length = strnlen(g_server_dev.config_info.config_dev_name[0],
MAX_DEV_NAME_LEN)+1;
ctrlcmd.value = (char *)g_server_dev.config_info.config_dev_name[0];
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
return rc;
}
static int msm_send_close_server(struct msm_cam_v4l2_device *pcam)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
D("%s\n", __func__);
ctrlcmd.type = MSM_V4L2_CLOSE;
ctrlcmd.timeout_ms = 10000;
ctrlcmd.length = strnlen(g_server_dev.config_info.config_dev_name[0],
MAX_DEV_NAME_LEN)+1;
ctrlcmd.value = (char *)g_server_dev.config_info.config_dev_name[0];
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
return rc;
}
static int msm_server_set_fmt(struct msm_cam_v4l2_device *pcam, int idx,
struct v4l2_format *pfmt)
{
int rc = 0;
int i = 0;
struct v4l2_pix_format *pix = &pfmt->fmt.pix;
struct msm_ctrl_cmd ctrlcmd;
struct img_plane_info plane_info;
plane_info.width = pix->width;
plane_info.height = pix->height;
plane_info.pixelformat = pix->pixelformat;
plane_info.buffer_type = pfmt->type;
plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
plane_info.num_planes = 1;
D("%s: %d, %d, 0x%x\n", __func__,
pfmt->fmt.pix.width, pfmt->fmt.pix.height,
pfmt->fmt.pix.pixelformat);
if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
D("%s, Attention! Wrong buf-type %d\n", __func__, pfmt->type);
for (i = 0; i < pcam->num_fmts; i++)
if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
break;
if (i == pcam->num_fmts) {
pr_err("%s: User requested pixelformat %x not supported\n",
__func__, pix->pixelformat);
return -EINVAL;
}
ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
ctrlcmd.length = sizeof(struct img_plane_info);
ctrlcmd.value = (void *)&plane_info;
ctrlcmd.timeout_ms = 10000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
if (rc >= 0) {
pcam->dev_inst[idx]->vid_fmt = *pfmt;
pcam->dev_inst[idx]->sensor_pxlcode
= pcam->usr_fmts[i].pxlcode;
D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
__func__, (u32)pcam->dev_inst[idx], idx,
pcam->dev_inst[idx]->vid_fmt.fmt.pix.width,
pcam->dev_inst[idx]->vid_fmt.fmt.pix.height);
pcam->dev_inst[idx]->plane_info = plane_info;
}
return rc;
}
static int msm_server_set_fmt_mplane(struct msm_cam_v4l2_device *pcam, int idx,
struct v4l2_format *pfmt)
{
int rc = 0;
int i = 0;
struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
struct msm_ctrl_cmd ctrlcmd;
struct img_plane_info plane_info;
plane_info.width = pix_mp->width;
plane_info.height = pix_mp->height;
plane_info.pixelformat = pix_mp->pixelformat;
plane_info.buffer_type = pfmt->type;
plane_info.ext_mode = pcam->dev_inst[idx]->image_mode;
plane_info.num_planes = pix_mp->num_planes;
if (plane_info.num_planes <= 0 ||
plane_info.num_planes > VIDEO_MAX_PLANES) {
pr_err("%s Invalid number of planes set %d", __func__,
plane_info.num_planes);
return -EINVAL;
}
D("%s: %d, %d, 0x%x\n", __func__,
pfmt->fmt.pix_mp.width, pfmt->fmt.pix_mp.height,
pfmt->fmt.pix_mp.pixelformat);
if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
pr_err("%s, Attention! Wrong buf-type %d\n",
__func__, pfmt->type);
return -EINVAL;
}
for (i = 0; i < pcam->num_fmts; i++)
if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
break;
if (i == pcam->num_fmts) {
pr_err("%s: User requested pixelformat %x not supported\n",
__func__, pix_mp->pixelformat);
return -EINVAL;
}
ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
ctrlcmd.length = sizeof(struct img_plane_info);
ctrlcmd.value = (void *)&plane_info;
ctrlcmd.timeout_ms = 10000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
if (rc >= 0) {
pcam->dev_inst[idx]->vid_fmt = *pfmt;
pcam->dev_inst[idx]->sensor_pxlcode
= pcam->usr_fmts[i].pxlcode;
D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
__func__, (u32)pcam->dev_inst[idx], idx,
pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.width,
pcam->dev_inst[idx]->vid_fmt.fmt.pix_mp.height);
pcam->dev_inst[idx]->plane_info = plane_info;
}
return rc;
}
static int msm_server_streamon(struct msm_cam_v4l2_device *pcam, int idx)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
D("%s\n", __func__);
ctrlcmd.type = MSM_V4L2_STREAM_ON;
ctrlcmd.timeout_ms = 10000;
ctrlcmd.length = 0;
ctrlcmd.value = NULL;
ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
return rc;
}
static int msm_server_streamoff(struct msm_cam_v4l2_device *pcam, int idx)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
D("%s, pcam = 0x%x\n", __func__, (u32)pcam);
ctrlcmd.type = MSM_V4L2_STREAM_OFF;
ctrlcmd.timeout_ms = 10000;
ctrlcmd.length = 0;
ctrlcmd.value = NULL;
ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
return rc;
}
static int msm_server_proc_ctrl_cmd(struct msm_cam_v4l2_device *pcam,
struct v4l2_control *ctrl, int is_set_cmd)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd, *tmp_cmd;
uint8_t *ctrl_data = NULL;
void __user *uptr_cmd;
void __user *uptr_value;
uint32_t cmd_len = sizeof(struct msm_ctrl_cmd);
uint32_t value_len;
tmp_cmd = (struct msm_ctrl_cmd *)ctrl->value;
uptr_cmd = (void __user *)ctrl->value;
uptr_value = (void __user *)tmp_cmd->value;
value_len = tmp_cmd->length;
D("%s: cmd type = %d, up1=0x%x, ulen1=%d, up2=0x%x, ulen2=%d\n",
__func__, tmp_cmd->type, (uint32_t)uptr_cmd, cmd_len,
(uint32_t)uptr_value, tmp_cmd->length);
ctrl_data = kzalloc(value_len+cmd_len, GFP_KERNEL);
if (ctrl_data == 0) {
pr_err("%s could not allocate memory\n", __func__);
rc = -ENOMEM;
goto end;
}
tmp_cmd = (struct msm_ctrl_cmd *)ctrl_data;
if (copy_from_user((void *)ctrl_data, uptr_cmd,
cmd_len)) {
pr_err("%s: copy_from_user failed.\n", __func__);
rc = -EINVAL;
goto end;
}
tmp_cmd->value = (void *)(ctrl_data+cmd_len);
if (uptr_value && tmp_cmd->length > 0) {
if (copy_from_user((void *)tmp_cmd->value, uptr_value,
value_len)) {
pr_err("%s: copy_from_user failed, size=%d\n",
__func__, value_len);
rc = -EINVAL;
goto end;
}
} else
tmp_cmd->value = NULL;
ctrlcmd.type = MSM_V4L2_SET_CTRL_CMD;
ctrlcmd.length = cmd_len + value_len;
ctrlcmd.value = (void *)ctrl_data;
if (tmp_cmd->timeout_ms > 0)
ctrlcmd.timeout_ms = tmp_cmd->timeout_ms;
else
ctrlcmd.timeout_ms = 1000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
D("%s: msm_server_control rc=%d\n", __func__, rc);
if (rc == 0) {
if (uptr_value && tmp_cmd->length > 0 &&
copy_to_user((void __user *)uptr_value,
(void *)(ctrl_data+cmd_len), tmp_cmd->length)) {
pr_err("%s: copy_to_user failed, size=%d\n",
__func__, tmp_cmd->length);
rc = -EINVAL;
goto end;
}
tmp_cmd->value = uptr_value;
if (copy_to_user((void __user *)uptr_cmd,
(void *)tmp_cmd, cmd_len)) {
pr_err("%s: copy_to_user failed in cpy, size=%d\n",
__func__, cmd_len);
rc = -EINVAL;
goto end;
}
}
end:
D("%s: END, type = %d, vaddr = 0x%x, vlen = %d, status = %d, rc = %d\n",
__func__, tmp_cmd->type, (uint32_t)tmp_cmd->value,
tmp_cmd->length, tmp_cmd->status, rc);
kfree(ctrl_data);
return rc;
}
static int msm_server_s_ctrl(struct msm_cam_v4l2_device *pcam,
struct v4l2_control *ctrl)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
uint8_t ctrl_data[max_control_command_size];
WARN_ON(ctrl == NULL);
if (ctrl == NULL) {
pr_err("%s Invalid control\n", __func__);
return -EINVAL;
}
if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
return msm_server_proc_ctrl_cmd(pcam, ctrl, 1);
memset(ctrl_data, 0, sizeof(ctrl_data));
ctrlcmd.type = MSM_V4L2_SET_CTRL;
ctrlcmd.length = sizeof(struct v4l2_control);
ctrlcmd.value = (void *)ctrl_data;
memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
ctrlcmd.timeout_ms = 1000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
return rc;
}
static int msm_server_g_ctrl(struct msm_cam_v4l2_device *pcam,
struct v4l2_control *ctrl)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
uint8_t ctrl_data[max_control_command_size];
WARN_ON(ctrl == NULL);
if (ctrl == NULL) {
pr_err("%s Invalid control\n", __func__);
return -EINVAL;
}
if (ctrl->id == MSM_V4L2_PID_CTRL_CMD)
return msm_server_proc_ctrl_cmd(pcam, ctrl, 0);
memset(ctrl_data, 0, sizeof(ctrl_data));
ctrlcmd.type = MSM_V4L2_GET_CTRL;
ctrlcmd.length = sizeof(struct v4l2_control);
ctrlcmd.value = (void *)ctrl_data;
memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
ctrlcmd.timeout_ms = 1000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in usersspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
ctrl->value = ((struct v4l2_control *)ctrlcmd.value)->value;
return rc;
}
static int msm_server_q_ctrl(struct msm_cam_v4l2_device *pcam,
struct v4l2_queryctrl *queryctrl)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
uint8_t ctrl_data[max_control_command_size];
WARN_ON(queryctrl == NULL);
memset(ctrl_data, 0, sizeof(ctrl_data));
ctrlcmd.type = MSM_V4L2_QUERY_CTRL;
ctrlcmd.length = sizeof(struct v4l2_queryctrl);
ctrlcmd.value = (void *)ctrl_data;
memcpy(ctrlcmd.value, queryctrl, ctrlcmd.length);
ctrlcmd.timeout_ms = 1000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in userspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
D("%s: rc = %d\n", __func__, rc);
if (rc >= 0)
memcpy(queryctrl, ctrlcmd.value, sizeof(struct v4l2_queryctrl));
return rc;
}
static int msm_server_get_fmt(struct msm_cam_v4l2_device *pcam,
int idx, struct v4l2_format *pfmt)
{
struct v4l2_pix_format *pix = &pfmt->fmt.pix;
pix->width = pcam->dev_inst[idx]->vid_fmt.fmt.pix.width;
pix->height = pcam->dev_inst[idx]->vid_fmt.fmt.pix.height;
pix->field = pcam->dev_inst[idx]->vid_fmt.fmt.pix.field;
pix->pixelformat = pcam->dev_inst[idx]->vid_fmt.fmt.pix.pixelformat;
pix->bytesperline = pcam->dev_inst[idx]->vid_fmt.fmt.pix.bytesperline;
pix->colorspace = pcam->dev_inst[idx]->vid_fmt.fmt.pix.colorspace;
if (pix->bytesperline < 0)
return pix->bytesperline;
pix->sizeimage = pix->height * pix->bytesperline;
return 0;
}
static int msm_server_get_fmt_mplane(struct msm_cam_v4l2_device *pcam,
int idx, struct v4l2_format *pfmt)
{
*pfmt = pcam->dev_inst[idx]->vid_fmt;
return 0;
}
static int msm_server_try_fmt(struct msm_cam_v4l2_device *pcam,
struct v4l2_format *pfmt)
{
int rc = 0;
int i = 0;
struct v4l2_pix_format *pix = &pfmt->fmt.pix;
D("%s: 0x%x\n", __func__, pix->pixelformat);
if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
pr_err("%s: pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE!\n",
__func__);
return -EINVAL;
}
/* check if the format is supported by this host-sensor combo */
for (i = 0; i < pcam->num_fmts; i++) {
D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
pcam->usr_fmts[i].fourcc);
if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
break;
}
if (i == pcam->num_fmts) {
pr_err("%s: Format %x not found\n", __func__, pix->pixelformat);
return -EINVAL;
}
return rc;
}
static int msm_server_try_fmt_mplane(struct msm_cam_v4l2_device *pcam,
struct v4l2_format *pfmt)
{
int rc = 0;
int i = 0;
struct v4l2_pix_format_mplane *pix_mp = &pfmt->fmt.pix_mp;
D("%s: 0x%x\n", __func__, pix_mp->pixelformat);
if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
pr_err("%s: Incorrect format type %d ",
__func__, pfmt->type);
return -EINVAL;
}
/* check if the format is supported by this host-sensor combo */
for (i = 0; i < pcam->num_fmts; i++) {
D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
pcam->usr_fmts[i].fourcc);
if (pcam->usr_fmts[i].fourcc == pix_mp->pixelformat)
break;
}
if (i == pcam->num_fmts) {
pr_err("%s: Format %x not found\n",
__func__, pix_mp->pixelformat);
return -EINVAL;
}
return rc;
}
static int msm_camera_get_crop(struct msm_cam_v4l2_device *pcam,
int idx, struct v4l2_crop *crop)
{
int rc = 0;
struct msm_ctrl_cmd ctrlcmd;
BUG_ON(crop == NULL);
ctrlcmd.type = MSM_V4L2_GET_CROP;
ctrlcmd.length = sizeof(struct v4l2_crop);
ctrlcmd.value = (void *)crop;
ctrlcmd.timeout_ms = 1000;
ctrlcmd.vnode_id = pcam->vnode_id;
ctrlcmd.queue_idx = pcam->server_queue_idx;
ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
/* send command to config thread in userspace, and get return value */
rc = msm_server_control(&g_server_dev, &ctrlcmd);
D("%s: rc = %d\n", __func__, rc);
return rc;
}
/*
*
* 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_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_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;
case MSM_V4L2_PID_MMAP_ENTRY:
if (copy_from_user(&pcam_inst->mem_map,
(void *)ctrl->value,
sizeof(struct msm_mem_map_info))) {
rc = -EFAULT;
} else
D("%s:mmap entry:cookie=0x%x,mem_type=%d,len=%d\n",
__func__, pcam_inst->mem_map.cookie,
pcam_inst->mem_map.mem_type,
pcam_inst->mem_map.length);
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;
pcam_inst = container_of(f->private_data,
struct msm_cam_v4l2_dev_inst, eventHandle);
D("%s\n", __func__);
WARN_ON(pctx != f->private_data);
rc = vb2_reqbufs(&pcam_inst->vid_bufq, pb);
if (rc < 0) {
pr_err("%s reqbufs failed %d ", __func__, rc);
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]);
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__);
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]);
kfree(pcam_inst->buf_offset);
pcam_inst->buf_offset = NULL;
return -ENOMEM;
}
}
}
pcam_inst->buf_count = pb->count;
return rc;
}
static int msm_camera_v4l2_querybuf(struct file *f, void *pctx,
struct v4l2_buffer *pb)
{
/* get the video 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);
return vb2_querybuf(&pcam_inst->vid_bufq, pb);
}
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);
if (!pcam_inst->buf_offset) {
pr_err("%s Buffer is already released. Returning. ", __func__);
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 ", __func__);
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",
__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", __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);
return rc;
}
static int msm_camera_v4l2_dqbuf(struct file *f, void *pctx,
struct v4l2_buffer *pb)
{
int rc = 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);
rc = vb2_dqbuf(&pcam_inst->vid_bufq, pb, f->f_flags & O_NONBLOCK);
D("%s, videobuf_dqbuf returns %d\n", __func__, rc);
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);
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;
}
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);
mutex_lock(&pcam->vid_lock);
/* turn HW (VFE/sensor) streaming */
pcam_inst->streamon = 1;
rc = msm_server_streamon(pcam, pcam_inst->my_index);
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);
pcam_inst->streamon = 0;
if (g_server_dev.use_count > 0)
rc = msm_server_streamoff(pcam, pcam_inst->my_index);
mutex_unlock(&pcam->vid_lock);
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);
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);
rc = msm_server_try_fmt(pcam, pfmt);
if (rc)
pr_err("Format %x not found, rc = %d\n",
pfmt->fmt.pix.pixelformat, rc);
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);
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);
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_media_controller *pmctl;
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);
pmctl = msm_camera_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return -EINVAL;
if (!pcam_inst->vbqueue_initialized) {
pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
V4L2_BUF_TYPE_VIDEO_CAPTURE);
pcam_inst->vbqueue_initialized = 1;
}
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_media_controller *pmctl;
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);
pmctl = msm_camera_get_mctl(pcam->mctl_handle);
if (pmctl == NULL)
return -EINVAL;
if (!pcam_inst->vbqueue_initialized) {
pmctl->mctl_vbqueue_init(pcam_inst, &pcam_inst->vid_bufq,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
pcam_inst->vbqueue_initialized = 1;
}
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_camera_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_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;
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;
pcam_inst->pcam->dev_inst_map[pcam_inst->image_mode] = pcam_inst;
pcam_inst->path = msm_vidbuf_get_path(pcam_inst->image_mode);
D("%spath=%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);
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 int msm_server_v4l2_subscribe_event(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
int rc = 0;
D("%s: fh = 0x%x, type = 0x%x", __func__, (u32)fh, sub->type);
if (sub->type == V4L2_EVENT_ALL) {
/*sub->type = MSM_ISP_EVENT_START;*/
sub->type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_CTRL;
D("sub->type start = 0x%x\n", sub->type);
do {
rc = v4l2_event_subscribe(fh, sub);
if (rc < 0) {
D("%s: failed for evtType = 0x%x, rc = %d\n",
__func__, sub->type, rc);
/* unsubscribe all events here and return */
sub->type = V4L2_EVENT_ALL;
v4l2_event_unsubscribe(fh, sub);
return rc;
} else
D("%s: subscribed evtType = 0x%x, rc = %d\n",
__func__, sub->type, rc);
sub->type++;
D("sub->type while = 0x%x\n", sub->type);
} while (sub->type !=
V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_MAX);
} else {
D("sub->type not V4L2_EVENT_ALL = 0x%x\n", sub->type);
rc = v4l2_event_subscribe(fh, sub);
if (rc < 0)
D("%s: failed for evtType = 0x%x, rc = %d\n",
__func__, sub->type, rc);
}
D("%s: rc = %d\n", __func__, rc);
return rc;
}
static int msm_server_v4l2_unsubscribe_event(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
int rc = 0;
D("%s: fh = 0x%x\n", __func__, (u32)fh);
rc = v4l2_event_unsubscribe(fh, sub);
D("%s: rc = %d\n", __func__, rc);
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,
};
/* open an active camera session to manage the streaming logic */
static int msm_cam_server_open_session(struct msm_cam_server_dev *ps,
struct msm_cam_v4l2_device *pcam)
{
int rc = 0;
struct msm_cam_media_controller *pmctl;
D("%s\n", __func__);
if (!ps || !pcam) {
pr_err("%s NULL pointer passed in!\n", __func__);
return rc;
}
/* The number of camera instance should be controlled by the
resource manager. Currently supporting one active instance
until multiple instances are supported */
if (atomic_read(&ps->number_pcam_active) > 0) {
pr_err("%s Cannot have more than one active camera %d\n",
__func__, atomic_read(&ps->number_pcam_active));
return -EINVAL;
}
/* book keeping this camera session*/
ps->pcam_active = pcam;
atomic_inc(&ps->number_pcam_active);
D("config pcam = 0x%p\n", ps->pcam_active);
/* initialization the media controller module*/
msm_mctl_init(pcam);
/*for single VFE msms (8660, 8960v1), just populate the session
with our VFE devices that registered*/
pmctl = msm_camera_get_mctl(pcam->mctl_handle);
pmctl->axi_sdev = ps->axi_device[0];
pmctl->isp_sdev = ps->isp_subdev[0];
return rc;
}
/* close an active camera session to server */
static int msm_cam_server_close_session(struct msm_cam_server_dev *ps,
struct msm_cam_v4l2_device *pcam)
{
int rc = 0;
D("%s\n", __func__);
if (!ps || !pcam) {
D("%s NULL pointer passed in!\n", __func__);
return rc;
}
atomic_dec(&ps->number_pcam_active);
ps->pcam_active = NULL;
msm_mctl_free(pcam);
return rc;
}
/* v4l2_file_operations */
static int msm_open(struct file *f)
{
int i;
int rc = -EINVAL;
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
int ion_client_created = 0;
#endif
int server_q_idx = 0;
/*struct msm_isp_ops *p_isp = 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 (!g_server_dev.use_count) {
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;
}
server_q_idx = msm_find_free_queue();
if (server_q_idx < 0)
return server_q_idx;
/* 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;
}
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++;
if (pcam->use_count == 1) {
struct msm_cam_server_queue *queue;
pcam->server_queue_idx = server_q_idx;
queue = &g_server_dev.server_queue[server_q_idx];
queue->ctrl = NULL;
queue->ctrl_data = kzalloc(sizeof(uint8_t) *
max_control_command_size, GFP_KERNEL);
msm_queue_init(&queue->ctrl_q, "control");
msm_queue_init(&queue->eventData_q, "eventdata");
queue->queue_active = 1;
rc = msm_cam_server_open_session(&g_server_dev, pcam);
if (rc < 0) {
pr_err("%s: cam_server_open_session failed %d\n",
__func__, rc);
goto err;
}
pmctl = msm_camera_get_mctl(pcam->mctl_handle);
#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 err;
}
/* 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 err;
}
pmctl->pcam_ptr = pcam;
rc = msm_setup_v4l2_event_queue(&pcam_inst->eventHandle,
pcam->pvdev);
if (rc < 0) {
goto err;
}
}
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) {
mutex_unlock(&pcam->vid_lock);
pr_err("%s failed\n", __func__);
return rc;
}
}
mutex_unlock(&pcam->vid_lock);
D("%s: end", __func__);
/* rc = msm_cam_server_open_session(g_server_dev, pcam);*/
return rc;
err:
if (pcam->use_count == 1) {
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
if (ion_client_created) {
pr_err("%s: destroy ion client", __func__);
kref_put(&pmctl->refcount, msm_release_ion_client);
}
#endif
pcam->dev_inst[i] = NULL;
pcam->use_count = 0;
}
mutex_unlock(&pcam->vid_lock);
kfree(pcam_inst);
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_camera_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 ", __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_server_queue *queue;
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_camera_get_mctl(pcam->mctl_handle);
if (!pmctl) {
pr_err("%s NULL mctl pointer\n", __func__);
return -EINVAL;
}
mutex_lock(&pcam->vid_lock);
if (pcam_inst->streamon) {
/*something went wrong since instance
is closing without streamoff*/
if (pmctl->mctl_release) {
rc = pmctl->mctl_release(pmctl);
if (rc < 0)
pr_err("mctl_release fails %d\n", rc);
}
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) {
v4l2_fh_del(&pcam_inst->eventHandle);
v4l2_fh_exit(&pcam_inst->eventHandle);
}
kfree(pcam_inst);
f->private_data = NULL;
if (pcam->use_count == 0) {
if (g_server_dev.use_count > 0) {
rc = msm_send_close_server(pcam);
if (rc < 0)
pr_err("msm_send_close_server failed %d\n", rc);
}
if (pmctl->mctl_release) {
rc = pmctl->mctl_release(pmctl);
if (rc < 0)
pr_err("mctl_release fails %d\n", rc);
}
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
kref_put(&pmctl->refcount, msm_release_ion_client);
#endif
queue = &g_server_dev.server_queue[pcam->server_queue_idx];
queue->queue_active = 0;
kfree(queue->ctrl);
queue->ctrl = NULL;
kfree(queue->ctrl_data);
queue->ctrl_data = NULL;
msm_queue_drain(&queue->ctrl_q, list_control);
msm_drain_eventq(&queue->eventData_q);
rc = msm_cam_server_close_session(&g_server_dev, pcam);
if (rc < 0)
pr_err("msm_cam_server_close_session fails %d\n", rc);
if (g_server_dev.use_count == 0)
mutex_unlock(&g_server_dev.server_lock);
}
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.events->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;
}
static unsigned int msm_poll_server(struct file *fp,
struct poll_table_struct *wait)
{
int rc = 0;
D("%s\n", __func__);
poll_wait(fp,
&g_server_dev.server_command_queue.eventHandle.events->wait,
wait);
if (v4l2_event_pending(&g_server_dev.server_command_queue.eventHandle))
rc |= POLLPRI;
return rc;
}
static long msm_ioctl_server(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_camera_info temp_cam_info;
struct msm_cam_config_dev_info temp_config_info;
struct msm_mctl_node_info temp_mctl_info;
int i;
D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
switch (cmd) {
case MSM_CAM_V4L2_IOCTL_GET_CAMERA_INFO:
if (copy_from_user(&temp_cam_info,
(void __user *)ioctl_ptr->ioctl_ptr,
sizeof(struct msm_camera_info))) {
rc = -EINVAL;
return rc;
}
for (i = 0; i < g_server_dev.camera_info.num_cameras; i++) {
if (copy_to_user((void __user *)
temp_cam_info.video_dev_name[i],
g_server_dev.camera_info.video_dev_name[i],
strlen(g_server_dev.camera_info.video_dev_name[i]))) {
rc = -EINVAL;
return rc;
}
temp_cam_info.has_3d_support[i] =
g_server_dev.camera_info.has_3d_support[i];
temp_cam_info.is_internal_cam[i] =
g_server_dev.camera_info.is_internal_cam[i];
temp_cam_info.s_mount_angle[i] =
g_server_dev.camera_info.s_mount_angle[i];
temp_cam_info.sensor_type[i] =
g_server_dev.camera_info.sensor_type[i];
}
temp_cam_info.num_cameras =
g_server_dev.camera_info.num_cameras;
if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
&temp_cam_info,
sizeof(struct msm_camera_info))) {
rc = -EINVAL;
return rc;
}
rc = 0;
break;
case MSM_CAM_V4L2_IOCTL_GET_CONFIG_INFO:
if (copy_from_user(&temp_config_info,
(void __user *)ioctl_ptr->ioctl_ptr,
sizeof(struct msm_cam_config_dev_info))) {
rc = -EINVAL;
return rc;
}
for (i = 0;
i < g_server_dev.config_info.num_config_nodes; i++) {
if (copy_to_user(
(void __user *)temp_config_info.config_dev_name[i],
g_server_dev.config_info.config_dev_name[i],
strlen(g_server_dev.config_info.config_dev_name[i]))) {
rc = -EINVAL;
return rc;
}
}
temp_config_info.num_config_nodes =
g_server_dev.config_info.num_config_nodes;
if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
&temp_config_info,
sizeof(struct msm_cam_config_dev_info))) {
rc = -EINVAL;
return rc;
}
rc = 0;
break;
case MSM_CAM_V4L2_IOCTL_GET_MCTL_INFO:
if (copy_from_user(&temp_mctl_info,
(void __user *)ioctl_ptr->ioctl_ptr,
sizeof(struct msm_mctl_node_info))) {
rc = -EINVAL;
return rc;
}
for (i = 0; i < g_server_dev.mctl_node_info.num_mctl_nodes;
i++) {
if (copy_to_user((void __user *)
temp_mctl_info.mctl_node_name[i],
g_server_dev.mctl_node_info.mctl_node_name[i], strnlen(
g_server_dev.mctl_node_info.mctl_node_name[i],
MAX_DEV_NAME_LEN))) {
rc = -EINVAL;
return rc;
}
}
temp_mctl_info.num_mctl_nodes =
g_server_dev.mctl_node_info.num_mctl_nodes;
if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
&temp_mctl_info,
sizeof(struct msm_mctl_node_info))) {
rc = -EINVAL;
return rc;
}
rc = 0;
break;
case MSM_CAM_V4L2_IOCTL_CTRL_CMD_DONE:
D("%s: MSM_CAM_IOCTL_CTRL_CMD_DONE\n", __func__);
rc = msm_ctrl_cmd_done(arg);
break;
case MSM_CAM_V4L2_IOCTL_GET_EVENT_PAYLOAD: {
struct msm_queue_cmd *event_cmd;
struct msm_isp_event_ctrl u_isp_event;
struct msm_isp_event_ctrl *k_isp_event;
struct msm_device_queue *queue;
void __user *u_ctrl_value = NULL;
if (copy_from_user(&u_isp_event,
(void __user *)ioctl_ptr->ioctl_ptr,
sizeof(struct msm_isp_event_ctrl))) {
rc = -EINVAL;
return rc;
}
queue = &g_server_dev.server_queue
[u_isp_event.isp_data.ctrl.queue_idx].eventData_q;
event_cmd = msm_dequeue(queue, list_eventdata);
if (!event_cmd) {
pr_err("%s: No event payload\n", __func__);
rc = -EINVAL;
return rc;
}
k_isp_event = (struct msm_isp_event_ctrl *)
event_cmd->command;
free_qcmd(event_cmd);
/* Save the pointer of the user allocated command buffer*/
u_ctrl_value = u_isp_event.isp_data.ctrl.value;
/* Copy the event structure into user struct*/
u_isp_event = *k_isp_event;
/* Restore the saved pointer of the user
* allocated command buffer. */
u_isp_event.isp_data.ctrl.value = u_ctrl_value;
/* Copy the ctrl cmd, if present*/
if (k_isp_event->isp_data.ctrl.length > 0) {
void *k_ctrl_value =
k_isp_event->isp_data.ctrl.value;
if (copy_to_user(u_ctrl_value, k_ctrl_value,
k_isp_event->isp_data.ctrl.length)) {
rc = -EINVAL;
break;
}
}
if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
&u_isp_event,
sizeof(struct msm_isp_event_ctrl))) {
rc = -EINVAL;
return rc;
}
rc = 0;
break;
}
default:
break;
}
return rc;
}
static int msm_open_server(struct file *fp)
{
int rc = 0;
D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
mutex_lock(&g_server_dev.server_lock);
g_server_dev.use_count++;
if (g_server_dev.use_count == 1)
fp->private_data =
&g_server_dev.server_command_queue.eventHandle;
mutex_unlock(&g_server_dev.server_lock);
return rc;
}
static unsigned int msm_poll_config(struct file *fp,
struct poll_table_struct *wait)
{
int rc = 0;
struct msm_cam_config_dev *config = fp->private_data;
if (config == NULL)
return -EINVAL;
D("%s\n", __func__);
poll_wait(fp,
&config->config_stat_event_queue.eventHandle.events->wait, wait);
if (v4l2_event_pending(&config->config_stat_event_queue.eventHandle))
rc |= POLLPRI;
return rc;
}
static int msm_close_server(struct file *fp)
{
D("%s\n", __func__);
mutex_lock(&g_server_dev.server_lock);
if (g_server_dev.use_count > 0)
g_server_dev.use_count--;
mutex_unlock(&g_server_dev.server_lock);
if (g_server_dev.use_count == 0) {
if (g_server_dev.pcam_active) {
struct v4l2_event v4l2_ev;
mutex_lock(&g_server_dev.server_lock);
v4l2_ev.type = V4L2_EVENT_PRIVATE_START
+ MSM_CAM_APP_NOTIFY_ERROR_EVENT;
ktime_get_ts(&v4l2_ev.timestamp);
v4l2_event_queue(
g_server_dev.pcam_active->pvdev, &v4l2_ev);
}
}
return 0;
}
static long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
unsigned int cmd, unsigned long evt)
{
struct v4l2_event v4l2_ev;
struct msm_cam_v4l2_device *pcam = NULL;
if (!mctl) {
pr_err("%s: mctl is NULL\n", __func__);
return -EINVAL;
}
if (copy_from_user(&v4l2_ev, (void __user *)evt,
sizeof(struct v4l2_event))) {
ERR_COPY_FROM_USER();
return -EFAULT;
}
pcam = mctl->pcam_ptr;
ktime_get_ts(&v4l2_ev.timestamp);
v4l2_event_queue(pcam->pvdev, &v4l2_ev);
return 0;
}
static long msm_ioctl_config(struct file *fp, unsigned int cmd,
unsigned long arg)
{
int rc = 0;
struct v4l2_event ev;
struct msm_cam_config_dev *config_cam = fp->private_data;
struct v4l2_event_subscription temp_sub;
D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
switch (cmd) {
/* memory management shall be handeld here*/
case MSM_CAM_IOCTL_REGISTER_PMEM:
return msm_register_pmem(
&config_cam->p_mctl->stats_info.pmem_stats_list,
(void __user *)arg, config_cam->p_mctl->client);
break;
case MSM_CAM_IOCTL_UNREGISTER_PMEM:
return msm_pmem_table_del(
&config_cam->p_mctl->stats_info.pmem_stats_list,
(void __user *)arg, config_cam->p_mctl->client);
break;
case VIDIOC_SUBSCRIBE_EVENT:
if (copy_from_user(&temp_sub,
(void __user *)arg,
sizeof(struct v4l2_event_subscription))) {
rc = -EINVAL;
return rc;
}
rc = msm_server_v4l2_subscribe_event
(&config_cam->config_stat_event_queue.eventHandle,
&temp_sub);
if (rc < 0)
return rc;
break;
case VIDIOC_UNSUBSCRIBE_EVENT:
if (copy_from_user(&temp_sub, (void __user *)arg,
sizeof(struct v4l2_event_subscription))) {
rc = -EINVAL;
return rc;
}
rc = msm_server_v4l2_unsubscribe_event
(&config_cam->config_stat_event_queue.eventHandle,
&temp_sub);
if (rc < 0)
return rc;
break;
case VIDIOC_DQEVENT: {
void __user *u_msg_value = NULL, *user_ptr = NULL;
struct msm_isp_event_ctrl u_isp_event;
struct msm_isp_event_ctrl *k_isp_event;
/* First, copy the v4l2 event structure from userspace */
D("%s: VIDIOC_DQEVENT\n", __func__);
if (copy_from_user(&ev, (void __user *)arg,
sizeof(struct v4l2_event)))
break;
/* Next, get the pointer to event_ctrl structure
* embedded inside the v4l2_event.u.data array. */
user_ptr = (void __user *)(*((uint32_t *)ev.u.data));
/* Next, copy the userspace event ctrl structure */
if (copy_from_user((void *)&u_isp_event, user_ptr,
sizeof(struct msm_isp_event_ctrl))) {
break;
}
/* Save the pointer of the user allocated command buffer*/
u_msg_value = u_isp_event.isp_data.isp_msg.data;
/* Dequeue the event queued into the v4l2 queue*/
rc = v4l2_event_dequeue(
&config_cam->config_stat_event_queue.eventHandle,
&ev, fp->f_flags & O_NONBLOCK);
if (rc < 0) {
pr_err("no pending events?");
break;
}
/* Use k_isp_event to point to the event_ctrl structure
* embedded inside v4l2_event.u.data */
k_isp_event = (struct msm_isp_event_ctrl *)
(*((uint32_t *)ev.u.data));
/* Copy the event structure into user struct. */
u_isp_event = *k_isp_event;
if (ev.type != (V4L2_EVENT_PRIVATE_START +
MSM_CAM_RESP_DIV_FRAME_EVT_MSG) &&
ev.type != (V4L2_EVENT_PRIVATE_START +
MSM_CAM_RESP_MCTL_PP_EVENT)) {
/* Restore the saved pointer of the
* user allocated command buffer. */
u_isp_event.isp_data.isp_msg.data = u_msg_value;
if (ev.type == (V4L2_EVENT_PRIVATE_START +
MSM_CAM_RESP_STAT_EVT_MSG)) {
if (k_isp_event->isp_data.isp_msg.len > 0) {
void *k_msg_value =
k_isp_event->isp_data.isp_msg.data;
if (copy_to_user(u_msg_value,
k_msg_value,
k_isp_event->isp_data.isp_msg.len)) {
rc = -EINVAL;
break;
}
kfree(k_msg_value);
}
}
}
/* Copy the event ctrl structure back
* into user's structure. */
if (copy_to_user(user_ptr,
(void *)&u_isp_event, sizeof(
struct msm_isp_event_ctrl))) {
rc = -EINVAL;
break;
}
kfree(k_isp_event);
/* Copy the v4l2_event structure back to the user*/
if (copy_to_user((void __user *)arg, &ev,
sizeof(struct v4l2_event))) {
rc = -EINVAL;
break;
}
}
break;
case MSM_CAM_IOCTL_V4L2_EVT_NOTIFY:
rc = msm_v4l2_evt_notify(config_cam->p_mctl, cmd, arg);
break;
case MSM_CAM_IOCTL_SET_MEM_MAP_INFO:
if (copy_from_user(&config_cam->mem_map, (void __user *)arg,
sizeof(struct msm_mem_map_info)))
rc = -EINVAL;
break;
default:{
/* For the rest of config command, forward to media controller*/
struct msm_cam_media_controller *p_mctl = config_cam->p_mctl;
if (p_mctl && p_mctl->mctl_cmd) {
rc = config_cam->p_mctl->mctl_cmd(p_mctl, cmd, arg);
} else {
rc = -EINVAL;
pr_err("%s: media controller is null\n", __func__);
}
break;
} /* end of default*/
} /* end of switch*/
return rc;
}
static int msm_mmap_config(struct file *fp, struct vm_area_struct *vma)
{
struct msm_cam_config_dev *config_cam = fp->private_data;
int rc = 0;
int phyaddr;
int retval;
unsigned long size;
D("%s: phy_addr=0x%x", __func__, config_cam->mem_map.cookie);
phyaddr = (int)config_cam->mem_map.cookie;
if (!phyaddr) {
pr_err("%s: no physical memory to map", __func__);
return -EFAULT;
}
memset(&config_cam->mem_map, 0,
sizeof(struct msm_mem_map_info));
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: remap failed, rc = %d",
__func__, retval);
rc = -ENOMEM;
goto end;
}
D("%s: phy_addr=0x%x: %08lx-%08lx, pgoff %08lx\n",
__func__, (uint32_t)phyaddr,
vma->vm_start, vma->vm_end, vma->vm_pgoff);
end:
return rc;
}
static int msm_open_config(struct inode *inode, struct file *fp)
{
int rc;
struct msm_cam_config_dev *config_cam =
container_of(inode->i_cdev, struct msm_cam_config_dev, config_cdev);
D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
rc = nonseekable_open(inode, fp);
if (rc < 0) {
pr_err("%s: nonseekable_open error %d\n", __func__, rc);
return rc;
}
config_cam->use_count++;
/*config_cam->isp_subdev = g_server_dev.pcam_active->mctl.isp_sdev;*/
/* assume there is only one active camera possible*/
config_cam->p_mctl =
msm_camera_get_mctl(g_server_dev.pcam_active->mctl_handle);
INIT_HLIST_HEAD(&config_cam->p_mctl->stats_info.pmem_stats_list);
spin_lock_init(&config_cam->p_mctl->stats_info.pmem_stats_spinlock);
config_cam->p_mctl->config_device = config_cam;
kref_get(&config_cam->p_mctl->refcount);
fp->private_data = config_cam;
return rc;
}
static int msm_close_config(struct inode *node, struct file *f)
{
struct msm_cam_config_dev *config_cam = f->private_data;
D("%s Decrementing ref count of config node ", __func__);
kref_put(&config_cam->p_mctl->refcount, msm_release_ion_client);
return 0;
}
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,
};
/* Init a config node for ISP control,
* which will create a config device (/dev/config0/ and plug in
* ISP's operation "v4l2_ioctl_ops*"
*/
static const struct v4l2_file_operations msm_fops_server = {
.owner = THIS_MODULE,
.open = msm_open_server,
.poll = msm_poll_server,
.unlocked_ioctl = video_ioctl2,
.release = msm_close_server,
};
static const struct v4l2_ioctl_ops msm_ioctl_ops_server = {
.vidioc_subscribe_event = msm_server_v4l2_subscribe_event,
.vidioc_default = msm_ioctl_server,
};
static const struct file_operations msm_fops_config = {
.owner = THIS_MODULE,
.open = msm_open_config,
.poll = msm_poll_config,
.unlocked_ioctl = msm_ioctl_config,
.mmap = msm_mmap_config,
.release = msm_close_config,
};
int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
struct video_device *pvdev)
{
int rc = 0;
/* v4l2_fh support */
spin_lock_init(&pvdev->fh_lock);
INIT_LIST_HEAD(&pvdev->fh_list);
rc = v4l2_fh_init(eventHandle, pvdev);
if (rc < 0)
return rc;
if (eventHandle->events == NULL) {
rc = v4l2_event_init(eventHandle);
if (rc < 0)
return rc;
}
/* queue of max size 30 */
rc = v4l2_event_alloc(eventHandle, 30);
if (rc < 0)
return rc;
v4l2_fh_add(eventHandle);
return rc;
}
static int msm_setup_config_dev(int node, char *device_name)
{
int rc = -ENODEV;
struct device *device_config;
int dev_num = node;
dev_t devno;
struct msm_cam_config_dev *config_cam;
config_cam = kzalloc(sizeof(*config_cam), GFP_KERNEL);
if (!config_cam) {
pr_err("%s: could not allocate memory for msm_cam_config_device\n",
__func__);
return -ENOMEM;
}
D("%s\n", __func__);
devno = MKDEV(MAJOR(msm_devno), dev_num+1);
device_config = device_create(msm_class, NULL, devno, NULL,
"%s%d", device_name, dev_num);
if (IS_ERR(device_config)) {
rc = PTR_ERR(device_config);
pr_err("%s: error creating device: %d\n", __func__, rc);
goto config_setup_fail;
}
cdev_init(&config_cam->config_cdev,
&msm_fops_config);
config_cam->config_cdev.owner = THIS_MODULE;
rc = cdev_add(&config_cam->config_cdev, devno, 1);
if (rc < 0) {
pr_err("%s: error adding cdev: %d\n", __func__, rc);
device_destroy(msm_class, devno);
goto config_setup_fail;
}
g_server_dev.config_info.config_dev_name[dev_num]
= dev_name(device_config);
D("%s Connected config device %s\n", __func__,
g_server_dev.config_info.config_dev_name[dev_num]);
g_server_dev.config_info.config_dev_id[dev_num]
= dev_num;
config_cam->config_stat_event_queue.pvdev = video_device_alloc();
if (config_cam->config_stat_event_queue.pvdev == NULL) {
pr_err("%s: video_device_alloc failed\n", __func__);
goto config_setup_fail;
}
rc = msm_setup_v4l2_event_queue(
&config_cam->config_stat_event_queue.eventHandle,
config_cam->config_stat_event_queue.pvdev);
if (rc < 0) {
pr_err("%s failed to initialize event queue\n", __func__);
goto config_setup_fail;
}
return rc;
config_setup_fail:
kfree(config_cam);
return rc;
}
static void msm_cam_server_subdev_notify(struct v4l2_subdev *sd,
unsigned int notification, void *arg)
{
int rc = -EINVAL;
struct msm_sensor_ctrl_t *s_ctrl;
struct msm_camera_sensor_info *sinfo;
struct msm_camera_device_platform_data *camdev;
uint8_t csid_core = 0;
if (notification == NOTIFY_CID_CHANGE ||
notification == NOTIFY_ISPIF_STREAM ||
notification == NOTIFY_PCLK_CHANGE ||
notification == NOTIFY_CSIPHY_CFG ||
notification == NOTIFY_CSID_CFG ||
notification == NOTIFY_CSIC_CFG) {
s_ctrl = get_sctrl(sd);
sinfo = (struct msm_camera_sensor_info *) s_ctrl->sensordata;
camdev = sinfo->pdata;
csid_core = camdev->csid_core;
}
switch (notification) {
case NOTIFY_CID_CHANGE:
/* reconfig the ISPIF*/
if (g_server_dev.ispif_device) {
struct msm_ispif_params_list ispif_params;
ispif_params.len = 1;
ispif_params.params[0].intftype = PIX0;
ispif_params.params[0].cid_mask = 0x0001;
ispif_params.params[0].csid = csid_core;
rc = v4l2_subdev_call(
g_server_dev.ispif_device, core, ioctl,
VIDIOC_MSM_ISPIF_CFG, &ispif_params);
if (rc < 0)
return;
}
break;
case NOTIFY_ISPIF_STREAM:
/* call ISPIF stream on/off */
rc = v4l2_subdev_call(g_server_dev.ispif_device, video,
s_stream, (int)arg);
if (rc < 0)
return;
break;
case NOTIFY_ISP_MSG_EVT:
case NOTIFY_VFE_MSG_OUT:
case NOTIFY_VFE_MSG_STATS:
case NOTIFY_VFE_MSG_COMP_STATS:
case NOTIFY_VFE_BUF_EVT:
case NOTIFY_VFE_BUF_FREE_EVT:
if (g_server_dev.isp_subdev[0] &&
g_server_dev.isp_subdev[0]->isp_notify) {
rc = g_server_dev.isp_subdev[0]->isp_notify(
g_server_dev.vfe_device[0], notification, arg);
}
break;
case NOTIFY_VPE_MSG_EVT: {
struct msm_cam_media_controller *pmctl =
(struct msm_cam_media_controller *)
v4l2_get_subdev_hostdata(sd);
struct msm_vpe_resp *vdata = (struct msm_vpe_resp *)arg;
msm_mctl_pp_notify(pmctl,
(struct msm_mctl_pp_frame_info *)
vdata->extdata);
break;
}
case NOTIFY_VFE_IRQ:{
struct msm_vfe_cfg_cmd cfg_cmd;
struct msm_camvfe_params vfe_params;
cfg_cmd.cmd_type = CMD_VFE_PROCESS_IRQ;
vfe_params.vfe_cfg = &cfg_cmd;
vfe_params.data = arg;
rc = v4l2_subdev_call(g_server_dev.vfe_device[0],
core, ioctl, 0, &vfe_params);
}
break;
case NOTIFY_AXI_IRQ:
rc = v4l2_subdev_call(g_server_dev.axi_device[0],
core, ioctl, VIDIOC_MSM_AXI_IRQ, arg);
break;
case NOTIFY_PCLK_CHANGE:
if (g_server_dev.axi_device[0])
rc = v4l2_subdev_call(g_server_dev.axi_device[0], video,
s_crystal_freq, *(uint32_t *)arg, 0);
else
rc = v4l2_subdev_call(g_server_dev.vfe_device[0], video,
s_crystal_freq, *(uint32_t *)arg, 0);
break;
case NOTIFY_CSIPHY_CFG:
rc = v4l2_subdev_call(g_server_dev.csiphy_device[csid_core],
core, ioctl, VIDIOC_MSM_CSIPHY_CFG, arg);
break;
case NOTIFY_CSID_CFG:
rc = v4l2_subdev_call(g_server_dev.csid_device[csid_core],
core, ioctl, VIDIOC_MSM_CSID_CFG, arg);
break;
case NOTIFY_CSIC_CFG:
rc = v4l2_subdev_call(g_server_dev.csic_device[csid_core],
core, ioctl, VIDIOC_MSM_CSIC_CFG, arg);
break;
default:
break;
}
return;
}
int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
enum msm_cam_subdev_type sdev_type, uint8_t index)
{
struct video_device *vdev;
int err = 0;
if (sdev_type == CSIPHY_DEV) {
if (index >= MAX_NUM_CSIPHY_DEV)
return -EINVAL;
g_server_dev.csiphy_device[index] = sd;
} else if (sdev_type == CSID_DEV) {
if (index >= MAX_NUM_CSID_DEV)
return -EINVAL;
g_server_dev.csid_device[index] = sd;
} else if (sdev_type == CSIC_DEV) {
if (index >= MAX_NUM_CSIC_DEV)
return -EINVAL;
g_server_dev.csic_device[index] = sd;
} else if (sdev_type == ISPIF_DEV) {
g_server_dev.ispif_device = sd;
} else if (sdev_type == VFE_DEV) {
if (index >= MAX_NUM_VFE_DEV)
return -EINVAL;
g_server_dev.vfe_device[index] = sd;
} else if (sdev_type == VPE_DEV) {
if (index >= MAX_NUM_VPE_DEV)
return -EINVAL;
g_server_dev.vpe_device[index] = sd;
} else if (sdev_type == AXI_DEV) {
if (index >= MAX_NUM_AXI_DEV)
return -EINVAL;
g_server_dev.axi_device[index] = sd;
}
err = v4l2_device_register_subdev(&g_server_dev.v4l2_dev, sd);
if (err < 0)
return err;
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
return err;
vdev = &sd->devnode;
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = &g_server_dev.v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
vdev->release = video_device_release_empty;
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
if (err < 0)
return err;
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.v4l.major = VIDEO_MAJOR;
sd->entity.v4l.minor = vdev->minor;
#endif
return 0;
}
static int msm_setup_server_dev(struct platform_device *pdev)
{
int rc = -ENODEV, i;
D("%s\n", __func__);
g_server_dev.server_pdev = pdev;
g_server_dev.v4l2_dev.dev = &pdev->dev;
g_server_dev.v4l2_dev.notify = msm_cam_server_subdev_notify;
rc = v4l2_device_register(g_server_dev.v4l2_dev.dev,
&g_server_dev.v4l2_dev);
if (rc < 0)
return -EINVAL;
g_server_dev.video_dev = video_device_alloc();
if (g_server_dev.video_dev == NULL) {
pr_err("%s: video_device_alloc failed\n", __func__);
return rc;
}
strlcpy(g_server_dev.video_dev->name, pdev->name,
sizeof(g_server_dev.video_dev->name));
g_server_dev.video_dev->v4l2_dev = &g_server_dev.v4l2_dev;
g_server_dev.video_dev->fops = &msm_fops_server;
g_server_dev.video_dev->ioctl_ops = &msm_ioctl_ops_server;
g_server_dev.video_dev->release = video_device_release;
g_server_dev.video_dev->minor = 100;
g_server_dev.video_dev->vfl_type = 1;
video_set_drvdata(g_server_dev.video_dev, &g_server_dev);
strlcpy(g_server_dev.media_dev.model, "qcamera",
sizeof(g_server_dev.media_dev.model));
g_server_dev.media_dev.dev = &pdev->dev;
rc = media_device_register(&g_server_dev.media_dev);
g_server_dev.v4l2_dev.mdev = &g_server_dev.media_dev;
rc = video_register_device(g_server_dev.video_dev,
VFL_TYPE_GRABBER, 100);
mutex_init(&g_server_dev.server_lock);
mutex_init(&g_server_dev.server_queue_lock);
g_server_dev.pcam_active = NULL;
g_server_dev.camera_info.num_cameras = 0;
atomic_set(&g_server_dev.number_pcam_active, 0);
g_server_dev.server_evt_id = 0;
/*initialize fake video device and event queue*/
g_server_dev.server_command_queue.pvdev = g_server_dev.video_dev;
rc = msm_setup_v4l2_event_queue(
&g_server_dev.server_command_queue.eventHandle,
g_server_dev.server_command_queue.pvdev);
if (rc < 0)
pr_err("%s failed to initialize event queue\n", __func__);
for (i = 0; i < MAX_NUM_ACTIVE_CAMERA; i++) {
struct msm_cam_server_queue *queue;
queue = &g_server_dev.server_queue[i];
queue->queue_active = 0;
msm_queue_init(&queue->ctrl_q, "control");
msm_queue_init(&queue->eventData_q, "eventdata");
}
return rc;
}
static int msm_cam_dev_init(struct msm_cam_v4l2_device *pcam)
{
int rc = -ENOMEM;
struct video_device *pvdev = NULL;
struct i2c_client *client = v4l2_get_subdevdata(pcam->sensor_sdev);
D("%s\n", __func__);
/* first register the v4l2 device */
pcam->v4l2_dev.dev = &client->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));
pcam->media_dev.dev = &client->dev;
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 = 1;
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);
/* If isp HW registeration is successful,
* then create event queue to
* receievent event froms HW
*/
/* yyan: no global - each sensor will
* create a new vidoe node! */
/* g_pmsm_camera_v4l2_dev = pmsm_camera_v4l2_dev; */
/* g_pmsm_camera_v4l2_dev->pvdev = pvdev; */
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)
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;
}
}
return act_sdev;
client_fail:
i2c_unregister_device(act_client);
device_fail:
i2c_put_adapter(adapter);
adapter = NULL;
probe_fail:
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;
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);
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;
}
g_server_dev.camera_info.video_dev_name
[g_server_dev.camera_info.num_cameras]
= video_device_node_name(pcam->pvdev);
D("%s Connected video device %s\n", __func__,
g_server_dev.camera_info.video_dev_name
[g_server_dev.camera_info.num_cameras]);
g_server_dev.camera_info.s_mount_angle
[g_server_dev.camera_info.num_cameras]
= sdata->sensor_platform_info->mount_angle;
g_server_dev.camera_info.is_internal_cam
[g_server_dev.camera_info.num_cameras]
= sdata->camera_type;
g_server_dev.mctl_node_info.mctl_node_name
[g_server_dev.mctl_node_info.num_mctl_nodes]
= video_device_node_name(pcam->mctl_node.pvdev);
pr_info("%s mctl_node_name[%d] = %s\n", __func__,
g_server_dev.mctl_node_info.num_mctl_nodes,
g_server_dev.mctl_node_info.mctl_node_name
[g_server_dev.mctl_node_info.num_mctl_nodes]);
/*Temporary solution to store info in media device structure
until we can expand media device structure to support more
device info*/
snprintf(pcam->media_dev.serial,
sizeof(pcam->media_dev.serial),
"%s-%d-%d", QCAMERA_NAME,
sdata->sensor_platform_info->mount_angle,
sdata->camera_type);
g_server_dev.camera_info.num_cameras++;
g_server_dev.mctl_node_info.num_mctl_nodes++;
D("%s done, rc = %d\n", __func__, rc);
D("%s number of sensors connected is %d\n", __func__,
g_server_dev.camera_info.num_cameras);
/* register the subdevice, must be done for callbacks */
rc = msm_cam_register_subdev_node(sensor_sd, SENSOR_DEV, vnode_count);
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;
}
}
pcam->vnode_id = vnode_count++;
return rc;
failure:
kzfree(pcam);
return rc;
}
EXPORT_SYMBOL(msm_sensor_register);
static int __devinit msm_camera_probe(struct platform_device *pdev)
{
int rc = 0, i;
/*for now just create a config 0 node
put logic here later to know how many configs to create*/
g_server_dev.config_info.num_config_nodes = 1;
rc = msm_isp_init_module(g_server_dev.config_info.num_config_nodes);
if (rc < 0) {
pr_err("Failed to initialize isp\n");
return rc;
}
if (!msm_class) {
rc = alloc_chrdev_region(&msm_devno, 0,
g_server_dev.config_info.num_config_nodes+1, "msm_camera");
if (rc < 0) {
pr_err("%s: failed to allocate chrdev: %d\n", __func__,
rc);
return rc;
}
msm_class = class_create(THIS_MODULE, "msm_camera");
if (IS_ERR(msm_class)) {
rc = PTR_ERR(msm_class);
pr_err("%s: create device class failed: %d\n",
__func__, rc);
return rc;
}
}
D("creating server and config nodes\n");
rc = msm_setup_server_dev(pdev);
if (rc < 0) {
pr_err("%s: failed to create server dev: %d\n", __func__,
rc);
return rc;
}
for (i = 0; i < g_server_dev.config_info.num_config_nodes; i++) {
rc = msm_setup_config_dev(i, "config");
if (rc < 0) {
pr_err("%s:failed to create config dev: %d\n",
__func__, rc);
return rc;
}
}
msm_isp_register(&g_server_dev);
return rc;
}
static int __exit msm_camera_exit(struct platform_device *pdev)
{
msm_isp_unregister(&g_server_dev);
return 0;
}
static struct platform_driver msm_cam_server_driver = {
.probe = msm_camera_probe,
.remove = msm_camera_exit,
.driver = {
.name = "msm_cam_server",
.owner = THIS_MODULE,
},
};
static int __init msm_camera_init(void)
{
return platform_driver_register(&msm_cam_server_driver);
}
static void __exit msm_cam_server_exit(void)
{
platform_driver_unregister(&msm_cam_server_driver);
}
module_init(msm_camera_init);
module_exit(msm_cam_server_exit);