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