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