diff options
10 files changed, 746 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt index bb413af4b54d..c5c82a89f662 100644 --- a/Documentation/devicetree/bindings/media/video/msm-cci.txt +++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt @@ -205,6 +205,31 @@ Optional properties: (in the same order). - cam_vaf-supply : should contain regulator from which AF voltage is supplied +* Qualcomm Technologies, Inc. MSM LASER LED + +Required properties: +- cell-index : should contain unique identifier to differentiate + between multiple laser led modules +- reg : should contain i2c slave address of the laser led and length of + data field which is 0x0 +- compatible : + - "qcom,laser-led" +- qcom,cci-master : should contain i2c master id to be used for this camera + sensor + - 0 -> MASTER 0 + - 1 -> MASTER 1 + +Optional properties: +- qcom,cam-vreg-name : should contain names of all regulators needed by this + laser led +- qcom,cam-vreg-min-voltage : should contain minimum voltage level in microvolts + for regulators mentioned in qcom,cam-vreg-name property (in the same order) +- qcom,cam-vreg-max-voltage : should contain maximum voltage level in microvolts + for regulators mentioned in qcom,cam-vreg-name property (in the same order) +- qcom,cam-vreg-op-mode : should contain the maximum current in microamps + required from the regulators mentioned in the qcom,cam-vreg-name property + (in the same order). + * Qualcomm Technologies, Inc. MSM OIS Required properties: @@ -277,6 +302,13 @@ Example: qcom,cam-vreg-op-mode = <100000>; }; + laserled0: qcom,laserled@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,laser-led"; + qcom,cci-master = <1>; + }; + qcom,camera@0 { cell-index = <0>; compatible = "qcom,camera"; diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile index 872dc59d218e..b04560fe42bc 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/Makefile +++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile @@ -5,4 +5,5 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ ir_led/ ir_cut/ +obj-$(CONFIG_MSMB_CAMERA) += laser_led/ obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor_init.o msm_sensor_driver.o msm_sensor.o diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile b/drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile new file mode 100644 index 000000000000..e981fc2e1f9c --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_v2 +ccflags-y += -Idrivers/media/platform/msm/camera_v2/common +ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io +ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci +obj-$(CONFIG_MSMB_CAMERA) += msm_laser_led.o diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c new file mode 100644 index 000000000000..c368f081f97b --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c @@ -0,0 +1,573 @@ +/* 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 <linux/module.h> +#include "msm_laser_led.h" +#include "msm_camera_dt_util.h" +#include "msm_sd.h" +#include "msm_cci.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +DEFINE_MSM_MUTEX(msm_laser_led_mutex); + +static struct v4l2_file_operations msm_laser_led_v4l2_subdev_fops; + +static const struct of_device_id msm_laser_led_dt_match[] = { + {.compatible = "qcom,laser-led", .data = NULL}, + {} +}; + +static long msm_laser_led_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg); + +static int32_t msm_laser_led_get_subdev_id( + struct msm_laser_led_ctrl_t *laser_led_ctrl, void __user *arg) +{ + int32_t __user *subdev_id = (int32_t __user *)arg; + + CDBG("Enter\n"); + if (!subdev_id) { + pr_err("subdevice ID is not valid\n"); + return -EINVAL; + } + + if (laser_led_ctrl->laser_led_device_type != + MSM_CAMERA_PLATFORM_DEVICE) { + pr_err("device type is not matching\n"); + return -EINVAL; + } + + if (copy_to_user(arg, &laser_led_ctrl->pdev->id, + sizeof(int32_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + CDBG("Exit: subdev_id %d\n", laser_led_ctrl->pdev->id); + return 0; +} + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; +#ifdef CONFIG_COMPAT +static int32_t msm_laser_led_init( + struct msm_laser_led_ctrl_t *laser_led_ctrl, + struct msm_laser_led_cfg_data_t32 __user *laser_led_data) +#else +static int32_t msm_laser_led_init( + struct msm_laser_led_ctrl_t *laser_led_ctrl, + struct msm_laser_led_cfg_data_t __user *laser_led_data) +#endif +{ + int32_t rc = -EFAULT; + struct msm_camera_cci_client *cci_client = NULL; + + CDBG("Enter\n"); + + if (laser_led_ctrl->laser_led_state == MSM_CAMERA_LASER_LED_INIT) { + pr_err("Invalid laser_led state = %d\n", + laser_led_ctrl->laser_led_state); + return 0; + } + + rc = laser_led_ctrl->i2c_client.i2c_func_tbl->i2c_util( + &laser_led_ctrl->i2c_client, MSM_CCI_INIT); + if (rc < 0) + pr_err("cci_init failed\n"); + + cci_client = laser_led_ctrl->i2c_client.cci_client; + + if (copy_from_user(&(cci_client->sid), + &(laser_led_data->i2c_addr), + sizeof(uint16_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + cci_client->sid = cci_client->sid >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + + if (copy_from_user(&(cci_client->i2c_freq_mode), + &(laser_led_data->i2c_freq_mode), + sizeof(enum i2c_freq_mode_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + laser_led_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_INIT; + + CDBG("Exit\n"); + return 0; +} + +static int msm_laser_led_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + int rc = 0; + struct msm_laser_led_ctrl_t *l_ctrl = v4l2_get_subdevdata(sd); + + CDBG("Enter\n"); + if (!l_ctrl) { + pr_err("failed: subdev data is null\n"); + return -EINVAL; + } + mutex_lock(l_ctrl->laser_led_mutex); + if (l_ctrl->laser_led_device_type == MSM_CAMERA_PLATFORM_DEVICE && + l_ctrl->laser_led_state != MSM_CAMERA_LASER_LED_RELEASE) { + rc = l_ctrl->i2c_client.i2c_func_tbl->i2c_util( + &l_ctrl->i2c_client, MSM_CCI_RELEASE); + if (rc < 0) + pr_err("cci_init failed: %d\n", rc); + } + l_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_RELEASE; + mutex_unlock(l_ctrl->laser_led_mutex); + CDBG("Exit\n"); + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_laser_led_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + int32_t rc = 0; + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + CDBG("Enter\n"); + switch (cmd) { + case VIDIOC_MSM_LASER_LED_CFG32: + cmd = VIDIOC_MSM_LASER_LED_CFG; + default: + rc = msm_laser_led_subdev_ioctl(sd, cmd, arg); + } + + CDBG("Exit\n"); + return rc; +} + +static long msm_laser_led_subdev_fops_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return msm_laser_led_subdev_do_ioctl(file, cmd, (void *)arg); +} + +static int32_t msm_laser_led_control32( + struct msm_laser_led_ctrl_t *laser_led_ctrl, + void __user *argp) +{ + struct msm_camera_i2c_reg_setting32 conf_array32; + struct msm_camera_i2c_reg_setting conf_array; + int32_t rc = 0; + struct msm_laser_led_cfg_data_t32 laser_led_data; + uint32_t *debug_reg; + int i; + uint16_t local_data; + + if (laser_led_ctrl->laser_led_state != MSM_CAMERA_LASER_LED_INIT) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, laser_led_ctrl->laser_led_state); + return -EFAULT; + } + + if (copy_from_user(&laser_led_data, + argp, + sizeof(struct msm_laser_led_cfg_data_t32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + if (copy_from_user(&conf_array32, + (compat_ptr)(laser_led_data.setting), + sizeof(struct msm_camera_i2c_reg_setting32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + conf_array.addr_type = conf_array32.addr_type; + conf_array.data_type = conf_array32.data_type; + conf_array.delay = conf_array32.delay; + conf_array.size = conf_array32.size; + + if (!conf_array.size || + conf_array.size > I2C_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + conf_array.reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!conf_array.reg_setting) + return -ENOMEM; + + if (copy_from_user(conf_array.reg_setting, + (compat_ptr)(conf_array32.reg_setting), + conf_array.size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(conf_array.reg_setting); + return -EFAULT; + } + + debug_reg = kzalloc(laser_led_data.debug_reg_size * + (sizeof(uint32_t)), GFP_KERNEL); + if (!debug_reg) { + kfree(conf_array.reg_setting); + return -ENOMEM; + } + + if (copy_from_user(debug_reg, + (void __user *)compat_ptr(laser_led_data.debug_reg), + laser_led_data.debug_reg_size * + sizeof(uint32_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(conf_array.reg_setting); + kfree(debug_reg); + return -EFAULT; + } + + laser_led_ctrl->i2c_client.addr_type = conf_array.addr_type; + + rc = laser_led_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_table(&(laser_led_ctrl->i2c_client), + &conf_array); + + for (i = 0; i < laser_led_data.debug_reg_size; i++) { + rc = laser_led_ctrl->i2c_client.i2c_func_tbl->i2c_read( + &(laser_led_ctrl->i2c_client), + debug_reg[i], + &local_data, conf_array.data_type); + } + + kfree(conf_array.reg_setting); + kfree(debug_reg); + + return rc; +} +#endif + +static int32_t msm_laser_led_control( + struct msm_laser_led_ctrl_t *laser_led_ctrl, + void __user *argp) +{ + struct msm_camera_i2c_reg_setting conf_array; + struct msm_laser_led_cfg_data_t laser_led_data; + + uint32_t *debug_reg; + int i; + uint16_t local_data; + int32_t rc = 0; + + if (laser_led_ctrl->laser_led_state != MSM_CAMERA_LASER_LED_INIT) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, laser_led_ctrl->laser_led_state); + return -EFAULT; + } + + if (copy_from_user(&laser_led_data, + argp, + sizeof(struct msm_laser_led_cfg_data_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + if (copy_from_user(&conf_array, + (laser_led_data.setting), + sizeof(struct msm_camera_i2c_reg_setting))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + if (!conf_array.size || + conf_array.size > I2C_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EFAULT; + } + + conf_array.reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!conf_array.reg_setting) + return -ENOMEM; + + if (copy_from_user(conf_array.reg_setting, (void __user *)( + conf_array.reg_setting), + conf_array.size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(conf_array.reg_setting); + return -EFAULT; + } + + debug_reg = kzalloc(laser_led_data.debug_reg_size * + (sizeof(uint32_t)), GFP_KERNEL); + if (!debug_reg) { + kfree(conf_array.reg_setting); + return -ENOMEM; + } + + if (copy_from_user(debug_reg, + (laser_led_data.debug_reg), + laser_led_data.debug_reg_size * + sizeof(uint32_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(debug_reg); + kfree(conf_array.reg_setting); + return -EFAULT; + } + + laser_led_ctrl->i2c_client.addr_type = conf_array.addr_type; + + rc = laser_led_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_table(&(laser_led_ctrl->i2c_client), + &conf_array); + + for (i = 0; i < laser_led_data.debug_reg_size; i++) { + rc = laser_led_ctrl->i2c_client.i2c_func_tbl->i2c_read( + &(laser_led_ctrl->i2c_client), + debug_reg[i], + &local_data, conf_array.data_type); + } + + kfree(conf_array.reg_setting); + kfree(debug_reg); + + return rc; +} + +static int32_t msm_laser_led_config(struct msm_laser_led_ctrl_t *laser_led_ctrl, + void __user *argp) +{ + int32_t rc = -EINVAL; + enum msm_laser_led_cfg_type_t cfg_type; + +#ifdef CONFIG_COMPAT + struct msm_laser_led_cfg_data_t32 __user *laser_led_data = + (struct msm_laser_led_cfg_data_t32 __user *) argp; +#else + struct msm_laser_led_cfg_data_t __user *laser_led_data = + (struct msm_laser_led_cfg_data_t __user *) argp; +#endif + + mutex_lock(laser_led_ctrl->laser_led_mutex); + + if (copy_from_user(&(cfg_type), + &(laser_led_data->cfg_type), + sizeof(enum msm_laser_led_cfg_type_t))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + mutex_unlock(laser_led_ctrl->laser_led_mutex); + return -EFAULT; + } + + CDBG("type %d\n", cfg_type); + + switch (cfg_type) { + case CFG_LASER_LED_INIT: + rc = msm_laser_led_init(laser_led_ctrl, laser_led_data); + break; + case CFG_LASER_LED_CONTROL: +#ifdef CONFIG_COMPAT + if (is_compat_task()) + rc = msm_laser_led_control32(laser_led_ctrl, argp); + else +#endif + rc = msm_laser_led_control(laser_led_ctrl, argp); + break; + default: + rc = -EFAULT; + break; + } + + mutex_unlock(laser_led_ctrl->laser_led_mutex); + + CDBG("Exit: type %d\n", cfg_type); + + return rc; +} + +static long msm_laser_led_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_laser_led_ctrl_t *lctrl = NULL; + void __user *argp = (void __user *)arg; + + CDBG("Enter\n"); + + if (!sd) { + pr_err(" v4l2 ir led subdevice is NULL\n"); + return -EINVAL; + } + lctrl = v4l2_get_subdevdata(sd); + if (!lctrl) { + pr_err("lctrl NULL\n"); + return -EINVAL; + } + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_laser_led_get_subdev_id(lctrl, argp); + case VIDIOC_MSM_LASER_LED_CFG: + return msm_laser_led_config(lctrl, argp); + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_SHUTDOWN: + if (!lctrl->i2c_client.i2c_func_tbl) { + pr_err("a_ctrl->i2c_client.i2c_func_tbl NULL\n"); + return -EINVAL; + } + return msm_laser_led_close(sd, NULL); + + default: + pr_err("invalid cmd %d\n", cmd); + return -ENOIOCTLCMD; + } + CDBG("Exit\n"); +} + +static struct v4l2_subdev_core_ops msm_laser_led_subdev_core_ops = { + .ioctl = msm_laser_led_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_laser_led_subdev_ops = { + .core = &msm_laser_led_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops msm_laser_led_internal_ops = { + .close = msm_laser_led_close, +}; + +static int32_t msm_laser_led_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct msm_laser_led_ctrl_t *laser_led_ctrl = NULL; + struct msm_camera_cci_client *cci_client = NULL; + + CDBG("Enter\n"); + if (!pdev->dev.of_node) { + pr_err("IR LED device node is not present in device tree\n"); + return -EINVAL; + } + + laser_led_ctrl = devm_kzalloc(&pdev->dev, + sizeof(struct msm_laser_led_ctrl_t), GFP_KERNEL); + if (!laser_led_ctrl) + return -ENOMEM; + + laser_led_ctrl->pdev = pdev; + + rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index", + &pdev->id); + CDBG("cell-index %d, rc %d\n", pdev->id, rc); + if (rc < 0) { + kfree(laser_led_ctrl); + pr_err("reading cell index failed: rc %d\n", rc); + return rc; + } + + rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master", + &laser_led_ctrl->cci_master); + CDBG("qcom,cci-master %d, rc %d\n", laser_led_ctrl->cci_master, rc); + if (rc < 0 || laser_led_ctrl->cci_master >= MASTER_MAX) { + kfree(laser_led_ctrl); + pr_err("invalid cci master info: rc %d\n", rc); + return rc; + } + + laser_led_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_RELEASE; + laser_led_ctrl->power_info.dev = &laser_led_ctrl->pdev->dev; + laser_led_ctrl->laser_led_device_type = MSM_CAMERA_PLATFORM_DEVICE; + laser_led_ctrl->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + laser_led_ctrl->laser_led_mutex = &msm_laser_led_mutex; + + laser_led_ctrl->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!laser_led_ctrl->i2c_client.cci_client) + return -ENOMEM; + + cci_client = laser_led_ctrl->i2c_client.cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->cci_i2c_master = laser_led_ctrl->cci_master; + + /* Initialize sub device */ + v4l2_subdev_init(&laser_led_ctrl->msm_sd.sd, &msm_laser_led_subdev_ops); + v4l2_set_subdevdata(&laser_led_ctrl->msm_sd.sd, laser_led_ctrl); + + laser_led_ctrl->msm_sd.sd.internal_ops = &msm_laser_led_internal_ops; + laser_led_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(laser_led_ctrl->msm_sd.sd.name, + ARRAY_SIZE(laser_led_ctrl->msm_sd.sd.name), + "msm_camera_laser_led"); + media_entity_init(&laser_led_ctrl->msm_sd.sd.entity, 0, NULL, 0); + laser_led_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + laser_led_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_LASER_LED; + laser_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1; + msm_sd_register(&laser_led_ctrl->msm_sd); + + laser_led_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_RELEASE; + + CDBG("laser_led sd name = %s\n", + laser_led_ctrl->msm_sd.sd.entity.name); + msm_laser_led_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_laser_led_v4l2_subdev_fops.compat_ioctl32 = + msm_laser_led_subdev_fops_ioctl; +#endif + laser_led_ctrl->msm_sd.sd.devnode->fops = + &msm_laser_led_v4l2_subdev_fops; + + CDBG("probe success\n"); + return rc; +} + +MODULE_DEVICE_TABLE(of, msm_laser_led_dt_match); + +static struct platform_driver msm_laser_led_platform_driver = { + .probe = msm_laser_led_platform_probe, + .driver = { + .name = "qcom,laser-led", + .owner = THIS_MODULE, + .of_match_table = msm_laser_led_dt_match, + }, +}; + +static int __init msm_laser_led_init_module(void) +{ + int32_t rc; + + CDBG("Enter\n"); + rc = platform_driver_register(&msm_laser_led_platform_driver); + if (!rc) { + CDBG("Exit\n"); + return rc; + } + pr_err("laser-led driver register failed: %d\n", rc); + + return rc; +} + +static void __exit msm_laser_led_exit_module(void) +{ + platform_driver_unregister(&msm_laser_led_platform_driver); +} + +module_init(msm_laser_led_init_module); +module_exit(msm_laser_led_exit_module); +MODULE_DESCRIPTION("MSM IR LED"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.h b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.h new file mode 100644 index 000000000000..d5cb8b435d12 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +#ifndef MSM_LASER_LED_H +#define MSM_LASER_LED_H + +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <soc/qcom/camera2.h> +#include <media/v4l2-subdev.h> +#include <media/msmb_camera.h> +#include <linux/platform_device.h> +#include <media/v4l2-ioctl.h> +#include <media/msm_cam_sensor.h> +#include "msm_camera_i2c.h" +#include "msm_camera_dt_util.h" +#include "msm_camera_io_util.h" +#include "msm_sd.h" + + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +enum msm_camera_laser_led_state_t { + MSM_CAMERA_LASER_LED_INIT, + MSM_CAMERA_LASER_LED_RELEASE, +}; + +struct msm_laser_led_ctrl_t; + +struct msm_laser_led_ctrl_t { + struct msm_sd_subdev msm_sd; + struct platform_device *pdev; + struct msm_laser_led_func_t *func_tbl; + struct msm_camera_power_ctrl_t power_info; + struct i2c_driver *i2c_driver; + struct platform_driver *pdriver; + struct msm_camera_i2c_client i2c_client; + enum msm_camera_device_type_t laser_led_device_type; + struct v4l2_subdev sdev; + struct v4l2_subdev_ops *laser_led_v4l2_subdev_ops; + struct mutex *laser_led_mutex; + enum msm_camera_laser_led_state_t laser_led_state; + enum cci_i2c_master_t cci_master; +}; + +#endif diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 57bc392f54fd..167ed5492088 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -297,6 +297,45 @@ static int32_t msm_sensor_fill_actuator_subdevid_by_name( return rc; } +static int32_t msm_sensor_fill_laser_led_subdevid_by_name( + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct device_node *src_node = NULL; + uint32_t val = 0; + int32_t *laser_led_subdev_id; + struct msm_sensor_info_t *sensor_info; + struct device_node *of_node = s_ctrl->of_node; + + if (!of_node) + return -EINVAL; + + sensor_info = s_ctrl->sensordata->sensor_info; + laser_led_subdev_id = &sensor_info->subdev_id[SUB_MODULE_LASER_LED]; + /* set sudev id to -1 and try to found new id */ + *laser_led_subdev_id = -1; + + + src_node = of_parse_phandle(of_node, "qcom,laserled-src", 0); + if (!src_node) { + CDBG("%s:%d src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,laser led cell index %d, rc %d\n", __func__, + val, rc); + of_node_put(src_node); + src_node = NULL; + if (rc < 0) { + pr_err("%s cell index not found %d\n", + __func__, __LINE__); + return -EINVAL; + } + *laser_led_subdev_id = val; + } + + return rc; +} + static int32_t msm_sensor_fill_flash_subdevid_by_name( struct msm_sensor_ctrl_t *s_ctrl) { @@ -981,6 +1020,11 @@ CSID_TG: pr_err("%s failed %d\n", __func__, __LINE__); goto free_camera_info; } + rc = msm_sensor_fill_laser_led_subdevid_by_name(s_ctrl); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_camera_info; + } rc = msm_sensor_fill_ois_subdevid_by_name(s_ctrl); if (rc < 0) { diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 762f1c51620c..2c8b651147e0 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -84,6 +84,15 @@ struct msm_ir_cut_cfg_data_t32 { enum msm_ir_cut_cfg_type_t cfg_type; }; +struct msm_laser_led_cfg_data_t32 { + enum msm_laser_led_cfg_type_t cfg_type; + compat_uptr_t setting; + compat_uptr_t debug_reg; + uint32_t debug_reg_size; + uint16_t i2c_addr; + enum i2c_freq_mode_t i2c_freq_mode; +}; + struct eeprom_read_t32 { compat_uptr_t dbuffer; uint32_t num_bytes; @@ -276,7 +285,10 @@ struct msm_flash_cfg_data_t32 { #define VIDIOC_MSM_IR_CUT_CFG32 \ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t32) -#endif + +#define VIDIOC_MSM_LASER_LED_CFG32 \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_laser_led_cfg_data_t32) #endif +#endif diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h index c6144cd8f355..35529b0d7862 100644 --- a/include/uapi/media/msm_cam_sensor.h +++ b/include/uapi/media/msm_cam_sensor.h @@ -88,6 +88,7 @@ enum sensor_sub_module_t { SUB_MODULE_EXT, SUB_MODULE_IR_LED, SUB_MODULE_IR_CUT, + SUB_MODULE_LASER_LED, SUB_MODULE_MAX, }; @@ -301,6 +302,15 @@ struct msm_ir_cut_cfg_data_t { enum msm_ir_cut_cfg_type_t cfg_type; }; +struct msm_laser_led_cfg_data_t { + enum msm_laser_led_cfg_type_t cfg_type; + void __user *setting; + void __user *debug_reg; + uint32_t debug_reg_size; + uint16_t i2c_addr; + enum i2c_freq_mode_t i2c_freq_mode; +}; + struct msm_eeprom_cfg_data { enum eeprom_cfg_type_t cfgtype; uint8_t is_supported; @@ -616,5 +626,8 @@ struct sensor_init_cfg_data { #define VIDIOC_MSM_IR_CUT_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t) +#define VIDIOC_MSM_LASER_LED_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_laser_led_cfg_data_t) + #endif diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index a92c144f712e..e165f4482689 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -206,6 +206,13 @@ enum msm_ir_led_cfg_type_t { #define CFG_IR_LED_OFF CFG_IR_LED_OFF #define CFG_IR_LED_ON CFG_IR_LED_ON +enum msm_laser_led_cfg_type_t { + CFG_LASER_LED_INIT, + CFG_LASER_LED_CONTROL, +}; +#define CFG_LASER_LED_INIT CFG_LASER_LED_INIT +#define CFG_LASER_LED_CONTROL CFG_LASER_LED_CONTROL + enum msm_ir_cut_cfg_type_t { CFG_IR_CUT_INIT = 0, CFG_IR_CUT_RELEASE, diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index df9807e72e47..4b23806071d4 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -52,6 +52,7 @@ #define MSM_CAMERA_SUBDEV_IR_CUT 18 #define MSM_CAMERA_SUBDEV_EXT 19 #define MSM_CAMERA_SUBDEV_TOF 20 +#define MSM_CAMERA_SUBDEV_LASER_LED 21 #define MSM_MAX_CAMERA_SENSORS 5 /* The below macro is defined to put an upper limit on maximum |