xref: /openbmc/linux/drivers/media/i2c/adp1653.c (revision aaeb31c0)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * drivers/media/i2c/adp1653.c
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2008--2011 Nokia Corporation
6cb7a01acSMauro Carvalho Chehab  *
78c5dff90SSakari Ailus  * Contact: Sakari Ailus <sakari.ailus@iki.fi>
8cb7a01acSMauro Carvalho Chehab  *
9cb7a01acSMauro Carvalho Chehab  * Contributors:
108c5dff90SSakari Ailus  *	Sakari Ailus <sakari.ailus@iki.fi>
11cb7a01acSMauro Carvalho Chehab  *	Tuukka Toivonen <tuukkat76@gmail.com>
12074c57a2SPavel Machek  *	Pavel Machek <pavel@ucw.cz>
13cb7a01acSMauro Carvalho Chehab  *
14cb7a01acSMauro Carvalho Chehab  * TODO:
15cb7a01acSMauro Carvalho Chehab  * - fault interrupt handling
16cb7a01acSMauro Carvalho Chehab  * - hardware strobe
17cb7a01acSMauro Carvalho Chehab  * - power doesn't need to be ON if all lights are off
18cb7a01acSMauro Carvalho Chehab  */
19cb7a01acSMauro Carvalho Chehab 
20cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
24074c57a2SPavel Machek #include <linux/of.h>
25074c57a2SPavel Machek #include <linux/gpio/consumer.h>
26b5dcee22SMauro Carvalho Chehab #include <media/i2c/adp1653.h>
27cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
28cb7a01acSMauro Carvalho Chehab 
29cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MAX		820000
30cb7a01acSMauro Carvalho Chehab #define TIMEOUT_STEP		54600
31cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MIN		(TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
32cb7a01acSMauro Carvalho Chehab 				 * TIMEOUT_STEP)
33cb7a01acSMauro Carvalho Chehab #define TIMEOUT_US_TO_CODE(t)	((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
34cb7a01acSMauro Carvalho Chehab 				 / TIMEOUT_STEP)
35cb7a01acSMauro Carvalho Chehab #define TIMEOUT_CODE_TO_US(c)	(TIMEOUT_MAX - (c) * TIMEOUT_STEP)
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab /* Write values into ADP1653 registers. */
adp1653_update_hw(struct adp1653_flash * flash)38cb7a01acSMauro Carvalho Chehab static int adp1653_update_hw(struct adp1653_flash *flash)
39cb7a01acSMauro Carvalho Chehab {
40cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
41cb7a01acSMauro Carvalho Chehab 	u8 out_sel;
42cb7a01acSMauro Carvalho Chehab 	u8 config = 0;
43cb7a01acSMauro Carvalho Chehab 	int rval;
44cb7a01acSMauro Carvalho Chehab 
45cb7a01acSMauro Carvalho Chehab 	out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
46cb7a01acSMauro Carvalho Chehab 		flash->indicator_intensity->val)
47cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
48cb7a01acSMauro Carvalho Chehab 
49cb7a01acSMauro Carvalho Chehab 	switch (flash->led_mode->val) {
50cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_NONE:
51cb7a01acSMauro Carvalho Chehab 		break;
52cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_FLASH:
53cb7a01acSMauro Carvalho Chehab 		/* Flash mode, light on with strobe, duration from timer */
54cb7a01acSMauro Carvalho Chehab 		config = ADP1653_REG_CONFIG_TMR_CFG;
55cb7a01acSMauro Carvalho Chehab 		config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
56cb7a01acSMauro Carvalho Chehab 			  << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
57cb7a01acSMauro Carvalho Chehab 		break;
58cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_TORCH:
59cb7a01acSMauro Carvalho Chehab 		/* Torch mode, light immediately on, duration indefinite */
60cb7a01acSMauro Carvalho Chehab 		out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
61cb7a01acSMauro Carvalho Chehab 			flash->torch_intensity->val)
62cb7a01acSMauro Carvalho Chehab 			<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
63cb7a01acSMauro Carvalho Chehab 		break;
64cb7a01acSMauro Carvalho Chehab 	}
65cb7a01acSMauro Carvalho Chehab 
66cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
67cb7a01acSMauro Carvalho Chehab 	if (rval < 0)
68cb7a01acSMauro Carvalho Chehab 		return rval;
69cb7a01acSMauro Carvalho Chehab 
70cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
71cb7a01acSMauro Carvalho Chehab 	if (rval < 0)
72cb7a01acSMauro Carvalho Chehab 		return rval;
73cb7a01acSMauro Carvalho Chehab 
74cb7a01acSMauro Carvalho Chehab 	return 0;
75cb7a01acSMauro Carvalho Chehab }
76cb7a01acSMauro Carvalho Chehab 
adp1653_get_fault(struct adp1653_flash * flash)77cb7a01acSMauro Carvalho Chehab static int adp1653_get_fault(struct adp1653_flash *flash)
78cb7a01acSMauro Carvalho Chehab {
79cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
80cb7a01acSMauro Carvalho Chehab 	int fault;
81cb7a01acSMauro Carvalho Chehab 	int rval;
82cb7a01acSMauro Carvalho Chehab 
83cb7a01acSMauro Carvalho Chehab 	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
84287980e4SArnd Bergmann 	if (fault < 0)
85cb7a01acSMauro Carvalho Chehab 		return fault;
86cb7a01acSMauro Carvalho Chehab 
87cb7a01acSMauro Carvalho Chehab 	flash->fault |= fault;
88cb7a01acSMauro Carvalho Chehab 
89cb7a01acSMauro Carvalho Chehab 	if (!flash->fault)
90cb7a01acSMauro Carvalho Chehab 		return 0;
91cb7a01acSMauro Carvalho Chehab 
92cb7a01acSMauro Carvalho Chehab 	/* Clear faults. */
93cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
94287980e4SArnd Bergmann 	if (rval < 0)
95cb7a01acSMauro Carvalho Chehab 		return rval;
96cb7a01acSMauro Carvalho Chehab 
97cb7a01acSMauro Carvalho Chehab 	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
98cb7a01acSMauro Carvalho Chehab 
99cb7a01acSMauro Carvalho Chehab 	rval = adp1653_update_hw(flash);
100287980e4SArnd Bergmann 	if (rval)
101cb7a01acSMauro Carvalho Chehab 		return rval;
102cb7a01acSMauro Carvalho Chehab 
103cb7a01acSMauro Carvalho Chehab 	return flash->fault;
104cb7a01acSMauro Carvalho Chehab }
105cb7a01acSMauro Carvalho Chehab 
adp1653_strobe(struct adp1653_flash * flash,int enable)106cb7a01acSMauro Carvalho Chehab static int adp1653_strobe(struct adp1653_flash *flash, int enable)
107cb7a01acSMauro Carvalho Chehab {
108cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
109cb7a01acSMauro Carvalho Chehab 	u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
110cb7a01acSMauro Carvalho Chehab 		flash->indicator_intensity->val)
111cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
112cb7a01acSMauro Carvalho Chehab 	int rval;
113cb7a01acSMauro Carvalho Chehab 
114cb7a01acSMauro Carvalho Chehab 	if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
115cb7a01acSMauro Carvalho Chehab 		return -EBUSY;
116cb7a01acSMauro Carvalho Chehab 
117cb7a01acSMauro Carvalho Chehab 	if (!enable)
118cb7a01acSMauro Carvalho Chehab 		return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
119cb7a01acSMauro Carvalho Chehab 						 out_sel);
120cb7a01acSMauro Carvalho Chehab 
121cb7a01acSMauro Carvalho Chehab 	out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
122cb7a01acSMauro Carvalho Chehab 		flash->flash_intensity->val)
123cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
124cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
125cb7a01acSMauro Carvalho Chehab 	if (rval)
126cb7a01acSMauro Carvalho Chehab 		return rval;
127cb7a01acSMauro Carvalho Chehab 
128cb7a01acSMauro Carvalho Chehab 	/* Software strobe using i2c */
129cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
130cb7a01acSMauro Carvalho Chehab 		ADP1653_REG_SW_STROBE_SW_STROBE);
131cb7a01acSMauro Carvalho Chehab 	if (rval)
132cb7a01acSMauro Carvalho Chehab 		return rval;
133cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
134cb7a01acSMauro Carvalho Chehab }
135cb7a01acSMauro Carvalho Chehab 
136cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
137cb7a01acSMauro Carvalho Chehab  * V4L2 controls
138cb7a01acSMauro Carvalho Chehab  */
139cb7a01acSMauro Carvalho Chehab 
adp1653_get_ctrl(struct v4l2_ctrl * ctrl)140cb7a01acSMauro Carvalho Chehab static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
141cb7a01acSMauro Carvalho Chehab {
142cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash =
143cb7a01acSMauro Carvalho Chehab 		container_of(ctrl->handler, struct adp1653_flash, ctrls);
144cb7a01acSMauro Carvalho Chehab 	int rval;
145cb7a01acSMauro Carvalho Chehab 
146cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
147287980e4SArnd Bergmann 	if (rval)
148cb7a01acSMauro Carvalho Chehab 		return rval;
149cb7a01acSMauro Carvalho Chehab 
150cb7a01acSMauro Carvalho Chehab 	ctrl->cur.val = 0;
151cb7a01acSMauro Carvalho Chehab 
152cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
153cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
154cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
155cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
156cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
157cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
158cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
159cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
160cb7a01acSMauro Carvalho Chehab 
161cb7a01acSMauro Carvalho Chehab 	flash->fault = 0;
162cb7a01acSMauro Carvalho Chehab 
163cb7a01acSMauro Carvalho Chehab 	return 0;
164cb7a01acSMauro Carvalho Chehab }
165cb7a01acSMauro Carvalho Chehab 
adp1653_set_ctrl(struct v4l2_ctrl * ctrl)166cb7a01acSMauro Carvalho Chehab static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
167cb7a01acSMauro Carvalho Chehab {
168cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash =
169cb7a01acSMauro Carvalho Chehab 		container_of(ctrl->handler, struct adp1653_flash, ctrls);
170cb7a01acSMauro Carvalho Chehab 	int rval;
171cb7a01acSMauro Carvalho Chehab 
172cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
173287980e4SArnd Bergmann 	if (rval)
174cb7a01acSMauro Carvalho Chehab 		return rval;
175cb7a01acSMauro Carvalho Chehab 	if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
176cb7a01acSMauro Carvalho Chehab 		     ADP1653_REG_FAULT_FLT_OT |
177cb7a01acSMauro Carvalho Chehab 		     ADP1653_REG_FAULT_FLT_OV)) &&
178cb7a01acSMauro Carvalho Chehab 	    (ctrl->id == V4L2_CID_FLASH_STROBE ||
179cb7a01acSMauro Carvalho Chehab 	     ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
180cb7a01acSMauro Carvalho Chehab 	     ctrl->id == V4L2_CID_FLASH_LED_MODE))
181cb7a01acSMauro Carvalho Chehab 		return -EBUSY;
182cb7a01acSMauro Carvalho Chehab 
183cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
184cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_FLASH_STROBE:
185cb7a01acSMauro Carvalho Chehab 		return adp1653_strobe(flash, 1);
186cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_FLASH_STROBE_STOP:
187cb7a01acSMauro Carvalho Chehab 		return adp1653_strobe(flash, 0);
188cb7a01acSMauro Carvalho Chehab 	}
189cb7a01acSMauro Carvalho Chehab 
190cb7a01acSMauro Carvalho Chehab 	return adp1653_update_hw(flash);
191cb7a01acSMauro Carvalho Chehab }
192cb7a01acSMauro Carvalho Chehab 
193cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
194cb7a01acSMauro Carvalho Chehab 	.g_volatile_ctrl = adp1653_get_ctrl,
195cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adp1653_set_ctrl,
196cb7a01acSMauro Carvalho Chehab };
197cb7a01acSMauro Carvalho Chehab 
adp1653_init_controls(struct adp1653_flash * flash)198cb7a01acSMauro Carvalho Chehab static int adp1653_init_controls(struct adp1653_flash *flash)
199cb7a01acSMauro Carvalho Chehab {
200cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *fault;
201cb7a01acSMauro Carvalho Chehab 
202cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&flash->ctrls, 9);
203cb7a01acSMauro Carvalho Chehab 
204cb7a01acSMauro Carvalho Chehab 	flash->led_mode =
205cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
206cb7a01acSMauro Carvalho Chehab 				       V4L2_CID_FLASH_LED_MODE,
207cb7a01acSMauro Carvalho Chehab 				       V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
208cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
209cb7a01acSMauro Carvalho Chehab 			       V4L2_CID_FLASH_STROBE_SOURCE,
210cb7a01acSMauro Carvalho Chehab 			       V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
211cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
212cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
213cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
214cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
215cb7a01acSMauro Carvalho Chehab 	flash->flash_timeout =
216cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
217cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
218cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_timeout,
219cb7a01acSMauro Carvalho Chehab 				  TIMEOUT_STEP,
220cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_timeout);
221cb7a01acSMauro Carvalho Chehab 	flash->flash_intensity =
222cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
223cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_INTENSITY,
224cb7a01acSMauro Carvalho Chehab 				  ADP1653_FLASH_INTENSITY_MIN,
225cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_intensity,
226cb7a01acSMauro Carvalho Chehab 				  1, flash->platform_data->max_flash_intensity);
227cb7a01acSMauro Carvalho Chehab 	flash->torch_intensity =
228cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
229cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_TORCH_INTENSITY,
230cb7a01acSMauro Carvalho Chehab 				  ADP1653_TORCH_INTENSITY_MIN,
231cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_torch_intensity,
232cb7a01acSMauro Carvalho Chehab 				  ADP1653_FLASH_INTENSITY_STEP,
233cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_torch_intensity);
234cb7a01acSMauro Carvalho Chehab 	flash->indicator_intensity =
235cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
236cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_INDICATOR_INTENSITY,
237cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_MIN,
238cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_indicator_intensity,
239cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_STEP,
240cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_MIN);
241cb7a01acSMauro Carvalho Chehab 	fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
242cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_FAULT, 0,
243cb7a01acSMauro Carvalho Chehab 				  V4L2_FLASH_FAULT_OVER_VOLTAGE
244cb7a01acSMauro Carvalho Chehab 				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE
245cb7a01acSMauro Carvalho Chehab 				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
246cb7a01acSMauro Carvalho Chehab 
247cb7a01acSMauro Carvalho Chehab 	if (flash->ctrls.error)
248cb7a01acSMauro Carvalho Chehab 		return flash->ctrls.error;
249cb7a01acSMauro Carvalho Chehab 
250cb7a01acSMauro Carvalho Chehab 	fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
251cb7a01acSMauro Carvalho Chehab 
252cb7a01acSMauro Carvalho Chehab 	flash->subdev.ctrl_handler = &flash->ctrls;
253cb7a01acSMauro Carvalho Chehab 	return 0;
254cb7a01acSMauro Carvalho Chehab }
255cb7a01acSMauro Carvalho Chehab 
256cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
257cb7a01acSMauro Carvalho Chehab  * V4L2 subdev operations
258cb7a01acSMauro Carvalho Chehab  */
259cb7a01acSMauro Carvalho Chehab 
260cb7a01acSMauro Carvalho Chehab static int
adp1653_init_device(struct adp1653_flash * flash)261cb7a01acSMauro Carvalho Chehab adp1653_init_device(struct adp1653_flash *flash)
262cb7a01acSMauro Carvalho Chehab {
263cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
264cb7a01acSMauro Carvalho Chehab 	int rval;
265cb7a01acSMauro Carvalho Chehab 
266cb7a01acSMauro Carvalho Chehab 	/* Clear FAULT register by writing zero to OUT_SEL */
267cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
268cb7a01acSMauro Carvalho Chehab 	if (rval < 0) {
269cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "failed writing fault register\n");
270cb7a01acSMauro Carvalho Chehab 		return -EIO;
271cb7a01acSMauro Carvalho Chehab 	}
272cb7a01acSMauro Carvalho Chehab 
273cb7a01acSMauro Carvalho Chehab 	mutex_lock(flash->ctrls.lock);
274cb7a01acSMauro Carvalho Chehab 	/* Reset faults before reading new ones. */
275cb7a01acSMauro Carvalho Chehab 	flash->fault = 0;
276cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
277cb7a01acSMauro Carvalho Chehab 	mutex_unlock(flash->ctrls.lock);
278cb7a01acSMauro Carvalho Chehab 	if (rval > 0) {
279cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
280cb7a01acSMauro Carvalho Chehab 		return -EIO;
281cb7a01acSMauro Carvalho Chehab 	}
282cb7a01acSMauro Carvalho Chehab 
283cb7a01acSMauro Carvalho Chehab 	mutex_lock(flash->ctrls.lock);
284cb7a01acSMauro Carvalho Chehab 	rval = adp1653_update_hw(flash);
285cb7a01acSMauro Carvalho Chehab 	mutex_unlock(flash->ctrls.lock);
286cb7a01acSMauro Carvalho Chehab 	if (rval) {
287cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev,
288cb7a01acSMauro Carvalho Chehab 			"adp1653_update_hw failed at %s\n", __func__);
289cb7a01acSMauro Carvalho Chehab 		return -EIO;
290cb7a01acSMauro Carvalho Chehab 	}
291cb7a01acSMauro Carvalho Chehab 
292cb7a01acSMauro Carvalho Chehab 	return 0;
293cb7a01acSMauro Carvalho Chehab }
294cb7a01acSMauro Carvalho Chehab 
295cb7a01acSMauro Carvalho Chehab static int
__adp1653_set_power(struct adp1653_flash * flash,int on)296cb7a01acSMauro Carvalho Chehab __adp1653_set_power(struct adp1653_flash *flash, int on)
297cb7a01acSMauro Carvalho Chehab {
298be8e58d9SMauro Carvalho Chehab 	int ret;
299cb7a01acSMauro Carvalho Chehab 
300074c57a2SPavel Machek 	if (flash->platform_data->power) {
301cb7a01acSMauro Carvalho Chehab 		ret = flash->platform_data->power(&flash->subdev, on);
302cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
303cb7a01acSMauro Carvalho Chehab 			return ret;
304074c57a2SPavel Machek 	} else {
305074c57a2SPavel Machek 		gpiod_set_value(flash->platform_data->enable_gpio, on);
306074c57a2SPavel Machek 		if (on)
307074c57a2SPavel Machek 			/* Some delay is apparently required. */
308074c57a2SPavel Machek 			udelay(20);
309074c57a2SPavel Machek 	}
310cb7a01acSMauro Carvalho Chehab 
311cb7a01acSMauro Carvalho Chehab 	if (!on)
312cb7a01acSMauro Carvalho Chehab 		return 0;
313cb7a01acSMauro Carvalho Chehab 
314cb7a01acSMauro Carvalho Chehab 	ret = adp1653_init_device(flash);
315074c57a2SPavel Machek 	if (ret >= 0)
316074c57a2SPavel Machek 		return ret;
317074c57a2SPavel Machek 
318074c57a2SPavel Machek 	if (flash->platform_data->power)
319cb7a01acSMauro Carvalho Chehab 		flash->platform_data->power(&flash->subdev, 0);
320074c57a2SPavel Machek 	else
321074c57a2SPavel Machek 		gpiod_set_value(flash->platform_data->enable_gpio, 0);
322cb7a01acSMauro Carvalho Chehab 
323cb7a01acSMauro Carvalho Chehab 	return ret;
324cb7a01acSMauro Carvalho Chehab }
325cb7a01acSMauro Carvalho Chehab 
326cb7a01acSMauro Carvalho Chehab static int
adp1653_set_power(struct v4l2_subdev * subdev,int on)327cb7a01acSMauro Carvalho Chehab adp1653_set_power(struct v4l2_subdev *subdev, int on)
328cb7a01acSMauro Carvalho Chehab {
329cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
330cb7a01acSMauro Carvalho Chehab 	int ret = 0;
331cb7a01acSMauro Carvalho Chehab 
332cb7a01acSMauro Carvalho Chehab 	mutex_lock(&flash->power_lock);
333cb7a01acSMauro Carvalho Chehab 
334cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
335cb7a01acSMauro Carvalho Chehab 	 * update the power state.
336cb7a01acSMauro Carvalho Chehab 	 */
337cb7a01acSMauro Carvalho Chehab 	if (flash->power_count == !on) {
338cb7a01acSMauro Carvalho Chehab 		ret = __adp1653_set_power(flash, !!on);
339cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
340cb7a01acSMauro Carvalho Chehab 			goto done;
341cb7a01acSMauro Carvalho Chehab 	}
342cb7a01acSMauro Carvalho Chehab 
343cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
344cb7a01acSMauro Carvalho Chehab 	flash->power_count += on ? 1 : -1;
345cb7a01acSMauro Carvalho Chehab 	WARN_ON(flash->power_count < 0);
346cb7a01acSMauro Carvalho Chehab 
347cb7a01acSMauro Carvalho Chehab done:
348cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&flash->power_lock);
349cb7a01acSMauro Carvalho Chehab 	return ret;
350cb7a01acSMauro Carvalho Chehab }
351cb7a01acSMauro Carvalho Chehab 
adp1653_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)352cb7a01acSMauro Carvalho Chehab static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
353cb7a01acSMauro Carvalho Chehab {
354cb7a01acSMauro Carvalho Chehab 	return adp1653_set_power(sd, 1);
355cb7a01acSMauro Carvalho Chehab }
356cb7a01acSMauro Carvalho Chehab 
adp1653_close(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)357cb7a01acSMauro Carvalho Chehab static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
358cb7a01acSMauro Carvalho Chehab {
359cb7a01acSMauro Carvalho Chehab 	return adp1653_set_power(sd, 0);
360cb7a01acSMauro Carvalho Chehab }
361cb7a01acSMauro Carvalho Chehab 
362cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adp1653_core_ops = {
363cb7a01acSMauro Carvalho Chehab 	.s_power = adp1653_set_power,
364cb7a01acSMauro Carvalho Chehab };
365cb7a01acSMauro Carvalho Chehab 
366cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adp1653_ops = {
367cb7a01acSMauro Carvalho Chehab 	.core = &adp1653_core_ops,
368cb7a01acSMauro Carvalho Chehab };
369cb7a01acSMauro Carvalho Chehab 
370cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
371cb7a01acSMauro Carvalho Chehab 	.open = adp1653_open,
372cb7a01acSMauro Carvalho Chehab 	.close = adp1653_close,
373cb7a01acSMauro Carvalho Chehab };
374cb7a01acSMauro Carvalho Chehab 
375cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
376cb7a01acSMauro Carvalho Chehab  * I2C driver
377cb7a01acSMauro Carvalho Chehab  */
378cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_PM
379cb7a01acSMauro Carvalho Chehab 
adp1653_suspend(struct device * dev)380cb7a01acSMauro Carvalho Chehab static int adp1653_suspend(struct device *dev)
381cb7a01acSMauro Carvalho Chehab {
38222a7c931SKrzysztof Kozlowski 	struct v4l2_subdev *subdev = dev_get_drvdata(dev);
383cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
384cb7a01acSMauro Carvalho Chehab 
385cb7a01acSMauro Carvalho Chehab 	if (!flash->power_count)
386cb7a01acSMauro Carvalho Chehab 		return 0;
387cb7a01acSMauro Carvalho Chehab 
388cb7a01acSMauro Carvalho Chehab 	return __adp1653_set_power(flash, 0);
389cb7a01acSMauro Carvalho Chehab }
390cb7a01acSMauro Carvalho Chehab 
adp1653_resume(struct device * dev)391cb7a01acSMauro Carvalho Chehab static int adp1653_resume(struct device *dev)
392cb7a01acSMauro Carvalho Chehab {
39322a7c931SKrzysztof Kozlowski 	struct v4l2_subdev *subdev = dev_get_drvdata(dev);
394cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
395cb7a01acSMauro Carvalho Chehab 
396cb7a01acSMauro Carvalho Chehab 	if (!flash->power_count)
397cb7a01acSMauro Carvalho Chehab 		return 0;
398cb7a01acSMauro Carvalho Chehab 
399cb7a01acSMauro Carvalho Chehab 	return __adp1653_set_power(flash, 1);
400cb7a01acSMauro Carvalho Chehab }
401cb7a01acSMauro Carvalho Chehab 
402cb7a01acSMauro Carvalho Chehab #else
403cb7a01acSMauro Carvalho Chehab 
404cb7a01acSMauro Carvalho Chehab #define adp1653_suspend	NULL
405cb7a01acSMauro Carvalho Chehab #define adp1653_resume	NULL
406cb7a01acSMauro Carvalho Chehab 
407cb7a01acSMauro Carvalho Chehab #endif /* CONFIG_PM */
408cb7a01acSMauro Carvalho Chehab 
adp1653_of_init(struct i2c_client * client,struct adp1653_flash * flash,struct device_node * node)409074c57a2SPavel Machek static int adp1653_of_init(struct i2c_client *client,
410074c57a2SPavel Machek 			   struct adp1653_flash *flash,
411074c57a2SPavel Machek 			   struct device_node *node)
412074c57a2SPavel Machek {
413074c57a2SPavel Machek 	struct adp1653_platform_data *pd;
414074c57a2SPavel Machek 	struct device_node *child;
415074c57a2SPavel Machek 
416074c57a2SPavel Machek 	pd = devm_kzalloc(&client->dev, sizeof(*pd), GFP_KERNEL);
417074c57a2SPavel Machek 	if (!pd)
418074c57a2SPavel Machek 		return -ENOMEM;
419074c57a2SPavel Machek 	flash->platform_data = pd;
420074c57a2SPavel Machek 
421074c57a2SPavel Machek 	child = of_get_child_by_name(node, "flash");
422074c57a2SPavel Machek 	if (!child)
423074c57a2SPavel Machek 		return -EINVAL;
424074c57a2SPavel Machek 
425074c57a2SPavel Machek 	if (of_property_read_u32(child, "flash-timeout-us",
426074c57a2SPavel Machek 				 &pd->max_flash_timeout))
427074c57a2SPavel Machek 		goto err;
428074c57a2SPavel Machek 
429074c57a2SPavel Machek 	if (of_property_read_u32(child, "flash-max-microamp",
430074c57a2SPavel Machek 				 &pd->max_flash_intensity))
431074c57a2SPavel Machek 		goto err;
432074c57a2SPavel Machek 
433074c57a2SPavel Machek 	pd->max_flash_intensity /= 1000;
434074c57a2SPavel Machek 
435074c57a2SPavel Machek 	if (of_property_read_u32(child, "led-max-microamp",
436074c57a2SPavel Machek 				 &pd->max_torch_intensity))
437074c57a2SPavel Machek 		goto err;
438074c57a2SPavel Machek 
439074c57a2SPavel Machek 	pd->max_torch_intensity /= 1000;
440074c57a2SPavel Machek 	of_node_put(child);
441074c57a2SPavel Machek 
442074c57a2SPavel Machek 	child = of_get_child_by_name(node, "indicator");
443074c57a2SPavel Machek 	if (!child)
444074c57a2SPavel Machek 		return -EINVAL;
445074c57a2SPavel Machek 
446074c57a2SPavel Machek 	if (of_property_read_u32(child, "led-max-microamp",
447074c57a2SPavel Machek 				 &pd->max_indicator_intensity))
448074c57a2SPavel Machek 		goto err;
449074c57a2SPavel Machek 
450074c57a2SPavel Machek 	of_node_put(child);
451074c57a2SPavel Machek 
452a33c380eSUwe Kleine-König 	pd->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW);
453806f8ffaSVladimir Zapolskiy 	if (IS_ERR(pd->enable_gpio)) {
454074c57a2SPavel Machek 		dev_err(&client->dev, "Error getting GPIO\n");
455806f8ffaSVladimir Zapolskiy 		return PTR_ERR(pd->enable_gpio);
456074c57a2SPavel Machek 	}
457074c57a2SPavel Machek 
458074c57a2SPavel Machek 	return 0;
459074c57a2SPavel Machek err:
460074c57a2SPavel Machek 	dev_err(&client->dev, "Required property not found\n");
461074c57a2SPavel Machek 	of_node_put(child);
462074c57a2SPavel Machek 	return -EINVAL;
463074c57a2SPavel Machek }
464074c57a2SPavel Machek 
465074c57a2SPavel Machek 
adp1653_probe(struct i2c_client * client)4668981b1e8SUwe Kleine-König static int adp1653_probe(struct i2c_client *client)
467cb7a01acSMauro Carvalho Chehab {
468cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash;
469cb7a01acSMauro Carvalho Chehab 	int ret;
470cb7a01acSMauro Carvalho Chehab 
471c02b211dSLaurent Pinchart 	flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
472cb7a01acSMauro Carvalho Chehab 	if (flash == NULL)
473cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
474cb7a01acSMauro Carvalho Chehab 
475074c57a2SPavel Machek 	if (client->dev.of_node) {
476074c57a2SPavel Machek 		ret = adp1653_of_init(client, flash, client->dev.of_node);
477074c57a2SPavel Machek 		if (ret)
478074c57a2SPavel Machek 			return ret;
479074c57a2SPavel Machek 	} else {
480074c57a2SPavel Machek 		if (!client->dev.platform_data) {
481074c57a2SPavel Machek 			dev_err(&client->dev,
482074c57a2SPavel Machek 				"Neither DT not platform data provided\n");
483b888d232SAnton Protopopov 			return -EINVAL;
484074c57a2SPavel Machek 		}
485cb7a01acSMauro Carvalho Chehab 		flash->platform_data = client->dev.platform_data;
486074c57a2SPavel Machek 	}
487cb7a01acSMauro Carvalho Chehab 
488cb7a01acSMauro Carvalho Chehab 	mutex_init(&flash->power_lock);
489cb7a01acSMauro Carvalho Chehab 
490cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
491cb7a01acSMauro Carvalho Chehab 	flash->subdev.internal_ops = &adp1653_internal_ops;
492cb7a01acSMauro Carvalho Chehab 	flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
493cb7a01acSMauro Carvalho Chehab 
494cb7a01acSMauro Carvalho Chehab 	ret = adp1653_init_controls(flash);
495cb7a01acSMauro Carvalho Chehab 	if (ret)
496cb7a01acSMauro Carvalho Chehab 		goto free_and_quit;
497cb7a01acSMauro Carvalho Chehab 
498ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL);
499cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
500cb7a01acSMauro Carvalho Chehab 		goto free_and_quit;
501cb7a01acSMauro Carvalho Chehab 
5024ca72efaSMauro Carvalho Chehab 	flash->subdev.entity.function = MEDIA_ENT_F_FLASH;
503be8e58d9SMauro Carvalho Chehab 
504cb7a01acSMauro Carvalho Chehab 	return 0;
505cb7a01acSMauro Carvalho Chehab 
506cb7a01acSMauro Carvalho Chehab free_and_quit:
507074c57a2SPavel Machek 	dev_err(&client->dev, "adp1653: failed to register device\n");
508cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&flash->ctrls);
509cb7a01acSMauro Carvalho Chehab 	return ret;
510cb7a01acSMauro Carvalho Chehab }
511cb7a01acSMauro Carvalho Chehab 
adp1653_remove(struct i2c_client * client)512ed5c2f5fSUwe Kleine-König static void adp1653_remove(struct i2c_client *client)
513cb7a01acSMauro Carvalho Chehab {
514cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
515cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
516cb7a01acSMauro Carvalho Chehab 
517cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(&flash->subdev);
518cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&flash->ctrls);
519cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&flash->subdev.entity);
520cb7a01acSMauro Carvalho Chehab }
521cb7a01acSMauro Carvalho Chehab 
522cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adp1653_id_table[] = {
523cb7a01acSMauro Carvalho Chehab 	{ ADP1653_NAME, 0 },
524cb7a01acSMauro Carvalho Chehab 	{ }
525cb7a01acSMauro Carvalho Chehab };
526cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
527cb7a01acSMauro Carvalho Chehab 
528074c57a2SPavel Machek static const struct dev_pm_ops adp1653_pm_ops = {
529cb7a01acSMauro Carvalho Chehab 	.suspend	= adp1653_suspend,
530cb7a01acSMauro Carvalho Chehab 	.resume		= adp1653_resume,
531cb7a01acSMauro Carvalho Chehab };
532cb7a01acSMauro Carvalho Chehab 
533cb7a01acSMauro Carvalho Chehab static struct i2c_driver adp1653_i2c_driver = {
534cb7a01acSMauro Carvalho Chehab 	.driver		= {
535cb7a01acSMauro Carvalho Chehab 		.name	= ADP1653_NAME,
536cb7a01acSMauro Carvalho Chehab 		.pm	= &adp1653_pm_ops,
537cb7a01acSMauro Carvalho Chehab 	},
538*aaeb31c0SUwe Kleine-König 	.probe		= adp1653_probe,
539bf306900SDmitry Torokhov 	.remove		= adp1653_remove,
540cb7a01acSMauro Carvalho Chehab 	.id_table	= adp1653_id_table,
541cb7a01acSMauro Carvalho Chehab };
542cb7a01acSMauro Carvalho Chehab 
543cb7a01acSMauro Carvalho Chehab module_i2c_driver(adp1653_i2c_driver);
544cb7a01acSMauro Carvalho Chehab 
545cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
546cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
547cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
548