aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorDaniel Jeong <gshark.jeong@gmail.com>2013-10-16 03:12:19 -0400
committerMauro Carvalho Chehab <m.chehab@samsung.com>2013-10-31 04:36:39 -0400
commit7f6b11a18c30743a7099d6e3110e45bd1b2cf54c (patch)
treee281e6953f4483995094d719537c9948bff05387 /drivers/media
parent8fdd33b1bcf32ed9cf21aa9e4a66fa68c6beaa1b (diff)
[media] media: i2c: add driver for dual LED Flash, lm3560
Adds the driver for the LM3560, dual LED Flash The LM3560 has two 1A constant current driver for high current white LEDs. It is controlled via an I2C compatible interface(up to 400kHz). Each flash brightness, torch brightness and enable/disable can be controlled independantly, but flash timeout and operation mode are shared. Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/i2c/Kconfig9
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/lm3560.c488
3 files changed, 498 insertions, 0 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index d18be19c96cd..75c8a035539a 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -621,6 +621,15 @@ config VIDEO_AS3645A
621 This is a driver for the AS3645A and LM3555 flash controllers. It has 621 This is a driver for the AS3645A and LM3555 flash controllers. It has
622 build in control for flash, torch and indicator LEDs. 622 build in control for flash, torch and indicator LEDs.
623 623
624config VIDEO_LM3560
625 tristate "LM3560 dual flash driver support"
626 depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
627 depends on MEDIA_CAMERA_SUPPORT
628 select REGMAP_I2C
629 ---help---
630 This is a driver for the lm3560 dual flash controllers. It controls
631 flash, torch LEDs.
632
624comment "Video improvement chips" 633comment "Video improvement chips"
625 634
626config VIDEO_UPD64031A 635config VIDEO_UPD64031A
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 9f462df77b4a..e03f1776f4f4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
70obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ 70obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
71obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o 71obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
72obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o 72obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
73obj-$(CONFIG_VIDEO_LM3560) += lm3560.o
73obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o 74obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
74obj-$(CONFIG_VIDEO_AK881X) += ak881x.o 75obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
75obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o 76obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
new file mode 100644
index 000000000000..3317a9ae3961
--- /dev/null
+++ b/drivers/media/i2c/lm3560.c
@@ -0,0 +1,488 @@
1/*
2 * drivers/media/i2c/lm3560.c
3 * General device driver for TI lm3560, FLASH LED Driver
4 *
5 * Copyright (C) 2013 Texas Instruments
6 *
7 * Contact: Daniel Jeong <gshark.jeong@gmail.com>
8 * Ldd-Mlp <ldd-mlp@list.ti.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 *
24 */
25
26#include <linux/delay.h>
27#include <linux/module.h>
28#include <linux/i2c.h>
29#include <linux/slab.h>
30#include <linux/mutex.h>
31#include <linux/regmap.h>
32#include <linux/videodev2.h>
33#include <media/lm3560.h>
34#include <media/v4l2-ctrls.h>
35#include <media/v4l2-device.h>
36
37/* registers definitions */
38#define REG_ENABLE 0x10
39#define REG_TORCH_BR 0xa0
40#define REG_FLASH_BR 0xb0
41#define REG_FLASH_TOUT 0xc0
42#define REG_FLAG 0xd0
43#define REG_CONFIG1 0xe0
44
45/* Fault Mask */
46#define FAULT_TIMEOUT (1<<0)
47#define FAULT_OVERTEMP (1<<1)
48#define FAULT_SHORT_CIRCUIT (1<<2)
49
50enum led_enable {
51 MODE_SHDN = 0x0,
52 MODE_TORCH = 0x2,
53 MODE_FLASH = 0x3,
54};
55
56/* struct lm3560_flash
57 *
58 * @pdata: platform data
59 * @regmap: reg. map for i2c
60 * @lock: muxtex for serial access.
61 * @led_mode: V4L2 LED mode
62 * @ctrls_led: V4L2 contols
63 * @subdev_led: V4L2 subdev
64 */
65struct lm3560_flash {
66 struct device *dev;
67 struct lm3560_platform_data *pdata;
68 struct regmap *regmap;
69 struct mutex lock;
70
71 enum v4l2_flash_led_mode led_mode;
72 struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX];
73 struct v4l2_subdev subdev_led[LM3560_LED_MAX];
74};
75
76#define to_lm3560_flash(_ctrl, _no) \
77 container_of(_ctrl->handler, struct lm3560_flash, ctrls_led[_no])
78
79/* enable mode control */
80static int lm3560_mode_ctrl(struct lm3560_flash *flash)
81{
82 int rval = -EINVAL;
83
84 switch (flash->led_mode) {
85 case V4L2_FLASH_LED_MODE_NONE:
86 rval = regmap_update_bits(flash->regmap,
87 REG_ENABLE, 0x03, MODE_SHDN);
88 break;
89 case V4L2_FLASH_LED_MODE_TORCH:
90 rval = regmap_update_bits(flash->regmap,
91 REG_ENABLE, 0x03, MODE_TORCH);
92 break;
93 case V4L2_FLASH_LED_MODE_FLASH:
94 rval = regmap_update_bits(flash->regmap,
95 REG_ENABLE, 0x03, MODE_FLASH);
96 break;
97 }
98 return rval;
99}
100
101/* led1/2 enable/disable */
102static int lm3560_enable_ctrl(struct lm3560_flash *flash,
103 enum lm3560_led_id led_no, bool on)
104{
105 int rval;
106
107 if (led_no == LM3560_LED0) {
108 if (on == true)
109 rval = regmap_update_bits(flash->regmap,
110 REG_ENABLE, 0x08, 0x08);
111 else
112 rval = regmap_update_bits(flash->regmap,
113 REG_ENABLE, 0x08, 0x00);
114 } else {
115 if (on == true)
116 rval = regmap_update_bits(flash->regmap,
117 REG_ENABLE, 0x10, 0x10);
118 else
119 rval = regmap_update_bits(flash->regmap,
120 REG_ENABLE, 0x10, 0x00);
121 }
122 return rval;
123}
124
125/* torch1/2 brightness control */
126static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash,
127 enum lm3560_led_id led_no, unsigned int brt)
128{
129 int rval;
130 u8 br_bits;
131
132 if (brt < LM3560_TORCH_BRT_MIN)
133 return lm3560_enable_ctrl(flash, led_no, false);
134 else
135 rval = lm3560_enable_ctrl(flash, led_no, true);
136
137 br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt);
138 if (led_no == LM3560_LED0)
139 rval = regmap_update_bits(flash->regmap,
140 REG_TORCH_BR, 0x07, br_bits);
141 else
142 rval = regmap_update_bits(flash->regmap,
143 REG_TORCH_BR, 0x38, br_bits << 3);
144
145 return rval;
146}
147
148/* flash1/2 brightness control */
149static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
150 enum lm3560_led_id led_no, unsigned int brt)
151{
152 int rval;
153 u8 br_bits;
154
155 if (brt < LM3560_FLASH_BRT_MIN)
156 return lm3560_enable_ctrl(flash, led_no, false);
157 else
158 rval = lm3560_enable_ctrl(flash, led_no, true);
159
160 br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt);
161 if (led_no == LM3560_LED0)
162 rval = regmap_update_bits(flash->regmap,
163 REG_FLASH_BR, 0x0f, br_bits);
164 else
165 rval = regmap_update_bits(flash->regmap,
166 REG_FLASH_BR, 0xf0, br_bits << 4);
167
168 return rval;
169}
170
171/* V4L2 controls */
172static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
173{
174 struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
175
176 mutex_lock(&flash->lock);
177
178 if (ctrl->id == V4L2_CID_FLASH_FAULT) {
179 int rval;
180 s32 fault = 0;
181 unsigned int reg_val;
182 rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
183 if (rval < 0)
184 return rval;
185 if (rval & FAULT_SHORT_CIRCUIT)
186 fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
187 if (rval & FAULT_OVERTEMP)
188 fault |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
189 if (rval & FAULT_TIMEOUT)
190 fault |= V4L2_FLASH_FAULT_TIMEOUT;
191 ctrl->cur.val = fault;
192 return 0;
193 }
194
195 mutex_unlock(&flash->lock);
196 return -EINVAL;
197}
198
199static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
200{
201 struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
202 u8 tout_bits;
203 int rval = -EINVAL;
204
205 mutex_lock(&flash->lock);
206
207 switch (ctrl->id) {
208 case V4L2_CID_FLASH_LED_MODE:
209 flash->led_mode = ctrl->val;
210 if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
211 rval = lm3560_mode_ctrl(flash);
212 break;
213
214 case V4L2_CID_FLASH_STROBE_SOURCE:
215 rval = regmap_update_bits(flash->regmap,
216 REG_CONFIG1, 0x04, (ctrl->val) << 2);
217 if (rval < 0)
218 goto err_out;
219 break;
220
221 case V4L2_CID_FLASH_STROBE:
222 if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
223 return -EBUSY;
224 flash->led_mode = V4L2_FLASH_LED_MODE_FLASH;
225 rval = lm3560_mode_ctrl(flash);
226 break;
227
228 case V4L2_CID_FLASH_STROBE_STOP:
229 if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
230 return -EBUSY;
231 flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
232 rval = lm3560_mode_ctrl(flash);
233 break;
234
235 case V4L2_CID_FLASH_TIMEOUT:
236 tout_bits = LM3560_FLASH_TOUT_ms_TO_REG(ctrl->val);
237 rval = regmap_update_bits(flash->regmap,
238 REG_FLASH_TOUT, 0x1f, tout_bits);
239 break;
240
241 case V4L2_CID_FLASH_INTENSITY:
242 rval = lm3560_flash_brt_ctrl(flash, led_no, ctrl->val);
243 break;
244
245 case V4L2_CID_FLASH_TORCH_INTENSITY:
246 rval = lm3560_torch_brt_ctrl(flash, led_no, ctrl->val);
247 break;
248 }
249
250 mutex_unlock(&flash->lock);
251err_out:
252 return rval;
253}
254
255static int lm3560_led1_get_ctrl(struct v4l2_ctrl *ctrl)
256{
257 return lm3560_get_ctrl(ctrl, LM3560_LED1);
258}
259
260static int lm3560_led1_set_ctrl(struct v4l2_ctrl *ctrl)
261{
262 return lm3560_set_ctrl(ctrl, LM3560_LED1);
263}
264
265static int lm3560_led0_get_ctrl(struct v4l2_ctrl *ctrl)
266{
267 return lm3560_get_ctrl(ctrl, LM3560_LED0);
268}
269
270static int lm3560_led0_set_ctrl(struct v4l2_ctrl *ctrl)
271{
272 return lm3560_set_ctrl(ctrl, LM3560_LED0);
273}
274
275static const struct v4l2_ctrl_ops lm3560_led_ctrl_ops[LM3560_LED_MAX] = {
276 [LM3560_LED0] = {
277 .g_volatile_ctrl = lm3560_led0_get_ctrl,
278 .s_ctrl = lm3560_led0_set_ctrl,
279 },
280 [LM3560_LED1] = {
281 .g_volatile_ctrl = lm3560_led1_get_ctrl,
282 .s_ctrl = lm3560_led1_set_ctrl,
283 }
284};
285
286static int lm3560_init_controls(struct lm3560_flash *flash,
287 enum lm3560_led_id led_no)
288{
289 struct v4l2_ctrl *fault;
290 u32 max_flash_brt = flash->pdata->max_flash_brt[led_no];
291 u32 max_torch_brt = flash->pdata->max_torch_brt[led_no];
292 struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no];
293 const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
294
295 v4l2_ctrl_handler_init(hdl, 8);
296 /* flash mode */
297 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
298 V4L2_FLASH_LED_MODE_TORCH, ~0x7,
299 V4L2_FLASH_LED_MODE_NONE);
300 flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
301
302 /* flash source */
303 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,
304 0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
305
306 /* flash strobe */
307 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
308 /* flash strobe stop */
309 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
310
311 /* flash strobe timeout */
312 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
313 LM3560_FLASH_TOUT_MIN,
314 flash->pdata->max_flash_timeout,
315 LM3560_FLASH_TOUT_STEP,
316 flash->pdata->max_flash_timeout);
317
318 /* flash brt */
319 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
320 LM3560_FLASH_BRT_MIN, max_flash_brt,
321 LM3560_FLASH_BRT_STEP, max_flash_brt);
322
323 /* torch brt */
324 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
325 LM3560_TORCH_BRT_MIN, max_torch_brt,
326 LM3560_TORCH_BRT_STEP, max_torch_brt);
327
328 /* fault */
329 fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
330 V4L2_FLASH_FAULT_OVER_VOLTAGE
331 | V4L2_FLASH_FAULT_OVER_TEMPERATURE
332 | V4L2_FLASH_FAULT_SHORT_CIRCUIT
333 | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);
334 if (fault != NULL)
335 fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
336
337 if (hdl->error)
338 return hdl->error;
339
340 flash->subdev_led[led_no].ctrl_handler = hdl;
341 return 0;
342}
343
344/* initialize device */
345static const struct v4l2_subdev_ops lm3560_ops = {
346 .core = NULL,
347};
348
349static const struct regmap_config lm3560_regmap = {
350 .reg_bits = 8,
351 .val_bits = 8,
352 .max_register = 0xFF,
353};
354
355static int lm3560_subdev_init(struct lm3560_flash *flash,
356 enum lm3560_led_id led_no, char *led_name)
357{
358 struct i2c_client *client = to_i2c_client(flash->dev);
359 int rval;
360
361 v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops);
362 flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
363 strcpy(flash->subdev_led[led_no].name, led_name);
364 rval = lm3560_init_controls(flash, led_no);
365 if (rval)
366 goto err_out;
367 rval = media_entity_init(&flash->subdev_led[led_no].entity, 0, NULL, 0);
368 if (rval < 0)
369 goto err_out;
370 flash->subdev_led[led_no].entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
371
372 return rval;
373
374err_out:
375 v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
376 return rval;
377}
378
379static int lm3560_init_device(struct lm3560_flash *flash)
380{
381 int rval;
382 unsigned int reg_val;
383
384 /* set peak current */
385 rval = regmap_update_bits(flash->regmap,
386 REG_FLASH_TOUT, 0x60, flash->pdata->peak);
387 if (rval < 0)
388 return rval;
389 /* output disable */
390 flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
391 rval = lm3560_mode_ctrl(flash);
392 if (rval < 0)
393 return rval;
394 /* Reset faults */
395 rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
396 return rval;
397}
398
399static int lm3560_probe(struct i2c_client *client,
400 const struct i2c_device_id *devid)
401{
402 struct lm3560_flash *flash;
403 struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev);
404 int rval;
405
406 flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
407 if (flash == NULL)
408 return -ENOMEM;
409
410 flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap);
411 if (IS_ERR(flash->regmap)) {
412 rval = PTR_ERR(flash->regmap);
413 return rval;
414 }
415
416 /* if there is no platform data, use chip default value */
417 if (pdata == NULL) {
418 pdata =
419 kzalloc(sizeof(struct lm3560_platform_data), GFP_KERNEL);
420 if (pdata == NULL)
421 return -ENODEV;
422 pdata->peak = LM3560_PEAK_3600mA;
423 pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX;
424 /* led 1 */
425 pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX;
426 pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX;
427 /* led 2 */
428 pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX;
429 pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX;
430 }
431 flash->pdata = pdata;
432 flash->dev = &client->dev;
433 mutex_init(&flash->lock);
434
435 rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0");
436 if (rval < 0)
437 return rval;
438
439 rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1");
440 if (rval < 0)
441 return rval;
442
443 rval = lm3560_init_device(flash);
444 if (rval < 0)
445 return rval;
446
447 return 0;
448}
449
450static int lm3560_remove(struct i2c_client *client)
451{
452 struct v4l2_subdev *subdev = i2c_get_clientdata(client);
453 struct lm3560_flash *flash = container_of(subdev, struct lm3560_flash,
454 subdev_led[LM3560_LED_MAX]);
455 unsigned int i;
456
457 for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) {
458 v4l2_device_unregister_subdev(&flash->subdev_led[i]);
459 v4l2_ctrl_handler_free(&flash->ctrls_led[i]);
460 media_entity_cleanup(&flash->subdev_led[i].entity);
461 }
462
463 return 0;
464}
465
466static const struct i2c_device_id lm3560_id_table[] = {
467 {LM3560_NAME, 0},
468 {}
469};
470
471MODULE_DEVICE_TABLE(i2c, lm3560_id_table);
472
473static struct i2c_driver lm3560_i2c_driver = {
474 .driver = {
475 .name = LM3560_NAME,
476 .pm = NULL,
477 },
478 .probe = lm3560_probe,
479 .remove = lm3560_remove,
480 .id_table = lm3560_id_table,
481};
482
483module_i2c_driver(lm3560_i2c_driver);
484
485MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
486MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
487MODULE_DESCRIPTION("Texas Instruments LM3560 LED flash driver");
488MODULE_LICENSE("GPL");