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