summaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c
diff options
context:
space:
mode:
authorDaniel Jeong <gshark.jeong@gmail.com>2014-03-03 04:52:10 -0500
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-03-11 09:00:26 -0400
commitdc76df5d48ba4e8b219269b890bb3043b05a8700 (patch)
tree126797c4135b57e832db940ae77e9168a908d6f0 /drivers/media/i2c
parent935aa6b2e8a911e81baecec0537dd7e478dc8c91 (diff)
[media] lm3646: add new dual LED Flash driver
This patch adds the driver for the LM3646, dual LED Flash driver. The LM3646 has two 1.5A sync. boost converter with dual white current source. It is controlled via an I2C compatible interface. Each flash brightness, torch brightness and enable/disable can be controlled. Under voltage, input voltage monitor and thermal threshhold Faults are added. Please refer the datasheet http://www.ti.com/lit/ds/snvs962/snvs962.pdf Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r--drivers/media/i2c/Kconfig9
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/lm3646.c414
3 files changed, 424 insertions, 0 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 4aa9c5311cc5..c7f2823ac81f 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -629,6 +629,15 @@ config VIDEO_LM3560
629 This is a driver for the lm3560 dual flash controllers. It controls 629 This is a driver for the lm3560 dual flash controllers. It controls
630 flash, torch LEDs. 630 flash, torch LEDs.
631 631
632config VIDEO_LM3646
633 tristate "LM3646 dual flash driver support"
634 depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
635 depends on MEDIA_CAMERA_SUPPORT
636 select REGMAP_I2C
637 ---help---
638 This is a driver for the lm3646 dual flash controllers. It controls
639 flash, torch LEDs.
640
632comment "Video improvement chips" 641comment "Video improvement chips"
633 642
634config VIDEO_UPD64031A 643config VIDEO_UPD64031A
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 48888ae876fb..01b6bfc0db5b 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
72obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o 72obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
73obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o 73obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
74obj-$(CONFIG_VIDEO_LM3560) += lm3560.o 74obj-$(CONFIG_VIDEO_LM3560) += lm3560.o
75obj-$(CONFIG_VIDEO_LM3646) += lm3646.o
75obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o 76obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
76obj-$(CONFIG_VIDEO_AK881X) += ak881x.o 77obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
77obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o 78obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c
new file mode 100644
index 000000000000..626fb4679c02
--- /dev/null
+++ b/drivers/media/i2c/lm3646.c
@@ -0,0 +1,414 @@
1/*
2 * drivers/media/i2c/lm3646.c
3 * General device driver for TI lm3646, Dual FLASH LED Driver
4 *
5 * Copyright (C) 2014 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
15#include <linux/delay.h>
16#include <linux/i2c.h>
17#include <linux/module.h>
18#include <linux/slab.h>
19#include <linux/regmap.h>
20#include <linux/videodev2.h>
21#include <media/lm3646.h>
22#include <media/v4l2-ctrls.h>
23#include <media/v4l2-device.h>
24
25/* registers definitions */
26#define REG_ENABLE 0x01
27#define REG_TORCH_BR 0x05
28#define REG_FLASH_BR 0x05
29#define REG_FLASH_TOUT 0x04
30#define REG_FLAG 0x08
31#define REG_STROBE_SRC 0x06
32#define REG_LED1_FLASH_BR 0x06
33#define REG_LED1_TORCH_BR 0x07
34
35#define MASK_ENABLE 0x03
36#define MASK_TORCH_BR 0x70
37#define MASK_FLASH_BR 0x0F
38#define MASK_FLASH_TOUT 0x07
39#define MASK_FLAG 0xFF
40#define MASK_STROBE_SRC 0x80
41
42/* Fault Mask */
43#define FAULT_TIMEOUT (1<<0)
44#define FAULT_SHORT_CIRCUIT (1<<1)
45#define FAULT_UVLO (1<<2)
46#define FAULT_IVFM (1<<3)
47#define FAULT_OCP (1<<4)
48#define FAULT_OVERTEMP (1<<5)
49#define FAULT_NTC_TRIP (1<<6)
50#define FAULT_OVP (1<<7)
51
52enum led_mode {
53 MODE_SHDN = 0x0,
54 MODE_TORCH = 0x2,
55 MODE_FLASH = 0x3,
56};
57
58/*
59 * struct lm3646_flash
60 *
61 * @pdata: platform data
62 * @regmap: reg. map for i2c
63 * @lock: muxtex for serial access.
64 * @led_mode: V4L2 LED mode
65 * @ctrls_led: V4L2 contols
66 * @subdev_led: V4L2 subdev
67 * @mode_reg : mode register value
68 */
69struct lm3646_flash {
70 struct device *dev;
71 struct lm3646_platform_data *pdata;
72 struct regmap *regmap;
73
74 struct v4l2_ctrl_handler ctrls_led;
75 struct v4l2_subdev subdev_led;
76
77 u8 mode_reg;
78};
79
80#define to_lm3646_flash(_ctrl) \
81 container_of(_ctrl->handler, struct lm3646_flash, ctrls_led)
82
83/* enable mode control */
84static int lm3646_mode_ctrl(struct lm3646_flash *flash,
85 enum v4l2_flash_led_mode led_mode)
86{
87 switch (led_mode) {
88 case V4L2_FLASH_LED_MODE_NONE:
89 return regmap_write(flash->regmap,
90 REG_ENABLE, flash->mode_reg | MODE_SHDN);
91 case V4L2_FLASH_LED_MODE_TORCH:
92 return regmap_write(flash->regmap,
93 REG_ENABLE, flash->mode_reg | MODE_TORCH);
94 case V4L2_FLASH_LED_MODE_FLASH:
95 return regmap_write(flash->regmap,
96 REG_ENABLE, flash->mode_reg | MODE_FLASH);
97 }
98 return -EINVAL;
99}
100
101/* V4L2 controls */
102static int lm3646_get_ctrl(struct v4l2_ctrl *ctrl)
103{
104 struct lm3646_flash *flash = to_lm3646_flash(ctrl);
105 unsigned int reg_val;
106 int rval;
107
108 if (ctrl->id != V4L2_CID_FLASH_FAULT)
109 return -EINVAL;
110
111 rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
112 if (rval < 0)
113 return rval;
114
115 ctrl->val = 0;
116 if (reg_val & FAULT_TIMEOUT)
117 ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT;
118 if (reg_val & FAULT_SHORT_CIRCUIT)
119 ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
120 if (reg_val & FAULT_UVLO)
121 ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE;
122 if (reg_val & FAULT_IVFM)
123 ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE;
124 if (reg_val & FAULT_OCP)
125 ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT;
126 if (reg_val & FAULT_OVERTEMP)
127 ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
128 if (reg_val & FAULT_NTC_TRIP)
129 ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE;
130 if (reg_val & FAULT_OVP)
131 ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
132
133 return 0;
134}
135
136static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl)
137{
138 struct lm3646_flash *flash = to_lm3646_flash(ctrl);
139 unsigned int reg_val;
140 int rval = -EINVAL;
141
142 switch (ctrl->id) {
143 case V4L2_CID_FLASH_LED_MODE:
144
145 if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH)
146 return lm3646_mode_ctrl(flash, ctrl->val);
147 /* switch to SHDN mode before flash strobe on */
148 return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
149
150 case V4L2_CID_FLASH_STROBE_SOURCE:
151 return regmap_update_bits(flash->regmap,
152 REG_STROBE_SRC, MASK_STROBE_SRC,
153 (ctrl->val) << 7);
154
155 case V4L2_CID_FLASH_STROBE:
156
157 /* read and check current mode of chip to start flash */
158 rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
159 if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN))
160 return rval;
161 /* flash on */
162 return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_FLASH);
163
164 case V4L2_CID_FLASH_STROBE_STOP:
165
166 /*
167 * flash mode will be turned automatically
168 * from FLASH mode to SHDN mode after flash duration timeout
169 * read and check current mode of chip to stop flash
170 */
171 rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
172 if (rval < 0)
173 return rval;
174 if ((reg_val & MASK_ENABLE) == MODE_FLASH)
175 return lm3646_mode_ctrl(flash,
176 V4L2_FLASH_LED_MODE_NONE);
177 return rval;
178
179 case V4L2_CID_FLASH_TIMEOUT:
180 return regmap_update_bits(flash->regmap,
181 REG_FLASH_TOUT, MASK_FLASH_TOUT,
182 LM3646_FLASH_TOUT_ms_TO_REG
183 (ctrl->val));
184
185 case V4L2_CID_FLASH_INTENSITY:
186 return regmap_update_bits(flash->regmap,
187 REG_FLASH_BR, MASK_FLASH_BR,
188 LM3646_TOTAL_FLASH_BRT_uA_TO_REG
189 (ctrl->val));
190
191 case V4L2_CID_FLASH_TORCH_INTENSITY:
192 return regmap_update_bits(flash->regmap,
193 REG_TORCH_BR, MASK_TORCH_BR,
194 LM3646_TOTAL_TORCH_BRT_uA_TO_REG
195 (ctrl->val) << 4);
196 }
197
198 return -EINVAL;
199}
200
201static const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = {
202 .g_volatile_ctrl = lm3646_get_ctrl,
203 .s_ctrl = lm3646_set_ctrl,
204};
205
206static int lm3646_init_controls(struct lm3646_flash *flash)
207{
208 struct v4l2_ctrl *fault;
209 struct v4l2_ctrl_handler *hdl = &flash->ctrls_led;
210 const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops;
211
212 v4l2_ctrl_handler_init(hdl, 8);
213 /* flash mode */
214 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
215 V4L2_FLASH_LED_MODE_TORCH, ~0x7,
216 V4L2_FLASH_LED_MODE_NONE);
217
218 /* flash source */
219 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,
220 0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
221
222 /* flash strobe */
223 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
224 /* flash strobe stop */
225 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
226
227 /* flash strobe timeout */
228 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
229 LM3646_FLASH_TOUT_MIN,
230 LM3646_FLASH_TOUT_MAX,
231 LM3646_FLASH_TOUT_STEP, flash->pdata->flash_timeout);
232
233 /* max flash current */
234 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
235 LM3646_TOTAL_FLASH_BRT_MIN,
236 LM3646_TOTAL_FLASH_BRT_MAX,
237 LM3646_TOTAL_FLASH_BRT_STEP,
238 LM3646_TOTAL_FLASH_BRT_MAX);
239
240 /* max torch current */
241 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
242 LM3646_TOTAL_TORCH_BRT_MIN,
243 LM3646_TOTAL_TORCH_BRT_MAX,
244 LM3646_TOTAL_TORCH_BRT_STEP,
245 LM3646_TOTAL_TORCH_BRT_MAX);
246
247 /* fault */
248 fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
249 V4L2_FLASH_FAULT_OVER_VOLTAGE
250 | V4L2_FLASH_FAULT_OVER_TEMPERATURE
251 | V4L2_FLASH_FAULT_SHORT_CIRCUIT
252 | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);
253 if (fault != NULL)
254 fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
255
256 if (hdl->error)
257 return hdl->error;
258
259 flash->subdev_led.ctrl_handler = hdl;
260 return 0;
261}
262
263/* initialize device */
264static const struct v4l2_subdev_ops lm3646_ops = {
265 .core = NULL,
266};
267
268static const struct regmap_config lm3646_regmap = {
269 .reg_bits = 8,
270 .val_bits = 8,
271 .max_register = 0xFF,
272};
273
274static int lm3646_subdev_init(struct lm3646_flash *flash)
275{
276 struct i2c_client *client = to_i2c_client(flash->dev);
277 int rval;
278
279 v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops);
280 flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
281 strcpy(flash->subdev_led.name, LM3646_NAME);
282 rval = lm3646_init_controls(flash);
283 if (rval)
284 goto err_out;
285 rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0);
286 if (rval < 0)
287 goto err_out;
288 flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
289 return rval;
290
291err_out:
292 v4l2_ctrl_handler_free(&flash->ctrls_led);
293 return rval;
294}
295
296static int lm3646_init_device(struct lm3646_flash *flash)
297{
298 unsigned int reg_val;
299 int rval;
300
301 /* read the value of mode register to reduce redundant i2c accesses */
302 rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
303 if (rval < 0)
304 return rval;
305 flash->mode_reg = reg_val & 0xfc;
306
307 /* output disable */
308 rval = lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
309 if (rval < 0)
310 return rval;
311
312 /*
313 * LED1 flash current setting
314 * LED2 flash current = Total(Max) flash current - LED1 flash current
315 */
316 rval = regmap_update_bits(flash->regmap,
317 REG_LED1_FLASH_BR, 0x7F,
318 LM3646_LED1_FLASH_BRT_uA_TO_REG
319 (flash->pdata->led1_flash_brt));
320
321 if (rval < 0)
322 return rval;
323
324 /*
325 * LED1 torch current setting
326 * LED2 torch current = Total(Max) torch current - LED1 torch current
327 */
328 rval = regmap_update_bits(flash->regmap,
329 REG_LED1_TORCH_BR, 0x7F,
330 LM3646_LED1_TORCH_BRT_uA_TO_REG
331 (flash->pdata->led1_torch_brt));
332 if (rval < 0)
333 return rval;
334
335 /* Reset flag register */
336 return regmap_read(flash->regmap, REG_FLAG, &reg_val);
337}
338
339static int lm3646_probe(struct i2c_client *client,
340 const struct i2c_device_id *devid)
341{
342 struct lm3646_flash *flash;
343 struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev);
344 int rval;
345
346 flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
347 if (flash == NULL)
348 return -ENOMEM;
349
350 flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap);
351 if (IS_ERR(flash->regmap))
352 return PTR_ERR(flash->regmap);
353
354 /* check device tree if there is no platform data */
355 if (pdata == NULL) {
356 pdata = devm_kzalloc(&client->dev,
357 sizeof(struct lm3646_platform_data),
358 GFP_KERNEL);
359 if (pdata == NULL)
360 return -ENOMEM;
361 /* use default data in case of no platform data */
362 pdata->flash_timeout = LM3646_FLASH_TOUT_MAX;
363 pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX;
364 pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX;
365 }
366 flash->pdata = pdata;
367 flash->dev = &client->dev;
368
369 rval = lm3646_subdev_init(flash);
370 if (rval < 0)
371 return rval;
372
373 rval = lm3646_init_device(flash);
374 if (rval < 0)
375 return rval;
376
377 i2c_set_clientdata(client, flash);
378
379 return 0;
380}
381
382static int lm3646_remove(struct i2c_client *client)
383{
384 struct lm3646_flash *flash = i2c_get_clientdata(client);
385
386 v4l2_device_unregister_subdev(&flash->subdev_led);
387 v4l2_ctrl_handler_free(&flash->ctrls_led);
388 media_entity_cleanup(&flash->subdev_led.entity);
389
390 return 0;
391}
392
393static const struct i2c_device_id lm3646_id_table[] = {
394 {LM3646_NAME, 0},
395 {}
396};
397
398MODULE_DEVICE_TABLE(i2c, lm3646_id_table);
399
400static struct i2c_driver lm3646_i2c_driver = {
401 .driver = {
402 .name = LM3646_NAME,
403 },
404 .probe = lm3646_probe,
405 .remove = lm3646_remove,
406 .id_table = lm3646_id_table,
407};
408
409module_i2c_driver(lm3646_i2c_driver);
410
411MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
412MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
413MODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver");
414MODULE_LICENSE("GPL");