diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2015-06-19 03:31:47 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2015-06-22 16:53:16 -0400 |
commit | 42bd6f59ae90244484746696aabcafc0003f59c7 (patch) | |
tree | 732281bf5b3544fbef93f39eb4f8d083cc264b66 | |
parent | 862176512b55ff177fad008cb45ac7adb192300a (diff) |
media: Add registration helpers for V4L2 flash sub-devices
This patch adds helper functions for registering/unregistering
LED Flash class devices as V4L2 sub-devices. The functions should
be called from the LED subsystem device driver. In case the
support for V4L2 Flash sub-devices is disabled in the kernel
config the functions' empty versions will be used.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Sakari Ailus <sakari.ailus@iki.fi>
Cc: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r-- | drivers/media/v4l2-core/Kconfig | 11 | ||||
-rw-r--r-- | drivers/media/v4l2-core/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/v4l2-core/v4l2-flash-led-class.c | 710 | ||||
-rw-r--r-- | include/media/v4l2-flash-led-class.h | 148 |
4 files changed, 871 insertions, 0 deletions
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index ba7e21a73023..c505dc33e4db 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig | |||
@@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV | |||
44 | tristate | 44 | tristate |
45 | depends on VIDEOBUF2_CORE | 45 | depends on VIDEOBUF2_CORE |
46 | 46 | ||
47 | # Used by LED subsystem flash drivers | ||
48 | config V4L2_FLASH_LED_CLASS | ||
49 | tristate "V4L2 flash API for LED flash class devices" | ||
50 | depends on VIDEO_V4L2_SUBDEV_API | ||
51 | depends on LEDS_CLASS_FLASH | ||
52 | ---help--- | ||
53 | Say Y here to enable V4L2 flash API support for LED flash | ||
54 | class drivers. | ||
55 | |||
56 | When in doubt, say N. | ||
57 | |||
47 | # Used by drivers that need Videobuf modules | 58 | # Used by drivers that need Videobuf modules |
48 | config VIDEOBUF_GEN | 59 | config VIDEOBUF_GEN |
49 | tristate | 60 | tristate |
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 63d29f27538c..dc3de00d68b5 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile | |||
@@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o | |||
22 | 22 | ||
23 | obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o | 23 | obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o |
24 | 24 | ||
25 | obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o | ||
26 | |||
25 | obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o | 27 | obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o |
26 | obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o | 28 | obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o |
27 | obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o | 29 | obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o |
diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c new file mode 100644 index 000000000000..5bdfb8d5263a --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c | |||
@@ -0,0 +1,710 @@ | |||
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"); | ||
diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h new file mode 100644 index 000000000000..098236c083b8 --- /dev/null +++ b/include/media/v4l2-flash-led-class.h | |||
@@ -0,0 +1,148 @@ | |||
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 | #ifndef _V4L2_FLASH_H | ||
13 | #define _V4L2_FLASH_H | ||
14 | |||
15 | #include <media/v4l2-ctrls.h> | ||
16 | #include <media/v4l2-subdev.h> | ||
17 | |||
18 | struct led_classdev_flash; | ||
19 | struct led_classdev; | ||
20 | struct v4l2_flash; | ||
21 | enum led_brightness; | ||
22 | |||
23 | /* | ||
24 | * struct v4l2_flash_ctrl_data - flash control initialization data, filled | ||
25 | * basing on the features declared by the LED flash | ||
26 | * class driver in the v4l2_flash_config | ||
27 | * @config: initialization data for a control | ||
28 | * @cid: contains v4l2 flash control id if the config | ||
29 | * field was initialized, 0 otherwise | ||
30 | */ | ||
31 | struct v4l2_flash_ctrl_data { | ||
32 | struct v4l2_ctrl_config config; | ||
33 | u32 cid; | ||
34 | }; | ||
35 | |||
36 | struct v4l2_flash_ops { | ||
37 | /* setup strobing the flash by hardware pin state assertion */ | ||
38 | int (*external_strobe_set)(struct v4l2_flash *v4l2_flash, | ||
39 | bool enable); | ||
40 | /* convert intensity to brightness in a device specific manner */ | ||
41 | enum led_brightness (*intensity_to_led_brightness) | ||
42 | (struct v4l2_flash *v4l2_flash, s32 intensity); | ||
43 | /* convert brightness to intensity in a device specific manner */ | ||
44 | s32 (*led_brightness_to_intensity) | ||
45 | (struct v4l2_flash *v4l2_flash, enum led_brightness); | ||
46 | }; | ||
47 | |||
48 | /** | ||
49 | * struct v4l2_flash_config - V4L2 Flash sub-device initialization data | ||
50 | * @dev_name: the name of the media entity, | ||
51 | unique in the system | ||
52 | * @torch_intensity: constraints for the LED in torch mode | ||
53 | * @indicator_intensity: constraints for the indicator LED | ||
54 | * @flash_faults: bitmask of flash faults that the LED flash class | ||
55 | device can report; corresponding LED_FAULT* bit | ||
56 | definitions are available in the header file | ||
57 | <linux/led-class-flash.h> | ||
58 | * @has_external_strobe: external strobe capability | ||
59 | */ | ||
60 | struct v4l2_flash_config { | ||
61 | char dev_name[32]; | ||
62 | struct led_flash_setting torch_intensity; | ||
63 | struct led_flash_setting indicator_intensity; | ||
64 | u32 flash_faults; | ||
65 | unsigned int has_external_strobe:1; | ||
66 | }; | ||
67 | |||
68 | /** | ||
69 | * struct v4l2_flash - Flash sub-device context | ||
70 | * @fled_cdev: LED flash class device controlled by this sub-device | ||
71 | * @iled_cdev: LED class device representing indicator LED associated | ||
72 | * with the LED flash class device | ||
73 | * @ops: V4L2 specific flash ops | ||
74 | * @sd: V4L2 sub-device | ||
75 | * @hdl: flash controls handler | ||
76 | * @ctrls: array of pointers to controls, whose values define | ||
77 | * the sub-device state | ||
78 | */ | ||
79 | struct v4l2_flash { | ||
80 | struct led_classdev_flash *fled_cdev; | ||
81 | struct led_classdev_flash *iled_cdev; | ||
82 | const struct v4l2_flash_ops *ops; | ||
83 | |||
84 | struct v4l2_subdev sd; | ||
85 | struct v4l2_ctrl_handler hdl; | ||
86 | struct v4l2_ctrl **ctrls; | ||
87 | }; | ||
88 | |||
89 | static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash( | ||
90 | struct v4l2_subdev *sd) | ||
91 | { | ||
92 | return container_of(sd, struct v4l2_flash, sd); | ||
93 | } | ||
94 | |||
95 | static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c) | ||
96 | { | ||
97 | return container_of(c->handler, struct v4l2_flash, hdl); | ||
98 | } | ||
99 | |||
100 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
101 | /** | ||
102 | * v4l2_flash_init - initialize V4L2 flash led sub-device | ||
103 | * @dev: flash device, e.g. an I2C device | ||
104 | * @of_node: of_node of the LED, may be NULL if the same as device's | ||
105 | * @fled_cdev: LED flash class device to wrap | ||
106 | * @iled_cdev: LED flash class device representing indicator LED associated | ||
107 | * with fled_cdev, may be NULL | ||
108 | * @flash_ops: V4L2 Flash device ops | ||
109 | * @config: initialization data for V4L2 Flash sub-device | ||
110 | * | ||
111 | * Create V4L2 Flash sub-device wrapping given LED subsystem device. | ||
112 | * | ||
113 | * Returns: A valid pointer, or, when an error occurs, the return | ||
114 | * value is encoded using ERR_PTR(). Use IS_ERR() to check and | ||
115 | * PTR_ERR() to obtain the numeric return value. | ||
116 | */ | ||
117 | struct v4l2_flash *v4l2_flash_init( | ||
118 | struct device *dev, struct device_node *of_node, | ||
119 | struct led_classdev_flash *fled_cdev, | ||
120 | struct led_classdev_flash *iled_cdev, | ||
121 | const struct v4l2_flash_ops *ops, | ||
122 | struct v4l2_flash_config *config); | ||
123 | |||
124 | /** | ||
125 | * v4l2_flash_release - release V4L2 Flash sub-device | ||
126 | * @flash: the V4L2 Flash sub-device to release | ||
127 | * | ||
128 | * Release V4L2 Flash sub-device. | ||
129 | */ | ||
130 | void v4l2_flash_release(struct v4l2_flash *v4l2_flash); | ||
131 | |||
132 | #else | ||
133 | static inline struct v4l2_flash *v4l2_flash_init( | ||
134 | struct device *dev, struct device_node *of_node, | ||
135 | struct led_classdev_flash *fled_cdev, | ||
136 | struct led_classdev_flash *iled_cdev, | ||
137 | const struct v4l2_flash_ops *ops, | ||
138 | struct v4l2_flash_config *config) | ||
139 | { | ||
140 | return NULL; | ||
141 | } | ||
142 | |||
143 | static inline void v4l2_flash_release(struct v4l2_flash *v4l2_flash) | ||
144 | { | ||
145 | } | ||
146 | #endif /* CONFIG_V4L2_FLASH_LED_CLASS */ | ||
147 | |||
148 | #endif /* _V4L2_FLASH_H */ | ||