camera: flash: add gpio based led flashlight driver

- Ported camera led gpio based flash driver from kernel 3.10 to
  kernel 4.9 for 8905
- media_entity_pads_init function are change from kernel 3.10 to 4.9
- media entity.function are change from kernel 3.10 to 4.9
- Add proper led_classdev name value as "flashlight" before registering
  led_classdev
- setting " led->cdev.flags |= LED_KEEP_TRIGGER" flags before registering
  led classdev
- call msm_led_torch_create_classdev for led_classdev registration.

Change-Id: Ibaffef4c7f2393108348472213c50339eda1beb8
Signed-off-by: Darshan Kumsi Srinivasa <darssr@codeaurora.org>
diff --git a/arch/arm64/boot/dts/qcom/msm8905-camera-sensor-skub.dtsi b/arch/arm64/boot/dts/qcom/msm8905-camera-sensor-skub.dtsi
index d662415..80b5de1 100644
--- a/arch/arm64/boot/dts/qcom/msm8905-camera-sensor-skub.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8905-camera-sensor-skub.dtsi
@@ -47,7 +47,6 @@
 		qcom,torch-seq-val = <1 0>;
 		qcom,flash-seq-val = <0 1>;
 		qcom,duty-cycle = <0 30>;
-		qcom,clk-freq = <0 150000>;
 		linux,name = "flashlight";
 		linux,default-trigger = "flashlight-trigger";
 	};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile b/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile
index 69be506..1121c6a 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile
@@ -2,5 +2,8 @@
 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_led_flash.o
+obj-$(CONFIG_MSMB_CAMERA) += msm_led_trigger.o
+obj-$(CONFIG_MSMB_CAMERA) += msm_led_torch.o
 obj-$(CONFIG_MSMB_CAMERA) += msm_flash.o
 obj-$(CONFIG_MSMB_CAMERA) += qm215_gpio_flash.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
