1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/media/i2c/lm3646.c |
4 | * General device driver for TI lm3646, Dual FLASH LED Driver |
5 | * |
6 | * Copyright (C) 2014 Texas Instruments |
7 | * |
8 | * Contact: Daniel Jeong <gshark.jeong@gmail.com> |
9 | * Ldd-Mlp <ldd-mlp@list.ti.com> |
10 | */ |
11 | |
12 | #include <linux/delay.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/videodev2.h> |
18 | #include <media/i2c/lm3646.h> |
19 | #include <media/v4l2-ctrls.h> |
20 | #include <media/v4l2-device.h> |
21 | |
22 | /* registers definitions */ |
23 | #define REG_ENABLE 0x01 |
24 | #define REG_TORCH_BR 0x05 |
25 | #define REG_FLASH_BR 0x05 |
26 | #define REG_FLASH_TOUT 0x04 |
27 | #define REG_FLAG 0x08 |
28 | #define REG_STROBE_SRC 0x06 |
29 | #define REG_LED1_FLASH_BR 0x06 |
30 | #define REG_LED1_TORCH_BR 0x07 |
31 | |
32 | #define MASK_ENABLE 0x03 |
33 | #define MASK_TORCH_BR 0x70 |
34 | #define MASK_FLASH_BR 0x0F |
35 | #define MASK_FLASH_TOUT 0x07 |
36 | #define MASK_FLAG 0xFF |
37 | #define MASK_STROBE_SRC 0x80 |
38 | |
39 | /* Fault Mask */ |
40 | #define FAULT_TIMEOUT (1<<0) |
41 | #define FAULT_SHORT_CIRCUIT (1<<1) |
42 | #define FAULT_UVLO (1<<2) |
43 | #define FAULT_IVFM (1<<3) |
44 | #define FAULT_OCP (1<<4) |
45 | #define FAULT_OVERTEMP (1<<5) |
46 | #define FAULT_NTC_TRIP (1<<6) |
47 | #define FAULT_OVP (1<<7) |
48 | |
49 | enum led_mode { |
50 | MODE_SHDN = 0x0, |
51 | MODE_TORCH = 0x2, |
52 | MODE_FLASH = 0x3, |
53 | }; |
54 | |
55 | /* |
56 | * struct lm3646_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 controls |
63 | * @subdev_led: V4L2 subdev |
64 | * @mode_reg : mode register value |
65 | */ |
66 | struct lm3646_flash { |
67 | struct device *dev; |
68 | struct lm3646_platform_data *pdata; |
69 | struct regmap *regmap; |
70 | |
71 | struct v4l2_ctrl_handler ctrls_led; |
72 | struct v4l2_subdev subdev_led; |
73 | |
74 | u8 mode_reg; |
75 | }; |
76 | |
77 | #define to_lm3646_flash(_ctrl) \ |
78 | container_of(_ctrl->handler, struct lm3646_flash, ctrls_led) |
79 | |
80 | /* enable mode control */ |
81 | static int lm3646_mode_ctrl(struct lm3646_flash *flash, |
82 | enum v4l2_flash_led_mode led_mode) |
83 | { |
84 | switch (led_mode) { |
85 | case V4L2_FLASH_LED_MODE_NONE: |
86 | return regmap_write(map: flash->regmap, |
87 | REG_ENABLE, val: flash->mode_reg | MODE_SHDN); |
88 | case V4L2_FLASH_LED_MODE_TORCH: |
89 | return regmap_write(map: flash->regmap, |
90 | REG_ENABLE, val: flash->mode_reg | MODE_TORCH); |
91 | case V4L2_FLASH_LED_MODE_FLASH: |
92 | return regmap_write(map: flash->regmap, |
93 | REG_ENABLE, val: flash->mode_reg | MODE_FLASH); |
94 | } |
95 | return -EINVAL; |
96 | } |
97 | |
98 | /* V4L2 controls */ |
99 | static int lm3646_get_ctrl(struct v4l2_ctrl *ctrl) |
100 | { |
101 | struct lm3646_flash *flash = to_lm3646_flash(ctrl); |
102 | unsigned int reg_val; |
103 | int rval; |
104 | |
105 | if (ctrl->id != V4L2_CID_FLASH_FAULT) |
106 | return -EINVAL; |
107 | |
108 | rval = regmap_read(map: flash->regmap, REG_FLAG, val: ®_val); |
109 | if (rval < 0) |
110 | return rval; |
111 | |
112 | ctrl->val = 0; |
113 | if (reg_val & FAULT_TIMEOUT) |
114 | ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT; |
115 | if (reg_val & FAULT_SHORT_CIRCUIT) |
116 | ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; |
117 | if (reg_val & FAULT_UVLO) |
118 | ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE; |
119 | if (reg_val & FAULT_IVFM) |
120 | ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE; |
121 | if (reg_val & FAULT_OCP) |
122 | ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT; |
123 | if (reg_val & FAULT_OVERTEMP) |
124 | ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; |
125 | if (reg_val & FAULT_NTC_TRIP) |
126 | ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE; |
127 | if (reg_val & FAULT_OVP) |
128 | ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl) |
134 | { |
135 | struct lm3646_flash *flash = to_lm3646_flash(ctrl); |
136 | unsigned int reg_val; |
137 | int rval; |
138 | |
139 | switch (ctrl->id) { |
140 | case V4L2_CID_FLASH_LED_MODE: |
141 | |
142 | if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH) |
143 | return lm3646_mode_ctrl(flash, led_mode: ctrl->val); |
144 | /* switch to SHDN mode before flash strobe on */ |
145 | return lm3646_mode_ctrl(flash, led_mode: V4L2_FLASH_LED_MODE_NONE); |
146 | |
147 | case V4L2_CID_FLASH_STROBE_SOURCE: |
148 | return regmap_update_bits(map: flash->regmap, |
149 | REG_STROBE_SRC, MASK_STROBE_SRC, |
150 | val: (ctrl->val) << 7); |
151 | |
152 | case V4L2_CID_FLASH_STROBE: |
153 | |
154 | /* read and check current mode of chip to start flash */ |
155 | rval = regmap_read(map: flash->regmap, REG_ENABLE, val: ®_val); |
156 | if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN)) |
157 | return rval; |
158 | /* flash on */ |
159 | return lm3646_mode_ctrl(flash, led_mode: V4L2_FLASH_LED_MODE_FLASH); |
160 | |
161 | case V4L2_CID_FLASH_STROBE_STOP: |
162 | |
163 | /* |
164 | * flash mode will be turned automatically |
165 | * from FLASH mode to SHDN mode after flash duration timeout |
166 | * read and check current mode of chip to stop flash |
167 | */ |
168 | rval = regmap_read(map: flash->regmap, REG_ENABLE, val: ®_val); |
169 | if (rval < 0) |
170 | return rval; |
171 | if ((reg_val & MASK_ENABLE) == MODE_FLASH) |
172 | return lm3646_mode_ctrl(flash, |
173 | led_mode: V4L2_FLASH_LED_MODE_NONE); |
174 | return rval; |
175 | |
176 | case V4L2_CID_FLASH_TIMEOUT: |
177 | return regmap_update_bits(map: flash->regmap, |
178 | REG_FLASH_TOUT, MASK_FLASH_TOUT, |
179 | LM3646_FLASH_TOUT_ms_TO_REG |
180 | (ctrl->val)); |
181 | |
182 | case V4L2_CID_FLASH_INTENSITY: |
183 | return regmap_update_bits(map: flash->regmap, |
184 | REG_FLASH_BR, MASK_FLASH_BR, |
185 | LM3646_TOTAL_FLASH_BRT_uA_TO_REG |
186 | (ctrl->val)); |
187 | |
188 | case V4L2_CID_FLASH_TORCH_INTENSITY: |
189 | return regmap_update_bits(map: flash->regmap, |
190 | REG_TORCH_BR, MASK_TORCH_BR, |
191 | LM3646_TOTAL_TORCH_BRT_uA_TO_REG |
192 | (ctrl->val) << 4); |
193 | } |
194 | |
195 | return -EINVAL; |
196 | } |
197 | |
198 | static const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = { |
199 | .g_volatile_ctrl = lm3646_get_ctrl, |
200 | .s_ctrl = lm3646_set_ctrl, |
201 | }; |
202 | |
203 | static int lm3646_init_controls(struct lm3646_flash *flash) |
204 | { |
205 | struct v4l2_ctrl *fault; |
206 | struct v4l2_ctrl_handler *hdl = &flash->ctrls_led; |
207 | const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops; |
208 | |
209 | v4l2_ctrl_handler_init(hdl, 8); |
210 | /* flash mode */ |
211 | v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE, |
212 | max: V4L2_FLASH_LED_MODE_TORCH, mask: ~0x7, |
213 | def: V4L2_FLASH_LED_MODE_NONE); |
214 | |
215 | /* flash source */ |
216 | v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE, |
217 | max: 0x1, mask: ~0x3, def: V4L2_FLASH_STROBE_SOURCE_SOFTWARE); |
218 | |
219 | /* flash strobe */ |
220 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, min: 0, max: 0, step: 0, def: 0); |
221 | /* flash strobe stop */ |
222 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, min: 0, max: 0, step: 0, def: 0); |
223 | |
224 | /* flash strobe timeout */ |
225 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, |
226 | LM3646_FLASH_TOUT_MIN, |
227 | LM3646_FLASH_TOUT_MAX, |
228 | LM3646_FLASH_TOUT_STEP, def: flash->pdata->flash_timeout); |
229 | |
230 | /* max flash current */ |
231 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, |
232 | LM3646_TOTAL_FLASH_BRT_MIN, |
233 | LM3646_TOTAL_FLASH_BRT_MAX, |
234 | LM3646_TOTAL_FLASH_BRT_STEP, |
235 | LM3646_TOTAL_FLASH_BRT_MAX); |
236 | |
237 | /* max torch current */ |
238 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, |
239 | LM3646_TOTAL_TORCH_BRT_MIN, |
240 | LM3646_TOTAL_TORCH_BRT_MAX, |
241 | LM3646_TOTAL_TORCH_BRT_STEP, |
242 | LM3646_TOTAL_TORCH_BRT_MAX); |
243 | |
244 | /* fault */ |
245 | fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, min: 0, |
246 | V4L2_FLASH_FAULT_OVER_VOLTAGE |
247 | | V4L2_FLASH_FAULT_OVER_TEMPERATURE |
248 | | V4L2_FLASH_FAULT_SHORT_CIRCUIT |
249 | | V4L2_FLASH_FAULT_TIMEOUT, step: 0, def: 0); |
250 | if (fault != NULL) |
251 | fault->flags |= V4L2_CTRL_FLAG_VOLATILE; |
252 | |
253 | if (hdl->error) |
254 | return hdl->error; |
255 | |
256 | flash->subdev_led.ctrl_handler = hdl; |
257 | return 0; |
258 | } |
259 | |
260 | /* initialize device */ |
261 | static const struct v4l2_subdev_ops lm3646_ops = { |
262 | .core = NULL, |
263 | }; |
264 | |
265 | static const struct regmap_config lm3646_regmap = { |
266 | .reg_bits = 8, |
267 | .val_bits = 8, |
268 | .max_register = 0xFF, |
269 | }; |
270 | |
271 | static int lm3646_subdev_init(struct lm3646_flash *flash) |
272 | { |
273 | struct i2c_client *client = to_i2c_client(flash->dev); |
274 | int rval; |
275 | |
276 | v4l2_i2c_subdev_init(sd: &flash->subdev_led, client, ops: &lm3646_ops); |
277 | flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
278 | strscpy(flash->subdev_led.name, LM3646_NAME, |
279 | sizeof(flash->subdev_led.name)); |
280 | rval = lm3646_init_controls(flash); |
281 | if (rval) |
282 | goto err_out; |
283 | rval = media_entity_pads_init(entity: &flash->subdev_led.entity, num_pads: 0, NULL); |
284 | if (rval < 0) |
285 | goto err_out; |
286 | flash->subdev_led.entity.function = MEDIA_ENT_F_FLASH; |
287 | return rval; |
288 | |
289 | err_out: |
290 | v4l2_ctrl_handler_free(hdl: &flash->ctrls_led); |
291 | return rval; |
292 | } |
293 | |
294 | static int lm3646_init_device(struct lm3646_flash *flash) |
295 | { |
296 | unsigned int reg_val; |
297 | int rval; |
298 | |
299 | /* read the value of mode register to reduce redundant i2c accesses */ |
300 | rval = regmap_read(map: flash->regmap, REG_ENABLE, val: ®_val); |
301 | if (rval < 0) |
302 | return rval; |
303 | flash->mode_reg = reg_val & 0xfc; |
304 | |
305 | /* output disable */ |
306 | rval = lm3646_mode_ctrl(flash, led_mode: V4L2_FLASH_LED_MODE_NONE); |
307 | if (rval < 0) |
308 | return rval; |
309 | |
310 | /* |
311 | * LED1 flash current setting |
312 | * LED2 flash current = Total(Max) flash current - LED1 flash current |
313 | */ |
314 | rval = regmap_update_bits(map: flash->regmap, |
315 | REG_LED1_FLASH_BR, mask: 0x7F, |
316 | LM3646_LED1_FLASH_BRT_uA_TO_REG |
317 | (flash->pdata->led1_flash_brt)); |
318 | |
319 | if (rval < 0) |
320 | return rval; |
321 | |
322 | /* |
323 | * LED1 torch current setting |
324 | * LED2 torch current = Total(Max) torch current - LED1 torch current |
325 | */ |
326 | rval = regmap_update_bits(map: flash->regmap, |
327 | REG_LED1_TORCH_BR, mask: 0x7F, |
328 | LM3646_LED1_TORCH_BRT_uA_TO_REG |
329 | (flash->pdata->led1_torch_brt)); |
330 | if (rval < 0) |
331 | return rval; |
332 | |
333 | /* Reset flag register */ |
334 | return regmap_read(map: flash->regmap, REG_FLAG, val: ®_val); |
335 | } |
336 | |
337 | static int lm3646_probe(struct i2c_client *client) |
338 | { |
339 | struct lm3646_flash *flash; |
340 | struct lm3646_platform_data *pdata = dev_get_platdata(dev: &client->dev); |
341 | int rval; |
342 | |
343 | flash = devm_kzalloc(dev: &client->dev, size: sizeof(*flash), GFP_KERNEL); |
344 | if (flash == NULL) |
345 | return -ENOMEM; |
346 | |
347 | flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap); |
348 | if (IS_ERR(ptr: flash->regmap)) |
349 | return PTR_ERR(ptr: flash->regmap); |
350 | |
351 | /* check device tree if there is no platform data */ |
352 | if (pdata == NULL) { |
353 | pdata = devm_kzalloc(dev: &client->dev, |
354 | size: sizeof(struct lm3646_platform_data), |
355 | GFP_KERNEL); |
356 | if (pdata == NULL) |
357 | return -ENOMEM; |
358 | /* use default data in case of no platform data */ |
359 | pdata->flash_timeout = LM3646_FLASH_TOUT_MAX; |
360 | pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX; |
361 | pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX; |
362 | } |
363 | flash->pdata = pdata; |
364 | flash->dev = &client->dev; |
365 | |
366 | rval = lm3646_subdev_init(flash); |
367 | if (rval < 0) |
368 | return rval; |
369 | |
370 | rval = lm3646_init_device(flash); |
371 | if (rval < 0) |
372 | return rval; |
373 | |
374 | i2c_set_clientdata(client, data: flash); |
375 | |
376 | return 0; |
377 | } |
378 | |
379 | static void lm3646_remove(struct i2c_client *client) |
380 | { |
381 | struct lm3646_flash *flash = i2c_get_clientdata(client); |
382 | |
383 | v4l2_device_unregister_subdev(sd: &flash->subdev_led); |
384 | v4l2_ctrl_handler_free(hdl: &flash->ctrls_led); |
385 | media_entity_cleanup(entity: &flash->subdev_led.entity); |
386 | } |
387 | |
388 | static const struct i2c_device_id lm3646_id_table[] = { |
389 | {LM3646_NAME, 0}, |
390 | {} |
391 | }; |
392 | |
393 | MODULE_DEVICE_TABLE(i2c, lm3646_id_table); |
394 | |
395 | static struct i2c_driver lm3646_i2c_driver = { |
396 | .driver = { |
397 | .name = LM3646_NAME, |
398 | }, |
399 | .probe = lm3646_probe, |
400 | .remove = lm3646_remove, |
401 | .id_table = lm3646_id_table, |
402 | }; |
403 | |
404 | module_i2c_driver(lm3646_i2c_driver); |
405 | |
406 | MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>" ); |
407 | MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>" ); |
408 | MODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver" ); |
409 | MODULE_LICENSE("GPL" ); |
410 | |