Commit | Line | Data |
---|---|---|
42bd6f59 JA |
1 | /* |
2 | * V4L2 flash LED sub-device registration helpers. | |
3 | * | |
4 | * Copyright (C) 2015 Samsung Electronics Co., Ltd | |
5 | * Author: Jacek Anaszewski <j.anaszewski@samsung.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/led-class-flash.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/mutex.h> | |
15 | #include <linux/of.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/types.h> | |
18 | #include <media/v4l2-flash-led-class.h> | |
19 | ||
20 | #define has_flash_op(v4l2_flash, op) \ | |
21 | (v4l2_flash && v4l2_flash->ops->op) | |
22 | ||
23 | #define call_flash_op(v4l2_flash, op, arg) \ | |
24 | (has_flash_op(v4l2_flash, op) ? \ | |
25 | v4l2_flash->ops->op(v4l2_flash, arg) : \ | |
26 | -EINVAL) | |
27 | ||
28 | enum ctrl_init_data_id { | |
29 | LED_MODE, | |
30 | TORCH_INTENSITY, | |
31 | FLASH_INTENSITY, | |
32 | INDICATOR_INTENSITY, | |
33 | FLASH_TIMEOUT, | |
34 | STROBE_SOURCE, | |
35 | /* | |
36 | * Only above values are applicable to | |
37 | * the 'ctrls' array in the struct v4l2_flash. | |
38 | */ | |
39 | FLASH_STROBE, | |
40 | STROBE_STOP, | |
41 | STROBE_STATUS, | |
42 | FLASH_FAULT, | |
43 | NUM_FLASH_CTRLS, | |
44 | }; | |
45 | ||
46 | static enum led_brightness __intensity_to_led_brightness( | |
47 | struct v4l2_ctrl *ctrl, s32 intensity) | |
48 | { | |
49 | intensity -= ctrl->minimum; | |
50 | intensity /= (u32) ctrl->step; | |
51 | ||
52 | /* | |
53 | * Indicator LEDs, unlike torch LEDs, are turned on/off basing on | |
54 | * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. | |
55 | * Therefore it must be possible to set it to 0 level which in | |
56 | * the LED subsystem reflects LED_OFF state. | |
57 | */ | |
58 | if (ctrl->minimum) | |
59 | ++intensity; | |
60 | ||
61 | return intensity; | |
62 | } | |
63 | ||
64 | static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl, | |
65 | enum led_brightness brightness) | |
66 | { | |
67 | /* | |
68 | * Indicator LEDs, unlike torch LEDs, are turned on/off basing on | |
69 | * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. | |
70 | * Do not decrement brightness read from the LED subsystem for | |
71 | * indicator LED as it may equal 0. For torch LEDs this function | |
72 | * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the | |
73 | * brightness read is guaranteed to be greater than 0. In the mode | |
74 | * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used. | |
75 | */ | |
76 | if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY) | |
77 | --brightness; | |
78 | ||
79 | return (brightness * ctrl->step) + ctrl->minimum; | |
80 | } | |
81 | ||
82 | static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, | |
83 | struct v4l2_ctrl *ctrl) | |
84 | { | |
85 | struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; | |
86 | enum led_brightness brightness; | |
87 | ||
88 | if (has_flash_op(v4l2_flash, intensity_to_led_brightness)) | |
89 | brightness = call_flash_op(v4l2_flash, | |
90 | intensity_to_led_brightness, | |
91 | ctrl->val); | |
92 | else | |
93 | brightness = __intensity_to_led_brightness(ctrl, ctrl->val); | |
94 | /* | |
95 | * In case a LED Flash class driver provides ops for custom | |
96 | * brightness <-> intensity conversion, it also must have defined | |
97 | * related v4l2 control step == 1. In such a case a backward conversion | |
98 | * from led brightness to v4l2 intensity is required to find out the | |
99 | * the aligned intensity value. | |
100 | */ | |
101 | if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) | |
102 | ctrl->val = call_flash_op(v4l2_flash, | |
103 | led_brightness_to_intensity, | |
104 | brightness); | |
105 | ||
106 | if (ctrl == ctrls[TORCH_INTENSITY]) { | |
107 | if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) | |
108 | return; | |
109 | ||
110 | led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, | |
111 | brightness); | |
112 | } else { | |
113 | led_set_brightness(&v4l2_flash->iled_cdev->led_cdev, | |
114 | brightness); | |
115 | } | |
116 | } | |
117 | ||
118 | static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, | |
119 | struct v4l2_ctrl *ctrl) | |
120 | { | |
121 | struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; | |
122 | struct led_classdev *led_cdev; | |
123 | int ret; | |
124 | ||
125 | if (ctrl == ctrls[TORCH_INTENSITY]) { | |
126 | /* | |
127 | * Update torch brightness only if in TORCH_MODE. In other modes | |
128 | * torch led is turned off, which would spuriously inform the | |
129 | * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value | |
130 | * has changed to 0. | |
131 | */ | |
132 | if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) | |
133 | return 0; | |
134 | led_cdev = &v4l2_flash->fled_cdev->led_cdev; | |
135 | } else { | |
136 | led_cdev = &v4l2_flash->iled_cdev->led_cdev; | |
137 | } | |
138 | ||
139 | ret = led_update_brightness(led_cdev); | |
140 | if (ret < 0) | |
141 | return ret; | |
142 | ||
143 | if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) | |
144 | ctrl->val = call_flash_op(v4l2_flash, | |
145 | led_brightness_to_intensity, | |
146 | led_cdev->brightness); | |
147 | else | |
148 | ctrl->val = __led_brightness_to_intensity(ctrl, | |
149 | led_cdev->brightness); | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c) | |
155 | { | |
156 | struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); | |
157 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | |
158 | bool is_strobing; | |
159 | int ret; | |
160 | ||
161 | switch (c->id) { | |
162 | case V4L2_CID_FLASH_TORCH_INTENSITY: | |
163 | case V4L2_CID_FLASH_INDICATOR_INTENSITY: | |
164 | return v4l2_flash_update_led_brightness(v4l2_flash, c); | |
165 | case V4L2_CID_FLASH_INTENSITY: | |
166 | ret = led_update_flash_brightness(fled_cdev); | |
167 | if (ret < 0) | |
168 | return ret; | |
169 | /* | |
170 | * No conversion is needed as LED Flash class also uses | |
171 | * microamperes for flash intensity units. | |
172 | */ | |
173 | c->val = fled_cdev->brightness.val; | |
174 | return 0; | |
175 | case V4L2_CID_FLASH_STROBE_STATUS: | |
176 | ret = led_get_flash_strobe(fled_cdev, &is_strobing); | |
177 | if (ret < 0) | |
178 | return ret; | |
179 | c->val = is_strobing; | |
180 | return 0; | |
181 | case V4L2_CID_FLASH_FAULT: | |
182 | /* LED faults map directly to V4L2 flash faults */ | |
183 | return led_get_flash_fault(fled_cdev, &c->val); | |
184 | default: | |
185 | return -EINVAL; | |
186 | } | |
187 | } | |
188 | ||
189 | static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls) | |
190 | { | |
191 | return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) || | |
192 | (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val != | |
193 | V4L2_FLASH_STROBE_SOURCE_SOFTWARE))); | |
194 | } | |
195 | ||
196 | static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) | |
197 | { | |
198 | struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); | |
199 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | |
200 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | |
201 | struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; | |
202 | bool external_strobe; | |
203 | int ret = 0; | |
204 | ||
205 | switch (c->id) { | |
206 | case V4L2_CID_FLASH_LED_MODE: | |
207 | switch (c->val) { | |
208 | case V4L2_FLASH_LED_MODE_NONE: | |
209 | led_set_brightness(led_cdev, LED_OFF); | |
210 | return led_set_flash_strobe(fled_cdev, false); | |
211 | case V4L2_FLASH_LED_MODE_FLASH: | |
212 | /* Turn the torch LED off */ | |
213 | led_set_brightness(led_cdev, LED_OFF); | |
214 | if (ctrls[STROBE_SOURCE]) { | |
215 | external_strobe = (ctrls[STROBE_SOURCE]->val == | |
216 | V4L2_FLASH_STROBE_SOURCE_EXTERNAL); | |
217 | ||
218 | ret = call_flash_op(v4l2_flash, | |
219 | external_strobe_set, | |
220 | external_strobe); | |
221 | } | |
222 | return ret; | |
223 | case V4L2_FLASH_LED_MODE_TORCH: | |
224 | if (ctrls[STROBE_SOURCE]) { | |
225 | ret = call_flash_op(v4l2_flash, | |
226 | external_strobe_set, | |
227 | false); | |
228 | if (ret < 0) | |
229 | return ret; | |
230 | } | |
231 | /* Stop flash strobing */ | |
232 | ret = led_set_flash_strobe(fled_cdev, false); | |
233 | if (ret < 0) | |
234 | return ret; | |
235 | ||
236 | v4l2_flash_set_led_brightness(v4l2_flash, | |
237 | ctrls[TORCH_INTENSITY]); | |
238 | return 0; | |
239 | } | |
240 | break; | |
241 | case V4L2_CID_FLASH_STROBE_SOURCE: | |
242 | external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL); | |
243 | /* | |
244 | * For some hardware arrangements setting strobe source may | |
245 | * affect torch mode. Therefore, if not in the flash mode, | |
246 | * cache only this setting. It will be applied upon switching | |
247 | * to flash mode. | |
248 | */ | |
249 | if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) | |
250 | return 0; | |
251 | ||
252 | return call_flash_op(v4l2_flash, external_strobe_set, | |
253 | external_strobe); | |
254 | case V4L2_CID_FLASH_STROBE: | |
255 | if (__software_strobe_mode_inactive(ctrls)) | |
256 | return -EBUSY; | |
257 | return led_set_flash_strobe(fled_cdev, true); | |
258 | case V4L2_CID_FLASH_STROBE_STOP: | |
259 | if (__software_strobe_mode_inactive(ctrls)) | |
260 | return -EBUSY; | |
261 | return led_set_flash_strobe(fled_cdev, false); | |
262 | case V4L2_CID_FLASH_TIMEOUT: | |
263 | /* | |
264 | * No conversion is needed as LED Flash class also uses | |
265 | * microseconds for flash timeout units. | |
266 | */ | |
267 | return led_set_flash_timeout(fled_cdev, c->val); | |
268 | case V4L2_CID_FLASH_INTENSITY: | |
269 | /* | |
270 | * No conversion is needed as LED Flash class also uses | |
271 | * microamperes for flash intensity units. | |
272 | */ | |
273 | return led_set_flash_brightness(fled_cdev, c->val); | |
274 | case V4L2_CID_FLASH_TORCH_INTENSITY: | |
275 | case V4L2_CID_FLASH_INDICATOR_INTENSITY: | |
276 | v4l2_flash_set_led_brightness(v4l2_flash, c); | |
277 | return 0; | |
278 | } | |
279 | ||
280 | return -EINVAL; | |
281 | } | |
282 | ||
283 | static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = { | |
284 | .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl, | |
285 | .s_ctrl = v4l2_flash_s_ctrl, | |
286 | }; | |
287 | ||
288 | static void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s, | |
289 | struct v4l2_ctrl_config *c) | |
290 | { | |
291 | c->min = s->min; | |
292 | c->max = s->max; | |
293 | c->step = s->step; | |
294 | c->def = s->val; | |
295 | } | |
296 | ||
297 | static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash, | |
298 | struct v4l2_flash_config *flash_cfg, | |
299 | struct v4l2_flash_ctrl_data *ctrl_init_data) | |
300 | { | |
301 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | |
302 | const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops; | |
303 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | |
304 | struct v4l2_ctrl_config *ctrl_cfg; | |
305 | u32 mask; | |
306 | ||
307 | /* Init FLASH_FAULT ctrl data */ | |
308 | if (flash_cfg->flash_faults) { | |
309 | ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT; | |
310 | ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config; | |
311 | ctrl_cfg->id = V4L2_CID_FLASH_FAULT; | |
312 | ctrl_cfg->max = flash_cfg->flash_faults; | |
313 | ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | | |
314 | V4L2_CTRL_FLAG_READ_ONLY; | |
315 | } | |
316 | ||
317 | /* Init FLASH_LED_MODE ctrl data */ | |
318 | mask = 1 << V4L2_FLASH_LED_MODE_NONE | | |
319 | 1 << V4L2_FLASH_LED_MODE_TORCH; | |
320 | if (led_cdev->flags & LED_DEV_CAP_FLASH) | |
321 | mask |= 1 << V4L2_FLASH_LED_MODE_FLASH; | |
322 | ||
323 | ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE; | |
324 | ctrl_cfg = &ctrl_init_data[LED_MODE].config; | |
325 | ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE; | |
326 | ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH; | |
327 | ctrl_cfg->menu_skip_mask = ~mask; | |
328 | ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE; | |
329 | ctrl_cfg->flags = 0; | |
330 | ||
331 | /* Init TORCH_INTENSITY ctrl data */ | |
332 | ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY; | |
333 | ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config; | |
334 | __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg); | |
335 | ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY; | |
336 | ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | | |
337 | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; | |
338 | ||
339 | /* Init INDICATOR_INTENSITY ctrl data */ | |
340 | if (v4l2_flash->iled_cdev) { | |
341 | ctrl_init_data[INDICATOR_INTENSITY].cid = | |
342 | V4L2_CID_FLASH_INDICATOR_INTENSITY; | |
343 | ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config; | |
344 | __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity, | |
345 | ctrl_cfg); | |
346 | ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY; | |
347 | ctrl_cfg->min = 0; | |
348 | ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | | |
349 | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; | |
350 | } | |
351 | ||
352 | if (!(led_cdev->flags & LED_DEV_CAP_FLASH)) | |
353 | return; | |
354 | ||
355 | /* Init FLASH_STROBE ctrl data */ | |
356 | ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE; | |
357 | ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config; | |
358 | ctrl_cfg->id = V4L2_CID_FLASH_STROBE; | |
359 | ||
360 | /* Init STROBE_STOP ctrl data */ | |
361 | ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP; | |
362 | ctrl_cfg = &ctrl_init_data[STROBE_STOP].config; | |
363 | ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP; | |
364 | ||
365 | /* Init FLASH_STROBE_SOURCE ctrl data */ | |
366 | if (flash_cfg->has_external_strobe) { | |
367 | mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) | | |
368 | (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL); | |
369 | ctrl_init_data[STROBE_SOURCE].cid = | |
370 | V4L2_CID_FLASH_STROBE_SOURCE; | |
371 | ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config; | |
372 | ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE; | |
373 | ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL; | |
374 | ctrl_cfg->menu_skip_mask = ~mask; | |
375 | ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; | |
376 | } | |
377 | ||
378 | /* Init STROBE_STATUS ctrl data */ | |
379 | if (fled_cdev_ops->strobe_get) { | |
380 | ctrl_init_data[STROBE_STATUS].cid = | |
381 | V4L2_CID_FLASH_STROBE_STATUS; | |
382 | ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config; | |
383 | ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS; | |
384 | ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | | |
385 | V4L2_CTRL_FLAG_READ_ONLY; | |
386 | } | |
387 | ||
388 | /* Init FLASH_TIMEOUT ctrl data */ | |
389 | if (fled_cdev_ops->timeout_set) { | |
390 | ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT; | |
391 | ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config; | |
392 | __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg); | |
393 | ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT; | |
394 | } | |
395 | ||
396 | /* Init FLASH_INTENSITY ctrl data */ | |
397 | if (fled_cdev_ops->flash_brightness_set) { | |
398 | ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY; | |
399 | ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config; | |
400 | __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg); | |
401 | ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY; | |
402 | ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | | |
403 | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; | |
404 | } | |
405 | } | |
406 | ||
407 | static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash, | |
408 | struct v4l2_flash_config *flash_cfg) | |
409 | ||
410 | { | |
411 | struct v4l2_flash_ctrl_data *ctrl_init_data; | |
412 | struct v4l2_ctrl *ctrl; | |
413 | struct v4l2_ctrl_config *ctrl_cfg; | |
414 | int i, ret, num_ctrls = 0; | |
415 | ||
416 | v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev, | |
417 | sizeof(*v4l2_flash->ctrls) * | |
418 | (STROBE_SOURCE + 1), GFP_KERNEL); | |
419 | if (!v4l2_flash->ctrls) | |
420 | return -ENOMEM; | |
421 | ||
422 | /* allocate memory dynamically so as not to exceed stack frame size */ | |
423 | ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data), | |
424 | GFP_KERNEL); | |
425 | if (!ctrl_init_data) | |
426 | return -ENOMEM; | |
427 | ||
428 | __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data); | |
429 | ||
430 | for (i = 0; i < NUM_FLASH_CTRLS; ++i) | |
431 | if (ctrl_init_data[i].cid) | |
432 | ++num_ctrls; | |
433 | ||
434 | v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls); | |
435 | ||
436 | for (i = 0; i < NUM_FLASH_CTRLS; ++i) { | |
437 | ctrl_cfg = &ctrl_init_data[i].config; | |
438 | if (!ctrl_init_data[i].cid) | |
439 | continue; | |
440 | ||
441 | if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE || | |
442 | ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE) | |
443 | ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl, | |
444 | &v4l2_flash_ctrl_ops, | |
445 | ctrl_cfg->id, | |
446 | ctrl_cfg->max, | |
447 | ctrl_cfg->menu_skip_mask, | |
448 | ctrl_cfg->def); | |
449 | else | |
450 | ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, | |
451 | &v4l2_flash_ctrl_ops, | |
452 | ctrl_cfg->id, | |
453 | ctrl_cfg->min, | |
454 | ctrl_cfg->max, | |
455 | ctrl_cfg->step, | |
456 | ctrl_cfg->def); | |
457 | ||
458 | if (ctrl) | |
459 | ctrl->flags |= ctrl_cfg->flags; | |
460 | ||
461 | if (i <= STROBE_SOURCE) | |
462 | v4l2_flash->ctrls[i] = ctrl; | |
463 | } | |
464 | ||
465 | kfree(ctrl_init_data); | |
466 | ||
467 | if (v4l2_flash->hdl.error) { | |
468 | ret = v4l2_flash->hdl.error; | |
469 | goto error_free_handler; | |
470 | } | |
471 | ||
472 | v4l2_ctrl_handler_setup(&v4l2_flash->hdl); | |
473 | ||
474 | v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl; | |
475 | ||
476 | return 0; | |
477 | ||
478 | error_free_handler: | |
479 | v4l2_ctrl_handler_free(&v4l2_flash->hdl); | |
480 | return ret; | |
481 | } | |
482 | ||
483 | static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) | |
484 | { | |
485 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | |
486 | struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; | |
487 | int ret = 0; | |
488 | ||
489 | v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]); | |
490 | ||
491 | if (ctrls[INDICATOR_INTENSITY]) | |
492 | v4l2_flash_set_led_brightness(v4l2_flash, | |
493 | ctrls[INDICATOR_INTENSITY]); | |
494 | ||
495 | if (ctrls[FLASH_TIMEOUT]) { | |
496 | ret = led_set_flash_timeout(fled_cdev, | |
497 | ctrls[FLASH_TIMEOUT]->val); | |
498 | if (ret < 0) | |
499 | return ret; | |
500 | } | |
501 | ||
502 | if (ctrls[FLASH_INTENSITY]) { | |
503 | ret = led_set_flash_brightness(fled_cdev, | |
504 | ctrls[FLASH_INTENSITY]->val); | |
505 | if (ret < 0) | |
506 | return ret; | |
507 | } | |
508 | ||
509 | /* | |
510 | * For some hardware arrangements setting strobe source may affect | |
511 | * torch mode. Synchronize strobe source setting only if not in torch | |
512 | * mode. For torch mode case it will get synchronized upon switching | |
513 | * to flash mode. | |
514 | */ | |
515 | if (ctrls[STROBE_SOURCE] && | |
516 | ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) | |
517 | ret = call_flash_op(v4l2_flash, external_strobe_set, | |
518 | ctrls[STROBE_SOURCE]->val); | |
519 | ||
520 | return ret; | |
521 | } | |
522 | ||
523 | /* | |
524 | * V4L2 subdev internal operations | |
525 | */ | |
526 | ||
527 | static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
528 | { | |
529 | struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); | |
530 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | |
531 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | |
532 | struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev; | |
533 | struct led_classdev *led_cdev_ind = NULL; | |
534 | int ret = 0; | |
535 | ||
536 | if (!v4l2_fh_is_singular(&fh->vfh)) | |
537 | return 0; | |
538 | ||
539 | mutex_lock(&led_cdev->led_access); | |
540 | ||
541 | led_sysfs_disable(led_cdev); | |
542 | led_trigger_remove(led_cdev); | |
543 | ||
544 | mutex_unlock(&led_cdev->led_access); | |
545 | ||
546 | if (iled_cdev) { | |
547 | led_cdev_ind = &iled_cdev->led_cdev; | |
548 | ||
549 | mutex_lock(&led_cdev_ind->led_access); | |
550 | ||
551 | led_sysfs_disable(led_cdev_ind); | |
552 | led_trigger_remove(led_cdev_ind); | |
553 | ||
554 | mutex_unlock(&led_cdev_ind->led_access); | |
555 | } | |
556 | ||
557 | ret = __sync_device_with_v4l2_controls(v4l2_flash); | |
558 | if (ret < 0) | |
559 | goto out_sync_device; | |
560 | ||
561 | return 0; | |
562 | out_sync_device: | |
563 | mutex_lock(&led_cdev->led_access); | |
564 | led_sysfs_enable(led_cdev); | |
565 | mutex_unlock(&led_cdev->led_access); | |
566 | ||
567 | if (led_cdev_ind) { | |
568 | mutex_lock(&led_cdev_ind->led_access); | |
569 | led_sysfs_enable(led_cdev_ind); | |
570 | mutex_unlock(&led_cdev_ind->led_access); | |
571 | } | |
572 | ||
573 | return ret; | |
574 | } | |
575 | ||
576 | static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
577 | { | |
578 | struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); | |
579 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | |
580 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | |
581 | struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev; | |
582 | int ret = 0; | |
583 | ||
584 | if (!v4l2_fh_is_singular(&fh->vfh)) | |
585 | return 0; | |
586 | ||
587 | mutex_lock(&led_cdev->led_access); | |
588 | ||
589 | if (v4l2_flash->ctrls[STROBE_SOURCE]) | |
590 | ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE], | |
591 | V4L2_FLASH_STROBE_SOURCE_SOFTWARE); | |
592 | led_sysfs_enable(led_cdev); | |
593 | ||
594 | mutex_unlock(&led_cdev->led_access); | |
595 | ||
596 | if (iled_cdev) { | |
597 | struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev; | |
598 | ||
599 | mutex_lock(&led_cdev_ind->led_access); | |
600 | led_sysfs_enable(led_cdev_ind); | |
601 | mutex_unlock(&led_cdev_ind->led_access); | |
602 | } | |
603 | ||
604 | return ret; | |
605 | } | |
606 | ||
607 | static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = { | |
608 | .open = v4l2_flash_open, | |
609 | .close = v4l2_flash_close, | |
610 | }; | |
611 | ||
612 | static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = { | |
613 | .queryctrl = v4l2_subdev_queryctrl, | |
614 | .querymenu = v4l2_subdev_querymenu, | |
615 | }; | |
616 | ||
617 | static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = { | |
618 | .core = &v4l2_flash_core_ops, | |
619 | }; | |
620 | ||
621 | struct v4l2_flash *v4l2_flash_init( | |
622 | struct device *dev, struct device_node *of_node, | |
623 | struct led_classdev_flash *fled_cdev, | |
624 | struct led_classdev_flash *iled_cdev, | |
625 | const struct v4l2_flash_ops *ops, | |
626 | struct v4l2_flash_config *config) | |
627 | { | |
628 | struct v4l2_flash *v4l2_flash; | |
629 | struct led_classdev *led_cdev; | |
630 | struct v4l2_subdev *sd; | |
631 | int ret; | |
632 | ||
633 | if (!fled_cdev || !ops || !config) | |
634 | return ERR_PTR(-EINVAL); | |
635 | ||
636 | led_cdev = &fled_cdev->led_cdev; | |
637 | ||
638 | v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash), | |
639 | GFP_KERNEL); | |
640 | if (!v4l2_flash) | |
641 | return ERR_PTR(-ENOMEM); | |
642 | ||
643 | sd = &v4l2_flash->sd; | |
644 | v4l2_flash->fled_cdev = fled_cdev; | |
645 | v4l2_flash->iled_cdev = iled_cdev; | |
646 | v4l2_flash->ops = ops; | |
647 | sd->dev = dev; | |
648 | sd->of_node = of_node; | |
649 | v4l2_subdev_init(sd, &v4l2_flash_subdev_ops); | |
650 | sd->internal_ops = &v4l2_flash_subdev_internal_ops; | |
651 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
652 | strlcpy(sd->name, config->dev_name, sizeof(sd->name)); | |
653 | ||
654 | ret = media_entity_init(&sd->entity, 0, NULL, 0); | |
655 | if (ret < 0) | |
656 | return ERR_PTR(ret); | |
657 | ||
658 | sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; | |
659 | ||
660 | ret = v4l2_flash_init_controls(v4l2_flash, config); | |
661 | if (ret < 0) | |
662 | goto err_init_controls; | |
663 | ||
664 | if (sd->of_node) | |
665 | of_node_get(sd->of_node); | |
666 | else | |
667 | of_node_get(led_cdev->dev->of_node); | |
668 | ||
669 | ret = v4l2_async_register_subdev(sd); | |
670 | if (ret < 0) | |
671 | goto err_async_register_sd; | |
672 | ||
673 | return v4l2_flash; | |
674 | ||
675 | err_async_register_sd: | |
676 | of_node_put(led_cdev->dev->of_node); | |
677 | v4l2_ctrl_handler_free(sd->ctrl_handler); | |
678 | err_init_controls: | |
679 | media_entity_cleanup(&sd->entity); | |
680 | ||
681 | return ERR_PTR(ret); | |
682 | } | |
683 | EXPORT_SYMBOL_GPL(v4l2_flash_init); | |
684 | ||
685 | void v4l2_flash_release(struct v4l2_flash *v4l2_flash) | |
686 | { | |
687 | struct v4l2_subdev *sd; | |
688 | struct led_classdev *led_cdev; | |
689 | ||
690 | if (IS_ERR_OR_NULL(v4l2_flash)) | |
691 | return; | |
692 | ||
693 | sd = &v4l2_flash->sd; | |
694 | led_cdev = &v4l2_flash->fled_cdev->led_cdev; | |
695 | ||
696 | v4l2_async_unregister_subdev(sd); | |
697 | ||
698 | if (sd->of_node) | |
699 | of_node_put(sd->of_node); | |
700 | else | |
701 | of_node_put(led_cdev->dev->of_node); | |
702 | ||
703 | v4l2_ctrl_handler_free(sd->ctrl_handler); | |
704 | media_entity_cleanup(&sd->entity); | |
705 | } | |
706 | EXPORT_SYMBOL_GPL(v4l2_flash_release); | |
707 | ||
708 | MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); | |
709 | MODULE_DESCRIPTION("V4L2 Flash sub-device helpers"); | |
710 | MODULE_LICENSE("GPL v2"); |