new file mode 100644
index 0000000..722ddb7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
@@ -0,0 +1,126 @@
+/* Copyright (c) 2009-2014, 2020, 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 "msm_led_flash.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct v4l2_file_operations msm_led_flash_v4l2_subdev_fops;
+
+static long msm_led_flash_subdev_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, void *arg)
+{
+	struct msm_led_flash_ctrl_t *fctrl = NULL;
+
+	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 fctrl->func_tbl->flash_get_subdev_id(fctrl, arg);
+	case VIDIOC_MSM_FLASH_LED_DATA_CFG:
+		return fctrl->func_tbl->flash_led_config(fctrl, arg);
+	case MSM_SD_NOTIFY_FREEZE:
+		return 0;
+	case MSM_SD_SHUTDOWN:
+		*(int *)arg = MSM_CAMERA_LED_RELEASE;
+		return fctrl->func_tbl->flash_led_config(fctrl, arg);
+	default:
+		pr_err_ratelimited("invalid cmd %d\n", cmd);
+		return -ENOIOCTLCMD;
+	}
+}
+
+static struct v4l2_subdev_core_ops msm_flash_subdev_core_ops = {
+	.ioctl = msm_led_flash_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_flash_subdev_ops = {
+	.core = &msm_flash_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_flash_internal_ops;
+
+int32_t msm_led_flash_create_v4lsubdev(struct platform_device *pdev, void *data)
+{
+	struct msm_led_flash_ctrl_t *fctrl =
+		(struct msm_led_flash_ctrl_t *)data;
+	CDBG("Enter\n");
+
+	if (!fctrl) {
+		pr_err("fctrl NULL\n");
+		return -EINVAL;
+	}
+
+	/* Initialize sub device */
+	v4l2_subdev_init(&fctrl->msm_sd.sd, &msm_flash_subdev_ops);
+	v4l2_set_subdevdata(&fctrl->msm_sd.sd, fctrl);
+
+	fctrl->pdev = pdev;
+	fctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops;
+	fctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(fctrl->msm_sd.sd.name, ARRAY_SIZE(fctrl->msm_sd.sd.name),
+		"msm_flash");
+	media_entity_pads_init(&fctrl->msm_sd.sd.entity, 0, NULL);
+	fctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_LED_FLASH;
+	fctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+	msm_sd_register(&fctrl->msm_sd);
+
+	msm_led_flash_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+	msm_led_flash_v4l2_subdev_fops.compat_ioctl32 =
+		msm_led_flash_v4l2_subdev_fops.unlocked_ioctl;
+#endif
+	fctrl->msm_sd.sd.devnode->fops = &msm_led_flash_v4l2_subdev_fops;
+	CDBG("probe success\n");
+	return 0;
+}
+
+int32_t msm_led_i2c_flash_create_v4lsubdev(void *data)
+{
+	struct msm_led_flash_ctrl_t *fctrl =
+		(struct msm_led_flash_ctrl_t *)data;
+	CDBG("Enter\n");
+
+	if (!fctrl) {
+		pr_err("fctrl NULL\n");
+		return -EINVAL;
+	}
+
+	/* Initialize sub device */
+	v4l2_subdev_init(&fctrl->msm_sd.sd, &msm_flash_subdev_ops);
+	v4l2_set_subdevdata(&fctrl->msm_sd.sd, fctrl);
+
+	fctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops;
+	fctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(fctrl->msm_sd.sd.name, ARRAY_SIZE(fctrl->msm_sd.sd.name),
+		"msm_flash");
+	media_entity_pads_init(&fctrl->msm_sd.sd.entity, 0, NULL);
+	fctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_LED_FLASH;
+	msm_sd_register(&fctrl->msm_sd);
+
+	msm_led_flash_v4l2_subdev_fops = v4l2_subdev_fops;
+	fctrl->msm_sd.sd.devnode->fops = &msm_led_flash_v4l2_subdev_fops;
+
+	CDBG("probe success\n");
+	return 0;
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h
new file mode 100644
index 0000000..66eff77
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h
@@ -0,0 +1,98 @@
+/* Copyright (c) 2009-2014, 2020 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_LED_FLASH_H
+#define MSM_LED_FLASH_H
+
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include <soc/qcom/camera2.h>
+#include "msm_camera_i2c.h"
+#include "msm_sd.h"
+
+
+struct msm_led_flash_ctrl_t;
+
+struct msm_flash_fn_t {
+	int32_t (*flash_get_subdev_id)(struct msm_led_flash_ctrl_t *, void *);
+	int32_t (*flash_led_config)(struct msm_led_flash_ctrl_t *, void *);
+	int32_t (*flash_led_init)(struct msm_led_flash_ctrl_t *);
+	int32_t (*flash_led_release)(struct msm_led_flash_ctrl_t *);
+	int32_t (*flash_led_off)(struct msm_led_flash_ctrl_t *);
+	int32_t (*flash_led_low)(struct msm_led_flash_ctrl_t *);
+	int32_t (*flash_led_high)(struct msm_led_flash_ctrl_t *);
+};
+
+struct msm_led_flash_reg_t {
+	struct msm_camera_i2c_reg_setting *init_setting;
+	struct msm_camera_i2c_reg_setting *off_setting;
+	struct msm_camera_i2c_reg_setting *release_setting;
+	struct msm_camera_i2c_reg_setting *low_setting;
+	struct msm_camera_i2c_reg_setting *high_setting;
+};
+
+struct msm_led_flash_ctrl_t {
+	struct msm_camera_i2c_client *flash_i2c_client;
+	struct msm_sd_subdev msm_sd;
+	struct platform_device *pdev;
+	struct msm_flash_fn_t *func_tbl;
+	struct msm_camera_sensor_board_info *flashdata;
+	struct msm_led_flash_reg_t *reg_setting;
+	/* Flash */
+	const char *flash_trigger_name[MAX_LED_TRIGGERS];
+	struct led_trigger *flash_trigger[MAX_LED_TRIGGERS];
+	uint32_t flash_num_sources;
+	uint32_t flash_op_current[MAX_LED_TRIGGERS];
+	uint32_t flash_max_current[MAX_LED_TRIGGERS];
+	uint32_t flash_max_duration[MAX_LED_TRIGGERS];
+	/* Torch */
+	const char *torch_trigger_name[MAX_LED_TRIGGERS];
+	struct led_trigger *torch_trigger[MAX_LED_TRIGGERS];
+	uint32_t torch_num_sources;
+	uint32_t torch_op_current[MAX_LED_TRIGGERS];
+	uint32_t torch_max_current[MAX_LED_TRIGGERS];
+
+	void *data;
+	enum msm_camera_device_type_t flash_device_type;
+	enum cci_i2c_master_t cci_i2c_master;
+	enum msm_camera_led_config_t led_state;
+	uint32_t subdev_id;
+	struct msm_pinctrl_info pinctrl_info;
+};
+
+int msm_flash_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+
+int msm_flash_probe(struct platform_device *pdev, const void *data);
+
+int32_t msm_led_flash_create_v4lsubdev(struct platform_device *pdev,
+	void *data);
+
+int32_t msm_led_torch_create_classdev(struct platform_device *pdev,
+	void *data);
+
+int32_t msm_led_i2c_flash_create_v4lsubdev(void *data);
+
+int32_t msm_led_i2c_trigger_get_subdev_id(struct msm_led_flash_ctrl_t *fctrl,
+	void *arg);
+
+int32_t msm_led_i2c_trigger_config(struct msm_led_flash_ctrl_t *fctrl,
+	void *data);
+
+int msm_flash_led_init(struct msm_led_flash_ctrl_t *fctrl);
+int msm_flash_led_release(struct msm_led_flash_ctrl_t *fctrl);
+int msm_flash_led_off(struct msm_led_flash_ctrl_t *fctrl);
+int msm_flash_led_low(struct msm_led_flash_ctrl_t *fctrl);
+int msm_flash_led_high(struct msm_led_flash_ctrl_t *fctrl);
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c
new file mode 100644
index 0000000..cc8e70b
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2013-2014, 2020, 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 <linux/module.h>
+#include "msm_led_flash.h"
+
+static struct led_trigger *torch_trigger;
+
+static void msm_led_torch_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	if (!torch_trigger) {
+		pr_err("No torch trigger found, can't set brightness\n");
+		return;
+	}
+
+	led_trigger_event(torch_trigger, value);
+};
+
+static struct led_classdev msm_torch_led[MAX_LED_TRIGGERS] = {
+	{
+		.name		= "flashlight",
+		.brightness_set	= msm_led_torch_brightness_set,
+		.brightness	= LED_OFF,
+	},
+	{
+		.name		= "torch-light1",
+		.brightness_set	= msm_led_torch_brightness_set,
+		.brightness	= LED_OFF,
+	},
+	{
+		.name		= "torch-light2",
+		.brightness_set	= msm_led_torch_brightness_set,
+		.brightness	= LED_OFF,
+	},
+};
+
+int32_t msm_led_torch_create_classdev(struct platform_device *pdev,
+				void *data)
+{
+	int32_t i, rc = 0;
+	struct msm_led_flash_ctrl_t *fctrl =
+		(struct msm_led_flash_ctrl_t *)data;
+
+	if (!fctrl) {
+		pr_err("Invalid fctrl\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < fctrl->torch_num_sources; i++) {
+		if (fctrl->torch_trigger[i]) {
+			torch_trigger = fctrl->torch_trigger[i];
+			msm_torch_led[i].flags |= LED_KEEP_TRIGGER;
+			msm_led_torch_brightness_set(&msm_torch_led[i],
+				LED_OFF);
+
+			rc = led_classdev_register(&pdev->dev,
+				&msm_torch_led[i]);
+			if (rc) {
+				pr_err("Failed to register %d led dev. rc = %d\n",
+						i, rc);
+				return rc;
+			}
+		} else {
+			pr_err("Invalid fctrl->torch_trigger[%d]\n", i);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c
new file mode 100644
index 0000000..33224c3
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c
@@ -0,0 +1,342 @@
+/* Copyright (c) 2012-2014, 2020,  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 <linux/module.h>
+#include "msm_led_flash.h"
+
+#define FLASH_NAME "camera-led-flash"
+
+//#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static enum flash_type flashtype;
+static struct msm_led_flash_ctrl_t fctrl;
+
+static int32_t msm_led_trigger_get_subdev_id(struct msm_led_flash_ctrl_t *fctrl,
+	void *arg)
+{
+	uint32_t *subdev_id = (uint32_t *)arg;
+
+	if (!subdev_id) {
+		pr_err("%s:%d failed\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	*subdev_id = fctrl->pdev->id;
+	CDBG("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id);
+	return 0;
+}
+
+static int32_t msm_led_trigger_config(struct msm_led_flash_ctrl_t *fctrl,
+	void *data)
+{
+	int rc = 0;
+	struct msm_camera_led_cfg_t *cfg = (struct msm_camera_led_cfg_t *)data;
+	uint32_t i;
+	uint32_t curr_l, max_curr_l;
+
+	CDBG("called led_state %d\n", cfg->cfgtype);
+
+	if (!fctrl) {
+		CDBG("flash failed\n");
+		return -EINVAL;
+	}
+
+	switch (cfg->cfgtype) {
+	case MSM_CAMERA_LED_OFF:
+		/* Flash off */
+		for (i = 0; i < fctrl->flash_num_sources; i++)
+			if (fctrl->flash_trigger[i])
+				led_trigger_event(fctrl->flash_trigger[i], 0);
+		/* Torch off */
+		for (i = 0; i < fctrl->torch_num_sources; i++)
+			if (fctrl->torch_trigger[i])
+				led_trigger_event(fctrl->torch_trigger[i], 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		for (i = 0; i < fctrl->torch_num_sources; i++)
+			if (fctrl->torch_trigger[i]) {
+				max_curr_l = fctrl->torch_max_current[i];
+				if (cfg->torch_current[i] >= 0 &&
+					cfg->torch_current[i] < max_curr_l) {
+					curr_l = cfg->torch_current[i];
+				} else {
+					curr_l = fctrl->torch_op_current[i];
+					pr_debug("LED torch %d clamped %d\n",
+						i, curr_l);
+				}
+				led_trigger_event(fctrl->torch_trigger[i],
+						curr_l);
+			}
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		/* Torch off */
+		for (i = 0; i < fctrl->torch_num_sources; i++)
+			if (fctrl->torch_trigger[i])
+				led_trigger_event(fctrl->torch_trigger[i], 0);
+
+		for (i = 0; i < fctrl->flash_num_sources; i++)
+			if (fctrl->flash_trigger[i]) {
+				max_curr_l = fctrl->flash_max_current[i];
+				if (cfg->flash_current[i] >= 0 &&
+					cfg->flash_current[i] < max_curr_l) {
+					curr_l = cfg->flash_current[i];
+				} else {
+					curr_l = fctrl->flash_op_current[i];
+					pr_err(" i %d clamped %d\n",
+						i, curr_l);
+				}
+				led_trigger_event(fctrl->flash_trigger[i],
+					curr_l);
+			}
+		break;
+
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		/* Flash off */
+		for (i = 0; i < fctrl->flash_num_sources; i++)
+			if (fctrl->flash_trigger[i])
+				led_trigger_event(fctrl->flash_trigger[i], 0);
+		/* Torch off */
+		for (i = 0; i < fctrl->torch_num_sources; i++)
+			if (fctrl->torch_trigger[i])
+				led_trigger_event(fctrl->torch_trigger[i], 0);
+		break;
+
+	default:
+		CDBG("default set\n");
+		rc = -EFAULT;
+		break;
+	}
+	CDBG("flash_set_led_state: return %d\n", rc);
+	return rc;
+}
+
+static const struct of_device_id msm_led_trigger_dt_match[] = {
+	{.compatible = "qcom,camera-led-flash"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_led_trigger_dt_match);
+
+static int32_t msm_led_trigger_probe(struct platform_device *pdev)
+{
+	int32_t rc = 0, rc_1 = 0, i = 0;
+	struct device_node *of_node = pdev->dev.of_node;
+	struct device_node *flash_src_node = NULL;
+	uint32_t count = 0;
+	struct led_trigger *temp = NULL;
+
+	CDBG("called\n");
+
+	if (!of_node) {
+		pr_err("of_node NULL\n");
+		return -EINVAL;
+	}
+
+	fctrl.pdev = pdev;
+	fctrl.flash_num_sources = 0;
+	fctrl.torch_num_sources = 0;
+
+	rc = of_property_read_u32(of_node, "cell-index", &pdev->id);
+	if (rc < 0) {
+		pr_err("failed\n");
+		return -EINVAL;
+	}
+	CDBG("pdev id %d\n", pdev->id);
+
+	rc = of_property_read_u32(of_node,
+			"qcom,flash-type", &flashtype);
+	if (rc < 0) {
+		pr_err("flash-type: read failed\n");
+		return -EINVAL;
+	}
+
+	/* Flash source */
+	if (of_get_property(of_node, "qcom,flash-source", &count)) {
+		count /= sizeof(uint32_t);
+		CDBG("qcom,flash-source count %d\n", count);
+		if (count > MAX_LED_TRIGGERS) {
+			pr_err("invalid count qcom,flash-source %d\n", count);
+			return -EINVAL;
+		}
+		fctrl.flash_num_sources = count;
+		for (i = 0; i < fctrl.flash_num_sources; i++) {
+			flash_src_node = of_parse_phandle(of_node,
+				"qcom,flash-source", i);
+			if (!flash_src_node) {
+				pr_err("flash_src_node %d NULL\n", i);
+				continue;
+			}
+
+			rc = of_property_read_string(flash_src_node,
+				"linux,default-trigger",
+				&fctrl.flash_trigger_name[i]);
+
+			rc_1 = of_property_read_string(flash_src_node,
+				"qcom,default-led-trigger",
+				&fctrl.flash_trigger_name[i]);
+			if ((rc < 0) && (rc_1 < 0)) {
+				pr_err("default-trigger: read failed\n");
+				of_node_put(flash_src_node);
+				continue;
+			}
+
+			CDBG("default trigger %s\n",
+				fctrl.flash_trigger_name[i]);
+
+			if (flashtype == GPIO_FLASH) {
+				/* use fake current */
+				fctrl.flash_op_current[i] = LED_FULL;
+			} else {
+				rc = of_property_read_u32(flash_src_node,
+					"qcom,current",
+					&fctrl.flash_op_current[i]);
+				rc_1 = of_property_read_u32(flash_src_node,
+					"qcom,max-current",
+					&fctrl.flash_max_current[i]);
+				if ((rc < 0) || (rc_1 < 0)) {
+					pr_err("current: read failed\n");
+					of_node_put(flash_src_node);
+					continue;
+				}
+			}
+
+			of_node_put(flash_src_node);
+
+			 CDBG("max_current[%d] %d\n",
+				i, fctrl.flash_op_current[i]);
+
+			led_trigger_register_simple(fctrl.flash_trigger_name[i],
+				&fctrl.flash_trigger[i]);
+
+			if (flashtype == GPIO_FLASH)
+				if (fctrl.flash_trigger[i])
+					temp = fctrl.flash_trigger[i];
+		}
+
+	}
+	/* Torch source */
+	if (of_get_property(of_node, "qcom,torch-source", &count)) {
+		count /= sizeof(uint32_t);
+		pr_err("qcom,torch-source count %d\n", count);
+		if (count > MAX_LED_TRIGGERS) {
+			pr_err("invalid count qcom,torch-source %d\n", count);
+			return -EINVAL;
+		}
+		fctrl.torch_num_sources = count;
+
+		for (i = 0; i < fctrl.torch_num_sources; i++) {
+			flash_src_node = of_parse_phandle(of_node,
+				"qcom,torch-source", i);
+			if (!flash_src_node) {
+				pr_err("torch_src_node %d NULL\n", i);
+				continue;
+			}
+
+			rc = of_property_read_string(flash_src_node,
+				"linux,default-trigger",
+				&fctrl.torch_trigger_name[i]);
+
+			rc_1 = of_property_read_string(flash_src_node,
+				"qcom,default-led-trigger",
+				&fctrl.torch_trigger_name[i]);
+			if ((rc < 0) && (rc_1 < 0)) {
+				pr_err("default-trigger: read failed\n");
+				of_node_put(flash_src_node);
+				continue;
+			}
+
+			CDBG("default trigger %s\n",
+				fctrl.torch_trigger_name[i]);
+
+			if (flashtype == GPIO_FLASH) {
+				/* use fake current */
+				fctrl.torch_op_current[i] = LED_HALF;
+			} else {
+				rc = of_property_read_u32(flash_src_node,
+					"qcom,current",
+					&fctrl.torch_op_current[i]);
+				rc_1 = of_property_read_u32(flash_src_node,
+					"qcom,max-current",
+					&fctrl.torch_max_current[i]);
+				if ((rc < 0) || (rc_1 < 0)) {
+					pr_err("current: read failed\n");
+					of_node_put(flash_src_node);
+					continue;
+				}
+			}
+
+			of_node_put(flash_src_node);
+
+			CDBG("torch max_current[%d] %d\n",
+				i, fctrl.torch_op_current[i]);
+
+			led_trigger_register_simple(fctrl.torch_trigger_name[i],
+				&fctrl.torch_trigger[i]);
+
+			if (flashtype == GPIO_FLASH)
+				if (temp && !fctrl.torch_trigger[i])
+					fctrl.torch_trigger[i] = temp;
+		}
+	}
+
+	rc = msm_led_flash_create_v4lsubdev(pdev, &fctrl);
+	//if (!rc)
+		msm_led_torch_create_classdev(pdev, &fctrl);
+
+	return rc;
+}
+
+static struct platform_driver msm_led_trigger_driver = {
+	.probe = msm_led_trigger_probe,
+	.driver = {
+		.name = FLASH_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_led_trigger_dt_match,
+	},
+};
+
+static int __init msm_led_trigger_init_module(void)
+{
+	int32_t rc = 0;
+
+	CDBG("called\n");
+	rc = platform_driver_register(&msm_led_trigger_driver);
+	if (rc)
+		pr_err("platform probe for flash failed");
+
+	return rc;
+}
+
+static void __exit msm_led_trigger_exit_module(void)
+{
+	platform_driver_unregister(&msm_led_trigger_driver);
+}
+
+static struct msm_flash_fn_t msm_led_trigger_func_tbl = {
+	.flash_get_subdev_id = msm_led_trigger_get_subdev_id,
+	.flash_led_config = msm_led_trigger_config,
+};
+
+static struct msm_led_flash_ctrl_t fctrl = {
+	.func_tbl = &msm_led_trigger_func_tbl,
+};
+
+module_init(msm_led_trigger_init_module);
+module_exit(msm_led_trigger_exit_module);
+MODULE_DESCRIPTION("LED TRIGGER FLASH");
+MODULE_LICENSE("GPL v2");