xref: /openbmc/linux/drivers/media/i2c/adp1653.c (revision bf306900)
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>
11cb7a01acSMauro Carvalho Chehab  *
12cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
13cb7a01acSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License
14cb7a01acSMauro Carvalho Chehab  * version 2 as published by the Free Software Foundation.
15cb7a01acSMauro Carvalho Chehab  *
16cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful, but
17cb7a01acSMauro Carvalho Chehab  * WITHOUT ANY WARRANTY; without even the implied warranty of
18cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19cb7a01acSMauro Carvalho Chehab  * General Public License for more details.
20cb7a01acSMauro Carvalho Chehab  *
21cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
22cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
23cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24cb7a01acSMauro Carvalho Chehab  * 02110-1301 USA
25cb7a01acSMauro Carvalho Chehab  *
26cb7a01acSMauro Carvalho Chehab  * TODO:
27cb7a01acSMauro Carvalho Chehab  * - fault interrupt handling
28cb7a01acSMauro Carvalho Chehab  * - hardware strobe
29cb7a01acSMauro Carvalho Chehab  * - power doesn't need to be ON if all lights are off
30cb7a01acSMauro Carvalho Chehab  *
31cb7a01acSMauro Carvalho Chehab  */
32cb7a01acSMauro Carvalho Chehab 
33cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
34cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
35cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
36cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
37cb7a01acSMauro Carvalho Chehab #include <media/adp1653.h>
38cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
39cb7a01acSMauro Carvalho Chehab 
40cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MAX		820000
41cb7a01acSMauro Carvalho Chehab #define TIMEOUT_STEP		54600
42cb7a01acSMauro Carvalho Chehab #define TIMEOUT_MIN		(TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
43cb7a01acSMauro Carvalho Chehab 				 * TIMEOUT_STEP)
44cb7a01acSMauro Carvalho Chehab #define TIMEOUT_US_TO_CODE(t)	((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
45cb7a01acSMauro Carvalho Chehab 				 / TIMEOUT_STEP)
46cb7a01acSMauro Carvalho Chehab #define TIMEOUT_CODE_TO_US(c)	(TIMEOUT_MAX - (c) * TIMEOUT_STEP)
47cb7a01acSMauro Carvalho Chehab 
48cb7a01acSMauro Carvalho Chehab /* Write values into ADP1653 registers. */
49cb7a01acSMauro Carvalho Chehab static int adp1653_update_hw(struct adp1653_flash *flash)
50cb7a01acSMauro Carvalho Chehab {
51cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
52cb7a01acSMauro Carvalho Chehab 	u8 out_sel;
53cb7a01acSMauro Carvalho Chehab 	u8 config = 0;
54cb7a01acSMauro Carvalho Chehab 	int rval;
55cb7a01acSMauro Carvalho Chehab 
56cb7a01acSMauro Carvalho Chehab 	out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
57cb7a01acSMauro Carvalho Chehab 		flash->indicator_intensity->val)
58cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
59cb7a01acSMauro Carvalho Chehab 
60cb7a01acSMauro Carvalho Chehab 	switch (flash->led_mode->val) {
61cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_NONE:
62cb7a01acSMauro Carvalho Chehab 		break;
63cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_FLASH:
64cb7a01acSMauro Carvalho Chehab 		/* Flash mode, light on with strobe, duration from timer */
65cb7a01acSMauro Carvalho Chehab 		config = ADP1653_REG_CONFIG_TMR_CFG;
66cb7a01acSMauro Carvalho Chehab 		config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
67cb7a01acSMauro Carvalho Chehab 			  << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
68cb7a01acSMauro Carvalho Chehab 		break;
69cb7a01acSMauro Carvalho Chehab 	case V4L2_FLASH_LED_MODE_TORCH:
70cb7a01acSMauro Carvalho Chehab 		/* Torch mode, light immediately on, duration indefinite */
71cb7a01acSMauro Carvalho Chehab 		out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
72cb7a01acSMauro Carvalho Chehab 			flash->torch_intensity->val)
73cb7a01acSMauro Carvalho Chehab 			<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
74cb7a01acSMauro Carvalho Chehab 		break;
75cb7a01acSMauro Carvalho Chehab 	}
76cb7a01acSMauro Carvalho Chehab 
77cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
78cb7a01acSMauro Carvalho Chehab 	if (rval < 0)
79cb7a01acSMauro Carvalho Chehab 		return rval;
80cb7a01acSMauro Carvalho Chehab 
81cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
82cb7a01acSMauro Carvalho Chehab 	if (rval < 0)
83cb7a01acSMauro Carvalho Chehab 		return rval;
84cb7a01acSMauro Carvalho Chehab 
85cb7a01acSMauro Carvalho Chehab 	return 0;
86cb7a01acSMauro Carvalho Chehab }
87cb7a01acSMauro Carvalho Chehab 
88cb7a01acSMauro Carvalho Chehab static int adp1653_get_fault(struct adp1653_flash *flash)
89cb7a01acSMauro Carvalho Chehab {
90cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
91cb7a01acSMauro Carvalho Chehab 	int fault;
92cb7a01acSMauro Carvalho Chehab 	int rval;
93cb7a01acSMauro Carvalho Chehab 
94cb7a01acSMauro Carvalho Chehab 	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
95cb7a01acSMauro Carvalho Chehab 	if (IS_ERR_VALUE(fault))
96cb7a01acSMauro Carvalho Chehab 		return fault;
97cb7a01acSMauro Carvalho Chehab 
98cb7a01acSMauro Carvalho Chehab 	flash->fault |= fault;
99cb7a01acSMauro Carvalho Chehab 
100cb7a01acSMauro Carvalho Chehab 	if (!flash->fault)
101cb7a01acSMauro Carvalho Chehab 		return 0;
102cb7a01acSMauro Carvalho Chehab 
103cb7a01acSMauro Carvalho Chehab 	/* Clear faults. */
104cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
105cb7a01acSMauro Carvalho Chehab 	if (IS_ERR_VALUE(rval))
106cb7a01acSMauro Carvalho Chehab 		return rval;
107cb7a01acSMauro Carvalho Chehab 
108cb7a01acSMauro Carvalho Chehab 	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
109cb7a01acSMauro Carvalho Chehab 
110cb7a01acSMauro Carvalho Chehab 	rval = adp1653_update_hw(flash);
111cb7a01acSMauro Carvalho Chehab 	if (IS_ERR_VALUE(rval))
112cb7a01acSMauro Carvalho Chehab 		return rval;
113cb7a01acSMauro Carvalho Chehab 
114cb7a01acSMauro Carvalho Chehab 	return flash->fault;
115cb7a01acSMauro Carvalho Chehab }
116cb7a01acSMauro Carvalho Chehab 
117cb7a01acSMauro Carvalho Chehab static int adp1653_strobe(struct adp1653_flash *flash, int enable)
118cb7a01acSMauro Carvalho Chehab {
119cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
120cb7a01acSMauro Carvalho Chehab 	u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
121cb7a01acSMauro Carvalho Chehab 		flash->indicator_intensity->val)
122cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
123cb7a01acSMauro Carvalho Chehab 	int rval;
124cb7a01acSMauro Carvalho Chehab 
125cb7a01acSMauro Carvalho Chehab 	if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
126cb7a01acSMauro Carvalho Chehab 		return -EBUSY;
127cb7a01acSMauro Carvalho Chehab 
128cb7a01acSMauro Carvalho Chehab 	if (!enable)
129cb7a01acSMauro Carvalho Chehab 		return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
130cb7a01acSMauro Carvalho Chehab 						 out_sel);
131cb7a01acSMauro Carvalho Chehab 
132cb7a01acSMauro Carvalho Chehab 	out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
133cb7a01acSMauro Carvalho Chehab 		flash->flash_intensity->val)
134cb7a01acSMauro Carvalho Chehab 		<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
135cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
136cb7a01acSMauro Carvalho Chehab 	if (rval)
137cb7a01acSMauro Carvalho Chehab 		return rval;
138cb7a01acSMauro Carvalho Chehab 
139cb7a01acSMauro Carvalho Chehab 	/* Software strobe using i2c */
140cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
141cb7a01acSMauro Carvalho Chehab 		ADP1653_REG_SW_STROBE_SW_STROBE);
142cb7a01acSMauro Carvalho Chehab 	if (rval)
143cb7a01acSMauro Carvalho Chehab 		return rval;
144cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
145cb7a01acSMauro Carvalho Chehab }
146cb7a01acSMauro Carvalho Chehab 
147cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
148cb7a01acSMauro Carvalho Chehab  * V4L2 controls
149cb7a01acSMauro Carvalho Chehab  */
150cb7a01acSMauro Carvalho Chehab 
151cb7a01acSMauro Carvalho Chehab static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
152cb7a01acSMauro Carvalho Chehab {
153cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash =
154cb7a01acSMauro Carvalho Chehab 		container_of(ctrl->handler, struct adp1653_flash, ctrls);
155cb7a01acSMauro Carvalho Chehab 	int rval;
156cb7a01acSMauro Carvalho Chehab 
157cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
158cb7a01acSMauro Carvalho Chehab 	if (IS_ERR_VALUE(rval))
159cb7a01acSMauro Carvalho Chehab 		return rval;
160cb7a01acSMauro Carvalho Chehab 
161cb7a01acSMauro Carvalho Chehab 	ctrl->cur.val = 0;
162cb7a01acSMauro Carvalho Chehab 
163cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
164cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
165cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
166cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
167cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
168cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
169cb7a01acSMauro Carvalho Chehab 	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
170cb7a01acSMauro Carvalho Chehab 		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
171cb7a01acSMauro Carvalho Chehab 
172cb7a01acSMauro Carvalho Chehab 	flash->fault = 0;
173cb7a01acSMauro Carvalho Chehab 
174cb7a01acSMauro Carvalho Chehab 	return 0;
175cb7a01acSMauro Carvalho Chehab }
176cb7a01acSMauro Carvalho Chehab 
177cb7a01acSMauro Carvalho Chehab static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
178cb7a01acSMauro Carvalho Chehab {
179cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash =
180cb7a01acSMauro Carvalho Chehab 		container_of(ctrl->handler, struct adp1653_flash, ctrls);
181cb7a01acSMauro Carvalho Chehab 	int rval;
182cb7a01acSMauro Carvalho Chehab 
183cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
184cb7a01acSMauro Carvalho Chehab 	if (IS_ERR_VALUE(rval))
185cb7a01acSMauro Carvalho Chehab 		return rval;
186cb7a01acSMauro Carvalho Chehab 	if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
187cb7a01acSMauro Carvalho Chehab 		     ADP1653_REG_FAULT_FLT_OT |
188cb7a01acSMauro Carvalho Chehab 		     ADP1653_REG_FAULT_FLT_OV)) &&
189cb7a01acSMauro Carvalho Chehab 	    (ctrl->id == V4L2_CID_FLASH_STROBE ||
190cb7a01acSMauro Carvalho Chehab 	     ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
191cb7a01acSMauro Carvalho Chehab 	     ctrl->id == V4L2_CID_FLASH_LED_MODE))
192cb7a01acSMauro Carvalho Chehab 		return -EBUSY;
193cb7a01acSMauro Carvalho Chehab 
194cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
195cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_FLASH_STROBE:
196cb7a01acSMauro Carvalho Chehab 		return adp1653_strobe(flash, 1);
197cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_FLASH_STROBE_STOP:
198cb7a01acSMauro Carvalho Chehab 		return adp1653_strobe(flash, 0);
199cb7a01acSMauro Carvalho Chehab 	}
200cb7a01acSMauro Carvalho Chehab 
201cb7a01acSMauro Carvalho Chehab 	return adp1653_update_hw(flash);
202cb7a01acSMauro Carvalho Chehab }
203cb7a01acSMauro Carvalho Chehab 
204cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
205cb7a01acSMauro Carvalho Chehab 	.g_volatile_ctrl = adp1653_get_ctrl,
206cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adp1653_set_ctrl,
207cb7a01acSMauro Carvalho Chehab };
208cb7a01acSMauro Carvalho Chehab 
209cb7a01acSMauro Carvalho Chehab static int adp1653_init_controls(struct adp1653_flash *flash)
210cb7a01acSMauro Carvalho Chehab {
211cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl *fault;
212cb7a01acSMauro Carvalho Chehab 
213cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&flash->ctrls, 9);
214cb7a01acSMauro Carvalho Chehab 
215cb7a01acSMauro Carvalho Chehab 	flash->led_mode =
216cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
217cb7a01acSMauro Carvalho Chehab 				       V4L2_CID_FLASH_LED_MODE,
218cb7a01acSMauro Carvalho Chehab 				       V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
219cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
220cb7a01acSMauro Carvalho Chehab 			       V4L2_CID_FLASH_STROBE_SOURCE,
221cb7a01acSMauro Carvalho Chehab 			       V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
222cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
223cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
224cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
225cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
226cb7a01acSMauro Carvalho Chehab 	flash->flash_timeout =
227cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
228cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
229cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_timeout,
230cb7a01acSMauro Carvalho Chehab 				  TIMEOUT_STEP,
231cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_timeout);
232cb7a01acSMauro Carvalho Chehab 	flash->flash_intensity =
233cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
234cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_INTENSITY,
235cb7a01acSMauro Carvalho Chehab 				  ADP1653_FLASH_INTENSITY_MIN,
236cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_flash_intensity,
237cb7a01acSMauro Carvalho Chehab 				  1, flash->platform_data->max_flash_intensity);
238cb7a01acSMauro Carvalho Chehab 	flash->torch_intensity =
239cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
240cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_TORCH_INTENSITY,
241cb7a01acSMauro Carvalho Chehab 				  ADP1653_TORCH_INTENSITY_MIN,
242cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_torch_intensity,
243cb7a01acSMauro Carvalho Chehab 				  ADP1653_FLASH_INTENSITY_STEP,
244cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_torch_intensity);
245cb7a01acSMauro Carvalho Chehab 	flash->indicator_intensity =
246cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
247cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_INDICATOR_INTENSITY,
248cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_MIN,
249cb7a01acSMauro Carvalho Chehab 				  flash->platform_data->max_indicator_intensity,
250cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_STEP,
251cb7a01acSMauro Carvalho Chehab 				  ADP1653_INDICATOR_INTENSITY_MIN);
252cb7a01acSMauro Carvalho Chehab 	fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
253cb7a01acSMauro Carvalho Chehab 				  V4L2_CID_FLASH_FAULT, 0,
254cb7a01acSMauro Carvalho Chehab 				  V4L2_FLASH_FAULT_OVER_VOLTAGE
255cb7a01acSMauro Carvalho Chehab 				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE
256cb7a01acSMauro Carvalho Chehab 				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
257cb7a01acSMauro Carvalho Chehab 
258cb7a01acSMauro Carvalho Chehab 	if (flash->ctrls.error)
259cb7a01acSMauro Carvalho Chehab 		return flash->ctrls.error;
260cb7a01acSMauro Carvalho Chehab 
261cb7a01acSMauro Carvalho Chehab 	fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
262cb7a01acSMauro Carvalho Chehab 
263cb7a01acSMauro Carvalho Chehab 	flash->subdev.ctrl_handler = &flash->ctrls;
264cb7a01acSMauro Carvalho Chehab 	return 0;
265cb7a01acSMauro Carvalho Chehab }
266cb7a01acSMauro Carvalho Chehab 
267cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
268cb7a01acSMauro Carvalho Chehab  * V4L2 subdev operations
269cb7a01acSMauro Carvalho Chehab  */
270cb7a01acSMauro Carvalho Chehab 
271cb7a01acSMauro Carvalho Chehab static int
272cb7a01acSMauro Carvalho Chehab adp1653_init_device(struct adp1653_flash *flash)
273cb7a01acSMauro Carvalho Chehab {
274cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
275cb7a01acSMauro Carvalho Chehab 	int rval;
276cb7a01acSMauro Carvalho Chehab 
277cb7a01acSMauro Carvalho Chehab 	/* Clear FAULT register by writing zero to OUT_SEL */
278cb7a01acSMauro Carvalho Chehab 	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
279cb7a01acSMauro Carvalho Chehab 	if (rval < 0) {
280cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "failed writing fault register\n");
281cb7a01acSMauro Carvalho Chehab 		return -EIO;
282cb7a01acSMauro Carvalho Chehab 	}
283cb7a01acSMauro Carvalho Chehab 
284cb7a01acSMauro Carvalho Chehab 	mutex_lock(flash->ctrls.lock);
285cb7a01acSMauro Carvalho Chehab 	/* Reset faults before reading new ones. */
286cb7a01acSMauro Carvalho Chehab 	flash->fault = 0;
287cb7a01acSMauro Carvalho Chehab 	rval = adp1653_get_fault(flash);
288cb7a01acSMauro Carvalho Chehab 	mutex_unlock(flash->ctrls.lock);
289cb7a01acSMauro Carvalho Chehab 	if (rval > 0) {
290cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
291cb7a01acSMauro Carvalho Chehab 		return -EIO;
292cb7a01acSMauro Carvalho Chehab 	}
293cb7a01acSMauro Carvalho Chehab 
294cb7a01acSMauro Carvalho Chehab 	mutex_lock(flash->ctrls.lock);
295cb7a01acSMauro Carvalho Chehab 	rval = adp1653_update_hw(flash);
296cb7a01acSMauro Carvalho Chehab 	mutex_unlock(flash->ctrls.lock);
297cb7a01acSMauro Carvalho Chehab 	if (rval) {
298cb7a01acSMauro Carvalho Chehab 		dev_err(&client->dev,
299cb7a01acSMauro Carvalho Chehab 			"adp1653_update_hw failed at %s\n", __func__);
300cb7a01acSMauro Carvalho Chehab 		return -EIO;
301cb7a01acSMauro Carvalho Chehab 	}
302cb7a01acSMauro Carvalho Chehab 
303cb7a01acSMauro Carvalho Chehab 	return 0;
304cb7a01acSMauro Carvalho Chehab }
305cb7a01acSMauro Carvalho Chehab 
306cb7a01acSMauro Carvalho Chehab static int
307cb7a01acSMauro Carvalho Chehab __adp1653_set_power(struct adp1653_flash *flash, int on)
308cb7a01acSMauro Carvalho Chehab {
309cb7a01acSMauro Carvalho Chehab 	int ret;
310cb7a01acSMauro Carvalho Chehab 
311cb7a01acSMauro Carvalho Chehab 	ret = flash->platform_data->power(&flash->subdev, on);
312cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
313cb7a01acSMauro Carvalho Chehab 		return ret;
314cb7a01acSMauro Carvalho Chehab 
315cb7a01acSMauro Carvalho Chehab 	if (!on)
316cb7a01acSMauro Carvalho Chehab 		return 0;
317cb7a01acSMauro Carvalho Chehab 
318cb7a01acSMauro Carvalho Chehab 	ret = adp1653_init_device(flash);
319cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
320cb7a01acSMauro Carvalho Chehab 		flash->platform_data->power(&flash->subdev, 0);
321cb7a01acSMauro Carvalho Chehab 
322cb7a01acSMauro Carvalho Chehab 	return ret;
323cb7a01acSMauro Carvalho Chehab }
324cb7a01acSMauro Carvalho Chehab 
325cb7a01acSMauro Carvalho Chehab static int
326cb7a01acSMauro Carvalho Chehab adp1653_set_power(struct v4l2_subdev *subdev, int on)
327cb7a01acSMauro Carvalho Chehab {
328cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
329cb7a01acSMauro Carvalho Chehab 	int ret = 0;
330cb7a01acSMauro Carvalho Chehab 
331cb7a01acSMauro Carvalho Chehab 	mutex_lock(&flash->power_lock);
332cb7a01acSMauro Carvalho Chehab 
333cb7a01acSMauro Carvalho Chehab 	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
334cb7a01acSMauro Carvalho Chehab 	 * update the power state.
335cb7a01acSMauro Carvalho Chehab 	 */
336cb7a01acSMauro Carvalho Chehab 	if (flash->power_count == !on) {
337cb7a01acSMauro Carvalho Chehab 		ret = __adp1653_set_power(flash, !!on);
338cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
339cb7a01acSMauro Carvalho Chehab 			goto done;
340cb7a01acSMauro Carvalho Chehab 	}
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab 	/* Update the power count. */
343cb7a01acSMauro Carvalho Chehab 	flash->power_count += on ? 1 : -1;
344cb7a01acSMauro Carvalho Chehab 	WARN_ON(flash->power_count < 0);
345cb7a01acSMauro Carvalho Chehab 
346cb7a01acSMauro Carvalho Chehab done:
347cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&flash->power_lock);
348cb7a01acSMauro Carvalho Chehab 	return ret;
349cb7a01acSMauro Carvalho Chehab }
350cb7a01acSMauro Carvalho Chehab 
351cb7a01acSMauro Carvalho Chehab static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
352cb7a01acSMauro Carvalho Chehab {
353cb7a01acSMauro Carvalho Chehab 	return adp1653_set_power(sd, 1);
354cb7a01acSMauro Carvalho Chehab }
355cb7a01acSMauro Carvalho Chehab 
356cb7a01acSMauro Carvalho Chehab static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
357cb7a01acSMauro Carvalho Chehab {
358cb7a01acSMauro Carvalho Chehab 	return adp1653_set_power(sd, 0);
359cb7a01acSMauro Carvalho Chehab }
360cb7a01acSMauro Carvalho Chehab 
361cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adp1653_core_ops = {
362cb7a01acSMauro Carvalho Chehab 	.s_power = adp1653_set_power,
363cb7a01acSMauro Carvalho Chehab };
364cb7a01acSMauro Carvalho Chehab 
365cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adp1653_ops = {
366cb7a01acSMauro Carvalho Chehab 	.core = &adp1653_core_ops,
367cb7a01acSMauro Carvalho Chehab };
368cb7a01acSMauro Carvalho Chehab 
369cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
370cb7a01acSMauro Carvalho Chehab 	.open = adp1653_open,
371cb7a01acSMauro Carvalho Chehab 	.close = adp1653_close,
372cb7a01acSMauro Carvalho Chehab };
373cb7a01acSMauro Carvalho Chehab 
374cb7a01acSMauro Carvalho Chehab /* --------------------------------------------------------------------------
375cb7a01acSMauro Carvalho Chehab  * I2C driver
376cb7a01acSMauro Carvalho Chehab  */
377cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_PM
378cb7a01acSMauro Carvalho Chehab 
379cb7a01acSMauro Carvalho Chehab static int adp1653_suspend(struct device *dev)
380cb7a01acSMauro Carvalho Chehab {
381cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = to_i2c_client(dev);
382cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
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 
391cb7a01acSMauro Carvalho Chehab static int adp1653_resume(struct device *dev)
392cb7a01acSMauro Carvalho Chehab {
393cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = to_i2c_client(dev);
394cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
395cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
396cb7a01acSMauro Carvalho Chehab 
397cb7a01acSMauro Carvalho Chehab 	if (!flash->power_count)
398cb7a01acSMauro Carvalho Chehab 		return 0;
399cb7a01acSMauro Carvalho Chehab 
400cb7a01acSMauro Carvalho Chehab 	return __adp1653_set_power(flash, 1);
401cb7a01acSMauro Carvalho Chehab }
402cb7a01acSMauro Carvalho Chehab 
403cb7a01acSMauro Carvalho Chehab #else
404cb7a01acSMauro Carvalho Chehab 
405cb7a01acSMauro Carvalho Chehab #define adp1653_suspend	NULL
406cb7a01acSMauro Carvalho Chehab #define adp1653_resume	NULL
407cb7a01acSMauro Carvalho Chehab 
408cb7a01acSMauro Carvalho Chehab #endif /* CONFIG_PM */
409cb7a01acSMauro Carvalho Chehab 
410cb7a01acSMauro Carvalho Chehab static int adp1653_probe(struct i2c_client *client,
411cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *devid)
412cb7a01acSMauro Carvalho Chehab {
413cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash;
414cb7a01acSMauro Carvalho Chehab 	int ret;
415cb7a01acSMauro Carvalho Chehab 
416cb7a01acSMauro Carvalho Chehab 	/* we couldn't work without platform data */
417cb7a01acSMauro Carvalho Chehab 	if (client->dev.platform_data == NULL)
418cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
419cb7a01acSMauro Carvalho Chehab 
420cb7a01acSMauro Carvalho Chehab 	flash = kzalloc(sizeof(*flash), GFP_KERNEL);
421cb7a01acSMauro Carvalho Chehab 	if (flash == NULL)
422cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
423cb7a01acSMauro Carvalho Chehab 
424cb7a01acSMauro Carvalho Chehab 	flash->platform_data = client->dev.platform_data;
425cb7a01acSMauro Carvalho Chehab 
426cb7a01acSMauro Carvalho Chehab 	mutex_init(&flash->power_lock);
427cb7a01acSMauro Carvalho Chehab 
428cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
429cb7a01acSMauro Carvalho Chehab 	flash->subdev.internal_ops = &adp1653_internal_ops;
430cb7a01acSMauro Carvalho Chehab 	flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
431cb7a01acSMauro Carvalho Chehab 
432cb7a01acSMauro Carvalho Chehab 	ret = adp1653_init_controls(flash);
433cb7a01acSMauro Carvalho Chehab 	if (ret)
434cb7a01acSMauro Carvalho Chehab 		goto free_and_quit;
435cb7a01acSMauro Carvalho Chehab 
436cb7a01acSMauro Carvalho Chehab 	ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
437cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
438cb7a01acSMauro Carvalho Chehab 		goto free_and_quit;
439cb7a01acSMauro Carvalho Chehab 
440cb7a01acSMauro Carvalho Chehab 	flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
441cb7a01acSMauro Carvalho Chehab 
442cb7a01acSMauro Carvalho Chehab 	return 0;
443cb7a01acSMauro Carvalho Chehab 
444cb7a01acSMauro Carvalho Chehab free_and_quit:
445cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&flash->ctrls);
446cb7a01acSMauro Carvalho Chehab 	kfree(flash);
447cb7a01acSMauro Carvalho Chehab 	return ret;
448cb7a01acSMauro Carvalho Chehab }
449cb7a01acSMauro Carvalho Chehab 
450bf306900SDmitry Torokhov static int adp1653_remove(struct i2c_client *client)
451cb7a01acSMauro Carvalho Chehab {
452cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
453cb7a01acSMauro Carvalho Chehab 	struct adp1653_flash *flash = to_adp1653_flash(subdev);
454cb7a01acSMauro Carvalho Chehab 
455cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(&flash->subdev);
456cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&flash->ctrls);
457cb7a01acSMauro Carvalho Chehab 	media_entity_cleanup(&flash->subdev.entity);
458cb7a01acSMauro Carvalho Chehab 	kfree(flash);
459cb7a01acSMauro Carvalho Chehab 	return 0;
460cb7a01acSMauro Carvalho Chehab }
461cb7a01acSMauro Carvalho Chehab 
462cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adp1653_id_table[] = {
463cb7a01acSMauro Carvalho Chehab 	{ ADP1653_NAME, 0 },
464cb7a01acSMauro Carvalho Chehab 	{ }
465cb7a01acSMauro Carvalho Chehab };
466cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
467cb7a01acSMauro Carvalho Chehab 
468cb7a01acSMauro Carvalho Chehab static struct dev_pm_ops adp1653_pm_ops = {
469cb7a01acSMauro Carvalho Chehab 	.suspend	= adp1653_suspend,
470cb7a01acSMauro Carvalho Chehab 	.resume		= adp1653_resume,
471cb7a01acSMauro Carvalho Chehab };
472cb7a01acSMauro Carvalho Chehab 
473cb7a01acSMauro Carvalho Chehab static struct i2c_driver adp1653_i2c_driver = {
474cb7a01acSMauro Carvalho Chehab 	.driver		= {
475cb7a01acSMauro Carvalho Chehab 		.name	= ADP1653_NAME,
476cb7a01acSMauro Carvalho Chehab 		.pm	= &adp1653_pm_ops,
477cb7a01acSMauro Carvalho Chehab 	},
478cb7a01acSMauro Carvalho Chehab 	.probe		= adp1653_probe,
479bf306900SDmitry Torokhov 	.remove		= adp1653_remove,
480cb7a01acSMauro Carvalho Chehab 	.id_table	= adp1653_id_table,
481cb7a01acSMauro Carvalho Chehab };
482cb7a01acSMauro Carvalho Chehab 
483cb7a01acSMauro Carvalho Chehab module_i2c_driver(adp1653_i2c_driver);
484cb7a01acSMauro Carvalho Chehab 
485cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
486cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
487cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
488