blob: a136fa7dd05d91b50f754457672c25d50f159d5d [file] [log] [blame]
/* Copyright (c) 2017, 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 "cam_csiphy_dev.h"
#include "cam_req_mgr_dev.h"
#include "cam_csiphy_soc.h"
#include "cam_csiphy_core.h"
#include <media/cam_sensor.h>
static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd);
int rc = 0;
switch (cmd) {
case VIDIOC_CAM_CONTROL:
rc = cam_csiphy_core_cfg(csiphy_dev, arg);
if (rc != 0) {
CAM_ERR(CAM_CSIPHY, "in configuring the device");
return rc;
}
break;
default:
CAM_ERR(CAM_CSIPHY, "Wrong ioctl : %d", cmd);
break;
}
return rc;
}
#ifdef CONFIG_COMPAT
static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
int32_t rc = 0;
struct cam_control cmd_data;
if (copy_from_user(&cmd_data, (void __user *)arg,
sizeof(cmd_data))) {
CAM_ERR(CAM_CSIPHY, "Failed to copy from user_ptr=%pK size=%zu",
(void __user *)arg, sizeof(cmd_data));
return -EFAULT;
}
/* All the arguments converted to 64 bit here
* Passed to the api in core.c
*/
switch (cmd) {
case VIDIOC_CAM_CONTROL:
rc = cam_csiphy_subdev_ioctl(sd, cmd, &cmd_data);
break;
default:
CAM_ERR(CAM_CSIPHY, "Invalid compat ioctl cmd: %d", cmd);
rc = -EINVAL;
}
if (!rc) {
if (copy_to_user((void __user *)arg, &cmd_data,
sizeof(cmd_data))) {
CAM_ERR(CAM_CSIPHY,
"Failed to copy to user_ptr=%pK size=%zu",
(void __user *)arg, sizeof(cmd_data));
rc = -EFAULT;
}
}
return rc;
}
#endif
static struct v4l2_subdev_core_ops csiphy_subdev_core_ops = {
.ioctl = cam_csiphy_subdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = cam_csiphy_subdev_compat_ioctl,
#endif
};
static const struct v4l2_subdev_ops csiphy_subdev_ops = {
.core = &csiphy_subdev_core_ops,
};
static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops;
static int32_t cam_csiphy_platform_probe(struct platform_device *pdev)
{
struct cam_cpas_register_params cpas_parms;
struct csiphy_device *new_csiphy_dev;
int32_t rc = 0;
new_csiphy_dev = devm_kzalloc(&pdev->dev,
sizeof(struct csiphy_device), GFP_KERNEL);
if (!new_csiphy_dev)
return -ENOMEM;
new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t),
GFP_KERNEL);
if (!new_csiphy_dev->ctrl_reg) {
devm_kfree(&pdev->dev, new_csiphy_dev);
return -ENOMEM;
}
mutex_init(&new_csiphy_dev->mutex);
new_csiphy_dev->v4l2_dev_str.pdev = pdev;
new_csiphy_dev->soc_info.pdev = pdev;
new_csiphy_dev->soc_info.dev = &pdev->dev;
new_csiphy_dev->soc_info.dev_name = pdev->name;
new_csiphy_dev->ref_count = 0;
rc = cam_csiphy_parse_dt_info(pdev, new_csiphy_dev);
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "DT parsing failed: %d", rc);
goto csiphy_no_resource;
}
new_csiphy_dev->v4l2_dev_str.internal_ops =
&csiphy_subdev_intern_ops;
new_csiphy_dev->v4l2_dev_str.ops =
&csiphy_subdev_ops;
strlcpy(new_csiphy_dev->device_name, CAMX_CSIPHY_DEV_NAME,
sizeof(new_csiphy_dev->device_name));
new_csiphy_dev->v4l2_dev_str.name =
new_csiphy_dev->device_name;
new_csiphy_dev->v4l2_dev_str.sd_flags =
(V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
new_csiphy_dev->v4l2_dev_str.ent_function =
CAM_CSIPHY_DEVICE_TYPE;
new_csiphy_dev->v4l2_dev_str.token =
new_csiphy_dev;
rc = cam_register_subdev(&(new_csiphy_dev->v4l2_dev_str));
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "cam_register_subdev Failed rc: %d", rc);
goto csiphy_no_resource;
}
platform_set_drvdata(pdev, &(new_csiphy_dev->v4l2_dev_str.sd));
v4l2_set_subdevdata(&(new_csiphy_dev->v4l2_dev_str.sd), new_csiphy_dev);
new_csiphy_dev->bridge_intf.device_hdl[0] = -1;
new_csiphy_dev->bridge_intf.device_hdl[1] = -1;
new_csiphy_dev->bridge_intf.ops.get_dev_info =
NULL;
new_csiphy_dev->bridge_intf.ops.link_setup =
NULL;
new_csiphy_dev->bridge_intf.ops.apply_req =
NULL;
new_csiphy_dev->acquire_count = 0;
new_csiphy_dev->start_dev_count = 0;
new_csiphy_dev->is_acquired_dev_combo_mode = 0;
cpas_parms.cam_cpas_client_cb = NULL;
cpas_parms.cell_index = new_csiphy_dev->soc_info.index;
cpas_parms.dev = &pdev->dev;
cpas_parms.userdata = new_csiphy_dev;
strlcpy(cpas_parms.identifier, "csiphy", CAM_HW_IDENTIFIER_LENGTH);
rc = cam_cpas_register_client(&cpas_parms);
if (rc) {
CAM_ERR(CAM_CSIPHY, "CPAS registration failed rc: %d", rc);
goto csiphy_no_resource;
}
CAM_DBG(CAM_CSIPHY, "CPAS registration successful handle=%d",
cpas_parms.client_handle);
new_csiphy_dev->cpas_handle = cpas_parms.client_handle;
return rc;
csiphy_no_resource:
mutex_destroy(&new_csiphy_dev->mutex);
kfree(new_csiphy_dev->ctrl_reg);
devm_kfree(&pdev->dev, new_csiphy_dev);
return rc;
}
static int32_t cam_csiphy_device_remove(struct platform_device *pdev)
{
struct v4l2_subdev *subdev =
platform_get_drvdata(pdev);
struct csiphy_device *csiphy_dev =
v4l2_get_subdevdata(subdev);
cam_cpas_unregister_client(csiphy_dev->cpas_handle);
cam_csiphy_soc_release(csiphy_dev);
kfree(csiphy_dev->ctrl_reg);
devm_kfree(&pdev->dev, csiphy_dev);
return 0;
}
static const struct of_device_id cam_csiphy_dt_match[] = {
{.compatible = "qcom,csiphy"},
{}
};
MODULE_DEVICE_TABLE(of, cam_csiphy_dt_match);
static struct platform_driver csiphy_driver = {
.probe = cam_csiphy_platform_probe,
.remove = cam_csiphy_device_remove,
.driver = {
.name = CAMX_CSIPHY_DEV_NAME,
.owner = THIS_MODULE,
.of_match_table = cam_csiphy_dt_match,
},
};
static int32_t __init cam_csiphy_init_module(void)
{
return platform_driver_register(&csiphy_driver);
}
static void __exit cam_csiphy_exit_module(void)
{
platform_driver_unregister(&csiphy_driver);
}
module_init(cam_csiphy_init_module);
module_exit(cam_csiphy_exit_module);
MODULE_DESCRIPTION("CAM CSIPHY driver");
MODULE_LICENSE("GPL v2");