Loading Documentation/devicetree/bindings/media/video/msm-cci.txt +32 −0 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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"; Loading arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi +8 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,13 @@ qcom,cam-vreg-op-mode = <0>; }; laserled0: qcom,laserled@0 { cell-index = <0>; reg = <0x0>; compatible = "qcom,laser-led"; qcom,cci-master = <1>; }; actuator1: qcom,actuator@1 { cell-index = <1>; reg = <0x1>; Loading Loading @@ -322,6 +329,7 @@ qcom,eeprom-src = <&eeprom2>; qcom,led-flash-src = <&led_flash1>; qcom,actuator-src = <&actuator1>; qcom,laserled-src = <&laserled0>; cam_vio-supply = <&pm8998_lvs1>; cam_vana-supply = <&pm8998_l22>; cam_vdig-supply = <&pm8998_s3>; Loading drivers/media/platform/msm/camera_v2/sensor/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile 0 → 100644 +5 −0 Original line number Diff line number Diff line 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 drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c 0 → 100644 +573 −0 Original line number Diff line number Diff line /* 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"); Loading
Documentation/devicetree/bindings/media/video/msm-cci.txt +32 −0 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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"; Loading
arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi +8 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,13 @@ qcom,cam-vreg-op-mode = <0>; }; laserled0: qcom,laserled@0 { cell-index = <0>; reg = <0x0>; compatible = "qcom,laser-led"; qcom,cci-master = <1>; }; actuator1: qcom,actuator@1 { cell-index = <1>; reg = <0x1>; Loading Loading @@ -322,6 +329,7 @@ qcom,eeprom-src = <&eeprom2>; qcom,led-flash-src = <&led_flash1>; qcom,actuator-src = <&actuator1>; qcom,laserled-src = <&laserled0>; cam_vio-supply = <&pm8998_lvs1>; cam_vana-supply = <&pm8998_l22>; cam_vdig-supply = <&pm8998_s3>; Loading
drivers/media/platform/msm/camera_v2/sensor/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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
drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile 0 → 100644 +5 −0 Original line number Diff line number Diff line 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
drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c 0 → 100644 +573 −0 Original line number Diff line number Diff line /* 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");