| /* 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_sensor_dev.h" |
| #include "cam_req_mgr_dev.h" |
| #include "cam_sensor_soc.h" |
| #include "cam_sensor_core.h" |
| |
| static long cam_sensor_subdev_ioctl(struct v4l2_subdev *sd, |
| unsigned int cmd, void *arg) |
| { |
| int rc = 0; |
| struct cam_sensor_ctrl_t *s_ctrl = |
| v4l2_get_subdevdata(sd); |
| |
| switch (cmd) { |
| case VIDIOC_CAM_CONTROL: |
| rc = cam_sensor_driver_cmd(s_ctrl, arg); |
| break; |
| default: |
| pr_err("%s:%d Invalid ioctl cmd: %d\n", |
| __func__, __LINE__, cmd); |
| rc = -EINVAL; |
| break; |
| } |
| return rc; |
| } |
| |
| static int32_t cam_sensor_driver_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int32_t rc = 0; |
| struct cam_sensor_ctrl_t *s_ctrl; |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| pr_err("%s %s :Error: i2c_check_functionality failed\n", |
| __func__, client->name); |
| return -EFAULT; |
| } |
| |
| /* Create sensor control structure */ |
| s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL); |
| if (!s_ctrl) |
| return -ENOMEM; |
| |
| i2c_set_clientdata(client, s_ctrl); |
| |
| /* Initialize sensor device type */ |
| s_ctrl->of_node = client->dev.of_node; |
| s_ctrl->io_master_info.master_type = I2C_MASTER; |
| |
| rc = cam_sensor_parse_dt(s_ctrl); |
| if (rc < 0) { |
| pr_err("%s:%d :Error: cam_sensor_parse_dt rc %d", |
| __func__, __LINE__, rc); |
| goto free_s_ctrl; |
| } |
| |
| return rc; |
| free_s_ctrl: |
| kfree(s_ctrl); |
| return rc; |
| } |
| |
| static int cam_sensor_platform_remove(struct platform_device *pdev) |
| { |
| struct cam_sensor_ctrl_t *s_ctrl; |
| |
| s_ctrl = platform_get_drvdata(pdev); |
| if (!s_ctrl) { |
| pr_err("%s: sensor device is NULL\n", __func__); |
| return 0; |
| } |
| |
| kfree(s_ctrl->i2c_data.per_frame); |
| devm_kfree(&pdev->dev, s_ctrl); |
| |
| return 0; |
| } |
| |
| static int cam_sensor_driver_i2c_remove(struct i2c_client *client) |
| { |
| struct cam_sensor_ctrl_t *s_ctrl = i2c_get_clientdata(client); |
| |
| if (!s_ctrl) { |
| pr_err("%s: sensor device is NULL\n", __func__); |
| return 0; |
| } |
| |
| kfree(s_ctrl->i2c_data.per_frame); |
| kfree(s_ctrl); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_COMPAT |
| static long cam_sensor_init_subdev_do_ioctl(struct v4l2_subdev *sd, |
| unsigned int cmd, unsigned long arg) |
| { |
| struct cam_control cmd_data; |
| int32_t rc = 0; |
| |
| if (copy_from_user(&cmd_data, (void __user *)arg, |
| sizeof(cmd_data))) { |
| pr_err("Failed to copy from user_ptr=%pK size=%zu\n", |
| (void __user *)arg, sizeof(cmd_data)); |
| return -EFAULT; |
| } |
| |
| switch (cmd) { |
| case VIDIOC_CAM_CONTROL: |
| rc = cam_sensor_subdev_ioctl(sd, cmd, &cmd_data); |
| if (rc < 0) |
| pr_err("%s:%d cam_sensor_subdev_ioctl failed\n", |
| __func__, __LINE__); |
| break; |
| default: |
| pr_err("%s:%d Invalid compat ioctl cmd_type: %d\n", |
| __func__, __LINE__, cmd); |
| rc = -EINVAL; |
| } |
| |
| if (!rc) { |
| if (copy_to_user((void __user *)arg, &cmd_data, |
| sizeof(cmd_data))) { |
| pr_err("Failed to copy to user_ptr=%pK size=%zu\n", |
| (void __user *)arg, sizeof(cmd_data)); |
| rc = -EFAULT; |
| } |
| } |
| return rc; |
| } |
| |
| #endif |
| |
| static struct v4l2_subdev_core_ops cam_sensor_subdev_core_ops = { |
| .ioctl = cam_sensor_subdev_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl32 = cam_sensor_init_subdev_do_ioctl, |
| #endif |
| .s_power = cam_sensor_power, |
| }; |
| |
| static struct v4l2_subdev_ops cam_sensor_subdev_ops = { |
| .core = &cam_sensor_subdev_core_ops, |
| }; |
| |
| static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops; |
| |
| static const struct of_device_id cam_sensor_driver_dt_match[] = { |
| {.compatible = "qcom,cam-sensor"}, |
| {} |
| }; |
| |
| static int32_t cam_sensor_driver_platform_probe( |
| struct platform_device *pdev) |
| { |
| int32_t rc = 0, i = 0; |
| struct cam_sensor_ctrl_t *s_ctrl = NULL; |
| struct cam_hw_soc_info *soc_info = NULL; |
| |
| /* Create sensor control structure */ |
| s_ctrl = devm_kzalloc(&pdev->dev, |
| sizeof(struct cam_sensor_ctrl_t), GFP_KERNEL); |
| if (!s_ctrl) |
| return -ENOMEM; |
| |
| soc_info = &s_ctrl->soc_info; |
| soc_info->pdev = pdev; |
| |
| /* Initialize sensor device type */ |
| s_ctrl->of_node = pdev->dev.of_node; |
| s_ctrl->is_probe_succeed = 0; |
| |
| /*fill in platform device*/ |
| s_ctrl->pdev = pdev; |
| |
| s_ctrl->io_master_info.master_type = CCI_MASTER; |
| |
| rc = cam_sensor_parse_dt(s_ctrl); |
| if (rc < 0) { |
| pr_err("failed: cam_sensor_parse_dt rc %d", rc); |
| goto free_s_ctrl; |
| } |
| |
| /* Fill platform device id*/ |
| pdev->id = soc_info->index; |
| |
| s_ctrl->v4l2_dev_str.internal_ops = |
| &cam_sensor_internal_ops; |
| s_ctrl->v4l2_dev_str.ops = |
| &cam_sensor_subdev_ops; |
| strlcpy(s_ctrl->device_name, CAMX_SENSOR_DEV_NAME, |
| sizeof(s_ctrl->device_name)); |
| s_ctrl->v4l2_dev_str.name = |
| s_ctrl->device_name; |
| s_ctrl->v4l2_dev_str.sd_flags = |
| (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); |
| s_ctrl->v4l2_dev_str.ent_function = |
| CAM_SENSOR_DEVICE_TYPE; |
| s_ctrl->v4l2_dev_str.token = s_ctrl; |
| |
| rc = cam_register_subdev(&(s_ctrl->v4l2_dev_str)); |
| if (rc < 0) { |
| pr_err("%s:%d :ERROR: Fail with cam_register_subdev\n", |
| __func__, __LINE__); |
| goto free_s_ctrl; |
| } |
| |
| s_ctrl->i2c_data.per_frame = |
| (struct i2c_settings_array *) |
| kzalloc(sizeof(struct i2c_settings_array) * |
| MAX_PER_FRAME_ARRAY, GFP_KERNEL); |
| if (s_ctrl->i2c_data.per_frame == NULL) { |
| rc = -ENOMEM; |
| goto free_s_ctrl; |
| } |
| |
| INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head)); |
| |
| for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) |
| INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head)); |
| |
| s_ctrl->bridge_intf.device_hdl = -1; |
| s_ctrl->bridge_intf.ops.get_dev_info = cam_sensor_publish_dev_info; |
| s_ctrl->bridge_intf.ops.link_setup = cam_sensor_establish_link; |
| s_ctrl->bridge_intf.ops.apply_req = cam_sensor_apply_request; |
| s_ctrl->bridge_intf.ops.flush_req = cam_sensor_flush_request; |
| |
| s_ctrl->sensordata->power_info.dev = &pdev->dev; |
| platform_set_drvdata(pdev, s_ctrl); |
| v4l2_set_subdevdata(&(s_ctrl->v4l2_dev_str.sd), s_ctrl); |
| |
| return rc; |
| free_s_ctrl: |
| devm_kfree(&pdev->dev, s_ctrl); |
| return rc; |
| } |
| |
| MODULE_DEVICE_TABLE(of, cam_sensor_driver_dt_match); |
| |
| static struct platform_driver cam_sensor_platform_driver = { |
| .probe = cam_sensor_driver_platform_probe, |
| .driver = { |
| .name = "qcom,camera", |
| .owner = THIS_MODULE, |
| .of_match_table = cam_sensor_driver_dt_match, |
| }, |
| .remove = cam_sensor_platform_remove, |
| }; |
| |
| static const struct i2c_device_id i2c_id[] = { |
| {SENSOR_DRIVER_I2C, (kernel_ulong_t)NULL}, |
| { } |
| }; |
| |
| static struct i2c_driver cam_sensor_driver_i2c = { |
| .id_table = i2c_id, |
| .probe = cam_sensor_driver_i2c_probe, |
| .remove = cam_sensor_driver_i2c_remove, |
| .driver = { |
| .name = SENSOR_DRIVER_I2C, |
| }, |
| }; |
| |
| static int __init cam_sensor_driver_init(void) |
| { |
| int32_t rc = 0; |
| |
| rc = platform_driver_register(&cam_sensor_platform_driver); |
| if (rc) |
| pr_err("%s platform_driver_register failed rc = %d", |
| __func__, rc); |
| rc = i2c_add_driver(&cam_sensor_driver_i2c); |
| if (rc) |
| pr_err("%s i2c_add_driver failed rc = %d", __func__, rc); |
| |
| return rc; |
| } |
| |
| static void __exit cam_sensor_driver_exit(void) |
| { |
| platform_driver_unregister(&cam_sensor_platform_driver); |
| i2c_del_driver(&cam_sensor_driver_i2c); |
| } |
| |
| module_init(cam_sensor_driver_init); |
| module_exit(cam_sensor_driver_exit); |
| MODULE_DESCRIPTION("cam_sensor_driver"); |
| MODULE_LICENSE("GPL v2"); |