blob: 52dd1758031b381561cf925b47a44741670a2732 [file] [log] [blame]
/* Copyright (c) 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/module.h>
#include <linux/interrupt.h>
#include <mach/irqs.h>
#include <media/msm_isp.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include "msm.h"
#include "server/msm_cam_server.h"
#include "msm_camirq_router.h"
#ifdef CONFIG_MSM_CAMERA_DEBUG
#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
#else
#define D(fmt, args...) do {} while (0)
#endif
static void msm_irqrouter_update_irqmap_entry(
struct msm_cam_server_irqmap_entry *entry,
int is_composite, int irq_idx, int cam_hw_idx)
{
int rc = 0;
entry->irq_idx = irq_idx;
entry->is_composite = is_composite;
entry->cam_hw_idx = cam_hw_idx;
rc = msm_cam_server_update_irqmap(entry);
if (rc < 0)
pr_err("%s Error updating irq %d information ",
__func__, irq_idx);
}
static void msm_irqrouter_send_default_irqmap(
struct irqrouter_ctrl_type *irqrouter_ctrl)
{
struct msm_cam_server_irqmap_entry *irqmap =
&irqrouter_ctrl->def_hw_irqmap[0];
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_0],
0, CAMERA_SS_IRQ_0, MSM_CAM_HW_MICRO);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_1],
0, CAMERA_SS_IRQ_1, MSM_CAM_HW_CCI);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_2],
0, CAMERA_SS_IRQ_2, MSM_CAM_HW_CSI0);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_3],
0, CAMERA_SS_IRQ_3, MSM_CAM_HW_CSI1);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_4],
0, CAMERA_SS_IRQ_4, MSM_CAM_HW_CSI2);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_5],
0, CAMERA_SS_IRQ_5, MSM_CAM_HW_CSI3);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_6],
0, CAMERA_SS_IRQ_6, MSM_CAM_HW_ISPIF);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_7],
0, CAMERA_SS_IRQ_7, MSM_CAM_HW_CPP);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_8],
0, CAMERA_SS_IRQ_8, MSM_CAM_HW_VFE0);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_9],
0, CAMERA_SS_IRQ_9, MSM_CAM_HW_VFE1);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_10],
0, CAMERA_SS_IRQ_10, MSM_CAM_HW_JPEG0);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_11],
0, CAMERA_SS_IRQ_11, MSM_CAM_HW_JPEG1);
msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_12],
0, CAMERA_SS_IRQ_12, MSM_CAM_HW_JPEG2);
}
static int msm_irqrouter_open(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
/* Only one object of IRQ Router allowed. */
if (atomic_read(&irqrouter_ctrl->active) != 0) {
pr_err("%s IRQ router is already opened\n", __func__);
return -EINVAL;
}
D("%s E ", __func__);
atomic_inc(&irqrouter_ctrl->active);
return 0;
}
static int msm_irqrouter_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
if (atomic_read(&irqrouter_ctrl->active) == 0) {
pr_err("%s IRQ router is already closed\n", __func__);
return -EINVAL;
}
D("%s E ", __func__);
atomic_dec(&irqrouter_ctrl->active);
return 0;
}
static const struct v4l2_subdev_internal_ops msm_irqrouter_internal_ops = {
.open = msm_irqrouter_open,
.close = msm_irqrouter_close,
};
long msm_irqrouter_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
struct msm_camera_irq_cfg *irq_cfg;
struct intr_table_entry irq_req;
int rc = 0;
/* Handle all IRQ Router Subdev IOCTLs here.
* Userspace sends the composite irq configuration.
* IRQ Router subdev then configures the registers to group
* together individual core hw irqs into a composite IRQ
* to the MSM IRQ controller. It also registers them with
* the irq manager in the camera server. */
switch (cmd) {
case MSM_IRQROUTER_CFG_COMPIRQ:
COPY_FROM_USER(rc, &irq_cfg, (void __user *)arg,
sizeof(struct msm_camera_irq_cfg));
if (rc) {
ERR_COPY_FROM_USER();
break;
}
if (!irq_cfg ||
(irq_cfg->irq_idx < CAMERA_SS_IRQ_0) ||
(irq_cfg->irq_idx >= CAMERA_SS_IRQ_MAX)) {
pr_err("%s Invalid input", __func__);
return -EINVAL;
} else {
irq_req.cam_hw_mask = irq_cfg->cam_hw_mask;
irq_req.irq_idx = irq_cfg->irq_idx;
irq_req.irq_num =
irqrouter_ctrl->def_hw_irqmap[irq_cfg->irq_idx].irq_num;
irq_req.is_composite = 1;
irq_req.irq_trigger_type = IRQF_TRIGGER_RISING;
irq_req.num_hwcore = irq_cfg->num_hwcore;
irq_req.data = NULL;
rc = msm_cam_server_request_irq(&irq_req);
if (rc < 0) {
pr_err("%s Error requesting comp irq %d ",
__func__, irq_req.irq_idx);
return rc;
}
irqrouter_ctrl->def_hw_irqmap
[irq_cfg->irq_idx].is_composite = 1;
}
break;
default:
pr_err("%s Invalid cmd %d ", __func__, cmd);
break;
}
return rc;
}
static const struct v4l2_subdev_core_ops msm_irqrouter_subdev_core_ops = {
.ioctl = msm_irqrouter_subdev_ioctl,
};
static const struct v4l2_subdev_ops msm_irqrouter_subdev_ops = {
.core = &msm_irqrouter_subdev_core_ops,
};
static int __devinit irqrouter_probe(struct platform_device *pdev)
{
int rc = 0;
struct irqrouter_ctrl_type *irqrouter_ctrl;
struct msm_cam_subdev_info sd_info;
D("%s: device id = %d\n", __func__, pdev->id);
irqrouter_ctrl = kzalloc(sizeof(struct irqrouter_ctrl_type),
GFP_KERNEL);
if (!irqrouter_ctrl) {
pr_err("%s: not enough memory\n", __func__);
return -ENOMEM;
}
v4l2_subdev_init(&irqrouter_ctrl->subdev, &msm_irqrouter_subdev_ops);
irqrouter_ctrl->subdev.internal_ops = &msm_irqrouter_internal_ops;
irqrouter_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(irqrouter_ctrl->subdev.name,
sizeof(irqrouter_ctrl->subdev.name), "msm_irqrouter");
v4l2_set_subdevdata(&irqrouter_ctrl->subdev, irqrouter_ctrl);
irqrouter_ctrl->pdev = pdev;
msm_irqrouter_send_default_irqmap(irqrouter_ctrl);
media_entity_init(&irqrouter_ctrl->subdev.entity, 0, NULL, 0);
irqrouter_ctrl->subdev.entity.type = MEDIA_ENT_T_DEVNODE_V4L;
irqrouter_ctrl->subdev.entity.group_id = IRQ_ROUTER_DEV;
irqrouter_ctrl->subdev.entity.name = pdev->name;
sd_info.sdev_type = IRQ_ROUTER_DEV;
sd_info.sd_index = 0;
sd_info.irq_num = 0;
/* Now register this subdev with the camera server. */
rc = msm_cam_register_subdev_node(&irqrouter_ctrl->subdev, &sd_info);
if (rc < 0) {
pr_err("%s Error registering irqr subdev %d", __func__, rc);
goto error;
}
irqrouter_ctrl->subdev.entity.revision =
irqrouter_ctrl->subdev.devnode->num;
atomic_set(&irqrouter_ctrl->active, 0);
platform_set_drvdata(pdev, &irqrouter_ctrl->subdev);
return rc;
error:
kfree(irqrouter_ctrl);
return rc;
}
static int __exit irqrouter_exit(struct platform_device *pdev)
{
kfree(irqrouter_ctrl);
return 0;
}
static struct platform_driver msm_irqrouter_driver = {
.probe = irqrouter_probe,
.remove = irqrouter_exit,
.driver = {
.name = MSM_IRQ_ROUTER_DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init msm_irqrouter_init_module(void)
{
return platform_driver_register(&msm_irqrouter_driver);
}
static void __exit msm_irqrouter_exit_module(void)
{
platform_driver_unregister(&msm_irqrouter_driver);
}
module_init(msm_irqrouter_init_module);
module_exit(msm_irqrouter_exit_module);
MODULE_DESCRIPTION("msm camera irq router");
MODULE_LICENSE("GPL v2");