From 1dc1147122c7370f924bec6758b3dc425cbd4701 Mon Sep 17 00:00:00 2001 From: Vivek Veenam Date: Fri, 24 Jun 2016 11:49:16 +0530 Subject: [PATCH 1/2] msm: camera: Add a driver to control IR LED device This driver is able to control a IR LED device. The interface to user space is: CFG_IR_LED_INIT CFG_IR_LED_OFF CFG_IR_LED_ON with intensity field CFG_IR_LED_RELEASE. Change-Id: I2e04fa47efd1454bb487eca67bd9ceaeab3e9edf Signed-off-by: Vivek Veenam --- .../bindings/media/video/msm-ir-led.txt | 26 + .../platform/msm/camera_v2/sensor/Makefile | 2 +- .../msm/camera_v2/sensor/ir_led/Makefile | 4 + .../msm/camera_v2/sensor/ir_led/msm_ir_led.c | 462 ++++++++++++++++++ .../msm/camera_v2/sensor/ir_led/msm_ir_led.h | 71 +++ include/media/msm_cam_sensor.h | 10 + include/uapi/media/msm_cam_sensor.h | 9 + include/uapi/media/msm_camsensor_sdk.h | 11 + include/uapi/media/msmb_camera.h | 3 +- 9 files changed, 596 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/video/msm-ir-led.txt create mode 100644 drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile create mode 100644 drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c create mode 100644 drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h diff --git a/Documentation/devicetree/bindings/media/video/msm-ir-led.txt b/Documentation/devicetree/bindings/media/video/msm-ir-led.txt new file mode 100644 index 000000000000..7e66fa0cef5b --- /dev/null +++ b/Documentation/devicetree/bindings/media/video/msm-ir-led.txt @@ -0,0 +1,26 @@ +* QTI MSM IR LED + +Required properties: +- cell-index : ir led hardware core index +- compatible : + - "qcom,ir-led" + +Optional properties: +- gpios : should specify the gpios to be used for the ir led. +- qcom,gpio-req-tbl-num : should contain index to gpios specific to ir led +- qcom,gpio-req-tbl-flags : should contain direction of gpios present in + qcom,gpio-req-tbl-num property (in the same order) +- qcom,gpio-req-tbl-label : should contain name of gpios present in + qcom,gpio-req-tbl-num property (in the same order) +- label : should contain unique ir led name +Example: + +qcom,ir-led { + cell-index = <0>; + compatible = "qcom,ir-led"; + label = "led-ir-label"; + gpios = <&tlmm 60 0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "LED_IR_EN"; + }; diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile index 539ba24e109b..de6abd46c60e 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/Makefile +++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile @@ -4,5 +4,5 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2 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/ +obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ ir_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/ir_led/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile new file mode 100644 index 000000000000..6e99ecc2c78d --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile @@ -0,0 +1,4 @@ +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 +obj-$(CONFIG_MSMB_CAMERA) += msm_ir_led.o diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c new file mode 100644 index 000000000000..9af7abc5cd04 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c @@ -0,0 +1,462 @@ +/* Copyright (c) 2016, 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. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include +#include "msm_ir_led.h" +#include "msm_camera_dt_util.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +DEFINE_MSM_MUTEX(msm_ir_led_mutex); + +static struct v4l2_file_operations msm_ir_led_v4l2_subdev_fops; + +static const struct of_device_id msm_ir_led_dt_match[] = { + {.compatible = "qcom,ir-led", .data = NULL}, + {} +}; + +static struct msm_ir_led_table msm_default_ir_led_table; + +static struct msm_ir_led_table *ir_led_table[] = { + &msm_default_ir_led_table, +}; + +static int32_t msm_ir_led_get_subdev_id( + struct msm_ir_led_ctrl_t *ir_led_ctrl, void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + + CDBG("Enter\n"); + if (!subdev_id) { + pr_err("subdevice ID is not valid\n"); + return -EINVAL; + } + if (ir_led_ctrl->ir_led_device_type != MSM_CAMERA_PLATFORM_DEVICE) { + pr_err("device type is not matching\n"); + return -EINVAL; + } + + *subdev_id = ir_led_ctrl->pdev->id; + + CDBG("subdev_id %d\n", *subdev_id); + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_ir_led_init( + struct msm_ir_led_ctrl_t *ir_led_ctrl, + struct msm_ir_led_cfg_data_t *ir_led_data) +{ + int32_t rc = 0; + + CDBG("Enter\n"); + + rc = ir_led_ctrl->func_tbl->camera_ir_led_off(ir_led_ctrl, ir_led_data); + + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_ir_led_release( + struct msm_ir_led_ctrl_t *ir_led_ctrl) +{ + int32_t rc = 0; + + if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_RELEASE) { + pr_err("Invalid ir_led state = %d\n", + ir_led_ctrl->ir_led_state); + return 0; + } + + rc = ir_led_ctrl->func_tbl->camera_ir_led_off(ir_led_ctrl, NULL); + if (rc < 0) { + pr_err("camera_ir_led_off failed (%d)\n", rc); + return rc; + } + ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_RELEASE; + return 0; +} + +static int32_t msm_ir_led_off(struct msm_ir_led_ctrl_t *ir_led_ctrl, + struct msm_ir_led_cfg_data_t *ir_led_data) +{ + CDBG("Enter\n"); + + if (ir_led_ctrl->pwm_dev) + pwm_disable(ir_led_ctrl->pwm_dev); + else + pr_err("pwm device is null\n"); + + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_ir_led_on( + struct msm_ir_led_ctrl_t *ir_led_ctrl, + struct msm_ir_led_cfg_data_t *ir_led_data) +{ + int rc; + + CDBG("pwm duty on(ns) %d, pwm period(ns) %d\n", + ir_led_data->pwm_duty_on_ns, ir_led_data->pwm_period_ns); + + if (ir_led_ctrl->pwm_dev) { + rc = pwm_config(ir_led_ctrl->pwm_dev, + ir_led_data->pwm_duty_on_ns, + ir_led_data->pwm_period_ns); + if (rc) { + pr_err("PWM config failed (%d)\n", rc); + return rc; + } + + rc = pwm_enable(ir_led_ctrl->pwm_dev); + if (rc) { + pr_err("PWM enable failed(%d)\n", rc); + return rc; + } + } else + pr_err("pwm device is null\n"); + + return 0; +} + +static int32_t msm_ir_led_handle_init( + struct msm_ir_led_ctrl_t *ir_led_ctrl, + struct msm_ir_led_cfg_data_t *ir_led_data) +{ + uint32_t i = 0; + int32_t rc = -EFAULT; + enum msm_ir_led_driver_type ir_led_driver_type = + ir_led_ctrl->ir_led_driver_type; + + CDBG("Enter\n"); + + if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) { + pr_err("Invalid ir_led state = %d\n", + ir_led_ctrl->ir_led_state); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(ir_led_table); i++) { + if (ir_led_driver_type == ir_led_table[i]->ir_led_driver_type) { + ir_led_ctrl->func_tbl = &ir_led_table[i]->func_tbl; + rc = 0; + break; + } + } + + if (rc < 0) { + pr_err("failed invalid ir_led_driver_type %d\n", + ir_led_driver_type); + return -EINVAL; + } + + rc = ir_led_ctrl->func_tbl->camera_ir_led_init( + ir_led_ctrl, ir_led_data); + if (rc < 0) { + pr_err("camera_ir_led_init failed (%d)\n", rc); + return rc; + } + + ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_INIT; + + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_ir_led_config(struct msm_ir_led_ctrl_t *ir_led_ctrl, + void __user *argp) +{ + int32_t rc = -EINVAL; + struct msm_ir_led_cfg_data_t *ir_led_data = + (struct msm_ir_led_cfg_data_t *) argp; + + mutex_lock(ir_led_ctrl->ir_led_mutex); + + CDBG("type %d\n", ir_led_data->cfg_type); + + switch (ir_led_data->cfg_type) { + case CFG_IR_LED_INIT: + rc = msm_ir_led_handle_init(ir_led_ctrl, ir_led_data); + break; + case CFG_IR_LED_RELEASE: + if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) + rc = ir_led_ctrl->func_tbl->camera_ir_led_release( + ir_led_ctrl); + break; + case CFG_IR_LED_OFF: + if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) + rc = ir_led_ctrl->func_tbl->camera_ir_led_off( + ir_led_ctrl, ir_led_data); + break; + case CFG_IR_LED_ON: + if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) + rc = ir_led_ctrl->func_tbl->camera_ir_led_on( + ir_led_ctrl, ir_led_data); + break; + default: + rc = -EFAULT; + break; + } + + mutex_unlock(ir_led_ctrl->ir_led_mutex); + + CDBG("Exit: type %d\n", ir_led_data->cfg_type); + + return rc; +} + +static long msm_ir_led_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_ir_led_ctrl_t *fctrl = NULL; + void __user *argp = (void __user *)arg; + + CDBG("Enter\n"); + + if (!sd) { + pr_err(" v4l2 ir led subdevice is NULL\n"); + return -EINVAL; + } + fctrl = v4l2_get_subdevdata(sd); + if (!fctrl) { + pr_err("fctrl NULL\n"); + return -EINVAL; + } + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_ir_led_get_subdev_id(fctrl, argp); + case VIDIOC_MSM_IR_LED_CFG: + return msm_ir_led_config(fctrl, argp); + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_SHUTDOWN: + if (!fctrl->func_tbl) { + pr_err("No call back funcions\n"); + return -EINVAL; + } else { + return fctrl->func_tbl->camera_ir_led_release(fctrl); + } + default: + pr_err_ratelimited("invalid cmd %d\n", cmd); + return -ENOIOCTLCMD; + } + CDBG("Exit\n"); +} + +static struct v4l2_subdev_core_ops msm_ir_led_subdev_core_ops = { + .ioctl = msm_ir_led_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_ir_led_subdev_ops = { + .core = &msm_ir_led_subdev_core_ops, +}; + +static int msm_ir_led_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + + int rc = 0; + struct msm_ir_led_ctrl_t *ir_led_ctrl = v4l2_get_subdevdata(sd); + + if (!ir_led_ctrl) { + pr_err("v4l2 subdevice data read failed\n"); + return -EINVAL; + } + + CDBG("Enter\n"); + + if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) + rc = ir_led_ctrl->func_tbl->camera_ir_led_release( + ir_led_ctrl); + + CDBG("Exit (%d)\n", rc); + + return rc; +} + +static const struct v4l2_subdev_internal_ops msm_ir_led_internal_ops = { + .close = msm_ir_led_close, +}; + +static int32_t msm_ir_led_get_dt_data(struct device_node *of_node, + struct msm_ir_led_ctrl_t *fctrl) +{ + int32_t rc = 0; + + CDBG("called\n"); + + /* Read the sub device */ + rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id); + if (rc < 0) { + pr_err("reading cell-index for ir-led node is failed(rc) %d\n", + rc); + return rc; + } + + fctrl->ir_led_driver_type = IR_LED_DRIVER_DEFAULT; + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_ir_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); + struct msm_ir_led_cfg_data_t32 *u32 = + (struct msm_ir_led_cfg_data_t32 *)arg; + struct msm_ir_led_cfg_data_t ir_led_data; + + CDBG("Enter\n"); + ir_led_data.cfg_type = u32->cfg_type; + ir_led_data.pwm_duty_on_ns = u32->pwm_duty_on_ns; + ir_led_data.pwm_period_ns = u32->pwm_period_ns; + + switch (cmd) { + case VIDIOC_MSM_IR_LED_CFG32: + cmd = VIDIOC_MSM_IR_LED_CFG; + break; + default: + return msm_ir_led_subdev_ioctl(sd, cmd, arg); + } + + rc = msm_ir_led_subdev_ioctl(sd, cmd, &ir_led_data); + + CDBG("Exit\n"); + return rc; +} + +static long msm_ir_led_subdev_fops_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_ir_led_subdev_do_ioctl); +} +#endif + +static int32_t msm_ir_led_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct msm_ir_led_ctrl_t *ir_led_ctrl = NULL; + + CDBG("Enter\n"); + if (!pdev->dev.of_node) { + pr_err("IR LED device node is not present in device tree\n"); + return -EINVAL; + } + + ir_led_ctrl = devm_kzalloc(&pdev->dev, sizeof(struct msm_ir_led_ctrl_t), + GFP_KERNEL); + if (!ir_led_ctrl) + return -ENOMEM; + + ir_led_ctrl->pdev = pdev; + + /* Reading PWM device node */ + ir_led_ctrl->pwm_dev = of_pwm_get(pdev->dev.of_node, NULL); + + if (IS_ERR(ir_led_ctrl->pwm_dev)) { + rc = PTR_ERR(ir_led_ctrl->pwm_dev); + pr_err("Cannot get PWM device (%d)\n", rc); + ir_led_ctrl->pwm_dev = NULL; + } + + rc = msm_ir_led_get_dt_data(pdev->dev.of_node, ir_led_ctrl); + if (rc < 0) { + pr_err("msm_ir_led_get_dt_data failed\n"); + devm_kfree(&pdev->dev, ir_led_ctrl); + return -EINVAL; + } + + ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_RELEASE; + ir_led_ctrl->power_info.dev = &ir_led_ctrl->pdev->dev; + ir_led_ctrl->ir_led_device_type = MSM_CAMERA_PLATFORM_DEVICE; + ir_led_ctrl->ir_led_mutex = &msm_ir_led_mutex; + + /* Initialize sub device */ + v4l2_subdev_init(&ir_led_ctrl->msm_sd.sd, &msm_ir_led_subdev_ops); + v4l2_set_subdevdata(&ir_led_ctrl->msm_sd.sd, ir_led_ctrl); + + ir_led_ctrl->msm_sd.sd.internal_ops = &msm_ir_led_internal_ops; + ir_led_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(ir_led_ctrl->msm_sd.sd.name, + ARRAY_SIZE(ir_led_ctrl->msm_sd.sd.name), + "msm_camera_ir_led"); + media_entity_init(&ir_led_ctrl->msm_sd.sd.entity, 0, NULL, 0); + ir_led_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + ir_led_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_IR_LED; + ir_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1; + msm_sd_register(&ir_led_ctrl->msm_sd); + + CDBG("ir_led sd name = %s\n", + ir_led_ctrl->msm_sd.sd.entity.name); + msm_ir_led_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_ir_led_v4l2_subdev_fops.compat_ioctl32 = + msm_ir_led_subdev_fops_ioctl; +#endif + ir_led_ctrl->msm_sd.sd.devnode->fops = &msm_ir_led_v4l2_subdev_fops; + + CDBG("probe success\n"); + return rc; +} + +MODULE_DEVICE_TABLE(of, msm_ir_led_dt_match); + +static struct platform_driver msm_ir_led_platform_driver = { + .probe = msm_ir_led_platform_probe, + .driver = { + .name = "qcom,ir-led", + .owner = THIS_MODULE, + .of_match_table = msm_ir_led_dt_match, + }, +}; + +static int __init msm_ir_led_init_module(void) +{ + int32_t rc = 0; + + CDBG("Enter\n"); + rc = platform_driver_register(&msm_ir_led_platform_driver); + if (!rc) + return rc; + + pr_err("ir-led driver register failed (%d)\n", rc); + + return rc; +} + +static void __exit msm_ir_led_exit_module(void) +{ + platform_driver_unregister(&msm_ir_led_platform_driver); +} + +static struct msm_ir_led_table msm_default_ir_led_table = { + .ir_led_driver_type = IR_LED_DRIVER_DEFAULT, + .func_tbl = { + .camera_ir_led_init = msm_ir_led_init, + .camera_ir_led_release = msm_ir_led_release, + .camera_ir_led_off = msm_ir_led_off, + .camera_ir_led_on = msm_ir_led_on, + }, +}; + +module_init(msm_ir_led_init_module); +module_exit(msm_ir_led_exit_module); +MODULE_DESCRIPTION("MSM IR LED"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h new file mode 100644 index 000000000000..a0923ffc81da --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2016, 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_IR_LED_H +#define MSM_IR_LED_H + +#include +#include +#include +#include +#include +#include "msm_sd.h" + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +enum msm_camera_ir_led_state_t { + MSM_CAMERA_IR_LED_INIT, + MSM_CAMERA_IR_LED_RELEASE, +}; + +enum msm_ir_led_driver_type { + IR_LED_DRIVER_GPIO, + IR_LED_DRIVER_DEFAULT, +}; + +struct msm_ir_led_ctrl_t; + +struct msm_ir_led_func_t { + int32_t (*camera_ir_led_init)(struct msm_ir_led_ctrl_t *, + struct msm_ir_led_cfg_data_t *); + int32_t (*camera_ir_led_release)(struct msm_ir_led_ctrl_t *); + int32_t (*camera_ir_led_off)(struct msm_ir_led_ctrl_t *, + struct msm_ir_led_cfg_data_t *); + int32_t (*camera_ir_led_on)(struct msm_ir_led_ctrl_t *, + struct msm_ir_led_cfg_data_t *); +}; + +struct msm_ir_led_table { + enum msm_ir_led_driver_type ir_led_driver_type; + struct msm_ir_led_func_t func_tbl; +}; + +struct msm_ir_led_ctrl_t { + struct msm_sd_subdev msm_sd; + struct platform_device *pdev; + struct pwm_device *pwm_dev; + struct msm_ir_led_func_t *func_tbl; + struct msm_camera_power_ctrl_t power_info; + + enum msm_camera_device_type_t ir_led_device_type; + struct mutex *ir_led_mutex; + + /* ir_led driver type */ + enum msm_ir_led_driver_type ir_led_driver_type; + + /* ir_led state */ + enum msm_camera_ir_led_state_t ir_led_state; +}; + +#endif diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index fb650ab2693f..57cddbde7d0d 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -2,6 +2,7 @@ #define __LINUX_MSM_CAM_SENSOR_H #include +#include #include @@ -72,6 +73,12 @@ struct csid_cfg_data32 { } cfg; }; +struct msm_ir_led_cfg_data_t32 { + enum msm_ir_led_cfg_type_t cfg_type; + int32_t pwm_duty_on_ns; + int32_t pwm_period_ns; +}; + struct eeprom_read_t32 { compat_uptr_t dbuffer; uint32_t num_bytes; @@ -258,6 +265,9 @@ struct msm_flash_cfg_data_t32 { #define VIDIOC_MSM_FLASH_CFG32 \ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_flash_cfg_data_t32) + +#define VIDIOC_MSM_IR_LED_CFG32 \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ir_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 2c7ada5d02cf..c4ee5a119cc1 100644 --- a/include/uapi/media/msm_cam_sensor.h +++ b/include/uapi/media/msm_cam_sensor.h @@ -289,6 +289,12 @@ struct msm_eeprom_info_t { struct msm_eeprom_memory_map_array *mem_map_array; }; +struct msm_ir_led_cfg_data_t { + enum msm_ir_led_cfg_type_t cfg_type; + int32_t pwm_duty_on_ns; + int32_t pwm_period_ns; +}; + struct msm_eeprom_cfg_data { enum eeprom_cfg_type_t cfgtype; uint8_t is_supported; @@ -598,5 +604,8 @@ struct sensor_init_cfg_data { #define VIDIOC_MSM_FLASH_QUERY_DATA \ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_flash_query_data_t) +#define VIDIOC_MSM_IR_LED_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_led_cfg_data_t) + #endif diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index 01e52b6f7b44..fb33c871f784 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -182,6 +182,17 @@ enum msm_flash_cfg_type_t { CFG_FLASH_HIGH, }; +enum msm_ir_led_cfg_type_t { + CFG_IR_LED_INIT = 0, + CFG_IR_LED_RELEASE, + CFG_IR_LED_OFF, + CFG_IR_LED_ON, +}; +#define CFG_IR_LED_INIT CFG_IR_LED_INIT +#define CFG_IR_LED_RELEASE CFG_IR_LED_RELEASE +#define CFG_IR_LED_OFF CFG_IR_LED_OFF +#define CFG_IR_LED_ON CFG_IR_LED_ON + enum msm_sensor_output_format_t { MSM_SENSOR_BAYER, MSM_SENSOR_YCBCR, diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index fe70daa772df..cd8e065318bb 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -48,7 +48,8 @@ #define MSM_CAMERA_SUBDEV_SENSOR_INIT 14 #define MSM_CAMERA_SUBDEV_OIS 15 #define MSM_CAMERA_SUBDEV_FLASH 16 -#define MSM_CAMERA_SUBDEV_EXT 17 +#define MSM_CAMERA_SUBDEV_IR_LED 17 +#define MSM_CAMERA_SUBDEV_EXT 18 #define MSM_MAX_CAMERA_SENSORS 5 From 3951ec5f08ca7e620c897a91559acfb1c8e2befa Mon Sep 17 00:00:00 2001 From: Vivek Veenam Date: Mon, 27 Jun 2016 16:50:33 +0530 Subject: [PATCH 2/2] msm: camera: Add a driver to control IR CUT device This driver is able to control a IR CUT device. The interface to user space is: CFG_IR_CUT_INIT CFG_IR_CUT_OFF CFG_IR_CUT_ON CFG_IR_CUT_RELEASE Change-Id: I30d1c4e6c40b8e58a70f06db9e05231b4c7f676f Signed-off-by: Vivek Veenam --- .../bindings/media/video/msm-ir-cut.txt | 26 + .../platform/msm/camera_v2/sensor/Makefile | 2 +- .../camera_v2/sensor/io/msm_camera_dt_util.c | 48 ++ .../msm/camera_v2/sensor/ir_cut/Makefile | 4 + .../msm/camera_v2/sensor/ir_cut/msm_ir_cut.c | 665 ++++++++++++++++++ .../msm/camera_v2/sensor/ir_cut/msm_ir_cut.h | 72 ++ include/media/msm_cam_sensor.h | 7 + include/uapi/media/msm_cam_sensor.h | 7 + include/uapi/media/msm_camsensor_sdk.h | 20 + include/uapi/media/msmb_camera.h | 3 +- 10 files changed, 852 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/video/msm-ir-cut.txt create mode 100644 drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile create mode 100644 drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c create mode 100644 drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h diff --git a/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt b/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt new file mode 100644 index 000000000000..96cb28d692c1 --- /dev/null +++ b/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt @@ -0,0 +1,26 @@ +* QTI MSM IR CUT + +Required properties: +- cell-index : ir cut filter hardware core index +- compatible : + - "qcom,ir-cut" + +Optional properties: +- gpios : should specify the gpios to be used for the ir cut filter. +- qcom,gpio-req-tbl-num : should contain index to gpios specific to ir cut filter +- qcom,gpio-req-tbl-flags : should contain direction of gpios present in + qcom,gpio-req-tbl-num property (in the same order) +- qcom,gpio-req-tbl-label : should contain name of gpios present in + qcom,gpio-req-tbl-num property (in the same order) +- label : should contain unique ir cut filter name +Example: + +qcom,ir-cut@60 { + cell-index = <0>; + compatible = "qcom,ir-cut"; + label = "led-ir-label"; + gpios = <&tlmm 60 0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "LED_IR_EN"; + }; diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile index de6abd46c60e..872dc59d218e 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/Makefile +++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile @@ -4,5 +4,5 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2 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/ +obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ ir_led/ ir_cut/ 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/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c index 6b867bfb5c4a..121b2848e59f 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c @@ -748,6 +748,54 @@ int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, rc = -ENOMEM; return rc; } + + rc = of_property_read_u32(of_node, "qcom,gpio-ir-p", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-ir-p failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-ir-p invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + + gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_P] = + gpio_array[val]; + gconf->gpio_num_info->valid[IR_CUT_FILTER_GPIO_P] = 1; + + CDBG("%s qcom,gpio-ir-p %d\n", __func__, + gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_P]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-ir-m", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-ir-m failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-ir-m invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + + gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M] = + gpio_array[val]; + + gconf->gpio_num_info->valid[IR_CUT_FILTER_GPIO_M] = 1; + + CDBG("%s qcom,gpio-ir-m %d\n", __func__, + gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M]); + } else { + rc = 0; + } + rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val); if (rc != -EINVAL) { if (rc < 0) { diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile new file mode 100644 index 000000000000..8950c1c83763 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile @@ -0,0 +1,4 @@ +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 +obj-$(CONFIG_MSMB_CAMERA) += msm_ir_cut.o diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c new file mode 100644 index 000000000000..a0e35ba99a25 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c @@ -0,0 +1,665 @@ +/* Copyright (c) 2016, 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. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include +#include "msm_ir_cut.h" +#include "msm_camera_dt_util.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +DEFINE_MSM_MUTEX(msm_ir_cut_mutex); + +static struct v4l2_file_operations msm_ir_cut_v4l2_subdev_fops; + +static const struct of_device_id msm_ir_cut_dt_match[] = { + {.compatible = "qcom,ir-cut", .data = NULL}, + {} +}; + +static struct msm_ir_cut_table msm_gpio_ir_cut_table; + +static struct msm_ir_cut_table *ir_cut_table[] = { + &msm_gpio_ir_cut_table, +}; + +static int32_t msm_ir_cut_get_subdev_id( + struct msm_ir_cut_ctrl_t *ir_cut_ctrl, void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + + CDBG("Enter\n"); + if (!subdev_id) { + pr_err("failed\n"); + return -EINVAL; + } + if (ir_cut_ctrl->ir_cut_device_type != MSM_CAMERA_PLATFORM_DEVICE) { + pr_err("failed\n"); + return -EINVAL; + } + + *subdev_id = ir_cut_ctrl->pdev->id; + + CDBG("subdev_id %d\n", *subdev_id); + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_ir_cut_init( + struct msm_ir_cut_ctrl_t *ir_cut_ctrl, + struct msm_ir_cut_cfg_data_t *ir_cut_data) +{ + int32_t rc = 0; + + CDBG("Enter"); + + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(ir_cut_ctrl, ir_cut_data); + + CDBG("Exit"); + return rc; +} + +static int32_t msm_ir_cut_release( + struct msm_ir_cut_ctrl_t *ir_cut_ctrl) +{ + int32_t rc = 0; + + if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_RELEASE) { + pr_err("%s:%d Invalid ir_cut state = %d", + __func__, __LINE__, ir_cut_ctrl->ir_cut_state); + return 0; + } + + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(ir_cut_ctrl, NULL); + if (rc < 0) { + pr_err("%s:%d camera_ir_cut_on failed rc = %d", + __func__, __LINE__, rc); + return rc; + } + ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_RELEASE; + return 0; +} + +static int32_t msm_ir_cut_off(struct msm_ir_cut_ctrl_t *ir_cut_ctrl, + struct msm_ir_cut_cfg_data_t *ir_cut_data) +{ + int rc = 0; + + CDBG("Enter cut off\n"); + + if (ir_cut_ctrl->gconf) { + rc = msm_camera_request_gpio_table( + ir_cut_ctrl->gconf->cam_gpio_req_tbl, + ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 1); + + if (rc < 0) { + pr_err("ERR:%s:Failed in selecting state: %d\n", + __func__, rc); + + return rc; + } + } else { + pr_err("%s: No IR CUT GPIOs\n", __func__); + return 0; + } + + if (ir_cut_ctrl->cam_pinctrl_status) { + rc = pinctrl_select_state( + ir_cut_ctrl->pinctrl_info.pinctrl, + ir_cut_ctrl->pinctrl_info.gpio_state_active); + + if (rc < 0) + pr_err("ERR:%s:%d cannot set pin to active state: %d", + __func__, __LINE__, rc); + } + + CDBG("ERR:%s:gpio_conf->gpio_num_info->gpio_num[0] = %d", + __func__, + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_P]); + + CDBG("ERR:%s:gpio_conf->gpio_num_info->gpio_num[1] = %d", + __func__, + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_M]); + + gpio_set_value_cansleep( + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_P], + 0); + + gpio_set_value_cansleep( + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_M], + 1); + + if (ir_cut_ctrl->gconf) { + rc = msm_camera_request_gpio_table( + ir_cut_ctrl->gconf->cam_gpio_req_tbl, + ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 0); + + if (rc < 0) { + pr_err("ERR:%s:Failed in selecting state: %d\n", + __func__, rc); + + return rc; + } + } else { + pr_err("%s: No IR CUT GPIOs\n", __func__); + return 0; + } + + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_ir_cut_on( + struct msm_ir_cut_ctrl_t *ir_cut_ctrl, + struct msm_ir_cut_cfg_data_t *ir_cut_data) +{ + int rc = 0; + + CDBG("Enter ir cut on\n"); + + if (ir_cut_ctrl->gconf) { + rc = msm_camera_request_gpio_table( + ir_cut_ctrl->gconf->cam_gpio_req_tbl, + ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 1); + + if (rc < 0) { + pr_err("ERR:%s:Failed in selecting state: %d\n", + __func__, rc); + + return rc; + } + } else { + pr_err("%s: No IR CUT GPIOs\n", __func__); + return 0; + } + + if (ir_cut_ctrl->cam_pinctrl_status) { + rc = pinctrl_select_state( + ir_cut_ctrl->pinctrl_info.pinctrl, + ir_cut_ctrl->pinctrl_info.gpio_state_active); + + if (rc < 0) + pr_err("ERR:%s:%d cannot set pin to active state: %d", + __func__, __LINE__, rc); + } + + CDBG("ERR:%s: gpio_conf->gpio_num_info->gpio_num[0] = %d", + __func__, + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_P]); + + CDBG("ERR:%s: gpio_conf->gpio_num_info->gpio_num[1] = %d", + __func__, + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_M]); + + gpio_set_value_cansleep( + ir_cut_ctrl->gconf->gpio_num_info-> + gpio_num[IR_CUT_FILTER_GPIO_P], + 1); + + gpio_set_value_cansleep( + ir_cut_ctrl->gconf-> + gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M], + 1); + + if (ir_cut_ctrl->gconf) { + rc = msm_camera_request_gpio_table( + ir_cut_ctrl->gconf->cam_gpio_req_tbl, + ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 0); + + if (rc < 0) { + pr_err("ERR:%s:Failed in selecting state: %d\n", + __func__, rc); + + return rc; + } + } else { + pr_err("%s: No IR CUT GPIOs\n", __func__); + return 0; + } + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_ir_cut_handle_init( + struct msm_ir_cut_ctrl_t *ir_cut_ctrl, + struct msm_ir_cut_cfg_data_t *ir_cut_data) +{ + uint32_t i = 0; + int32_t rc = -EFAULT; + enum msm_ir_cut_driver_type ir_cut_driver_type = + ir_cut_ctrl->ir_cut_driver_type; + + CDBG("Enter"); + + if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) { + pr_err("%s:%d Invalid ir_cut state = %d", + __func__, __LINE__, ir_cut_ctrl->ir_cut_state); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(ir_cut_table); i++) { + if (ir_cut_driver_type == ir_cut_table[i]->ir_cut_driver_type) { + ir_cut_ctrl->func_tbl = &ir_cut_table[i]->func_tbl; + rc = 0; + break; + } + } + + if (rc < 0) { + pr_err("%s:%d failed invalid ir_cut_driver_type %d\n", + __func__, __LINE__, ir_cut_driver_type); + return -EINVAL; + } + + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_init( + ir_cut_ctrl, ir_cut_data); + if (rc < 0) { + pr_err("%s:%d camera_ir_cut_init failed rc = %d", + __func__, __LINE__, rc); + return rc; + } + + ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_INIT; + + CDBG("Exit"); + return 0; +} + +static int32_t msm_ir_cut_config(struct msm_ir_cut_ctrl_t *ir_cut_ctrl, + void __user *argp) +{ + int32_t rc = -EINVAL; + struct msm_ir_cut_cfg_data_t *ir_cut_data = + (struct msm_ir_cut_cfg_data_t *) argp; + + mutex_lock(ir_cut_ctrl->ir_cut_mutex); + + CDBG("Enter %s type %d\n", __func__, ir_cut_data->cfg_type); + + switch (ir_cut_data->cfg_type) { + case CFG_IR_CUT_INIT: + rc = msm_ir_cut_handle_init(ir_cut_ctrl, ir_cut_data); + break; + case CFG_IR_CUT_RELEASE: + if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_release( + ir_cut_ctrl); + break; + case CFG_IR_CUT_OFF: + if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_off( + ir_cut_ctrl, ir_cut_data); + break; + case CFG_IR_CUT_ON: + if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on( + ir_cut_ctrl, ir_cut_data); + break; + default: + rc = -EFAULT; + break; + } + + mutex_unlock(ir_cut_ctrl->ir_cut_mutex); + + CDBG("Exit %s type %d\n", __func__, ir_cut_data->cfg_type); + + return rc; +} + +static long msm_ir_cut_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_ir_cut_ctrl_t *fctrl = NULL; + void __user *argp = (void __user *)arg; + + CDBG("Enter\n"); + + if (!sd) { + pr_err("sd NULL\n"); + return -EINVAL; + } + fctrl = v4l2_get_subdevdata(sd); + if (!fctrl) { + pr_err("fctrl NULL\n"); + return -EINVAL; + } + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_ir_cut_get_subdev_id(fctrl, argp); + case VIDIOC_MSM_IR_CUT_CFG: + return msm_ir_cut_config(fctrl, argp); + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_SHUTDOWN: + if (!fctrl->func_tbl) { + pr_err("fctrl->func_tbl NULL\n"); + return -EINVAL; + } else { + return fctrl->func_tbl->camera_ir_cut_release(fctrl); + } + default: + pr_err_ratelimited("invalid cmd %d\n", cmd); + return -ENOIOCTLCMD; + } + CDBG("Exit\n"); +} + +static struct v4l2_subdev_core_ops msm_ir_cut_subdev_core_ops = { + .ioctl = msm_ir_cut_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_ir_cut_subdev_ops = { + .core = &msm_ir_cut_subdev_core_ops, +}; +static int msm_ir_cut_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + + int rc = 0; + struct msm_ir_cut_ctrl_t *ir_cut_ctrl = v4l2_get_subdevdata(sd); + + CDBG("Enter\n"); + + if (!ir_cut_ctrl) { + pr_err("%s: failed\n", __func__); + return -EINVAL; + } + + if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) + rc = ir_cut_ctrl->func_tbl->camera_ir_cut_release( + ir_cut_ctrl); + + CDBG("Exit\n"); + + return rc; +}; + +static const struct v4l2_subdev_internal_ops msm_ir_cut_internal_ops = { + .close = msm_ir_cut_close, +}; + +static int32_t msm_ir_cut_get_gpio_dt_data(struct device_node *of_node, + struct msm_ir_cut_ctrl_t *fctrl) +{ + int32_t rc = 0, i = 0; + uint16_t *gpio_array = NULL; + int16_t gpio_array_size = 0; + struct msm_camera_gpio_conf *gconf = NULL; + + gpio_array_size = of_gpio_count(of_node); + CDBG("%s gpio count %d\n", __func__, gpio_array_size); + + if (gpio_array_size > 0) { + fctrl->power_info.gpio_conf = + kzalloc(sizeof(struct msm_camera_gpio_conf), + GFP_KERNEL); + if (!fctrl->power_info.gpio_conf) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + return rc; + } + gconf = fctrl->power_info.gpio_conf; + + gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t), + GFP_KERNEL); + if (!gpio_array) + return -ENOMEM; + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + if (((int16_t)gpio_array[i]) < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -EINVAL; + goto free_gpio_array; + } + CDBG("%s gpio_array[%d] = %d\n", __func__, i, + gpio_array[i]); + } + + rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_gpio_array; + } + kfree(gpio_array); + + if (fctrl->ir_cut_driver_type == IR_CUT_DRIVER_DEFAULT) + fctrl->ir_cut_driver_type = IR_CUT_DRIVER_GPIO; + CDBG("%s:%d fctrl->ir_cut_driver_type = %d", __func__, __LINE__, + fctrl->ir_cut_driver_type); + } + + return rc; + +free_gpio_array: + kfree(gpio_array); + return rc; +} + +static int32_t msm_ir_cut_get_dt_data(struct device_node *of_node, + struct msm_ir_cut_ctrl_t *fctrl) +{ + int32_t rc = 0; + + CDBG("called\n"); + + if (!of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + /* Read the sub device */ + rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + return rc; + } + + fctrl->ir_cut_driver_type = IR_CUT_DRIVER_DEFAULT; + + /* Read the gpio information from device tree */ + rc = msm_ir_cut_get_gpio_dt_data(of_node, fctrl); + if (rc < 0) { + pr_err("%s:%d msm_ir_cut_get_gpio_dt_data failed rc %d\n", + __func__, __LINE__, rc); + return rc; + } + + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_ir_cut_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); + struct msm_ir_cut_cfg_data_t32 *u32 = + (struct msm_ir_cut_cfg_data_t32 *)arg; + struct msm_ir_cut_cfg_data_t ir_cut_data; + + CDBG("Enter"); + ir_cut_data.cfg_type = u32->cfg_type; + + switch (cmd) { + case VIDIOC_MSM_IR_CUT_CFG32: + cmd = VIDIOC_MSM_IR_CUT_CFG; + break; + default: + return msm_ir_cut_subdev_ioctl(sd, cmd, arg); + } + + rc = msm_ir_cut_subdev_ioctl(sd, cmd, &ir_cut_data); + + CDBG("Exit"); + return rc; +} + +static long msm_ir_cut_subdev_fops_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_ir_cut_subdev_do_ioctl); +} +#endif + +static int32_t msm_ir_cut_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0, i = 0; + struct msm_ir_cut_ctrl_t *ir_cut_ctrl = NULL; + + CDBG("Enter"); + if (!pdev->dev.of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + ir_cut_ctrl = kzalloc(sizeof(struct msm_ir_cut_ctrl_t), GFP_KERNEL); + if (!ir_cut_ctrl) + return -ENOMEM; + + memset(ir_cut_ctrl, 0, sizeof(struct msm_ir_cut_ctrl_t)); + + ir_cut_ctrl->pdev = pdev; + + rc = msm_ir_cut_get_dt_data(pdev->dev.of_node, ir_cut_ctrl); + + if (rc < 0) { + pr_err("%s:%d msm_ir_cut_get_dt_data failed\n", + __func__, __LINE__); + kfree(ir_cut_ctrl); + return -EINVAL; + } + + rc = msm_sensor_driver_get_gpio_data(&(ir_cut_ctrl->gconf), + (&pdev->dev)->of_node); + + if ((rc < 0) || (ir_cut_ctrl->gconf == NULL)) { + pr_err("%s: No IR CUT GPIOs\n", __func__); + + kfree(ir_cut_ctrl); + return -EINVAL; + } + + CDBG("%s: gpio_request_table_size = %d\n", + __func__, + ir_cut_ctrl->gconf->cam_gpio_req_tbl_size); + + for (i = 0; + i < ir_cut_ctrl->gconf->cam_gpio_req_tbl_size; i++) { + CDBG("%s: gpio = %d\n", __func__, + ir_cut_ctrl->gconf->cam_gpio_req_tbl[i].gpio); + CDBG("%s: gpio-flags = %lu\n", __func__, + ir_cut_ctrl->gconf->cam_gpio_req_tbl[i].flags); + CDBG("%s: gconf->gpio_num_info->gpio_num[%d] = %d\n", + __func__, i, + ir_cut_ctrl->gconf->gpio_num_info->gpio_num[i]); + } + + ir_cut_ctrl->cam_pinctrl_status = 1; + + rc = msm_camera_pinctrl_init( + &(ir_cut_ctrl->pinctrl_info), &(pdev->dev)); + + if (rc < 0) { + pr_err("ERR:%s: Error in reading IR CUT pinctrl\n", + __func__); + ir_cut_ctrl->cam_pinctrl_status = 0; + } + + ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_RELEASE; + ir_cut_ctrl->power_info.dev = &ir_cut_ctrl->pdev->dev; + ir_cut_ctrl->ir_cut_device_type = MSM_CAMERA_PLATFORM_DEVICE; + ir_cut_ctrl->ir_cut_mutex = &msm_ir_cut_mutex; + + /* Initialize sub device */ + v4l2_subdev_init(&ir_cut_ctrl->msm_sd.sd, &msm_ir_cut_subdev_ops); + v4l2_set_subdevdata(&ir_cut_ctrl->msm_sd.sd, ir_cut_ctrl); + + ir_cut_ctrl->msm_sd.sd.internal_ops = &msm_ir_cut_internal_ops; + ir_cut_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(ir_cut_ctrl->msm_sd.sd.name, + ARRAY_SIZE(ir_cut_ctrl->msm_sd.sd.name), + "msm_camera_ir_cut"); + media_entity_init(&ir_cut_ctrl->msm_sd.sd.entity, 0, NULL, 0); + ir_cut_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + ir_cut_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_IR_CUT; + ir_cut_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1; + msm_sd_register(&ir_cut_ctrl->msm_sd); + + CDBG("%s:%d ir_cut sd name = %s", __func__, __LINE__, + ir_cut_ctrl->msm_sd.sd.entity.name); + msm_ir_cut_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_ir_cut_v4l2_subdev_fops.compat_ioctl32 = + msm_ir_cut_subdev_fops_ioctl; +#endif + ir_cut_ctrl->msm_sd.sd.devnode->fops = &msm_ir_cut_v4l2_subdev_fops; + + CDBG("probe success\n"); + return rc; +} + +MODULE_DEVICE_TABLE(of, msm_ir_cut_dt_match); + +static struct platform_driver msm_ir_cut_platform_driver = { + .probe = msm_ir_cut_platform_probe, + .driver = { + .name = "qcom,ir-cut", + .owner = THIS_MODULE, + .of_match_table = msm_ir_cut_dt_match, + }, +}; + +static int __init msm_ir_cut_init_module(void) +{ + int32_t rc = 0; + + CDBG("Enter\n"); + rc = platform_driver_register(&msm_ir_cut_platform_driver); + if (!rc) + return rc; + + pr_err("platform probe for ir_cut failed"); + + return rc; +} + +static void __exit msm_ir_cut_exit_module(void) +{ + platform_driver_unregister(&msm_ir_cut_platform_driver); +} + +static struct msm_ir_cut_table msm_gpio_ir_cut_table = { + .ir_cut_driver_type = IR_CUT_DRIVER_GPIO, + .func_tbl = { + .camera_ir_cut_init = msm_ir_cut_init, + .camera_ir_cut_release = msm_ir_cut_release, + .camera_ir_cut_off = msm_ir_cut_off, + .camera_ir_cut_on = msm_ir_cut_on, + }, +}; + +module_init(msm_ir_cut_init_module); +module_exit(msm_ir_cut_exit_module); +MODULE_DESCRIPTION("MSM IR CUT"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h new file mode 100644 index 000000000000..23fd23952523 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2016, 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_IR_CUT_H +#define MSM_IR_CUT_H + +#include +#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_ir_cut_state_t { + MSM_CAMERA_IR_CUT_INIT, + MSM_CAMERA_IR_CUT_RELEASE, +}; + +enum msm_ir_cut_driver_type { + IR_CUT_DRIVER_GPIO, + IR_CUT_DRIVER_DEFAULT, +}; + +struct msm_ir_cut_ctrl_t; + +struct msm_ir_cut_func_t { + int32_t (*camera_ir_cut_init)(struct msm_ir_cut_ctrl_t *, + struct msm_ir_cut_cfg_data_t *); + int32_t (*camera_ir_cut_release)(struct msm_ir_cut_ctrl_t *); + int32_t (*camera_ir_cut_off)(struct msm_ir_cut_ctrl_t *, + struct msm_ir_cut_cfg_data_t *); + int32_t (*camera_ir_cut_on)(struct msm_ir_cut_ctrl_t *, + struct msm_ir_cut_cfg_data_t *); +}; + +struct msm_ir_cut_table { + enum msm_ir_cut_driver_type ir_cut_driver_type; + struct msm_ir_cut_func_t func_tbl; +}; + +struct msm_ir_cut_ctrl_t { + struct msm_sd_subdev msm_sd; + struct platform_device *pdev; + struct msm_ir_cut_func_t *func_tbl; + struct msm_camera_power_ctrl_t power_info; + + enum msm_camera_device_type_t ir_cut_device_type; + struct mutex *ir_cut_mutex; + + /* ir_cut driver type */ + enum msm_ir_cut_driver_type ir_cut_driver_type; + + /* ir_cut state */ + enum msm_camera_ir_cut_state_t ir_cut_state; + + struct msm_camera_gpio_conf *gconf; + struct msm_pinctrl_info pinctrl_info; + uint8_t cam_pinctrl_status; +}; + +#endif diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 57cddbde7d0d..75d0912aa459 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -79,6 +79,10 @@ struct msm_ir_led_cfg_data_t32 { int32_t pwm_period_ns; }; +struct msm_ir_cut_cfg_data_t32 { + enum msm_ir_cut_cfg_type_t cfg_type; +}; + struct eeprom_read_t32 { compat_uptr_t dbuffer; uint32_t num_bytes; @@ -268,6 +272,9 @@ struct msm_flash_cfg_data_t32 { #define VIDIOC_MSM_IR_LED_CFG32 \ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ir_led_cfg_data_t32) + +#define VIDIOC_MSM_IR_CUT_CFG32 \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t32) #endif #endif diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h index c4ee5a119cc1..4302497da245 100644 --- a/include/uapi/media/msm_cam_sensor.h +++ b/include/uapi/media/msm_cam_sensor.h @@ -295,6 +295,10 @@ struct msm_ir_led_cfg_data_t { int32_t pwm_period_ns; }; +struct msm_ir_cut_cfg_data_t { + enum msm_ir_cut_cfg_type_t cfg_type; +}; + struct msm_eeprom_cfg_data { enum eeprom_cfg_type_t cfgtype; uint8_t is_supported; @@ -607,5 +611,8 @@ struct sensor_init_cfg_data { #define VIDIOC_MSM_IR_LED_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_led_cfg_data_t) +#define VIDIOC_MSM_IR_CUT_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t) + #endif diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index fb33c871f784..ad0825e33217 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -114,6 +114,15 @@ enum msm_sensor_power_seq_gpio_t { SENSOR_GPIO_MAX, }; +enum msm_ir_cut_filter_gpio_t { + IR_CUT_FILTER_GPIO_P = 0, + IR_CUT_FILTER_GPIO_M, + IR_CUT_FILTER_GPIO_MAX, +}; +#define IR_CUT_FILTER_GPIO_P IR_CUT_FILTER_GPIO_P +#define IR_CUT_FILTER_GPIO_M IR_CUT_FILTER_GPIO_M +#define R_CUT_FILTER_GPIO_MAX IR_CUT_FILTER_GPIO_MAX + enum msm_camera_vreg_name_t { CAM_VDIG, CAM_VIO, @@ -193,6 +202,17 @@ 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_ir_cut_cfg_type_t { + CFG_IR_CUT_INIT = 0, + CFG_IR_CUT_RELEASE, + CFG_IR_CUT_OFF, + CFG_IR_CUT_ON, +}; +#define CFG_IR_CUT_INIT CFG_IR_CUT_INIT +#define CFG_IR_CUT_RELEASE CFG_IR_CUT_RELEASE +#define CFG_IR_CUT_OFF CFG_IR_CUT_OFF +#define CFG_IR_CUT_ON CFG_IR_CUT_ON + enum msm_sensor_output_format_t { MSM_SENSOR_BAYER, MSM_SENSOR_YCBCR, diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index cd8e065318bb..071331ef6882 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -49,7 +49,8 @@ #define MSM_CAMERA_SUBDEV_OIS 15 #define MSM_CAMERA_SUBDEV_FLASH 16 #define MSM_CAMERA_SUBDEV_IR_LED 17 -#define MSM_CAMERA_SUBDEV_EXT 18 +#define MSM_CAMERA_SUBDEV_IR_CUT 18 +#define MSM_CAMERA_SUBDEV_EXT 19 #define MSM_MAX_CAMERA_SENSORS 5