xref: /openbmc/linux/drivers/media/i2c/adp1653.c (revision 287980e4)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * drivers/media/i2c/adp1653.c
3cb7a01acSMauro Carvalho Chehab  *
4cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2008--2011 Nokia Corporation
5cb7a01acSMauro Carvalho Chehab  *
68c5dff90SSakari Ailus  * Contact: Sakari Ailus <sakari.ailus@iki.fi>
7cb7a01acSMauro Carvalho Chehab  *
8cb7a01acSMauro Carvalho Chehab  * Contributors:
98c5dff90SSakari Ailus  *	Sakari Ailus <sakari.ailus@iki.fi>
10cb7a01acSMauro Carvalho Chehab  *	Tuukka Toivonen <tuukkat76@gmail.com>
11074c57a2SPavel Machek  *	Pavel Machek <pavel@ucw.cz>
12cb7a01acSMauro Carvalho Chehab  *
13cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
14cb7a01acSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License
15cb7a01acSMauro Carvalho Chehab  * version 2 as published by the Free Software Foundation.
16cb7a01acSMauro Carvalho Chehab  *
17cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful, but
18cb7a01acSMauro Carvalho Chehab  * WITHOUT ANY WARRANTY; without even the implied warranty of
19cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20cb7a01acSMauro Carvalho Chehab  * General Public License for more details.
21cb7a01acSMauro Carvalho Chehab  *
22cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
23cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
24cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
25cb7a01acSMauro Carvalho Chehab  * 02110-1301 USA
26cb7a01acSMauro Carvalho Chehab  *
27cb7a01acSMauro Carvalho Chehab  * TODO:
28cb7a01acSMauro Carvalho Chehab  * - fault interrupt handling
29cb7a01acSMauro Carvalho Chehab  * - hardware strobe
30cb7a01acSMauro Carvalho Chehab  * - power doesn't need to be ON if all lights are off
31cb7a01acSMauro Carvalho Chehab  *
32cb7a01acSMauro Carvalho Chehab  */
33cb7a01acSMauro Carvalho Chehab 
34cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
35cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
36cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
37cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
38074c57a2SPavel Machek #include <linux/of.h>
39074c57a2SPavel Machek #include <linux/gpio/consumer.h>
40b5dcee22SMauro Carvalho Chehab #include <media/i2c/adp1653.h>
41cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
42cb7a01acSMauro Carvalho Chehab 
43cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MAX		820000
44cb7a01acSMauro Carvalho Chehab #define TIMEOUT_STEP		54600
45cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MIN		(TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
46cb7a01acSMauro Carvalho Chehab 				 * TIMEOUT_STEP)
47cb7a01acSMauro Carvalho Chehab #define TIMEOUT_US_TO_CODE(t)	((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
48cb7a01acSMauro Carvalho Chehab 				 / TIMEOUT_STEP)
49cb7a01acSMauro Carvalho Chehab #define TIMEOUT_CODE_TO_US(c)	(TIMEOUT_MAX - (c) * TIMEOUT_STEP)
50cb7a01acSMauro Carvalho Chehab 
51cb7a01acSMauro Carvalho Chehab /* Write values into ADP1653 registers. */
52cb7a01acSMauro Carvalho Chehab static int adp1653_update_hw(struct adp1653_flash *flash)
53cb7a01acSMauro Carvalho Chehab {
54cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
55cb7a01acSMauro Carvalho Chehab 	u8 out_sel;
56cb7a01acSMauro Carvalho Chehab 	u8 config = 0;
57cb7a01acSMauro Carvalho Chehab 	int rval;
58cb7a01acSMauro Carvalho Chehab 
59cb7a01acSMauro Carvalho Chehab 	out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
60cb7a01acSMauro Carvalho Chehab 		flash->indicator_intensity->val)
61cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
62cb7a01acSMauro Carvalho Chehab 
63cb7a01acSMauro Carvalho Chehab 	switch (flash->led_mode->val) {
64cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_NONE:
65cb7a01acSMauro Carvalho Chehab 		break;
66cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_FLASH:
67cb7a01acSMauro Carvalho Chehab 		/* Flash mode, light on with strobe, duration from timer */
68cb7a01acSMauro Carvalho Chehab 		config = ADP1653_REG_CONFIG_TMR_CFG;
69cb7a01acSMauro Carvalho Chehab 		config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
70cb7a01acSMauro Carvalho Chehab 			  << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
71cb7a01acSMauro Carvalho Chehab 		break;
72cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_TORCH:
73cb7a01acSMauro Carvalho Chehab 		/* Torch mode, light immediately on, duration indefinite */
74cb7a01acSMauro Carvalho Chehab 		out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
75cb7a01acSMauro Carvalho Chehab 			flash->torch_intensity->val)
76cb7a01acSMauro Carvalho Chehab 			<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
77cb7a01acSMauro Carvalho Chehab 		break;
78cb7a01acSMauro Carvalho Chehab 	}
79cb7a01acSMauro Carvalho Chehab 
80cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
81cb7a01acSMauro Carvalho Chehab 	if (rval < 0)
82cb7a01acSMauro Carvalho Chehab 		return rval;
83cb7a01acSMauro Carvalho Chehab 
84cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
85cb7a01acSMauro Carvalho Chehab 	if (rval < 0)
86cb7a01acSMauro Carvalho Chehab 		return rval;
87cb7a01acSMauro Carvalho Chehab 
88cb7a01acSMauro Carvalho Chehab 	return 0;
89cb7a01acSMauro Carvalho Chehab }
90cb7a01acSMauro Carvalho Chehab 
91cb7a01acSMauro Carvalho Chehab static int adp1653_get_fault(struct adp1653_flash *flash)
92cb7a01acSMauro Carvalho Chehab {
93cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
94cb7a01acSMauro Carvalho Chehab 	int fault;
95cb7a01acSMauro Carvalho Chehab 	int rval;
96cb7a01acSMauro Carvalho Chehab 
97cb7a01acSMauro Carvalho Chehab 	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
98287980e4SArnd Bergmann 	if (fault < 0)
99cb7a01acSMauro Carvalho Chehab 		return fault;
100cb7a01acSMauro Carvalho Chehab 
101cb7a01acSMauro Carvalho Chehab 	flash->fault |= fault;
102cb7a01acSMauro Carvalho Chehab 
103cb7a01acSMauro Carvalho Chehab 	if (!flash->fault)
104cb7a01acSMauro Carvalho Chehab 		return 0;
105cb7a01acSMauro Carvalho Chehab 
106cb7a01acSMauro Carvalho Chehab 	/* Clear faults. */
107cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
108287980e4SArnd Bergmann 	if (rval < 0)
109cb7a01acSMauro Carvalho Chehab 		return rval;
110cb7a01acSMauro Carvalho Chehab 
111cb7a01acSMauro Carvalho Chehab 	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
112cb7a01acSMauro Carvalho Chehab 
113cb7a01acSMauro Carvalho Chehab 	rval = adp1653_update_hw(flash);
114287980e4SArnd Bergmann 	if (rval)
115cb7a01acSMauro Carvalho Chehab 		return rval;
116cb7a01acSMauro Carvalho Chehab 
117cb7a01acSMauro Carvalho Chehab 	return flash->fault;
118cb7a01acSMauro Carvalho Chehab }
119cb7a01acSMauro Carvalho Chehab 
120cb7a01acSMauro Carvalho Chehab static int adp1653_strobe(struct adp1653_flash *flash, int enable)
121cb7a01acSMauro Carvalho Chehab {
122cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
123cb7a01acSMauro Carvalho Chehab 	u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
124cb7a01acSMauro Carvalho Chehab 		flash->indicator_intensity->val)
125cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
126cb7a01acSMauro Carvalho Chehab 	int rval;
127cb7a01acSMauro Carvalho Chehab 
128cb7a01acSMauro Carvalho Chehab 	if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
129cb7a01acSMauro Carvalho Chehab 		return -EBUSY;
130cb7a01acSMauro Carvalho Chehab 
131cb7a01acSMauro Carvalho Chehab 	if (!enable)
132cb7a01acSMauro Carvalho Chehab 		return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
133cb7a01acSMauro Carvalho Chehab 						 out_sel);
134cb7a01acSMauro Carvalho Chehab 
135cb7a01acSMauro Carvalho Chehab 	out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
136cb7a01acSMauro Carvalho Chehab 		flash->flash_intensity->val)
137cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
138cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
139cb7a01acSMauro Carvalho Chehab 	if (rval)
140cb7a01acSMauro Carvalho Chehab 		return rval;
141cb7a01acSMauro Carvalho Chehab 
142cb7a01acSMauro Carvalho Chehab 	/* Software strobe using i2c */
143cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
144cb7a01acSMauro Carvalho Chehab 		ADP1653_REG_SW_STROBE_SW_STROBE);
145cb7a01acSMauro Carvalho Chehab 	if (rval)
146cb7a01acSMauro Carvalho Chehab 		return rval;
147cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
148cb7a01acSMauro Carvalho Chehab }
149cb7a01acSMauro Carvalho Chehab 
150cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
151cb7a01acSMauro Carvalho Chehab  * V4L2 controls
152cb7a01acSMauro Carvalho Chehab  */
153cb7a01acSMauro Carvalho Chehab 
154cb7a01acSMauro Carvalho Chehab static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
155cb7a01acSMauro Carvalho Chehab {
156cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash =
157cb7a01acSMauro Carvalho Chehab 		container_of(ctrl->handler, struct adp1653_flash, ctrls);
158cb7a01acSMauro Carvalho Chehab 	int rval;
159cb7a01acSMauro Carvalho Chehab 
160cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
161287980e4SArnd Bergmann 	if (rval)
162cb7a01acSMauro Carvalho Chehab 		return rval;
163cb7a01acSMauro Carvalho Chehab 
164cb7a01acSMauro Carvalho Chehab 	ctrl->cur.val = 0;
165cb7a01acSMauro Carvalho Chehab 
166cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
167cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
168cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
169cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
170cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
171cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
172cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
173cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
174cb7a01acSMauro Carvalho Chehab 
175cb7a01acSMauro Carvalho Chehab 	flash->fault = 0;
176cb7a01acSMauro Carvalho Chehab 
177cb7a01acSMauro Carvalho Chehab 	return 0;
178cb7a01acSMauro Carvalho Chehab }
179cb7a01acSMauro Carvalho Chehab 
180cb7a01acSMauro Carvalho Chehab static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
181cb7a01acSMauro Carvalho Chehab {
182cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash =
183cb7a01acSMauro Carvalho Chehab 		container_of(ctrl->handler, struct adp1653_flash, ctrls);
184cb7a01acSMauro Carvalho Chehab 	int rval;
185cb7a01acSMauro Carvalho Chehab 
186cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
187287980e4SArnd Bergmann 	if (rval)
188cb7a01acSMauro Carvalho Chehab 		return rval;
189cb7a01acSMauro Carvalho Chehab 	if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
190cb7a01acSMauro Carvalho Chehab 		     ADP1653_REG_FAULT_FLT_OT |
191cb7a01acSMauro Carvalho Chehab 		     ADP1653_REG_FAULT_FLT_OV)) &&
192cb7a01acSMauro Carvalho Chehab 	    (ctrl->id == V4L2_CID_FLASH_STROBE ||
193cb7a01acSMauro Carvalho Chehab 	     ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
194cb7a01acSMauro Carvalho Chehab 	     ctrl->id == V4L2_CID_FLASH_LED_MODE))
195cb7a01acSMauro Carvalho Chehab 		return -EBUSY;
196cb7a01acSMauro Carvalho Chehab 
197cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
198cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_FLASH_STROBE:
199cb7a01acSMauro Carvalho Chehab 		return adp1653_strobe(flash, 1);
200cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_FLASH_STROBE_STOP:
201cb7a01acSMauro Carvalho Chehab 		return adp1653_strobe(flash, 0);
202cb7a01acSMauro Carvalho Chehab 	}
203cb7a01acSMauro Carvalho Chehab 
204cb7a01acSMauro Carvalho Chehab 	return adp1653_update_hw(flash);
205cb7a01acSMauro Carvalho Chehab }
206cb7a01acSMauro Carvalho Chehab 
207cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
208cb7a01acSMauro Carvalho Chehab 	.g_volatile_ctrl = adp1653_get_ctrl,
209cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adp1653_set_ctrl,
210cb7a01acSMauro Carvalho Chehab };
211cb7a01acSMauro Carvalho Chehab 
212cb7a01acSMauro Carvalho Chehab static int adp1653_init_controls(struct adp1653_flash *flash)
213cb7a01acSMauro Carvalho Chehab {
214cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *fault;
215cb7a01acSMauro Carvalho Chehab 
216cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&flash->ctrls, 9);
217cb7a01acSMauro Carvalho Chehab 
218cb7a01acSMauro Carvalho Chehab 	flash->led_mode =
219cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
220cb7a01acSMauro Carvalho Chehab 				       V4L2_CID_FLASH_LED_MODE,
221cb7a01acSMauro Carvalho Chehab 				       V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
222cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
223cb7a01acSMauro Carvalho Chehab 			       V4L2_CID_FLASH_STROBE_SOURCE,
224cb7a01acSMauro Carvalho Chehab 			       V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
225cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
226cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
227cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
228cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
229cb7a01acSMauro Carvalho Chehab 	flash->flash_timeout =
230cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
231cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
232cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_timeout,
233cb7a01acSMauro Carvalho Chehab 				  TIMEOUT_STEP,
234cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_timeout);
235cb7a01acSMauro Carvalho Chehab 	flash->flash_intensity =
236cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
237cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_INTENSITY,
238cb7a01acSMauro Carvalho Chehab 				  ADP1653_FLASH_INTENSITY_MIN,
239cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_intensity,
240cb7a01acSMauro Carvalho Chehab 				  1, flash->platform_data->max_flash_intensity);
241cb7a01acSMauro Carvalho Chehab 	flash->torch_intensity =
242cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
243cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_TORCH_INTENSITY,
244cb7a01acSMauro Carvalho Chehab 				  ADP1653_TORCH_INTENSITY_MIN,
245cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_torch_intensity,
246cb7a01acSMauro Carvalho Chehab 				  ADP1653_FLASH_INTENSITY_STEP,
247cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_torch_intensity);
248cb7a01acSMauro Carvalho Chehab 	flash->indicator_intensity =
249cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
250cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_INDICATOR_INTENSITY,
251cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_MIN,
252cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_indicator_intensity,
253cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_STEP,
254cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_MIN);
255cb7a01acSMauro Carvalho Chehab 	fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
256cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_FAULT, 0,
257cb7a01acSMauro Carvalho Chehab 				  V4L2_FLASH_FAULT_OVER_VOLTAGE
258cb7a01acSMauro Carvalho Chehab 				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE
259cb7a01acSMauro Carvalho Chehab 				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
260cb7a01acSMauro Carvalho Chehab 
261cb7a01acSMauro Carvalho Chehab 	if (flash->ctrls.error)
262cb7a01acSMauro Carvalho Chehab 		return flash->ctrls.error;
263cb7a01acSMauro Carvalho Chehab 
264cb7a01acSMauro Carvalho Chehab 	fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
265cb7a01acSMauro Carvalho Chehab 
266cb7a01acSMauro Carvalho Chehab 	flash->subdev.ctrl_handler = &flash->ctrls;
267cb7a01acSMauro Carvalho Chehab 	return 0;
268cb7a01acSMauro Carvalho Chehab }
269cb7a01acSMauro Carvalho Chehab 
270cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
271cb7a01acSMauro Carvalho Chehab  * V4L2 subdev operations
272cb7a01acSMauro Carvalho Chehab  */
273cb7a01acSMauro Carvalho Chehab 
274cb7a01acSMauro Carvalho Chehab static int
275cb7a01acSMauro Carvalho Chehab adp1653_init_device(struct adp1653_flash *flash)
276cb7a01acSMauro Carvalho Chehab {
277cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
278cb7a01acSMauro Carvalho Chehab 	int rval;
279cb7a01acSMauro Carvalho Chehab 
280cb7a01acSMauro Carvalho Chehab 	/* Clear FAULT register by writing zero to OUT_SEL */
281cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
282cb7a01acSMauro Carvalho Chehab 	if (rval < 0) {
283cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "failed writing fault register\n");
284cb7a01acSMauro Carvalho Chehab 		return -EIO;
285cb7a01acSMauro Carvalho Chehab 	}
286cb7a01acSMauro Carvalho Chehab 
287cb7a01acSMauro Carvalho Chehab 	mutex_lock(flash->ctrls.lock);
288cb7a01acSMauro Carvalho Chehab 	/* Reset faults before reading new ones. */
289cb7a01acSMauro Carvalho Chehab 	flash->fault = 0;
290cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
291cb7a01acSMauro Carvalho Chehab 	mutex_unlock(flash->ctrls.lock);
292cb7a01acSMauro Carvalho Chehab 	if (rval > 0) {
293cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
294cb7a01acSMauro Carvalho Chehab 		return -EIO;
295cb7a01acSMauro Carvalho Chehab 	}
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab 	mutex_lock(flash->ctrls.lock);
298cb7a01acSMauro Carvalho Chehab 	rval = adp1653_update_hw(flash);
299cb7a01acSMauro Carvalho Chehab 	mutex_unlock(flash->ctrls.lock);
300cb7a01acSMauro Carvalho Chehab 	if (rval) {
301cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev,
302cb7a01acSMauro Carvalho Chehab 			"adp1653_update_hw failed at %s\n", __func__);
303cb7a01acSMauro Carvalho Chehab 		return -EIO;
304cb7a01acSMauro Carvalho Chehab 	}
305cb7a01acSMauro Carvalho Chehab 
306cb7a01acSMauro Carvalho Chehab 	return 0;
307cb7a01acSMauro Carvalho Chehab }
308cb7a01acSMauro Carvalho Chehab 
309cb7a01acSMauro Carvalho Chehab static int
310cb7a01acSMauro Carvalho Chehab __adp1653_set_power(struct adp1653_flash *flash, int on)
311cb7a01acSMauro Carvalho Chehab {
312be8e58d9SMauro Carvalho Chehab 	int ret;
313cb7a01acSMauro Carvalho Chehab 
314074c57a2SPavel Machek 	if (flash->platform_data->power) {
315cb7a01acSMauro Carvalho Chehab 		ret = flash->platform_data->power(&flash->subdev, on);
316cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
317cb7a01acSMauro Carvalho Chehab 			return ret;
318074c57a2SPavel Machek 	} else {
319074c57a2SPavel Machek 		gpiod_set_value(flash->platform_data->enable_gpio, on);
320074c57a2SPavel Machek 		if (on)
321074c57a2SPavel Machek 			/* Some delay is apparently required. */
322074c57a2SPavel Machek 			udelay(20);
323074c57a2SPavel Machek 	}
324cb7a01acSMauro Carvalho Chehab 
325cb7a01acSMauro Carvalho Chehab 	if (!on)
326cb7a01acSMauro Carvalho Chehab 		return 0;
327cb7a01acSMauro Carvalho Chehab 
328cb7a01acSMauro Carvalho Chehab 	ret = adp1653_init_device(flash);
329074c57a2SPavel Machek 	if (ret >= 0)
330074c57a2SPavel Machek 		return ret;
331074c57a2SPavel Machek 
332074c57a2SPavel Machek 	if (flash->platform_data->power)
333cb7a01acSMauro Carvalho Chehab 		flash->platform_data->power(&flash->subdev, 0);
334074c57a2SPavel Machek 	else
335074c57a2SPavel Machek 		gpiod_set_value(flash->platform_data->enable_gpio, 0);
336cb7a01acSMauro Carvalho Chehab 
337cb7a01acSMauro Carvalho Chehab 	return ret;
338cb7a01acSMauro Carvalho Chehab }
339cb7a01acSMauro Carvalho Chehab 
340cb7a01acSMauro Carvalho Chehab static int
341cb7a01acSMauro Carvalho Chehab adp1653_set_power(struct v4l2_subdev *subdev, int on)
342cb7a01acSMauro Carvalho Chehab {
343cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
344cb7a01acSMauro Carvalho Chehab 	int ret = 0;
345cb7a01acSMauro Carvalho Chehab 
346cb7a01acSMauro Carvalho Chehab 	mutex_lock(&flash->power_lock);
347cb7a01acSMauro Carvalho Chehab 
348cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
349cb7a01acSMauro Carvalho Chehab 	 * update the power state.
350cb7a01acSMauro Carvalho Chehab 	 */
351cb7a01acSMauro Carvalho Chehab 	if (flash->power_count == !on) {
352cb7a01acSMauro Carvalho Chehab 		ret = __adp1653_set_power(flash, !!on);
353cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
354cb7a01acSMauro Carvalho Chehab 			goto done;
355cb7a01acSMauro Carvalho Chehab 	}
356cb7a01acSMauro Carvalho Chehab 
357cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
358cb7a01acSMauro Carvalho Chehab 	flash->power_count += on ? 1 : -1;
359cb7a01acSMauro Carvalho Chehab 	WARN_ON(flash->power_count < 0);
360cb7a01acSMauro Carvalho Chehab 
361cb7a01acSMauro Carvalho Chehab done:
362cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&flash->power_lock);
363cb7a01acSMauro Carvalho Chehab 	return ret;
364cb7a01acSMauro Carvalho Chehab }
365cb7a01acSMauro Carvalho Chehab 
366cb7a01acSMauro Carvalho Chehab static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
367cb7a01acSMauro Carvalho Chehab {
368cb7a01acSMauro Carvalho Chehab 	return adp1653_set_power(sd, 1);
369cb7a01acSMauro Carvalho Chehab }
370cb7a01acSMauro Carvalho Chehab 
371cb7a01acSMauro Carvalho Chehab static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
372cb7a01acSMauro Carvalho Chehab {
373cb7a01acSMauro Carvalho Chehab 	return adp1653_set_power(sd, 0);
374cb7a01acSMauro Carvalho Chehab }
375cb7a01acSMauro Carvalho Chehab 
376cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adp1653_core_ops = {
377cb7a01acSMauro Carvalho Chehab 	.s_power = adp1653_set_power,
378cb7a01acSMauro Carvalho Chehab };
379cb7a01acSMauro Carvalho Chehab 
380cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adp1653_ops = {
381cb7a01acSMauro Carvalho Chehab 	.core = &adp1653_core_ops,
382cb7a01acSMauro Carvalho Chehab };
383cb7a01acSMauro Carvalho Chehab 
384cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
385cb7a01acSMauro Carvalho Chehab 	.open = adp1653_open,
386cb7a01acSMauro Carvalho Chehab 	.close = adp1653_close,
387cb7a01acSMauro Carvalho Chehab };
388cb7a01acSMauro Carvalho Chehab 
389cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
390cb7a01acSMauro Carvalho Chehab  * I2C driver
391cb7a01acSMauro Carvalho Chehab  */
392cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_PM
393cb7a01acSMauro Carvalho Chehab 
394cb7a01acSMauro Carvalho Chehab static int adp1653_suspend(struct device *dev)
395cb7a01acSMauro Carvalho Chehab {
396cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = to_i2c_client(dev);
397cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
398cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
399cb7a01acSMauro Carvalho Chehab 
400cb7a01acSMauro Carvalho Chehab 	if (!flash->power_count)
401cb7a01acSMauro Carvalho Chehab 		return 0;
402cb7a01acSMauro Carvalho Chehab 
403cb7a01acSMauro Carvalho Chehab 	return __adp1653_set_power(flash, 0);
404cb7a01acSMauro Carvalho Chehab }
405cb7a01acSMauro Carvalho Chehab 
406cb7a01acSMauro Carvalho Chehab static int adp1653_resume(struct device *dev)
407cb7a01acSMauro Carvalho Chehab {
408cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = to_i2c_client(dev);
409cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
410cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab 	if (!flash->power_count)
413cb7a01acSMauro Carvalho Chehab 		return 0;
414cb7a01acSMauro Carvalho Chehab 
415cb7a01acSMauro Carvalho Chehab 	return __adp1653_set_power(flash, 1);
416cb7a01acSMauro Carvalho Chehab }
417cb7a01acSMauro Carvalho Chehab 
418cb7a01acSMauro Carvalho Chehab #else
419cb7a01acSMauro Carvalho Chehab 
420cb7a01acSMauro Carvalho Chehab #define adp1653_suspend	NULL
421cb7a01acSMauro Carvalho Chehab #define adp1653_resume	NULL
422cb7a01acSMauro Carvalho Chehab 
423cb7a01acSMauro Carvalho Chehab #endif /* CONFIG_PM */
424cb7a01acSMauro Carvalho Chehab 
425074c57a2SPavel Machek static int adp1653_of_init(struct i2c_client *client,
426074c57a2SPavel Machek 			   struct adp1653_flash *flash,
427074c57a2SPavel Machek 			   struct device_node *node)
428074c57a2SPavel Machek {
429074c57a2SPavel Machek 	struct adp1653_platform_data *pd;
430074c57a2SPavel Machek 	struct device_node *child;
431074c57a2SPavel Machek 
432074c57a2SPavel Machek 	pd = devm_kzalloc(&client->dev, sizeof(*pd), GFP_KERNEL);
433074c57a2SPavel Machek 	if (!pd)
434074c57a2SPavel Machek 		return -ENOMEM;
435074c57a2SPavel Machek 	flash->platform_data = pd;
436074c57a2SPavel Machek 
437074c57a2SPavel Machek 	child = of_get_child_by_name(node, "flash");
438074c57a2SPavel Machek 	if (!child)
439074c57a2SPavel Machek 		return -EINVAL;
440074c57a2SPavel Machek 
441074c57a2SPavel Machek 	if (of_property_read_u32(child, "flash-timeout-us",
442074c57a2SPavel Machek 				 &pd->max_flash_timeout))
443074c57a2SPavel Machek 		goto err;
444074c57a2SPavel Machek 
445074c57a2SPavel Machek 	if (of_property_read_u32(child, "flash-max-microamp",
446074c57a2SPavel Machek 				 &pd->max_flash_intensity))
447074c57a2SPavel Machek 		goto err;
448074c57a2SPavel Machek 
449074c57a2SPavel Machek 	pd->max_flash_intensity /= 1000;
450074c57a2SPavel Machek 
451074c57a2SPavel Machek 	if (of_property_read_u32(child, "led-max-microamp",
452074c57a2SPavel Machek 				 &pd->max_torch_intensity))
453074c57a2SPavel Machek 		goto err;
454074c57a2SPavel Machek 
455074c57a2SPavel Machek 	pd->max_torch_intensity /= 1000;
456074c57a2SPavel Machek 	of_node_put(child);
457074c57a2SPavel Machek 
458074c57a2SPavel Machek 	child = of_get_child_by_name(node, "indicator");
459074c57a2SPavel Machek 	if (!child)
460074c57a2SPavel Machek 		return -EINVAL;
461074c57a2SPavel Machek 
462074c57a2SPavel Machek 	if (of_property_read_u32(child, "led-max-microamp",
463074c57a2SPavel Machek 				 &pd->max_indicator_intensity))
464074c57a2SPavel Machek 		goto err;
465074c57a2SPavel Machek 
466074c57a2SPavel Machek 	of_node_put(child);
467074c57a2SPavel Machek 
468a33c380eSUwe Kleine-König 	pd->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW);
469806f8ffaSVladimir Zapolskiy 	if (IS_ERR(pd->enable_gpio)) {
470074c57a2SPavel Machek 		dev_err(&client->dev, "Error getting GPIO\n");
471806f8ffaSVladimir Zapolskiy 		return PTR_ERR(pd->enable_gpio);
472074c57a2SPavel Machek 	}
473074c57a2SPavel Machek 
474074c57a2SPavel Machek 	return 0;
475074c57a2SPavel Machek err:
476074c57a2SPavel Machek 	dev_err(&client->dev, "Required property not found\n");
477074c57a2SPavel Machek 	of_node_put(child);
478074c57a2SPavel Machek 	return -EINVAL;
479074c57a2SPavel Machek }
480074c57a2SPavel Machek 
481074c57a2SPavel Machek 
482cb7a01acSMauro Carvalho Chehab static int adp1653_probe(struct i2c_client *client,
483cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *devid)
484cb7a01acSMauro Carvalho Chehab {
485cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash;
486cb7a01acSMauro Carvalho Chehab 	int ret;
487cb7a01acSMauro Carvalho Chehab 
488c02b211dSLaurent Pinchart 	flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
489cb7a01acSMauro Carvalho Chehab 	if (flash == NULL)
490cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
491cb7a01acSMauro Carvalho Chehab 
492074c57a2SPavel Machek 	if (client->dev.of_node) {
493074c57a2SPavel Machek 		ret = adp1653_of_init(client, flash, client->dev.of_node);
494074c57a2SPavel Machek 		if (ret)
495074c57a2SPavel Machek 			return ret;
496074c57a2SPavel Machek 	} else {
497074c57a2SPavel Machek 		if (!client->dev.platform_data) {
498074c57a2SPavel Machek 			dev_err(&client->dev,
499074c57a2SPavel Machek 				"Neither DT not platform data provided\n");
500b888d232SAnton Protopopov 			return -EINVAL;
501074c57a2SPavel Machek 		}
502cb7a01acSMauro Carvalho Chehab 		flash->platform_data = client->dev.platform_data;
503074c57a2SPavel Machek 	}
504cb7a01acSMauro Carvalho Chehab 
505cb7a01acSMauro Carvalho Chehab 	mutex_init(&flash->power_lock);
506cb7a01acSMauro Carvalho Chehab 
507cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
508cb7a01acSMauro Carvalho Chehab 	flash->subdev.internal_ops = &adp1653_internal_ops;
509cb7a01acSMauro Carvalho Chehab 	flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
510cb7a01acSMauro Carvalho Chehab 
511cb7a01acSMauro Carvalho Chehab 	ret = adp1653_init_controls(flash);
512cb7a01acSMauro Carvalho Chehab 	if (ret)
513cb7a01acSMauro Carvalho Chehab 		goto free_and_quit;
514cb7a01acSMauro Carvalho Chehab 
515ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL);
516cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
517cb7a01acSMauro Carvalho Chehab 		goto free_and_quit;
518cb7a01acSMauro Carvalho Chehab 
5194ca72efaSMauro Carvalho Chehab 	flash->subdev.entity.function = MEDIA_ENT_F_FLASH;
520be8e58d9SMauro Carvalho Chehab 
521cb7a01acSMauro Carvalho Chehab 	return 0;
522cb7a01acSMauro Carvalho Chehab 
523cb7a01acSMauro Carvalho Chehab free_and_quit:
524074c57a2SPavel Machek 	dev_err(&client->dev, "adp1653: failed to register device\n");
525cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&flash->ctrls);
526cb7a01acSMauro Carvalho Chehab 	return ret;
527cb7a01acSMauro Carvalho Chehab }
528cb7a01acSMauro Carvalho Chehab 
529bf306900SDmitry Torokhov static int adp1653_remove(struct i2c_client *client)
530cb7a01acSMauro Carvalho Chehab {
531cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
532cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
533cb7a01acSMauro Carvalho Chehab 
534cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(&flash->subdev);
535cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&flash->ctrls);
536cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&flash->subdev.entity);
537c02b211dSLaurent Pinchart 
538cb7a01acSMauro Carvalho Chehab 	return 0;
539cb7a01acSMauro Carvalho Chehab }
540cb7a01acSMauro Carvalho Chehab 
541cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adp1653_id_table[] = {
542cb7a01acSMauro Carvalho Chehab 	{ ADP1653_NAME, 0 },
543cb7a01acSMauro Carvalho Chehab 	{ }
544cb7a01acSMauro Carvalho Chehab };
545cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
546cb7a01acSMauro Carvalho Chehab 
547074c57a2SPavel Machek static const struct dev_pm_ops adp1653_pm_ops = {
548cb7a01acSMauro Carvalho Chehab 	.suspend	= adp1653_suspend,
549cb7a01acSMauro Carvalho Chehab 	.resume		= adp1653_resume,
550cb7a01acSMauro Carvalho Chehab };
551cb7a01acSMauro Carvalho Chehab 
552cb7a01acSMauro Carvalho Chehab static struct i2c_driver adp1653_i2c_driver = {
553cb7a01acSMauro Carvalho Chehab 	.driver		= {
554cb7a01acSMauro Carvalho Chehab 		.name	= ADP1653_NAME,
555cb7a01acSMauro Carvalho Chehab 		.pm	= &adp1653_pm_ops,
556cb7a01acSMauro Carvalho Chehab 	},
557cb7a01acSMauro Carvalho Chehab 	.probe		= adp1653_probe,
558bf306900SDmitry Torokhov 	.remove		= adp1653_remove,
559cb7a01acSMauro Carvalho Chehab 	.id_table	= adp1653_id_table,
560cb7a01acSMauro Carvalho Chehab };
561cb7a01acSMauro Carvalho Chehab 
562cb7a01acSMauro Carvalho Chehab module_i2c_driver(adp1653_i2c_driver);
563cb7a01acSMauro Carvalho Chehab 
564cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
565cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
566cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
567