aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorSakari Ailus <sakari.ailus@iki.fi>2011-05-05 14:39:25 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-07-27 16:55:39 -0400
commit13abadad177074b84f6697639e1d4f64a55b1773 (patch)
tree007a906c959474d2ced1848ad2a4c791a8b5f1ca /drivers/media
parent7ba85fa4fdbbe800e92a937ca567120ef32fe49f (diff)
[media] adp1653: Add driver for LED flash controller
This patch adds the driver for the adp1653 LED flash controller. This controller supports a high power led in flash and torch modes and an indicator light, sometimes also called privacy light. The adp1653 is used on the Nokia N900. Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi> Signed-off-by: Tuukka Toivonen <tuukkat76@gmail.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: David Cohen <dacohen@gmail.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/Kconfig9
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/adp1653.c491
3 files changed, 501 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 2cc9552f639..b1f7e19640d 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -489,6 +489,15 @@ config VIDEO_TCM825X
489 This is a driver for the Toshiba TCM825x VGA camera sensor. 489 This is a driver for the Toshiba TCM825x VGA camera sensor.
490 It is used for example in Nokia N800. 490 It is used for example in Nokia N800.
491 491
492comment "Flash devices"
493
494config VIDEO_ADP1653
495 tristate "ADP1653 flash support"
496 depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
497 ---help---
498 This is a driver for the ADP1653 flash controller. It is used for
499 example in Nokia N900.
500
492comment "Video improvement chips" 501comment "Video improvement chips"
493 502
494config VIDEO_UPD64031A 503config VIDEO_UPD64031A
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 459db028ace..5138a65f5e1 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
70obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o 70obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
71obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o 71obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
72obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ 72obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
73obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
73 74
74obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o 75obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
75obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o 76obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
new file mode 100644
index 00000000000..be7befd6094
--- /dev/null
+++ b/drivers/media/video/adp1653.c
@@ -0,0 +1,491 @@
1/*
2 * drivers/media/video/adp1653.c
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>
34#include <linux/i2c.h>
35#include <linux/slab.h>
36#include <linux/version.h>
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
261 fault->is_volatile = 1;
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
284 mutex_lock(&flash->ctrls.lock);
285 /* Reset faults before reading new ones. */
286 flash->fault = 0;
287 rval = adp1653_get_fault(flash);
288 mutex_unlock(&flash->ctrls.lock);
289 if (rval > 0) {
290 dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
291 return -EIO;
292 }
293
294 mutex_lock(&flash->ctrls.lock);
295 rval = adp1653_update_hw(flash);
296 mutex_unlock(&flash->ctrls.lock);
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
416 flash = kzalloc(sizeof(*flash), GFP_KERNEL);
417 if (flash == NULL)
418 return -ENOMEM;
419
420 flash->platform_data = client->dev.platform_data;
421
422 mutex_init(&flash->power_lock);
423
424 v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
425 flash->subdev.internal_ops = &adp1653_internal_ops;
426 flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
427
428 adp1653_init_controls(flash);
429
430 ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
431 if (ret < 0)
432 kfree(flash);
433
434 return ret;
435}
436
437static int __exit adp1653_remove(struct i2c_client *client)
438{
439 struct v4l2_subdev *subdev = i2c_get_clientdata(client);
440 struct adp1653_flash *flash = to_adp1653_flash(subdev);
441
442 v4l2_device_unregister_subdev(&flash->subdev);
443 v4l2_ctrl_handler_free(&flash->ctrls);
444 media_entity_cleanup(&flash->subdev.entity);
445 kfree(flash);
446 return 0;
447}
448
449static const struct i2c_device_id adp1653_id_table[] = {
450 { ADP1653_NAME, 0 },
451 { }
452};
453MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
454
455static struct dev_pm_ops adp1653_pm_ops = {
456 .suspend = adp1653_suspend,
457 .resume = adp1653_resume,
458};
459
460static struct i2c_driver adp1653_i2c_driver = {
461 .driver = {
462 .name = ADP1653_NAME,
463 .pm = &adp1653_pm_ops,
464 },
465 .probe = adp1653_probe,
466 .remove = __exit_p(adp1653_remove),
467 .id_table = adp1653_id_table,
468};
469
470static int __init adp1653_init(void)
471{
472 int rval;
473
474 rval = i2c_add_driver(&adp1653_i2c_driver);
475 if (rval)
476 printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
477
478 return rval;
479}
480
481static void __exit adp1653_exit(void)
482{
483 i2c_del_driver(&adp1653_i2c_driver);
484}
485
486module_init(adp1653_init);
487module_exit(adp1653_exit);
488
489MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
490MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
491MODULE_LICENSE("GPL");