18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/media/i2c/lm3646.c
48c2ecf20Sopenharmony_ci * General device driver for TI lm3646, Dual FLASH LED Driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Contact: Daniel Jeong <gshark.jeong@gmail.com>
98c2ecf20Sopenharmony_ci *			Ldd-Mlp <ldd-mlp@list.ti.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/regmap.h>
178c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
188c2ecf20Sopenharmony_ci#include <media/i2c/lm3646.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* registers definitions */
238c2ecf20Sopenharmony_ci#define REG_ENABLE		0x01
248c2ecf20Sopenharmony_ci#define REG_TORCH_BR	0x05
258c2ecf20Sopenharmony_ci#define REG_FLASH_BR	0x05
268c2ecf20Sopenharmony_ci#define REG_FLASH_TOUT	0x04
278c2ecf20Sopenharmony_ci#define REG_FLAG		0x08
288c2ecf20Sopenharmony_ci#define REG_STROBE_SRC	0x06
298c2ecf20Sopenharmony_ci#define REG_LED1_FLASH_BR 0x06
308c2ecf20Sopenharmony_ci#define REG_LED1_TORCH_BR 0x07
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MASK_ENABLE		0x03
338c2ecf20Sopenharmony_ci#define MASK_TORCH_BR	0x70
348c2ecf20Sopenharmony_ci#define MASK_FLASH_BR	0x0F
358c2ecf20Sopenharmony_ci#define MASK_FLASH_TOUT	0x07
368c2ecf20Sopenharmony_ci#define MASK_FLAG		0xFF
378c2ecf20Sopenharmony_ci#define MASK_STROBE_SRC	0x80
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Fault Mask */
408c2ecf20Sopenharmony_ci#define FAULT_TIMEOUT	(1<<0)
418c2ecf20Sopenharmony_ci#define FAULT_SHORT_CIRCUIT	(1<<1)
428c2ecf20Sopenharmony_ci#define FAULT_UVLO		(1<<2)
438c2ecf20Sopenharmony_ci#define FAULT_IVFM		(1<<3)
448c2ecf20Sopenharmony_ci#define FAULT_OCP		(1<<4)
458c2ecf20Sopenharmony_ci#define FAULT_OVERTEMP	(1<<5)
468c2ecf20Sopenharmony_ci#define FAULT_NTC_TRIP	(1<<6)
478c2ecf20Sopenharmony_ci#define FAULT_OVP		(1<<7)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cienum led_mode {
508c2ecf20Sopenharmony_ci	MODE_SHDN = 0x0,
518c2ecf20Sopenharmony_ci	MODE_TORCH = 0x2,
528c2ecf20Sopenharmony_ci	MODE_FLASH = 0x3,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * struct lm3646_flash
578c2ecf20Sopenharmony_ci *
588c2ecf20Sopenharmony_ci * @pdata: platform data
598c2ecf20Sopenharmony_ci * @regmap: reg. map for i2c
608c2ecf20Sopenharmony_ci * @lock: muxtex for serial access.
618c2ecf20Sopenharmony_ci * @led_mode: V4L2 LED mode
628c2ecf20Sopenharmony_ci * @ctrls_led: V4L2 controls
638c2ecf20Sopenharmony_ci * @subdev_led: V4L2 subdev
648c2ecf20Sopenharmony_ci * @mode_reg : mode register value
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistruct lm3646_flash {
678c2ecf20Sopenharmony_ci	struct device *dev;
688c2ecf20Sopenharmony_ci	struct lm3646_platform_data *pdata;
698c2ecf20Sopenharmony_ci	struct regmap *regmap;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler ctrls_led;
728c2ecf20Sopenharmony_ci	struct v4l2_subdev subdev_led;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	u8 mode_reg;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define to_lm3646_flash(_ctrl)	\
788c2ecf20Sopenharmony_ci	container_of(_ctrl->handler, struct lm3646_flash, ctrls_led)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* enable mode control */
818c2ecf20Sopenharmony_cistatic int lm3646_mode_ctrl(struct lm3646_flash *flash,
828c2ecf20Sopenharmony_ci			    enum v4l2_flash_led_mode led_mode)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	switch (led_mode) {
858c2ecf20Sopenharmony_ci	case V4L2_FLASH_LED_MODE_NONE:
868c2ecf20Sopenharmony_ci		return regmap_write(flash->regmap,
878c2ecf20Sopenharmony_ci				    REG_ENABLE, flash->mode_reg | MODE_SHDN);
888c2ecf20Sopenharmony_ci	case V4L2_FLASH_LED_MODE_TORCH:
898c2ecf20Sopenharmony_ci		return regmap_write(flash->regmap,
908c2ecf20Sopenharmony_ci				    REG_ENABLE, flash->mode_reg | MODE_TORCH);
918c2ecf20Sopenharmony_ci	case V4L2_FLASH_LED_MODE_FLASH:
928c2ecf20Sopenharmony_ci		return regmap_write(flash->regmap,
938c2ecf20Sopenharmony_ci				    REG_ENABLE, flash->mode_reg | MODE_FLASH);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci	return -EINVAL;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* V4L2 controls  */
998c2ecf20Sopenharmony_cistatic int lm3646_get_ctrl(struct v4l2_ctrl *ctrl)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct lm3646_flash *flash = to_lm3646_flash(ctrl);
1028c2ecf20Sopenharmony_ci	unsigned int reg_val;
1038c2ecf20Sopenharmony_ci	int rval;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (ctrl->id != V4L2_CID_FLASH_FAULT)
1068c2ecf20Sopenharmony_ci		return -EINVAL;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
1098c2ecf20Sopenharmony_ci	if (rval < 0)
1108c2ecf20Sopenharmony_ci		return rval;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ctrl->val = 0;
1138c2ecf20Sopenharmony_ci	if (reg_val & FAULT_TIMEOUT)
1148c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT;
1158c2ecf20Sopenharmony_ci	if (reg_val & FAULT_SHORT_CIRCUIT)
1168c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
1178c2ecf20Sopenharmony_ci	if (reg_val & FAULT_UVLO)
1188c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE;
1198c2ecf20Sopenharmony_ci	if (reg_val & FAULT_IVFM)
1208c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE;
1218c2ecf20Sopenharmony_ci	if (reg_val & FAULT_OCP)
1228c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT;
1238c2ecf20Sopenharmony_ci	if (reg_val & FAULT_OVERTEMP)
1248c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
1258c2ecf20Sopenharmony_ci	if (reg_val & FAULT_NTC_TRIP)
1268c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE;
1278c2ecf20Sopenharmony_ci	if (reg_val & FAULT_OVP)
1288c2ecf20Sopenharmony_ci		ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int lm3646_set_ctrl(struct v4l2_ctrl *ctrl)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct lm3646_flash *flash = to_lm3646_flash(ctrl);
1368c2ecf20Sopenharmony_ci	unsigned int reg_val;
1378c2ecf20Sopenharmony_ci	int rval;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	switch (ctrl->id) {
1408c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_LED_MODE:
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH)
1438c2ecf20Sopenharmony_ci			return lm3646_mode_ctrl(flash, ctrl->val);
1448c2ecf20Sopenharmony_ci		/* switch to SHDN mode before flash strobe on */
1458c2ecf20Sopenharmony_ci		return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_STROBE_SOURCE:
1488c2ecf20Sopenharmony_ci		return regmap_update_bits(flash->regmap,
1498c2ecf20Sopenharmony_ci					  REG_STROBE_SRC, MASK_STROBE_SRC,
1508c2ecf20Sopenharmony_ci					  (ctrl->val) << 7);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_STROBE:
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		/* read and check current mode of chip to start flash */
1558c2ecf20Sopenharmony_ci		rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
1568c2ecf20Sopenharmony_ci		if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN))
1578c2ecf20Sopenharmony_ci			return rval;
1588c2ecf20Sopenharmony_ci		/* flash on */
1598c2ecf20Sopenharmony_ci		return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_FLASH);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_STROBE_STOP:
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		/*
1648c2ecf20Sopenharmony_ci		 * flash mode will be turned automatically
1658c2ecf20Sopenharmony_ci		 * from FLASH mode to SHDN mode after flash duration timeout
1668c2ecf20Sopenharmony_ci		 * read and check current mode of chip to stop flash
1678c2ecf20Sopenharmony_ci		 */
1688c2ecf20Sopenharmony_ci		rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
1698c2ecf20Sopenharmony_ci		if (rval < 0)
1708c2ecf20Sopenharmony_ci			return rval;
1718c2ecf20Sopenharmony_ci		if ((reg_val & MASK_ENABLE) == MODE_FLASH)
1728c2ecf20Sopenharmony_ci			return lm3646_mode_ctrl(flash,
1738c2ecf20Sopenharmony_ci						V4L2_FLASH_LED_MODE_NONE);
1748c2ecf20Sopenharmony_ci		return rval;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_TIMEOUT:
1778c2ecf20Sopenharmony_ci		return regmap_update_bits(flash->regmap,
1788c2ecf20Sopenharmony_ci					  REG_FLASH_TOUT, MASK_FLASH_TOUT,
1798c2ecf20Sopenharmony_ci					  LM3646_FLASH_TOUT_ms_TO_REG
1808c2ecf20Sopenharmony_ci					  (ctrl->val));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_INTENSITY:
1838c2ecf20Sopenharmony_ci		return regmap_update_bits(flash->regmap,
1848c2ecf20Sopenharmony_ci					  REG_FLASH_BR, MASK_FLASH_BR,
1858c2ecf20Sopenharmony_ci					  LM3646_TOTAL_FLASH_BRT_uA_TO_REG
1868c2ecf20Sopenharmony_ci					  (ctrl->val));
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	case V4L2_CID_FLASH_TORCH_INTENSITY:
1898c2ecf20Sopenharmony_ci		return regmap_update_bits(flash->regmap,
1908c2ecf20Sopenharmony_ci					  REG_TORCH_BR, MASK_TORCH_BR,
1918c2ecf20Sopenharmony_ci					  LM3646_TOTAL_TORCH_BRT_uA_TO_REG
1928c2ecf20Sopenharmony_ci					  (ctrl->val) << 4);
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return -EINVAL;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = {
1998c2ecf20Sopenharmony_ci	.g_volatile_ctrl = lm3646_get_ctrl,
2008c2ecf20Sopenharmony_ci	.s_ctrl = lm3646_set_ctrl,
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int lm3646_init_controls(struct lm3646_flash *flash)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct v4l2_ctrl *fault;
2068c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &flash->ctrls_led;
2078c2ecf20Sopenharmony_ci	const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 8);
2108c2ecf20Sopenharmony_ci	/* flash mode */
2118c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
2128c2ecf20Sopenharmony_ci			       V4L2_FLASH_LED_MODE_TORCH, ~0x7,
2138c2ecf20Sopenharmony_ci			       V4L2_FLASH_LED_MODE_NONE);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* flash source */
2168c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,
2178c2ecf20Sopenharmony_ci			       0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* flash strobe */
2208c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
2218c2ecf20Sopenharmony_ci	/* flash strobe stop */
2228c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* flash strobe timeout */
2258c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
2268c2ecf20Sopenharmony_ci			  LM3646_FLASH_TOUT_MIN,
2278c2ecf20Sopenharmony_ci			  LM3646_FLASH_TOUT_MAX,
2288c2ecf20Sopenharmony_ci			  LM3646_FLASH_TOUT_STEP, flash->pdata->flash_timeout);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* max flash current */
2318c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
2328c2ecf20Sopenharmony_ci			  LM3646_TOTAL_FLASH_BRT_MIN,
2338c2ecf20Sopenharmony_ci			  LM3646_TOTAL_FLASH_BRT_MAX,
2348c2ecf20Sopenharmony_ci			  LM3646_TOTAL_FLASH_BRT_STEP,
2358c2ecf20Sopenharmony_ci			  LM3646_TOTAL_FLASH_BRT_MAX);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* max torch current */
2388c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
2398c2ecf20Sopenharmony_ci			  LM3646_TOTAL_TORCH_BRT_MIN,
2408c2ecf20Sopenharmony_ci			  LM3646_TOTAL_TORCH_BRT_MAX,
2418c2ecf20Sopenharmony_ci			  LM3646_TOTAL_TORCH_BRT_STEP,
2428c2ecf20Sopenharmony_ci			  LM3646_TOTAL_TORCH_BRT_MAX);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* fault */
2458c2ecf20Sopenharmony_ci	fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
2468c2ecf20Sopenharmony_ci				  V4L2_FLASH_FAULT_OVER_VOLTAGE
2478c2ecf20Sopenharmony_ci				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE
2488c2ecf20Sopenharmony_ci				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT
2498c2ecf20Sopenharmony_ci				  | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);
2508c2ecf20Sopenharmony_ci	if (fault != NULL)
2518c2ecf20Sopenharmony_ci		fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (hdl->error)
2548c2ecf20Sopenharmony_ci		return hdl->error;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	flash->subdev_led.ctrl_handler = hdl;
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/* initialize device */
2618c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops lm3646_ops = {
2628c2ecf20Sopenharmony_ci	.core = NULL,
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic const struct regmap_config lm3646_regmap = {
2668c2ecf20Sopenharmony_ci	.reg_bits = 8,
2678c2ecf20Sopenharmony_ci	.val_bits = 8,
2688c2ecf20Sopenharmony_ci	.max_register = 0xFF,
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int lm3646_subdev_init(struct lm3646_flash *flash)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(flash->dev);
2748c2ecf20Sopenharmony_ci	int rval;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops);
2778c2ecf20Sopenharmony_ci	flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
2788c2ecf20Sopenharmony_ci	strscpy(flash->subdev_led.name, LM3646_NAME,
2798c2ecf20Sopenharmony_ci		sizeof(flash->subdev_led.name));
2808c2ecf20Sopenharmony_ci	rval = lm3646_init_controls(flash);
2818c2ecf20Sopenharmony_ci	if (rval)
2828c2ecf20Sopenharmony_ci		goto err_out;
2838c2ecf20Sopenharmony_ci	rval = media_entity_pads_init(&flash->subdev_led.entity, 0, NULL);
2848c2ecf20Sopenharmony_ci	if (rval < 0)
2858c2ecf20Sopenharmony_ci		goto err_out;
2868c2ecf20Sopenharmony_ci	flash->subdev_led.entity.function = MEDIA_ENT_F_FLASH;
2878c2ecf20Sopenharmony_ci	return rval;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cierr_out:
2908c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&flash->ctrls_led);
2918c2ecf20Sopenharmony_ci	return rval;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int lm3646_init_device(struct lm3646_flash *flash)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	unsigned int reg_val;
2978c2ecf20Sopenharmony_ci	int rval;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* read the value of mode register to reduce redundant i2c accesses */
3008c2ecf20Sopenharmony_ci	rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
3018c2ecf20Sopenharmony_ci	if (rval < 0)
3028c2ecf20Sopenharmony_ci		return rval;
3038c2ecf20Sopenharmony_ci	flash->mode_reg = reg_val & 0xfc;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* output disable */
3068c2ecf20Sopenharmony_ci	rval = lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
3078c2ecf20Sopenharmony_ci	if (rval < 0)
3088c2ecf20Sopenharmony_ci		return rval;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/*
3118c2ecf20Sopenharmony_ci	 * LED1 flash current setting
3128c2ecf20Sopenharmony_ci	 * LED2 flash current = Total(Max) flash current - LED1 flash current
3138c2ecf20Sopenharmony_ci	 */
3148c2ecf20Sopenharmony_ci	rval = regmap_update_bits(flash->regmap,
3158c2ecf20Sopenharmony_ci				  REG_LED1_FLASH_BR, 0x7F,
3168c2ecf20Sopenharmony_ci				  LM3646_LED1_FLASH_BRT_uA_TO_REG
3178c2ecf20Sopenharmony_ci				  (flash->pdata->led1_flash_brt));
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (rval < 0)
3208c2ecf20Sopenharmony_ci		return rval;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/*
3238c2ecf20Sopenharmony_ci	 * LED1 torch current setting
3248c2ecf20Sopenharmony_ci	 * LED2 torch current = Total(Max) torch current - LED1 torch current
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	rval = regmap_update_bits(flash->regmap,
3278c2ecf20Sopenharmony_ci				  REG_LED1_TORCH_BR, 0x7F,
3288c2ecf20Sopenharmony_ci				  LM3646_LED1_TORCH_BRT_uA_TO_REG
3298c2ecf20Sopenharmony_ci				  (flash->pdata->led1_torch_brt));
3308c2ecf20Sopenharmony_ci	if (rval < 0)
3318c2ecf20Sopenharmony_ci		return rval;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* Reset flag register */
3348c2ecf20Sopenharmony_ci	return regmap_read(flash->regmap, REG_FLAG, &reg_val);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int lm3646_probe(struct i2c_client *client,
3388c2ecf20Sopenharmony_ci			const struct i2c_device_id *devid)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct lm3646_flash *flash;
3418c2ecf20Sopenharmony_ci	struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev);
3428c2ecf20Sopenharmony_ci	int rval;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
3458c2ecf20Sopenharmony_ci	if (flash == NULL)
3468c2ecf20Sopenharmony_ci		return -ENOMEM;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap);
3498c2ecf20Sopenharmony_ci	if (IS_ERR(flash->regmap))
3508c2ecf20Sopenharmony_ci		return PTR_ERR(flash->regmap);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* check device tree if there is no platform data */
3538c2ecf20Sopenharmony_ci	if (pdata == NULL) {
3548c2ecf20Sopenharmony_ci		pdata = devm_kzalloc(&client->dev,
3558c2ecf20Sopenharmony_ci				     sizeof(struct lm3646_platform_data),
3568c2ecf20Sopenharmony_ci				     GFP_KERNEL);
3578c2ecf20Sopenharmony_ci		if (pdata == NULL)
3588c2ecf20Sopenharmony_ci			return -ENOMEM;
3598c2ecf20Sopenharmony_ci		/* use default data in case of no platform data */
3608c2ecf20Sopenharmony_ci		pdata->flash_timeout = LM3646_FLASH_TOUT_MAX;
3618c2ecf20Sopenharmony_ci		pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX;
3628c2ecf20Sopenharmony_ci		pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	flash->pdata = pdata;
3658c2ecf20Sopenharmony_ci	flash->dev = &client->dev;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	rval = lm3646_subdev_init(flash);
3688c2ecf20Sopenharmony_ci	if (rval < 0)
3698c2ecf20Sopenharmony_ci		return rval;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	rval = lm3646_init_device(flash);
3728c2ecf20Sopenharmony_ci	if (rval < 0)
3738c2ecf20Sopenharmony_ci		return rval;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, flash);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int lm3646_remove(struct i2c_client *client)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct lm3646_flash *flash = i2c_get_clientdata(client);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(&flash->subdev_led);
3858c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&flash->ctrls_led);
3868c2ecf20Sopenharmony_ci	media_entity_cleanup(&flash->subdev_led.entity);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic const struct i2c_device_id lm3646_id_table[] = {
3928c2ecf20Sopenharmony_ci	{LM3646_NAME, 0},
3938c2ecf20Sopenharmony_ci	{}
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm3646_id_table);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic struct i2c_driver lm3646_i2c_driver = {
3998c2ecf20Sopenharmony_ci	.driver = {
4008c2ecf20Sopenharmony_ci		   .name = LM3646_NAME,
4018c2ecf20Sopenharmony_ci		   },
4028c2ecf20Sopenharmony_ci	.probe = lm3646_probe,
4038c2ecf20Sopenharmony_ci	.remove = lm3646_remove,
4048c2ecf20Sopenharmony_ci	.id_table = lm3646_id_table,
4058c2ecf20Sopenharmony_ci};
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cimodule_i2c_driver(lm3646_i2c_driver);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
4108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
4118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver");
4128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
413