diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-01 22:09:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-01 22:09:11 -0400 |
commit | 13d45f79a2af84de9083310db58b309a61065208 (patch) | |
tree | 06968d83ed6e5e23e1f729cf6f096f9a79f14f72 | |
parent | 05fde26a943a9c55d8b498d97bb49d3d207e5069 (diff) | |
parent | b67893206fc0a0e8af87130e67f3d8ae553fc87c (diff) |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull LED subsystem updates from Bryan Wu:
"In this cycle, we finished to merge patches for LED Flash class
driver.
Other than that we have some bug fixes and new drivers for LED
controllers"
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (33 commits)
leds:lp55xx: fix firmware loading error
leds: fix max77693-led build errors
leds: fix aat1290 build errors
leds: aat1290: pass flags parameter to devm_gpiod_get
leds: ktd2692: pass flags parameter to devm_gpiod_get
drivers/leds: don't use module_init in non-modular leds-cobalt-raq.c
leds: aat1290: add support for V4L2 Flash sub-device
DT: aat1290: Document handling external strobe sources
leds: max77693: add support for V4L2 Flash sub-device
media: Add registration helpers for V4L2 flash sub-devices
v4l: async: Add a pointer to of_node to struct v4l2_subdev, match it
Documentation: leds: Add description of v4l2-flash sub-device
leds: add BCM6358 LED driver
leds: add DT binding for BCM6358 LED controller
leds: fix brightness changing when software blinking is active
Documentation: leds-lp5523: describe master fader attributes
leds: lp5523: add master_fader support
leds: leds-gpio: Allow compile test if !GPIOLIB
leds: leds-gpio: Add missing #include <linux/of.h>
gpiolib: Add missing dummies for the unified device properties interface
...
31 files changed, 4939 insertions, 58 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt new file mode 100644 index 000000000000..c05ed91a4e42 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-aat1290.txt | |||
@@ -0,0 +1,73 @@ | |||
1 | * Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs | ||
2 | |||
3 | The device is controlled through two pins: FL_EN and EN_SET. The pins when, | ||
4 | asserted high, enable flash strobe and movie mode (max 1/2 of flash current) | ||
5 | respectively. In order to add a capability of selecting the strobe signal source | ||
6 | (e.g. CPU or camera sensor) there is an additional switch required, independent | ||
7 | of the flash chip. The switch is controlled with pin control. | ||
8 | |||
9 | Required properties: | ||
10 | |||
11 | - compatible : Must be "skyworks,aat1290". | ||
12 | - flen-gpios : Must be device tree identifier of the flash device FL_EN pin. | ||
13 | - enset-gpios : Must be device tree identifier of the flash device EN_SET pin. | ||
14 | |||
15 | Optional properties: | ||
16 | - pinctrl-names : Must contain entries: "default", "host", "isp". Entries | ||
17 | "default" and "host" must refer to the same pin configuration | ||
18 | node, which sets the host as a strobe signal provider. Entry | ||
19 | "isp" must refer to the pin configuration node, which sets the | ||
20 | ISP as a strobe signal provider. | ||
21 | |||
22 | A discrete LED element connected to the device must be represented by a child | ||
23 | node - see Documentation/devicetree/bindings/leds/common.txt. | ||
24 | |||
25 | Required properties of the LED child node: | ||
26 | - led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt | ||
27 | - flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt | ||
28 | Maximum flash LED supply current can be calculated using | ||
29 | following formula: I = 1A * 162kohm / Rset. | ||
30 | - flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt | ||
31 | Maximum flash timeout can be calculated using following | ||
32 | formula: T = 8.82 * 10^9 * Ct. | ||
33 | |||
34 | Optional properties of the LED child node: | ||
35 | - label : see Documentation/devicetree/bindings/leds/common.txt | ||
36 | |||
37 | Example (by Ct = 220nF, Rset = 160kohm and exynos4412-trats2 board with | ||
38 | a switch that allows for routing strobe signal either from the host or from | ||
39 | the camera sensor): | ||
40 | |||
41 | #include "exynos4412.dtsi" | ||
42 | |||
43 | aat1290 { | ||
44 | compatible = "skyworks,aat1290"; | ||
45 | flen-gpios = <&gpj1 1 GPIO_ACTIVE_HIGH>; | ||
46 | enset-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>; | ||
47 | |||
48 | pinctrl-names = "default", "host", "isp"; | ||
49 | pinctrl-0 = <&camera_flash_host>; | ||
50 | pinctrl-1 = <&camera_flash_host>; | ||
51 | pinctrl-2 = <&camera_flash_isp>; | ||
52 | |||
53 | camera_flash: flash-led { | ||
54 | label = "aat1290-flash"; | ||
55 | led-max-microamp = <520833>; | ||
56 | flash-max-microamp = <1012500>; | ||
57 | flash-timeout-us = <1940000>; | ||
58 | }; | ||
59 | }; | ||
60 | |||
61 | &pinctrl_0 { | ||
62 | camera_flash_host: camera-flash-host { | ||
63 | samsung,pins = "gpj1-0"; | ||
64 | samsung,pin-function = <1>; | ||
65 | samsung,pin-val = <0>; | ||
66 | }; | ||
67 | |||
68 | camera_flash_isp: camera-flash-isp { | ||
69 | samsung,pins = "gpj1-0"; | ||
70 | samsung,pin-function = <1>; | ||
71 | samsung,pin-val = <1>; | ||
72 | }; | ||
73 | }; | ||
diff --git a/Documentation/devicetree/bindings/leds/leds-bcm6328.txt b/Documentation/devicetree/bindings/leds/leds-bcm6328.txt new file mode 100644 index 000000000000..f9e36adc0ebf --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-bcm6328.txt | |||
@@ -0,0 +1,309 @@ | |||
1 | LEDs connected to Broadcom BCM6328 controller | ||
2 | |||
3 | This controller is present on BCM6318, BCM6328, BCM6362 and BCM63268. | ||
4 | In these SoCs it's possible to control LEDs both as GPIOs or by hardware. | ||
5 | However, on some devices there are Serial LEDs (LEDs connected to a 74x164 | ||
6 | controller), which can either be controlled by software (exporting the 74x164 | ||
7 | as spi-gpio. See Documentation/devicetree/bindings/gpio/gpio-74x164.txt), or | ||
8 | by hardware using this driver. | ||
9 | Some of these Serial LEDs are hardware controlled (e.g. ethernet LEDs) and | ||
10 | exporting the 74x164 as spi-gpio prevents those LEDs to be hardware | ||
11 | controlled, so the only chance to keep them working is by using this driver. | ||
12 | |||
13 | BCM6328 LED controller has a HWDIS register, which controls whether a LED | ||
14 | should be controlled by a hardware signal instead of the MODE register value, | ||
15 | with 0 meaning hardware control enabled and 1 hardware control disabled. This | ||
16 | is usually 1:1 for hardware to LED signals, but through the activity/link | ||
17 | registers you have some limited control over rerouting the LEDs (as | ||
18 | explained later in brcm,link-signal-sources). Even if a LED is hardware | ||
19 | controlled you are still able to make it blink or light it up if it isn't, | ||
20 | but you can't turn it off if the hardware decides to light it up. For this | ||
21 | reason, hardware controlled LEDs aren't registered as LED class devices. | ||
22 | |||
23 | Required properties: | ||
24 | - compatible : should be "brcm,bcm6328-leds". | ||
25 | - #address-cells : must be 1. | ||
26 | - #size-cells : must be 0. | ||
27 | - reg : BCM6328 LED controller address and size. | ||
28 | |||
29 | Optional properties: | ||
30 | - brcm,serial-leds : Boolean, enables Serial LEDs. | ||
31 | Default : false | ||
32 | |||
33 | Each LED is represented as a sub-node of the brcm,bcm6328-leds device. | ||
34 | |||
35 | LED sub-node required properties: | ||
36 | - reg : LED pin number (only LEDs 0 to 23 are valid). | ||
37 | |||
38 | LED sub-node optional properties: | ||
39 | a) Optional properties for sub-nodes related to software controlled LEDs: | ||
40 | - label : see Documentation/devicetree/bindings/leds/common.txt | ||
41 | - active-low : Boolean, makes LED active low. | ||
42 | Default : false | ||
43 | - default-state : see | ||
44 | Documentation/devicetree/bindings/leds/leds-gpio.txt | ||
45 | - linux,default-trigger : see | ||
46 | Documentation/devicetree/bindings/leds/common.txt | ||
47 | |||
48 | b) Optional properties for sub-nodes related to hardware controlled LEDs: | ||
49 | - brcm,hardware-controlled : Boolean, makes this LED hardware controlled. | ||
50 | Default : false | ||
51 | - brcm,link-signal-sources : An array of hardware link | ||
52 | signal sources. Up to four link hardware signals can get muxed into | ||
53 | these LEDs. Only valid for LEDs 0 to 7, where LED signals 0 to 3 may | ||
54 | be muxed to LEDs 0 to 3, and signals 4 to 7 may be muxed to LEDs | ||
55 | 4 to 7. A signal can be muxed to more than one LED, and one LED can | ||
56 | have more than one source signal. | ||
57 | - brcm,activity-signal-sources : An array of hardware activity | ||
58 | signal sources. Up to four activity hardware signals can get muxed into | ||
59 | these LEDs. Only valid for LEDs 0 to 7, where LED signals 0 to 3 may | ||
60 | be muxed to LEDs 0 to 3, and signals 4 to 7 may be muxed to LEDs | ||
61 | 4 to 7. A signal can be muxed to more than one LED, and one LED can | ||
62 | have more than one source signal. | ||
63 | |||
64 | Examples: | ||
65 | Scenario 1 : BCM6328 with 4 EPHY LEDs | ||
66 | leds0: led-controller@10000800 { | ||
67 | compatible = "brcm,bcm6328-leds"; | ||
68 | #address-cells = <1>; | ||
69 | #size-cells = <0>; | ||
70 | reg = <0x10000800 0x24>; | ||
71 | |||
72 | alarm_red@2 { | ||
73 | reg = <2>; | ||
74 | active-low; | ||
75 | label = "red:alarm"; | ||
76 | }; | ||
77 | inet_green@3 { | ||
78 | reg = <3>; | ||
79 | active-low; | ||
80 | label = "green:inet"; | ||
81 | }; | ||
82 | power_green@4 { | ||
83 | reg = <4>; | ||
84 | active-low; | ||
85 | label = "green:power"; | ||
86 | default-state = "on"; | ||
87 | }; | ||
88 | ephy0_spd@17 { | ||
89 | reg = <17>; | ||
90 | brcm,hardware-controlled; | ||
91 | }; | ||
92 | ephy1_spd@18 { | ||
93 | reg = <18>; | ||
94 | brcm,hardware-controlled; | ||
95 | }; | ||
96 | ephy2_spd@19 { | ||
97 | reg = <19>; | ||
98 | brcm,hardware-controlled; | ||
99 | }; | ||
100 | ephy3_spd@20 { | ||
101 | reg = <20>; | ||
102 | brcm,hardware-controlled; | ||
103 | }; | ||
104 | }; | ||
105 | |||
106 | Scenario 2 : BCM63268 with Serial/GPHY0 LEDs | ||
107 | leds0: led-controller@10001900 { | ||
108 | compatible = "brcm,bcm6328-leds"; | ||
109 | #address-cells = <1>; | ||
110 | #size-cells = <0>; | ||
111 | reg = <0x10001900 0x24>; | ||
112 | brcm,serial-leds; | ||
113 | |||
114 | gphy0_spd0@0 { | ||
115 | reg = <0>; | ||
116 | brcm,hardware-controlled; | ||
117 | brcm,link-signal-sources = <0>; | ||
118 | }; | ||
119 | gphy0_spd1@1 { | ||
120 | reg = <1>; | ||
121 | brcm,hardware-controlled; | ||
122 | brcm,link-signal-sources = <1>; | ||
123 | }; | ||
124 | inet_red@2 { | ||
125 | reg = <2>; | ||
126 | active-low; | ||
127 | label = "red:inet"; | ||
128 | }; | ||
129 | dsl_green@3 { | ||
130 | reg = <3>; | ||
131 | active-low; | ||
132 | label = "green:dsl"; | ||
133 | }; | ||
134 | usb_green@4 { | ||
135 | reg = <4>; | ||
136 | active-low; | ||
137 | label = "green:usb"; | ||
138 | }; | ||
139 | wps_green@7 { | ||
140 | reg = <7>; | ||
141 | active-low; | ||
142 | label = "green:wps"; | ||
143 | }; | ||
144 | inet_green@8 { | ||
145 | reg = <8>; | ||
146 | active-low; | ||
147 | label = "green:inet"; | ||
148 | }; | ||
149 | ephy0_act@9 { | ||
150 | reg = <9>; | ||
151 | brcm,hardware-controlled; | ||
152 | }; | ||
153 | ephy1_act@10 { | ||
154 | reg = <10>; | ||
155 | brcm,hardware-controlled; | ||
156 | }; | ||
157 | ephy2_act@11 { | ||
158 | reg = <11>; | ||
159 | brcm,hardware-controlled; | ||
160 | }; | ||
161 | gphy0_act@12 { | ||
162 | reg = <12>; | ||
163 | brcm,hardware-controlled; | ||
164 | }; | ||
165 | ephy0_spd@13 { | ||
166 | reg = <13>; | ||
167 | brcm,hardware-controlled; | ||
168 | }; | ||
169 | ephy1_spd@14 { | ||
170 | reg = <14>; | ||
171 | brcm,hardware-controlled; | ||
172 | }; | ||
173 | ephy2_spd@15 { | ||
174 | reg = <15>; | ||
175 | brcm,hardware-controlled; | ||
176 | }; | ||
177 | power_green@20 { | ||
178 | reg = <20>; | ||
179 | active-low; | ||
180 | label = "green:power"; | ||
181 | default-state = "on"; | ||
182 | }; | ||
183 | }; | ||
184 | |||
185 | Scenario 3 : BCM6362 with 1 LED for each EPHY | ||
186 | leds0: led-controller@10001900 { | ||
187 | compatible = "brcm,bcm6328-leds"; | ||
188 | #address-cells = <1>; | ||
189 | #size-cells = <0>; | ||
190 | reg = <0x10001900 0x24>; | ||
191 | |||
192 | usb@0 { | ||
193 | reg = <0>; | ||
194 | brcm,hardware-controlled; | ||
195 | brcm,link-signal-sources = <0>; | ||
196 | brcm,activity-signal-sources = <0>; | ||
197 | /* USB link/activity routed to USB LED */ | ||
198 | }; | ||
199 | inet@1 { | ||
200 | reg = <1>; | ||
201 | brcm,hardware-controlled; | ||
202 | brcm,activity-signal-sources = <1>; | ||
203 | /* INET activity routed to INET LED */ | ||
204 | }; | ||
205 | ephy0@4 { | ||
206 | reg = <4>; | ||
207 | brcm,hardware-controlled; | ||
208 | brcm,link-signal-sources = <4>; | ||
209 | /* EPHY0 link routed to EPHY0 LED */ | ||
210 | }; | ||
211 | ephy1@5 { | ||
212 | reg = <5>; | ||
213 | brcm,hardware-controlled; | ||
214 | brcm,link-signal-sources = <5>; | ||
215 | /* EPHY1 link routed to EPHY1 LED */ | ||
216 | }; | ||
217 | ephy2@6 { | ||
218 | reg = <6>; | ||
219 | brcm,hardware-controlled; | ||
220 | brcm,link-signal-sources = <6>; | ||
221 | /* EPHY2 link routed to EPHY2 LED */ | ||
222 | }; | ||
223 | ephy3@7 { | ||
224 | reg = <7>; | ||
225 | brcm,hardware-controlled; | ||
226 | brcm,link-signal-sources = <7>; | ||
227 | /* EPHY3 link routed to EPHY3 LED */ | ||
228 | }; | ||
229 | power_green@20 { | ||
230 | reg = <20>; | ||
231 | active-low; | ||
232 | label = "green:power"; | ||
233 | default-state = "on"; | ||
234 | }; | ||
235 | }; | ||
236 | |||
237 | Scenario 4 : BCM6362 with 1 LED for all EPHYs | ||
238 | leds0: led-controller@10001900 { | ||
239 | compatible = "brcm,bcm6328-leds"; | ||
240 | #address-cells = <1>; | ||
241 | #size-cells = <0>; | ||
242 | reg = <0x10001900 0x24>; | ||
243 | |||
244 | usb@0 { | ||
245 | reg = <0>; | ||
246 | brcm,hardware-controlled; | ||
247 | brcm,link-signal-sources = <0 1>; | ||
248 | brcm,activity-signal-sources = <0 1>; | ||
249 | /* USB/INET link/activity routed to USB LED */ | ||
250 | }; | ||
251 | ephy@4 { | ||
252 | reg = <4>; | ||
253 | brcm,hardware-controlled; | ||
254 | brcm,link-signal-sources = <4 5 6 7>; | ||
255 | /* EPHY0/1/2/3 link routed to EPHY0 LED */ | ||
256 | }; | ||
257 | power_green@20 { | ||
258 | reg = <20>; | ||
259 | active-low; | ||
260 | label = "green:power"; | ||
261 | default-state = "on"; | ||
262 | }; | ||
263 | }; | ||
264 | |||
265 | Scenario 5 : BCM6362 with EPHY LEDs swapped | ||
266 | leds0: led-controller@10001900 { | ||
267 | compatible = "brcm,bcm6328-leds"; | ||
268 | #address-cells = <1>; | ||
269 | #size-cells = <0>; | ||
270 | reg = <0x10001900 0x24>; | ||
271 | |||
272 | usb@0 { | ||
273 | reg = <0>; | ||
274 | brcm,hardware-controlled; | ||
275 | brcm,link-signal-sources = <0>; | ||
276 | brcm,activity-signal-sources = <0 1>; | ||
277 | /* USB link/act and INET act routed to USB LED */ | ||
278 | }; | ||
279 | ephy0@4 { | ||
280 | reg = <4>; | ||
281 | brcm,hardware-controlled; | ||
282 | brcm,link-signal-sources = <7>; | ||
283 | /* EPHY3 link routed to EPHY0 LED */ | ||
284 | }; | ||
285 | ephy1@5 { | ||
286 | reg = <5>; | ||
287 | brcm,hardware-controlled; | ||
288 | brcm,link-signal-sources = <6>; | ||
289 | /* EPHY2 link routed to EPHY1 LED */ | ||
290 | }; | ||
291 | ephy2@6 { | ||
292 | reg = <6>; | ||
293 | brcm,hardware-controlled; | ||
294 | brcm,link-signal-sources = <5>; | ||
295 | /* EPHY1 link routed to EPHY2 LED */ | ||
296 | }; | ||
297 | ephy3@7 { | ||
298 | reg = <7>; | ||
299 | brcm,hardware-controlled; | ||
300 | brcm,link-signal-sources = <4>; | ||
301 | /* EPHY0 link routed to EPHY3 LED */ | ||
302 | }; | ||
303 | power_green@20 { | ||
304 | reg = <20>; | ||
305 | active-low; | ||
306 | label = "green:power"; | ||
307 | default-state = "on"; | ||
308 | }; | ||
309 | }; | ||
diff --git a/Documentation/devicetree/bindings/leds/leds-bcm6358.txt b/Documentation/devicetree/bindings/leds/leds-bcm6358.txt new file mode 100644 index 000000000000..b22a55bcc65d --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-bcm6358.txt | |||
@@ -0,0 +1,145 @@ | |||
1 | LEDs connected to Broadcom BCM6358 controller | ||
2 | |||
3 | This controller is present on BCM6358 and BCM6368. | ||
4 | In these SoCs there are Serial LEDs (LEDs connected to a 74x164 controller), | ||
5 | which can either be controlled by software (exporting the 74x164 as spi-gpio. | ||
6 | See Documentation/devicetree/bindings/gpio/gpio-74x164.txt), or | ||
7 | by hardware using this driver. | ||
8 | |||
9 | Required properties: | ||
10 | - compatible : should be "brcm,bcm6358-leds". | ||
11 | - #address-cells : must be 1. | ||
12 | - #size-cells : must be 0. | ||
13 | - reg : BCM6358 LED controller address and size. | ||
14 | |||
15 | Optional properties: | ||
16 | - brcm,clk-div : SCK signal divider. Possible values are 1, 2, 4 and 8. | ||
17 | Default : 1 | ||
18 | - brcm,clk-dat-low : Boolean, makes clock and data signals active low. | ||
19 | Default : false | ||
20 | |||
21 | Each LED is represented as a sub-node of the brcm,bcm6358-leds device. | ||
22 | |||
23 | LED sub-node required properties: | ||
24 | - reg : LED pin number (only LEDs 0 to 31 are valid). | ||
25 | |||
26 | LED sub-node optional properties: | ||
27 | - label : see Documentation/devicetree/bindings/leds/common.txt | ||
28 | - active-low : Boolean, makes LED active low. | ||
29 | Default : false | ||
30 | - default-state : see | ||
31 | Documentation/devicetree/bindings/leds/leds-gpio.txt | ||
32 | - linux,default-trigger : see | ||
33 | Documentation/devicetree/bindings/leds/common.txt | ||
34 | |||
35 | Examples: | ||
36 | Scenario 1 : BCM6358 | ||
37 | leds0: led-controller@fffe00d0 { | ||
38 | compatible = "brcm,bcm6358-leds"; | ||
39 | #address-cells = <1>; | ||
40 | #size-cells = <0>; | ||
41 | reg = <0xfffe00d0 0x8>; | ||
42 | |||
43 | alarm_white { | ||
44 | reg = <0>; | ||
45 | active-low; | ||
46 | label = "white:alarm"; | ||
47 | }; | ||
48 | tv_white { | ||
49 | reg = <2>; | ||
50 | active-low; | ||
51 | label = "white:tv"; | ||
52 | }; | ||
53 | tel_white { | ||
54 | reg = <3>; | ||
55 | active-low; | ||
56 | label = "white:tel"; | ||
57 | }; | ||
58 | adsl_white { | ||
59 | reg = <4>; | ||
60 | active-low; | ||
61 | label = "white:adsl"; | ||
62 | }; | ||
63 | }; | ||
64 | |||
65 | Scenario 2 : BCM6368 | ||
66 | leds0: led-controller@100000d0 { | ||
67 | compatible = "brcm,bcm6358-leds"; | ||
68 | #address-cells = <1>; | ||
69 | #size-cells = <0>; | ||
70 | reg = <0x100000d0 0x8>; | ||
71 | brcm,pol-low; | ||
72 | brcm,clk-div = <4>; | ||
73 | |||
74 | power_red { | ||
75 | reg = <0>; | ||
76 | active-low; | ||
77 | label = "red:power"; | ||
78 | }; | ||
79 | power_green { | ||
80 | reg = <1>; | ||
81 | active-low; | ||
82 | label = "green:power"; | ||
83 | default-state = "on"; | ||
84 | }; | ||
85 | power_blue { | ||
86 | reg = <2>; | ||
87 | label = "blue:power"; | ||
88 | }; | ||
89 | broadband_red { | ||
90 | reg = <3>; | ||
91 | active-low; | ||
92 | label = "red:broadband"; | ||
93 | }; | ||
94 | broadband_green { | ||
95 | reg = <4>; | ||
96 | label = "green:broadband"; | ||
97 | }; | ||
98 | broadband_blue { | ||
99 | reg = <5>; | ||
100 | active-low; | ||
101 | label = "blue:broadband"; | ||
102 | }; | ||
103 | wireless_red { | ||
104 | reg = <6>; | ||
105 | active-low; | ||
106 | label = "red:wireless"; | ||
107 | }; | ||
108 | wireless_green { | ||
109 | reg = <7>; | ||
110 | active-low; | ||
111 | label = "green:wireless"; | ||
112 | }; | ||
113 | wireless_blue { | ||
114 | reg = <8>; | ||
115 | label = "blue:wireless"; | ||
116 | }; | ||
117 | phone_red { | ||
118 | reg = <9>; | ||
119 | active-low; | ||
120 | label = "red:phone"; | ||
121 | }; | ||
122 | phone_green { | ||
123 | reg = <10>; | ||
124 | active-low; | ||
125 | label = "green:phone"; | ||
126 | }; | ||
127 | phone_blue { | ||
128 | reg = <11>; | ||
129 | label = "blue:phone"; | ||
130 | }; | ||
131 | upgrading_red { | ||
132 | reg = <12>; | ||
133 | active-low; | ||
134 | label = "red:upgrading"; | ||
135 | }; | ||
136 | upgrading_green { | ||
137 | reg = <13>; | ||
138 | active-low; | ||
139 | label = "green:upgrading"; | ||
140 | }; | ||
141 | upgrading_blue { | ||
142 | reg = <14>; | ||
143 | label = "blue:upgrading"; | ||
144 | }; | ||
145 | }; | ||
diff --git a/Documentation/devicetree/bindings/leds/leds-ktd2692.txt b/Documentation/devicetree/bindings/leds/leds-ktd2692.txt new file mode 100644 index 000000000000..853737452580 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-ktd2692.txt | |||
@@ -0,0 +1,50 @@ | |||
1 | * Kinetic Technologies - KTD2692 Flash LED Driver | ||
2 | |||
3 | KTD2692 is the ideal power solution for high-power flash LEDs. | ||
4 | It uses ExpressWire single-wire programming for maximum flexibility. | ||
5 | |||
6 | The ExpressWire interface through CTRL pin can control LED on/off and | ||
7 | enable/disable the IC, Movie(max 1/3 of Flash current) / Flash mode current, | ||
8 | Flash timeout, LVP(low voltage protection). | ||
9 | |||
10 | Also, When the AUX pin is pulled high while CTRL pin is high, | ||
11 | LED current will be ramped up to the flash-mode current level. | ||
12 | |||
13 | Required properties: | ||
14 | - compatible : Should be "kinetic,ktd2692". | ||
15 | - ctrl-gpios : Specifier of the GPIO connected to CTRL pin. | ||
16 | - aux-gpios : Specifier of the GPIO connected to AUX pin. | ||
17 | |||
18 | Optional properties: | ||
19 | - vin-supply : "vin" LED supply (2.7V to 5.5V). | ||
20 | See Documentation/devicetree/bindings/regulator/regulator.txt | ||
21 | |||
22 | A discrete LED element connected to the device must be represented by a child | ||
23 | node - See Documentation/devicetree/bindings/leds/common.txt | ||
24 | |||
25 | Required properties for flash LED child nodes: | ||
26 | See Documentation/devicetree/bindings/leds/common.txt | ||
27 | - led-max-microamp : Minimum Threshold for Timer protection | ||
28 | is defined internally (Maximum 300mA). | ||
29 | - flash-max-microamp : Flash LED maximum current | ||
30 | Formula : I(mA) = 15000 / Rset. | ||
31 | - flash-max-timeout-us : Flash LED maximum timeout. | ||
32 | |||
33 | Optional properties for flash LED child nodes: | ||
34 | - label : See Documentation/devicetree/bindings/leds/common.txt | ||
35 | |||
36 | Example: | ||
37 | |||
38 | ktd2692 { | ||
39 | compatible = "kinetic,ktd2692"; | ||
40 | ctrl-gpios = <&gpc0 1 0>; | ||
41 | aux-gpios = <&gpc0 2 0>; | ||
42 | vin-supply = <&vbat>; | ||
43 | |||
44 | flash-led { | ||
45 | label = "ktd2692-flash"; | ||
46 | led-max-microamp = <300000>; | ||
47 | flash-max-microamp = <1500000>; | ||
48 | flash-max-timeout-us = <1835000>; | ||
49 | }; | ||
50 | }; | ||
diff --git a/Documentation/devicetree/bindings/leds/leds-tlc591xx.txt b/Documentation/devicetree/bindings/leds/leds-tlc591xx.txt new file mode 100644 index 000000000000..3bbbf7024411 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-tlc591xx.txt | |||
@@ -0,0 +1,40 @@ | |||
1 | LEDs connected to tlc59116 or tlc59108 | ||
2 | |||
3 | Required properties | ||
4 | - compatible: should be "ti,tlc59116" or "ti,tlc59108" | ||
5 | - #address-cells: must be 1 | ||
6 | - #size-cells: must be 0 | ||
7 | - reg: typically 0x68 | ||
8 | |||
9 | Each led is represented as a sub-node of the ti,tlc59116. | ||
10 | See Documentation/devicetree/bindings/leds/common.txt | ||
11 | |||
12 | LED sub-node properties: | ||
13 | - reg: number of LED line, 0 to 15 or 0 to 7 | ||
14 | - label: (optional) name of LED | ||
15 | - linux,default-trigger : (optional) | ||
16 | |||
17 | Examples: | ||
18 | |||
19 | tlc59116@68 { | ||
20 | #address-cells = <1>; | ||
21 | #size-cells = <0>; | ||
22 | compatible = "ti,tlc59116"; | ||
23 | reg = <0x68>; | ||
24 | |||
25 | wan@0 { | ||
26 | label = "wrt1900ac:amber:wan"; | ||
27 | reg = <0x0>; | ||
28 | }; | ||
29 | |||
30 | 2g@2 { | ||
31 | label = "wrt1900ac:white:2g"; | ||
32 | reg = <0x2>; | ||
33 | }; | ||
34 | |||
35 | alive@9 { | ||
36 | label = "wrt1900ac:green:alive"; | ||
37 | reg = <0x9>; | ||
38 | linux,default_trigger = "heartbeat"; | ||
39 | }; | ||
40 | }; | ||
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 7b607761b774..347c8fdcf250 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt | |||
@@ -114,6 +114,7 @@ isee ISEE 2007 S.L. | |||
114 | isil Intersil | 114 | isil Intersil |
115 | karo Ka-Ro electronics GmbH | 115 | karo Ka-Ro electronics GmbH |
116 | keymile Keymile GmbH | 116 | keymile Keymile GmbH |
117 | kinetic Kinetic Technologies | ||
117 | lacie LaCie | 118 | lacie LaCie |
118 | lantiq Lantiq Semiconductor | 119 | lantiq Lantiq Semiconductor |
119 | lenovo Lenovo Group Ltd. | 120 | lenovo Lenovo Group Ltd. |
diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt index 19bb67355424..8da3c6f4b60b 100644 --- a/Documentation/leds/leds-class-flash.txt +++ b/Documentation/leds/leds-class-flash.txt | |||
@@ -20,3 +20,54 @@ Following sysfs attributes are exposed for controlling flash LED devices: | |||
20 | - max_flash_timeout | 20 | - max_flash_timeout |
21 | - flash_strobe | 21 | - flash_strobe |
22 | - flash_fault | 22 | - flash_fault |
23 | |||
24 | |||
25 | V4L2 flash wrapper for flash LEDs | ||
26 | ================================= | ||
27 | |||
28 | A LED subsystem driver can be controlled also from the level of VideoForLinux2 | ||
29 | subsystem. In order to enable this CONFIG_V4L2_FLASH_LED_CLASS symbol has to | ||
30 | be defined in the kernel config. | ||
31 | |||
32 | The driver must call the v4l2_flash_init function to get registered in the | ||
33 | V4L2 subsystem. The function takes six arguments: | ||
34 | - dev : flash device, e.g. an I2C device | ||
35 | - of_node : of_node of the LED, may be NULL if the same as device's | ||
36 | - fled_cdev : LED flash class device to wrap | ||
37 | - iled_cdev : LED flash class device representing indicator LED associated with | ||
38 | fled_cdev, may be NULL | ||
39 | - ops : V4L2 specific ops | ||
40 | * external_strobe_set - defines the source of the flash LED strobe - | ||
41 | V4L2_CID_FLASH_STROBE control or external source, typically | ||
42 | a sensor, which makes it possible to synchronise the flash | ||
43 | strobe start with exposure start, | ||
44 | * intensity_to_led_brightness and led_brightness_to_intensity - perform | ||
45 | enum led_brightness <-> V4L2 intensity conversion in a device | ||
46 | specific manner - they can be used for devices with non-linear | ||
47 | LED current scale. | ||
48 | - config : configuration for V4L2 Flash sub-device | ||
49 | * dev_name - the name of the media entity, unique in the system, | ||
50 | * flash_faults - bitmask of flash faults that the LED flash class | ||
51 | device can report; corresponding LED_FAULT* bit definitions are | ||
52 | available in <linux/led-class-flash.h>, | ||
53 | * torch_intensity - constraints for the LED in TORCH mode | ||
54 | in microamperes, | ||
55 | * indicator_intensity - constraints for the indicator LED | ||
56 | in microamperes, | ||
57 | * has_external_strobe - determines whether the flash strobe source | ||
58 | can be switched to external, | ||
59 | |||
60 | On remove the v4l2_flash_release function has to be called, which takes one | ||
61 | argument - struct v4l2_flash pointer returned previously by v4l2_flash_init. | ||
62 | This function can be safely called with NULL or error pointer argument. | ||
63 | |||
64 | Please refer to drivers/leds/leds-max77693.c for an exemplary usage of the | ||
65 | v4l2 flash wrapper. | ||
66 | |||
67 | Once the V4L2 sub-device is registered by the driver which created the Media | ||
68 | controller device, the sub-device node acts just as a node of a native V4L2 | ||
69 | flash API device would. The calls are simply routed to the LED flash API. | ||
70 | |||
71 | Opening the V4L2 flash sub-device makes the LED subsystem sysfs interface | ||
72 | unavailable. The interface is re-enabled after the V4L2 flash sub-device | ||
73 | is closed. | ||
diff --git a/Documentation/leds/leds-lp5523.txt b/Documentation/leds/leds-lp5523.txt index 5b3e91d4ac59..0dbbd279c9b9 100644 --- a/Documentation/leds/leds-lp5523.txt +++ b/Documentation/leds/leds-lp5523.txt | |||
@@ -49,6 +49,36 @@ There are two ways to run LED patterns. | |||
49 | 2) Firmware interface - LP55xx common interface | 49 | 2) Firmware interface - LP55xx common interface |
50 | For the details, please refer to 'firmware' section in leds-lp55xx.txt | 50 | For the details, please refer to 'firmware' section in leds-lp55xx.txt |
51 | 51 | ||
52 | LP5523 has three master faders. If a channel is mapped to one of | ||
53 | the master faders, its output is dimmed based on the value of the master | ||
54 | fader. | ||
55 | |||
56 | For example, | ||
57 | |||
58 | echo "123000123" > master_fader_leds | ||
59 | |||
60 | creates the following channel-fader mappings: | ||
61 | |||
62 | channel 0,6 to master_fader1 | ||
63 | channel 1,7 to master_fader2 | ||
64 | channel 2,8 to master_fader3 | ||
65 | |||
66 | Then, to have 25% of the original output on channel 0,6: | ||
67 | |||
68 | echo 64 > master_fader1 | ||
69 | |||
70 | To have 0% of the original output (i.e. no output) channel 1,7: | ||
71 | |||
72 | echo 0 > master_fader2 | ||
73 | |||
74 | To have 100% of the original output (i.e. no dimming) on channel 2,8: | ||
75 | |||
76 | echo 255 > master_fader3 | ||
77 | |||
78 | To clear all master fader controls: | ||
79 | |||
80 | echo "000000000" > master_fader_leds | ||
81 | |||
52 | Selftest uses always the current from the platform data. | 82 | Selftest uses always the current from the platform data. |
53 | 83 | ||
54 | Each channel contains led current settings. | 84 | Each channel contains led current settings. |
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 4191614c4651..9ad35f72ab4c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -39,6 +39,32 @@ config LEDS_88PM860X | |||
39 | This option enables support for on-chip LED drivers found on Marvell | 39 | This option enables support for on-chip LED drivers found on Marvell |
40 | Semiconductor 88PM8606 PMIC. | 40 | Semiconductor 88PM8606 PMIC. |
41 | 41 | ||
42 | config LEDS_AAT1290 | ||
43 | tristate "LED support for the AAT1290" | ||
44 | depends on LEDS_CLASS_FLASH | ||
45 | depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS | ||
46 | depends on GPIOLIB | ||
47 | depends on OF | ||
48 | depends on PINCTRL | ||
49 | help | ||
50 | This option enables support for the LEDs on the AAT1290. | ||
51 | |||
52 | config LEDS_BCM6328 | ||
53 | tristate "LED Support for Broadcom BCM6328" | ||
54 | depends on LEDS_CLASS | ||
55 | depends on OF | ||
56 | help | ||
57 | This option enables support for LEDs connected to the BCM6328 | ||
58 | LED HW controller accessed via MMIO registers. | ||
59 | |||
60 | config LEDS_BCM6358 | ||
61 | tristate "LED Support for Broadcom BCM6358" | ||
62 | depends on LEDS_CLASS | ||
63 | depends on OF | ||
64 | help | ||
65 | This option enables support for LEDs connected to the BCM6358 | ||
66 | LED HW controller accessed via MMIO registers. | ||
67 | |||
42 | config LEDS_LM3530 | 68 | config LEDS_LM3530 |
43 | tristate "LCD Backlight driver for LM3530" | 69 | tristate "LCD Backlight driver for LM3530" |
44 | depends on LEDS_CLASS | 70 | depends on LEDS_CLASS |
@@ -179,7 +205,7 @@ config LEDS_PCA9532_GPIO | |||
179 | config LEDS_GPIO | 205 | config LEDS_GPIO |
180 | tristate "LED Support for GPIO connected LEDs" | 206 | tristate "LED Support for GPIO connected LEDs" |
181 | depends on LEDS_CLASS | 207 | depends on LEDS_CLASS |
182 | depends on GPIOLIB | 208 | depends on GPIOLIB || COMPILE_TEST |
183 | help | 209 | help |
184 | This option enables support for the LEDs connected to GPIO | 210 | This option enables support for the LEDs connected to GPIO |
185 | outputs. To be useful the particular board must have LEDs | 211 | outputs. To be useful the particular board must have LEDs |
@@ -203,6 +229,7 @@ config LEDS_LP55XX_COMMON | |||
203 | tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" | 229 | tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" |
204 | depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 | 230 | depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 |
205 | select FW_LOADER | 231 | select FW_LOADER |
232 | select FW_LOADER_USER_HELPER_FALLBACK | ||
206 | help | 233 | help |
207 | This option supports common operations for LP5521/5523/55231/5562/8501 | 234 | This option supports common operations for LP5521/5523/55231/5562/8501 |
208 | devices. | 235 | devices. |
@@ -464,6 +491,25 @@ config LEDS_TCA6507 | |||
464 | LED driver chips accessed via the I2C bus. | 491 | LED driver chips accessed via the I2C bus. |
465 | Driver support brightness control and hardware-assisted blinking. | 492 | Driver support brightness control and hardware-assisted blinking. |
466 | 493 | ||
494 | config LEDS_TLC591XX | ||
495 | tristate "LED driver for TLC59108 and TLC59116 controllers" | ||
496 | depends on LEDS_CLASS && I2C | ||
497 | select REGMAP_I2C | ||
498 | help | ||
499 | This option enables support for Texas Instruments TLC59108 | ||
500 | and TLC59116 LED controllers. | ||
501 | |||
502 | config LEDS_MAX77693 | ||
503 | tristate "LED support for MAX77693 Flash" | ||
504 | depends on LEDS_CLASS_FLASH | ||
505 | depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS | ||
506 | depends on MFD_MAX77693 | ||
507 | depends on OF | ||
508 | help | ||
509 | This option enables support for the flash part of the MAX77693 | ||
510 | multifunction device. It has build in control for two leds in flash | ||
511 | and torch mode. | ||
512 | |||
467 | config LEDS_MAX8997 | 513 | config LEDS_MAX8997 |
468 | tristate "LED support for MAX8997 PMIC" | 514 | tristate "LED support for MAX8997 PMIC" |
469 | depends on LEDS_CLASS && MFD_MAX8997 | 515 | depends on LEDS_CLASS && MFD_MAX8997 |
@@ -495,6 +541,15 @@ config LEDS_MENF21BMC | |||
495 | This driver can also be built as a module. If so the module | 541 | This driver can also be built as a module. If so the module |
496 | will be called leds-menf21bmc. | 542 | will be called leds-menf21bmc. |
497 | 543 | ||
544 | config LEDS_KTD2692 | ||
545 | tristate "LED support for KTD2692 flash LED controller" | ||
546 | depends on LEDS_CLASS_FLASH && GPIOLIB && OF | ||
547 | help | ||
548 | This option enables support for KTD2692 LED flash connected | ||
549 | through ExpressWire interface. | ||
550 | |||
551 | Say Y to enable this driver. | ||
552 | |||
498 | comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" | 553 | comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" |
499 | 554 | ||
500 | config LEDS_BLINKM | 555 | config LEDS_BLINKM |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index bf4609338e10..8d6a24a2f513 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -7,6 +7,9 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o | |||
7 | 7 | ||
8 | # LED Platform Drivers | 8 | # LED Platform Drivers |
9 | obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o | 9 | obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o |
10 | obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o | ||
11 | obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o | ||
12 | obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o | ||
10 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o | 13 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o |
11 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o | 14 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o |
12 | obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o | 15 | obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o |
@@ -31,6 +34,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o | |||
31 | obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o | 34 | obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o |
32 | obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o | 35 | obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o |
33 | obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o | 36 | obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o |
37 | obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o | ||
34 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o | 38 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o |
35 | obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o | 39 | obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o |
36 | obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o | 40 | obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o |
@@ -52,6 +56,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o | |||
52 | obj-$(CONFIG_LEDS_NS2) += leds-ns2.o | 56 | obj-$(CONFIG_LEDS_NS2) += leds-ns2.o |
53 | obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o | 57 | obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o |
54 | obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o | 58 | obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o |
59 | obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o | ||
55 | obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o | 60 | obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o |
56 | obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o | 61 | obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o |
57 | obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o | 62 | obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o |
@@ -59,6 +64,7 @@ obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o | |||
59 | obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o | 64 | obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o |
60 | obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o | 65 | obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o |
61 | obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o | 66 | obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o |
67 | obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o | ||
62 | 68 | ||
63 | # LED SPI Drivers | 69 | # LED SPI Drivers |
64 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 70 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 7fb2a19ac649..beabfbc6f7cd 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
@@ -121,6 +121,11 @@ static void led_timer_function(unsigned long data) | |||
121 | brightness = led_get_brightness(led_cdev); | 121 | brightness = led_get_brightness(led_cdev); |
122 | if (!brightness) { | 122 | if (!brightness) { |
123 | /* Time to switch the LED on. */ | 123 | /* Time to switch the LED on. */ |
124 | if (led_cdev->delayed_set_value) { | ||
125 | led_cdev->blink_brightness = | ||
126 | led_cdev->delayed_set_value; | ||
127 | led_cdev->delayed_set_value = 0; | ||
128 | } | ||
124 | brightness = led_cdev->blink_brightness; | 129 | brightness = led_cdev->blink_brightness; |
125 | delay = led_cdev->blink_delay_on; | 130 | delay = led_cdev->blink_delay_on; |
126 | } else { | 131 | } else { |
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 9886dace5ad2..549de7e24cfd 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c | |||
@@ -119,10 +119,11 @@ void led_set_brightness(struct led_classdev *led_cdev, | |||
119 | { | 119 | { |
120 | int ret = 0; | 120 | int ret = 0; |
121 | 121 | ||
122 | /* delay brightness setting if need to stop soft-blink timer */ | 122 | /* delay brightness if soft-blink is active */ |
123 | if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { | 123 | if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { |
124 | led_cdev->delayed_set_value = brightness; | 124 | led_cdev->delayed_set_value = brightness; |
125 | schedule_work(&led_cdev->set_brightness_work); | 125 | if (brightness == LED_OFF) |
126 | schedule_work(&led_cdev->set_brightness_work); | ||
126 | return; | 127 | return; |
127 | } | 128 | } |
128 | 129 | ||
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c new file mode 100644 index 000000000000..fd7c25fd29c1 --- /dev/null +++ b/drivers/leds/leds-aat1290.c | |||
@@ -0,0 +1,576 @@ | |||
1 | /* | ||
2 | * LED Flash class driver for the AAT1290 | ||
3 | * 1.5A Step-Up Current Regulator for Flash LEDs | ||
4 | * | ||
5 | * Copyright (C) 2015, Samsung Electronics Co., Ltd. | ||
6 | * Author: Jacek Anaszewski <j.anaszewski@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/gpio/consumer.h> | ||
15 | #include <linux/led-class-flash.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/pinctrl/consumer.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <media/v4l2-flash-led-class.h> | ||
25 | |||
26 | #define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 | ||
27 | #define AAT1290_MAX_MM_CURR_PERCENT_0 16 | ||
28 | #define AAT1290_MAX_MM_CURR_PERCENT_100 1 | ||
29 | |||
30 | #define AAT1290_FLASH_SAFETY_TIMER_ADDR 18 | ||
31 | |||
32 | #define AAT1290_MOVIE_MODE_CONFIG_ADDR 19 | ||
33 | #define AAT1290_MOVIE_MODE_OFF 1 | ||
34 | #define AAT1290_MOVIE_MODE_ON 3 | ||
35 | |||
36 | #define AAT1290_MM_CURRENT_RATIO_ADDR 20 | ||
37 | #define AAT1290_MM_TO_FL_1_92 1 | ||
38 | |||
39 | #define AAT1290_MM_TO_FL_RATIO 1000 / 1920 | ||
40 | #define AAT1290_MAX_MM_CURRENT(fl_max) (fl_max * AAT1290_MM_TO_FL_RATIO) | ||
41 | |||
42 | #define AAT1290_LATCH_TIME_MIN_US 500 | ||
43 | #define AAT1290_LATCH_TIME_MAX_US 1000 | ||
44 | #define AAT1290_EN_SET_TICK_TIME_US 1 | ||
45 | #define AAT1290_FLEN_OFF_DELAY_TIME_US 10 | ||
46 | #define AAT1290_FLASH_TM_NUM_LEVELS 16 | ||
47 | #define AAT1290_MM_CURRENT_SCALE_SIZE 15 | ||
48 | |||
49 | |||
50 | struct aat1290_led_config_data { | ||
51 | /* maximum LED current in movie mode */ | ||
52 | u32 max_mm_current; | ||
53 | /* maximum LED current in flash mode */ | ||
54 | u32 max_flash_current; | ||
55 | /* maximum flash timeout */ | ||
56 | u32 max_flash_tm; | ||
57 | /* external strobe capability */ | ||
58 | bool has_external_strobe; | ||
59 | /* max LED brightness level */ | ||
60 | enum led_brightness max_brightness; | ||
61 | }; | ||
62 | |||
63 | struct aat1290_led { | ||
64 | /* platform device data */ | ||
65 | struct platform_device *pdev; | ||
66 | /* secures access to the device */ | ||
67 | struct mutex lock; | ||
68 | |||
69 | /* corresponding LED Flash class device */ | ||
70 | struct led_classdev_flash fled_cdev; | ||
71 | /* V4L2 Flash device */ | ||
72 | struct v4l2_flash *v4l2_flash; | ||
73 | |||
74 | /* FLEN pin */ | ||
75 | struct gpio_desc *gpio_fl_en; | ||
76 | /* EN|SET pin */ | ||
77 | struct gpio_desc *gpio_en_set; | ||
78 | /* movie mode current scale */ | ||
79 | int *mm_current_scale; | ||
80 | /* device mode */ | ||
81 | bool movie_mode; | ||
82 | |||
83 | /* brightness cache */ | ||
84 | unsigned int torch_brightness; | ||
85 | /* assures led-triggers compatibility */ | ||
86 | struct work_struct work_brightness_set; | ||
87 | }; | ||
88 | |||
89 | static struct aat1290_led *fled_cdev_to_led( | ||
90 | struct led_classdev_flash *fled_cdev) | ||
91 | { | ||
92 | return container_of(fled_cdev, struct aat1290_led, fled_cdev); | ||
93 | } | ||
94 | |||
95 | static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value) | ||
96 | { | ||
97 | int i; | ||
98 | |||
99 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
100 | gpiod_direction_output(led->gpio_en_set, 0); | ||
101 | |||
102 | udelay(AAT1290_FLEN_OFF_DELAY_TIME_US); | ||
103 | |||
104 | /* write address */ | ||
105 | for (i = 0; i < addr; ++i) { | ||
106 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
107 | gpiod_direction_output(led->gpio_en_set, 0); | ||
108 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
109 | gpiod_direction_output(led->gpio_en_set, 1); | ||
110 | } | ||
111 | |||
112 | usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); | ||
113 | |||
114 | /* write data */ | ||
115 | for (i = 0; i < value; ++i) { | ||
116 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
117 | gpiod_direction_output(led->gpio_en_set, 0); | ||
118 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
119 | gpiod_direction_output(led->gpio_en_set, 1); | ||
120 | } | ||
121 | |||
122 | usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); | ||
123 | } | ||
124 | |||
125 | static void aat1290_set_flash_safety_timer(struct aat1290_led *led, | ||
126 | unsigned int micro_sec) | ||
127 | { | ||
128 | struct led_classdev_flash *fled_cdev = &led->fled_cdev; | ||
129 | struct led_flash_setting *flash_tm = &fled_cdev->timeout; | ||
130 | int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS - | ||
131 | (micro_sec / flash_tm->step) + 1; | ||
132 | |||
133 | aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR, | ||
134 | flash_tm_reg); | ||
135 | } | ||
136 | |||
137 | static void aat1290_brightness_set(struct aat1290_led *led, | ||
138 | enum led_brightness brightness) | ||
139 | { | ||
140 | mutex_lock(&led->lock); | ||
141 | |||
142 | if (brightness == 0) { | ||
143 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
144 | gpiod_direction_output(led->gpio_en_set, 0); | ||
145 | led->movie_mode = false; | ||
146 | } else { | ||
147 | if (!led->movie_mode) { | ||
148 | aat1290_as2cwire_write(led, | ||
149 | AAT1290_MM_CURRENT_RATIO_ADDR, | ||
150 | AAT1290_MM_TO_FL_1_92); | ||
151 | led->movie_mode = true; | ||
152 | } | ||
153 | |||
154 | aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR, | ||
155 | AAT1290_MAX_MM_CURR_PERCENT_0 - brightness); | ||
156 | aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR, | ||
157 | AAT1290_MOVIE_MODE_ON); | ||
158 | } | ||
159 | |||
160 | mutex_unlock(&led->lock); | ||
161 | } | ||
162 | |||
163 | /* LED subsystem callbacks */ | ||
164 | |||
165 | static void aat1290_brightness_set_work(struct work_struct *work) | ||
166 | { | ||
167 | struct aat1290_led *led = | ||
168 | container_of(work, struct aat1290_led, work_brightness_set); | ||
169 | |||
170 | aat1290_brightness_set(led, led->torch_brightness); | ||
171 | } | ||
172 | |||
173 | static void aat1290_led_brightness_set(struct led_classdev *led_cdev, | ||
174 | enum led_brightness brightness) | ||
175 | { | ||
176 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
177 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
178 | |||
179 | led->torch_brightness = brightness; | ||
180 | schedule_work(&led->work_brightness_set); | ||
181 | } | ||
182 | |||
183 | static int aat1290_led_brightness_set_sync(struct led_classdev *led_cdev, | ||
184 | enum led_brightness brightness) | ||
185 | { | ||
186 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
187 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
188 | |||
189 | aat1290_brightness_set(led, brightness); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int aat1290_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, | ||
195 | bool state) | ||
196 | |||
197 | { | ||
198 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
199 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | ||
200 | struct led_flash_setting *timeout = &fled_cdev->timeout; | ||
201 | |||
202 | mutex_lock(&led->lock); | ||
203 | |||
204 | if (state) { | ||
205 | aat1290_set_flash_safety_timer(led, timeout->val); | ||
206 | gpiod_direction_output(led->gpio_fl_en, 1); | ||
207 | } else { | ||
208 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
209 | gpiod_direction_output(led->gpio_en_set, 0); | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * To reenter movie mode after a flash event the part must be cycled | ||
214 | * off and back on to reset the movie mode and reprogrammed via the | ||
215 | * AS2Cwire. Therefore the brightness and movie_mode properties needs | ||
216 | * to be updated here to reflect the actual state. | ||
217 | */ | ||
218 | led_cdev->brightness = 0; | ||
219 | led->movie_mode = false; | ||
220 | |||
221 | mutex_unlock(&led->lock); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, | ||
227 | u32 timeout) | ||
228 | { | ||
229 | /* | ||
230 | * Don't do anything - flash timeout is cached in the led-class-flash | ||
231 | * core and will be applied in the strobe_set op, as writing the | ||
232 | * safety timer register spuriously turns the torch mode on. | ||
233 | */ | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int aat1290_led_parse_dt(struct aat1290_led *led, | ||
239 | struct aat1290_led_config_data *cfg, | ||
240 | struct device_node **sub_node) | ||
241 | { | ||
242 | struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; | ||
243 | struct device *dev = &led->pdev->dev; | ||
244 | struct device_node *child_node; | ||
245 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
246 | struct pinctrl *pinctrl; | ||
247 | #endif | ||
248 | int ret = 0; | ||
249 | |||
250 | led->gpio_fl_en = devm_gpiod_get(dev, "flen", GPIOD_ASIS); | ||
251 | if (IS_ERR(led->gpio_fl_en)) { | ||
252 | ret = PTR_ERR(led->gpio_fl_en); | ||
253 | dev_err(dev, "Unable to claim gpio \"flen\".\n"); | ||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | led->gpio_en_set = devm_gpiod_get(dev, "enset", GPIOD_ASIS); | ||
258 | if (IS_ERR(led->gpio_en_set)) { | ||
259 | ret = PTR_ERR(led->gpio_en_set); | ||
260 | dev_err(dev, "Unable to claim gpio \"enset\".\n"); | ||
261 | return ret; | ||
262 | } | ||
263 | |||
264 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
265 | pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev); | ||
266 | if (IS_ERR(pinctrl)) { | ||
267 | cfg->has_external_strobe = false; | ||
268 | dev_info(dev, | ||
269 | "No support for external strobe detected.\n"); | ||
270 | } else { | ||
271 | cfg->has_external_strobe = true; | ||
272 | } | ||
273 | #endif | ||
274 | |||
275 | child_node = of_get_next_available_child(dev->of_node, NULL); | ||
276 | if (!child_node) { | ||
277 | dev_err(dev, "No DT child node found for connected LED.\n"); | ||
278 | return -EINVAL; | ||
279 | } | ||
280 | |||
281 | led_cdev->name = of_get_property(child_node, "label", NULL) ? : | ||
282 | child_node->name; | ||
283 | |||
284 | ret = of_property_read_u32(child_node, "led-max-microamp", | ||
285 | &cfg->max_mm_current); | ||
286 | /* | ||
287 | * led-max-microamp will default to 1/20 of flash-max-microamp | ||
288 | * in case it is missing. | ||
289 | */ | ||
290 | if (ret < 0) | ||
291 | dev_warn(dev, | ||
292 | "led-max-microamp DT property missing\n"); | ||
293 | |||
294 | ret = of_property_read_u32(child_node, "flash-max-microamp", | ||
295 | &cfg->max_flash_current); | ||
296 | if (ret < 0) { | ||
297 | dev_err(dev, | ||
298 | "flash-max-microamp DT property missing\n"); | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | ret = of_property_read_u32(child_node, "flash-max-timeout-us", | ||
303 | &cfg->max_flash_tm); | ||
304 | if (ret < 0) { | ||
305 | dev_err(dev, | ||
306 | "flash-max-timeout-us DT property missing\n"); | ||
307 | return ret; | ||
308 | } | ||
309 | |||
310 | of_node_put(child_node); | ||
311 | |||
312 | *sub_node = child_node; | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | static void aat1290_led_validate_mm_current(struct aat1290_led *led, | ||
318 | struct aat1290_led_config_data *cfg) | ||
319 | { | ||
320 | int i, b = 0, e = AAT1290_MM_CURRENT_SCALE_SIZE; | ||
321 | |||
322 | while (e - b > 1) { | ||
323 | i = b + (e - b) / 2; | ||
324 | if (cfg->max_mm_current < led->mm_current_scale[i]) | ||
325 | e = i; | ||
326 | else | ||
327 | b = i; | ||
328 | } | ||
329 | |||
330 | cfg->max_mm_current = led->mm_current_scale[b]; | ||
331 | cfg->max_brightness = b + 1; | ||
332 | } | ||
333 | |||
334 | int init_mm_current_scale(struct aat1290_led *led, | ||
335 | struct aat1290_led_config_data *cfg) | ||
336 | { | ||
337 | int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, | ||
338 | 63, 71, 79, 89, 100 }; | ||
339 | int i, max_mm_current = | ||
340 | AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); | ||
341 | |||
342 | led->mm_current_scale = devm_kzalloc(&led->pdev->dev, | ||
343 | sizeof(max_mm_current_percent), | ||
344 | GFP_KERNEL); | ||
345 | if (!led->mm_current_scale) | ||
346 | return -ENOMEM; | ||
347 | |||
348 | for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i) | ||
349 | led->mm_current_scale[i] = max_mm_current * | ||
350 | max_mm_current_percent[i] / 100; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int aat1290_led_get_configuration(struct aat1290_led *led, | ||
356 | struct aat1290_led_config_data *cfg, | ||
357 | struct device_node **sub_node) | ||
358 | { | ||
359 | int ret; | ||
360 | |||
361 | ret = aat1290_led_parse_dt(led, cfg, sub_node); | ||
362 | if (ret < 0) | ||
363 | return ret; | ||
364 | /* | ||
365 | * Init non-linear movie mode current scale basing | ||
366 | * on the max flash current from led configuration. | ||
367 | */ | ||
368 | ret = init_mm_current_scale(led, cfg); | ||
369 | if (ret < 0) | ||
370 | return ret; | ||
371 | |||
372 | aat1290_led_validate_mm_current(led, cfg); | ||
373 | |||
374 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
375 | #else | ||
376 | devm_kfree(&led->pdev->dev, led->mm_current_scale); | ||
377 | #endif | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static void aat1290_init_flash_timeout(struct aat1290_led *led, | ||
383 | struct aat1290_led_config_data *cfg) | ||
384 | { | ||
385 | struct led_classdev_flash *fled_cdev = &led->fled_cdev; | ||
386 | struct led_flash_setting *setting; | ||
387 | |||
388 | /* Init flash timeout setting */ | ||
389 | setting = &fled_cdev->timeout; | ||
390 | setting->min = cfg->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS; | ||
391 | setting->max = cfg->max_flash_tm; | ||
392 | setting->step = setting->min; | ||
393 | setting->val = setting->max; | ||
394 | } | ||
395 | |||
396 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
397 | static enum led_brightness aat1290_intensity_to_brightness( | ||
398 | struct v4l2_flash *v4l2_flash, | ||
399 | s32 intensity) | ||
400 | { | ||
401 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | ||
402 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
403 | int i; | ||
404 | |||
405 | for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i) | ||
406 | if (intensity >= led->mm_current_scale[i]) | ||
407 | return i + 1; | ||
408 | |||
409 | return 1; | ||
410 | } | ||
411 | |||
412 | static s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash, | ||
413 | enum led_brightness brightness) | ||
414 | { | ||
415 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | ||
416 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
417 | |||
418 | return led->mm_current_scale[brightness - 1]; | ||
419 | } | ||
420 | |||
421 | static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash, | ||
422 | bool enable) | ||
423 | { | ||
424 | struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev); | ||
425 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | ||
426 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | ||
427 | struct pinctrl *pinctrl; | ||
428 | |||
429 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
430 | gpiod_direction_output(led->gpio_en_set, 0); | ||
431 | |||
432 | led->movie_mode = false; | ||
433 | led_cdev->brightness = 0; | ||
434 | |||
435 | pinctrl = devm_pinctrl_get_select(&led->pdev->dev, | ||
436 | enable ? "isp" : "host"); | ||
437 | if (IS_ERR(pinctrl)) { | ||
438 | dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n"); | ||
439 | return PTR_ERR(pinctrl); | ||
440 | } | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static void aat1290_init_v4l2_flash_config(struct aat1290_led *led, | ||
446 | struct aat1290_led_config_data *led_cfg, | ||
447 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
448 | { | ||
449 | struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; | ||
450 | struct led_flash_setting *s; | ||
451 | |||
452 | strlcpy(v4l2_sd_cfg->dev_name, led_cdev->name, | ||
453 | sizeof(v4l2_sd_cfg->dev_name)); | ||
454 | |||
455 | s = &v4l2_sd_cfg->torch_intensity; | ||
456 | s->min = led->mm_current_scale[0]; | ||
457 | s->max = led_cfg->max_mm_current; | ||
458 | s->step = 1; | ||
459 | s->val = s->max; | ||
460 | |||
461 | v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe; | ||
462 | } | ||
463 | |||
464 | static const struct v4l2_flash_ops v4l2_flash_ops = { | ||
465 | .external_strobe_set = aat1290_led_external_strobe_set, | ||
466 | .intensity_to_led_brightness = aat1290_intensity_to_brightness, | ||
467 | .led_brightness_to_intensity = aat1290_brightness_to_intensity, | ||
468 | }; | ||
469 | #else | ||
470 | static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led, | ||
471 | struct aat1290_led_config_data *led_cfg, | ||
472 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
473 | { | ||
474 | } | ||
475 | static const struct v4l2_flash_ops v4l2_flash_ops; | ||
476 | #endif | ||
477 | |||
478 | static const struct led_flash_ops flash_ops = { | ||
479 | .strobe_set = aat1290_led_flash_strobe_set, | ||
480 | .timeout_set = aat1290_led_flash_timeout_set, | ||
481 | }; | ||
482 | |||
483 | static int aat1290_led_probe(struct platform_device *pdev) | ||
484 | { | ||
485 | struct device *dev = &pdev->dev; | ||
486 | struct device_node *sub_node = NULL; | ||
487 | struct aat1290_led *led; | ||
488 | struct led_classdev *led_cdev; | ||
489 | struct led_classdev_flash *fled_cdev; | ||
490 | struct aat1290_led_config_data led_cfg = {}; | ||
491 | struct v4l2_flash_config v4l2_sd_cfg = {}; | ||
492 | int ret; | ||
493 | |||
494 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); | ||
495 | if (!led) | ||
496 | return -ENOMEM; | ||
497 | |||
498 | led->pdev = pdev; | ||
499 | platform_set_drvdata(pdev, led); | ||
500 | |||
501 | fled_cdev = &led->fled_cdev; | ||
502 | fled_cdev->ops = &flash_ops; | ||
503 | led_cdev = &fled_cdev->led_cdev; | ||
504 | |||
505 | ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node); | ||
506 | if (ret < 0) | ||
507 | return ret; | ||
508 | |||
509 | mutex_init(&led->lock); | ||
510 | |||
511 | /* Initialize LED Flash class device */ | ||
512 | led_cdev->brightness_set = aat1290_led_brightness_set; | ||
513 | led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync; | ||
514 | led_cdev->max_brightness = led_cfg.max_brightness; | ||
515 | led_cdev->flags |= LED_DEV_CAP_FLASH; | ||
516 | INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work); | ||
517 | |||
518 | aat1290_init_flash_timeout(led, &led_cfg); | ||
519 | |||
520 | /* Register LED Flash class device */ | ||
521 | ret = led_classdev_flash_register(&pdev->dev, fled_cdev); | ||
522 | if (ret < 0) | ||
523 | goto err_flash_register; | ||
524 | |||
525 | aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg); | ||
526 | |||
527 | /* Create V4L2 Flash subdev. */ | ||
528 | led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL, | ||
529 | &v4l2_flash_ops, &v4l2_sd_cfg); | ||
530 | if (IS_ERR(led->v4l2_flash)) { | ||
531 | ret = PTR_ERR(led->v4l2_flash); | ||
532 | goto error_v4l2_flash_init; | ||
533 | } | ||
534 | |||
535 | return 0; | ||
536 | |||
537 | error_v4l2_flash_init: | ||
538 | led_classdev_flash_unregister(fled_cdev); | ||
539 | err_flash_register: | ||
540 | mutex_destroy(&led->lock); | ||
541 | |||
542 | return ret; | ||
543 | } | ||
544 | |||
545 | static int aat1290_led_remove(struct platform_device *pdev) | ||
546 | { | ||
547 | struct aat1290_led *led = platform_get_drvdata(pdev); | ||
548 | |||
549 | v4l2_flash_release(led->v4l2_flash); | ||
550 | led_classdev_flash_unregister(&led->fled_cdev); | ||
551 | cancel_work_sync(&led->work_brightness_set); | ||
552 | |||
553 | mutex_destroy(&led->lock); | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static const struct of_device_id aat1290_led_dt_match[] = { | ||
559 | { .compatible = "skyworks,aat1290" }, | ||
560 | {}, | ||
561 | }; | ||
562 | |||
563 | static struct platform_driver aat1290_led_driver = { | ||
564 | .probe = aat1290_led_probe, | ||
565 | .remove = aat1290_led_remove, | ||
566 | .driver = { | ||
567 | .name = "aat1290", | ||
568 | .of_match_table = aat1290_led_dt_match, | ||
569 | }, | ||
570 | }; | ||
571 | |||
572 | module_platform_driver(aat1290_led_driver); | ||
573 | |||
574 | MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); | ||
575 | MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs"); | ||
576 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c new file mode 100644 index 000000000000..986fe1e28f84 --- /dev/null +++ b/drivers/leds/leds-bcm6328.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * Driver for BCM6328 memory-mapped LEDs, based on leds-syscon.c | ||
3 | * | ||
4 | * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com> | ||
5 | * Copyright 2015 Jonas Gorski <jogo@openwrt.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/leds.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | |||
19 | #define BCM6328_REG_INIT 0x00 | ||
20 | #define BCM6328_REG_MODE_HI 0x04 | ||
21 | #define BCM6328_REG_MODE_LO 0x08 | ||
22 | #define BCM6328_REG_HWDIS 0x0c | ||
23 | #define BCM6328_REG_STROBE 0x10 | ||
24 | #define BCM6328_REG_LNKACTSEL_HI 0x14 | ||
25 | #define BCM6328_REG_LNKACTSEL_LO 0x18 | ||
26 | #define BCM6328_REG_RBACK 0x1c | ||
27 | #define BCM6328_REG_SERMUX 0x20 | ||
28 | |||
29 | #define BCM6328_LED_MAX_COUNT 24 | ||
30 | #define BCM6328_LED_DEF_DELAY 500 | ||
31 | #define BCM6328_LED_INTERVAL_MS 20 | ||
32 | |||
33 | #define BCM6328_LED_INTV_MASK 0x3f | ||
34 | #define BCM6328_LED_FAST_INTV_SHIFT 6 | ||
35 | #define BCM6328_LED_FAST_INTV_MASK (BCM6328_LED_INTV_MASK << \ | ||
36 | BCM6328_LED_FAST_INTV_SHIFT) | ||
37 | #define BCM6328_SERIAL_LED_EN BIT(12) | ||
38 | #define BCM6328_SERIAL_LED_MUX BIT(13) | ||
39 | #define BCM6328_SERIAL_LED_CLK_NPOL BIT(14) | ||
40 | #define BCM6328_SERIAL_LED_DATA_PPOL BIT(15) | ||
41 | #define BCM6328_SERIAL_LED_SHIFT_DIR BIT(16) | ||
42 | #define BCM6328_LED_SHIFT_TEST BIT(30) | ||
43 | #define BCM6328_LED_TEST BIT(31) | ||
44 | |||
45 | #define BCM6328_LED_MODE_MASK 3 | ||
46 | #define BCM6328_LED_MODE_OFF 0 | ||
47 | #define BCM6328_LED_MODE_FAST 1 | ||
48 | #define BCM6328_LED_MODE_BLINK 2 | ||
49 | #define BCM6328_LED_MODE_ON 3 | ||
50 | #define BCM6328_LED_SHIFT(X) ((X) << 1) | ||
51 | |||
52 | /** | ||
53 | * struct bcm6328_led - state container for bcm6328 based LEDs | ||
54 | * @cdev: LED class device for this LED | ||
55 | * @mem: memory resource | ||
56 | * @lock: memory lock | ||
57 | * @pin: LED pin number | ||
58 | * @blink_leds: blinking LEDs | ||
59 | * @blink_delay: blinking delay | ||
60 | * @active_low: LED is active low | ||
61 | */ | ||
62 | struct bcm6328_led { | ||
63 | struct led_classdev cdev; | ||
64 | void __iomem *mem; | ||
65 | spinlock_t *lock; | ||
66 | unsigned long pin; | ||
67 | unsigned long *blink_leds; | ||
68 | unsigned long *blink_delay; | ||
69 | bool active_low; | ||
70 | }; | ||
71 | |||
72 | static void bcm6328_led_write(void __iomem *reg, unsigned long data) | ||
73 | { | ||
74 | iowrite32be(data, reg); | ||
75 | } | ||
76 | |||
77 | static unsigned long bcm6328_led_read(void __iomem *reg) | ||
78 | { | ||
79 | return ioread32be(reg); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * LEDMode 64 bits / 24 LEDs | ||
84 | * bits [31:0] -> LEDs 8-23 | ||
85 | * bits [47:32] -> LEDs 0-7 | ||
86 | * bits [63:48] -> unused | ||
87 | */ | ||
88 | static unsigned long bcm6328_pin2shift(unsigned long pin) | ||
89 | { | ||
90 | if (pin < 8) | ||
91 | return pin + 16; /* LEDs 0-7 (bits 47:32) */ | ||
92 | else | ||
93 | return pin - 8; /* LEDs 8-23 (bits 31:0) */ | ||
94 | } | ||
95 | |||
96 | static void bcm6328_led_mode(struct bcm6328_led *led, unsigned long value) | ||
97 | { | ||
98 | void __iomem *mode; | ||
99 | unsigned long val, shift; | ||
100 | |||
101 | shift = bcm6328_pin2shift(led->pin); | ||
102 | if (shift / 16) | ||
103 | mode = led->mem + BCM6328_REG_MODE_HI; | ||
104 | else | ||
105 | mode = led->mem + BCM6328_REG_MODE_LO; | ||
106 | |||
107 | val = bcm6328_led_read(mode); | ||
108 | val &= ~(BCM6328_LED_MODE_MASK << BCM6328_LED_SHIFT(shift % 16)); | ||
109 | val |= (value << BCM6328_LED_SHIFT(shift % 16)); | ||
110 | bcm6328_led_write(mode, val); | ||
111 | } | ||
112 | |||
113 | static void bcm6328_led_set(struct led_classdev *led_cdev, | ||
114 | enum led_brightness value) | ||
115 | { | ||
116 | struct bcm6328_led *led = | ||
117 | container_of(led_cdev, struct bcm6328_led, cdev); | ||
118 | unsigned long flags; | ||
119 | |||
120 | spin_lock_irqsave(led->lock, flags); | ||
121 | *(led->blink_leds) &= ~BIT(led->pin); | ||
122 | if ((led->active_low && value == LED_OFF) || | ||
123 | (!led->active_low && value != LED_OFF)) | ||
124 | bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); | ||
125 | else | ||
126 | bcm6328_led_mode(led, BCM6328_LED_MODE_ON); | ||
127 | spin_unlock_irqrestore(led->lock, flags); | ||
128 | } | ||
129 | |||
130 | static int bcm6328_blink_set(struct led_classdev *led_cdev, | ||
131 | unsigned long *delay_on, unsigned long *delay_off) | ||
132 | { | ||
133 | struct bcm6328_led *led = | ||
134 | container_of(led_cdev, struct bcm6328_led, cdev); | ||
135 | unsigned long delay, flags; | ||
136 | |||
137 | if (!*delay_on) | ||
138 | *delay_on = BCM6328_LED_DEF_DELAY; | ||
139 | if (!*delay_off) | ||
140 | *delay_off = BCM6328_LED_DEF_DELAY; | ||
141 | |||
142 | if (*delay_on != *delay_off) { | ||
143 | dev_dbg(led_cdev->dev, | ||
144 | "fallback to soft blinking (delay_on != delay_off)\n"); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | delay = *delay_on / BCM6328_LED_INTERVAL_MS; | ||
149 | if (delay == 0) | ||
150 | delay = 1; | ||
151 | else if (delay > BCM6328_LED_INTV_MASK) { | ||
152 | dev_dbg(led_cdev->dev, | ||
153 | "fallback to soft blinking (delay > %ums)\n", | ||
154 | BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | |||
158 | spin_lock_irqsave(led->lock, flags); | ||
159 | if (*(led->blink_leds) == 0 || | ||
160 | *(led->blink_leds) == BIT(led->pin) || | ||
161 | *(led->blink_delay) == delay) { | ||
162 | unsigned long val; | ||
163 | |||
164 | *(led->blink_leds) |= BIT(led->pin); | ||
165 | *(led->blink_delay) = delay; | ||
166 | |||
167 | val = bcm6328_led_read(led->mem + BCM6328_REG_INIT); | ||
168 | val &= ~BCM6328_LED_FAST_INTV_MASK; | ||
169 | val |= (delay << BCM6328_LED_FAST_INTV_SHIFT); | ||
170 | bcm6328_led_write(led->mem + BCM6328_REG_INIT, val); | ||
171 | |||
172 | bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK); | ||
173 | |||
174 | spin_unlock_irqrestore(led->lock, flags); | ||
175 | } else { | ||
176 | spin_unlock_irqrestore(led->lock, flags); | ||
177 | dev_dbg(led_cdev->dev, | ||
178 | "fallback to soft blinking (delay already set)\n"); | ||
179 | return -EINVAL; | ||
180 | } | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg, | ||
186 | void __iomem *mem, spinlock_t *lock) | ||
187 | { | ||
188 | int i, cnt; | ||
189 | unsigned long flags, val; | ||
190 | |||
191 | spin_lock_irqsave(lock, flags); | ||
192 | val = bcm6328_led_read(mem + BCM6328_REG_HWDIS); | ||
193 | val &= ~BIT(reg); | ||
194 | bcm6328_led_write(mem + BCM6328_REG_HWDIS, val); | ||
195 | spin_unlock_irqrestore(lock, flags); | ||
196 | |||
197 | /* Only LEDs 0-7 can be activity/link controlled */ | ||
198 | if (reg >= 8) | ||
199 | return 0; | ||
200 | |||
201 | cnt = of_property_count_elems_of_size(nc, "brcm,link-signal-sources", | ||
202 | sizeof(u32)); | ||
203 | for (i = 0; i < cnt; i++) { | ||
204 | u32 sel; | ||
205 | void __iomem *addr; | ||
206 | |||
207 | if (reg < 4) | ||
208 | addr = mem + BCM6328_REG_LNKACTSEL_LO; | ||
209 | else | ||
210 | addr = mem + BCM6328_REG_LNKACTSEL_HI; | ||
211 | |||
212 | of_property_read_u32_index(nc, "brcm,link-signal-sources", i, | ||
213 | &sel); | ||
214 | |||
215 | if (reg / 4 != sel / 4) { | ||
216 | dev_warn(dev, "invalid link signal source\n"); | ||
217 | continue; | ||
218 | } | ||
219 | |||
220 | spin_lock_irqsave(lock, flags); | ||
221 | val = bcm6328_led_read(addr); | ||
222 | val |= (BIT(reg) << (((sel % 4) * 4) + 16)); | ||
223 | bcm6328_led_write(addr, val); | ||
224 | spin_unlock_irqrestore(lock, flags); | ||
225 | } | ||
226 | |||
227 | cnt = of_property_count_elems_of_size(nc, | ||
228 | "brcm,activity-signal-sources", | ||
229 | sizeof(u32)); | ||
230 | for (i = 0; i < cnt; i++) { | ||
231 | u32 sel; | ||
232 | void __iomem *addr; | ||
233 | |||
234 | if (reg < 4) | ||
235 | addr = mem + BCM6328_REG_LNKACTSEL_LO; | ||
236 | else | ||
237 | addr = mem + BCM6328_REG_LNKACTSEL_HI; | ||
238 | |||
239 | of_property_read_u32_index(nc, "brcm,activity-signal-sources", | ||
240 | i, &sel); | ||
241 | |||
242 | if (reg / 4 != sel / 4) { | ||
243 | dev_warn(dev, "invalid activity signal source\n"); | ||
244 | continue; | ||
245 | } | ||
246 | |||
247 | spin_lock_irqsave(lock, flags); | ||
248 | val = bcm6328_led_read(addr); | ||
249 | val |= (BIT(reg) << ((sel % 4) * 4)); | ||
250 | bcm6328_led_write(addr, val); | ||
251 | spin_unlock_irqrestore(lock, flags); | ||
252 | } | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg, | ||
258 | void __iomem *mem, spinlock_t *lock, | ||
259 | unsigned long *blink_leds, unsigned long *blink_delay) | ||
260 | { | ||
261 | struct bcm6328_led *led; | ||
262 | unsigned long flags; | ||
263 | const char *state; | ||
264 | int rc; | ||
265 | |||
266 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); | ||
267 | if (!led) | ||
268 | return -ENOMEM; | ||
269 | |||
270 | led->pin = reg; | ||
271 | led->mem = mem; | ||
272 | led->lock = lock; | ||
273 | led->blink_leds = blink_leds; | ||
274 | led->blink_delay = blink_delay; | ||
275 | |||
276 | if (of_property_read_bool(nc, "active-low")) | ||
277 | led->active_low = true; | ||
278 | |||
279 | led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name; | ||
280 | led->cdev.default_trigger = of_get_property(nc, | ||
281 | "linux,default-trigger", | ||
282 | NULL); | ||
283 | |||
284 | if (!of_property_read_string(nc, "default-state", &state)) { | ||
285 | spin_lock_irqsave(lock, flags); | ||
286 | if (!strcmp(state, "on")) { | ||
287 | led->cdev.brightness = LED_FULL; | ||
288 | bcm6328_led_mode(led, BCM6328_LED_MODE_ON); | ||
289 | } else if (!strcmp(state, "keep")) { | ||
290 | void __iomem *mode; | ||
291 | unsigned long val, shift; | ||
292 | |||
293 | shift = bcm6328_pin2shift(led->pin); | ||
294 | if (shift / 16) | ||
295 | mode = mem + BCM6328_REG_MODE_HI; | ||
296 | else | ||
297 | mode = mem + BCM6328_REG_MODE_LO; | ||
298 | |||
299 | val = bcm6328_led_read(mode) >> (shift % 16); | ||
300 | val &= BCM6328_LED_MODE_MASK; | ||
301 | if (val == BCM6328_LED_MODE_ON) | ||
302 | led->cdev.brightness = LED_FULL; | ||
303 | else { | ||
304 | led->cdev.brightness = LED_OFF; | ||
305 | bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); | ||
306 | } | ||
307 | } else { | ||
308 | led->cdev.brightness = LED_OFF; | ||
309 | bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); | ||
310 | } | ||
311 | spin_unlock_irqrestore(lock, flags); | ||
312 | } | ||
313 | |||
314 | led->cdev.brightness_set = bcm6328_led_set; | ||
315 | led->cdev.blink_set = bcm6328_blink_set; | ||
316 | |||
317 | rc = led_classdev_register(dev, &led->cdev); | ||
318 | if (rc < 0) | ||
319 | return rc; | ||
320 | |||
321 | dev_dbg(dev, "registered LED %s\n", led->cdev.name); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static int bcm6328_leds_probe(struct platform_device *pdev) | ||
327 | { | ||
328 | struct device *dev = &pdev->dev; | ||
329 | struct device_node *np = pdev->dev.of_node; | ||
330 | struct device_node *child; | ||
331 | struct resource *mem_r; | ||
332 | void __iomem *mem; | ||
333 | spinlock_t *lock; | ||
334 | unsigned long val, *blink_leds, *blink_delay; | ||
335 | |||
336 | mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
337 | if (!mem_r) | ||
338 | return -EINVAL; | ||
339 | |||
340 | mem = devm_ioremap_resource(dev, mem_r); | ||
341 | if (IS_ERR(mem)) | ||
342 | return PTR_ERR(mem); | ||
343 | |||
344 | lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); | ||
345 | if (!lock) | ||
346 | return -ENOMEM; | ||
347 | |||
348 | blink_leds = devm_kzalloc(dev, sizeof(*blink_leds), GFP_KERNEL); | ||
349 | if (!blink_leds) | ||
350 | return -ENOMEM; | ||
351 | |||
352 | blink_delay = devm_kzalloc(dev, sizeof(*blink_delay), GFP_KERNEL); | ||
353 | if (!blink_delay) | ||
354 | return -ENOMEM; | ||
355 | |||
356 | spin_lock_init(lock); | ||
357 | |||
358 | bcm6328_led_write(mem + BCM6328_REG_HWDIS, ~0); | ||
359 | bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_HI, 0); | ||
360 | bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0); | ||
361 | |||
362 | val = bcm6328_led_read(mem + BCM6328_REG_INIT); | ||
363 | val &= ~BCM6328_SERIAL_LED_EN; | ||
364 | if (of_property_read_bool(np, "brcm,serial-leds")) | ||
365 | val |= BCM6328_SERIAL_LED_EN; | ||
366 | bcm6328_led_write(mem + BCM6328_REG_INIT, val); | ||
367 | |||
368 | for_each_available_child_of_node(np, child) { | ||
369 | int rc; | ||
370 | u32 reg; | ||
371 | |||
372 | if (of_property_read_u32(child, "reg", ®)) | ||
373 | continue; | ||
374 | |||
375 | if (reg >= BCM6328_LED_MAX_COUNT) { | ||
376 | dev_err(dev, "invalid LED (>= %d)\n", | ||
377 | BCM6328_LED_MAX_COUNT); | ||
378 | continue; | ||
379 | } | ||
380 | |||
381 | if (of_property_read_bool(child, "brcm,hardware-controlled")) | ||
382 | rc = bcm6328_hwled(dev, child, reg, mem, lock); | ||
383 | else | ||
384 | rc = bcm6328_led(dev, child, reg, mem, lock, | ||
385 | blink_leds, blink_delay); | ||
386 | |||
387 | if (rc < 0) | ||
388 | return rc; | ||
389 | } | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static const struct of_device_id bcm6328_leds_of_match[] = { | ||
395 | { .compatible = "brcm,bcm6328-leds", }, | ||
396 | { }, | ||
397 | }; | ||
398 | |||
399 | static struct platform_driver bcm6328_leds_driver = { | ||
400 | .probe = bcm6328_leds_probe, | ||
401 | .driver = { | ||
402 | .name = "leds-bcm6328", | ||
403 | .of_match_table = bcm6328_leds_of_match, | ||
404 | }, | ||
405 | }; | ||
406 | |||
407 | module_platform_driver(bcm6328_leds_driver); | ||
408 | |||
409 | MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>"); | ||
410 | MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); | ||
411 | MODULE_DESCRIPTION("LED driver for BCM6328 controllers"); | ||
412 | MODULE_LICENSE("GPL v2"); | ||
413 | MODULE_ALIAS("platform:leds-bcm6328"); | ||
diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c new file mode 100644 index 000000000000..21f96930b3be --- /dev/null +++ b/drivers/leds/leds-bcm6358.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c | ||
3 | * | ||
4 | * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/leds.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | |||
19 | #define BCM6358_REG_MODE 0x0 | ||
20 | #define BCM6358_REG_CTRL 0x4 | ||
21 | |||
22 | #define BCM6358_SLED_CLKDIV_MASK 3 | ||
23 | #define BCM6358_SLED_CLKDIV_1 0 | ||
24 | #define BCM6358_SLED_CLKDIV_2 1 | ||
25 | #define BCM6358_SLED_CLKDIV_4 2 | ||
26 | #define BCM6358_SLED_CLKDIV_8 3 | ||
27 | |||
28 | #define BCM6358_SLED_POLARITY BIT(2) | ||
29 | #define BCM6358_SLED_BUSY BIT(3) | ||
30 | |||
31 | #define BCM6358_SLED_MAX_COUNT 32 | ||
32 | #define BCM6358_SLED_WAIT 100 | ||
33 | |||
34 | /** | ||
35 | * struct bcm6358_led - state container for bcm6358 based LEDs | ||
36 | * @cdev: LED class device for this LED | ||
37 | * @mem: memory resource | ||
38 | * @lock: memory lock | ||
39 | * @pin: LED pin number | ||
40 | * @active_low: LED is active low | ||
41 | */ | ||
42 | struct bcm6358_led { | ||
43 | struct led_classdev cdev; | ||
44 | void __iomem *mem; | ||
45 | spinlock_t *lock; | ||
46 | unsigned long pin; | ||
47 | bool active_low; | ||
48 | }; | ||
49 | |||
50 | static void bcm6358_led_write(void __iomem *reg, unsigned long data) | ||
51 | { | ||
52 | iowrite32be(data, reg); | ||
53 | } | ||
54 | |||
55 | static unsigned long bcm6358_led_read(void __iomem *reg) | ||
56 | { | ||
57 | return ioread32be(reg); | ||
58 | } | ||
59 | |||
60 | static unsigned long bcm6358_led_busy(void __iomem *mem) | ||
61 | { | ||
62 | unsigned long val; | ||
63 | |||
64 | while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) & | ||
65 | BCM6358_SLED_BUSY) | ||
66 | udelay(BCM6358_SLED_WAIT); | ||
67 | |||
68 | return val; | ||
69 | } | ||
70 | |||
71 | static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value) | ||
72 | { | ||
73 | unsigned long val; | ||
74 | |||
75 | bcm6358_led_busy(led->mem); | ||
76 | |||
77 | val = bcm6358_led_read(led->mem + BCM6358_REG_MODE); | ||
78 | if ((led->active_low && value == LED_OFF) || | ||
79 | (!led->active_low && value != LED_OFF)) | ||
80 | val |= BIT(led->pin); | ||
81 | else | ||
82 | val &= ~(BIT(led->pin)); | ||
83 | bcm6358_led_write(led->mem + BCM6358_REG_MODE, val); | ||
84 | } | ||
85 | |||
86 | static void bcm6358_led_set(struct led_classdev *led_cdev, | ||
87 | enum led_brightness value) | ||
88 | { | ||
89 | struct bcm6358_led *led = | ||
90 | container_of(led_cdev, struct bcm6358_led, cdev); | ||
91 | unsigned long flags; | ||
92 | |||
93 | spin_lock_irqsave(led->lock, flags); | ||
94 | bcm6358_led_mode(led, value); | ||
95 | spin_unlock_irqrestore(led->lock, flags); | ||
96 | } | ||
97 | |||
98 | static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg, | ||
99 | void __iomem *mem, spinlock_t *lock) | ||
100 | { | ||
101 | struct bcm6358_led *led; | ||
102 | unsigned long flags; | ||
103 | const char *state; | ||
104 | int rc; | ||
105 | |||
106 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); | ||
107 | if (!led) | ||
108 | return -ENOMEM; | ||
109 | |||
110 | led->pin = reg; | ||
111 | led->mem = mem; | ||
112 | led->lock = lock; | ||
113 | |||
114 | if (of_property_read_bool(nc, "active-low")) | ||
115 | led->active_low = true; | ||
116 | |||
117 | led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name; | ||
118 | led->cdev.default_trigger = of_get_property(nc, | ||
119 | "linux,default-trigger", | ||
120 | NULL); | ||
121 | |||
122 | spin_lock_irqsave(lock, flags); | ||
123 | if (!of_property_read_string(nc, "default-state", &state)) { | ||
124 | if (!strcmp(state, "on")) { | ||
125 | led->cdev.brightness = LED_FULL; | ||
126 | } else if (!strcmp(state, "keep")) { | ||
127 | unsigned long val; | ||
128 | |||
129 | bcm6358_led_busy(led->mem); | ||
130 | |||
131 | val = bcm6358_led_read(led->mem + BCM6358_REG_MODE); | ||
132 | val &= BIT(led->pin); | ||
133 | if ((led->active_low && !val) || | ||
134 | (!led->active_low && val)) | ||
135 | led->cdev.brightness = LED_FULL; | ||
136 | else | ||
137 | led->cdev.brightness = LED_OFF; | ||
138 | } else { | ||
139 | led->cdev.brightness = LED_OFF; | ||
140 | } | ||
141 | } else { | ||
142 | led->cdev.brightness = LED_OFF; | ||
143 | } | ||
144 | bcm6358_led_mode(led, led->cdev.brightness); | ||
145 | spin_unlock_irqrestore(lock, flags); | ||
146 | |||
147 | led->cdev.brightness_set = bcm6358_led_set; | ||
148 | |||
149 | rc = led_classdev_register(dev, &led->cdev); | ||
150 | if (rc < 0) | ||
151 | return rc; | ||
152 | |||
153 | dev_dbg(dev, "registered LED %s\n", led->cdev.name); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int bcm6358_leds_probe(struct platform_device *pdev) | ||
159 | { | ||
160 | struct device *dev = &pdev->dev; | ||
161 | struct device_node *np = pdev->dev.of_node; | ||
162 | struct device_node *child; | ||
163 | struct resource *mem_r; | ||
164 | void __iomem *mem; | ||
165 | spinlock_t *lock; /* memory lock */ | ||
166 | unsigned long val; | ||
167 | u32 clk_div; | ||
168 | |||
169 | mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
170 | if (!mem_r) | ||
171 | return -EINVAL; | ||
172 | |||
173 | mem = devm_ioremap_resource(dev, mem_r); | ||
174 | if (IS_ERR(mem)) | ||
175 | return PTR_ERR(mem); | ||
176 | |||
177 | lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); | ||
178 | if (!lock) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | spin_lock_init(lock); | ||
182 | |||
183 | val = bcm6358_led_busy(mem); | ||
184 | val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK); | ||
185 | if (of_property_read_bool(np, "brcm,clk-dat-low")) | ||
186 | val |= BCM6358_SLED_POLARITY; | ||
187 | of_property_read_u32(np, "brcm,clk-div", &clk_div); | ||
188 | switch (clk_div) { | ||
189 | case 8: | ||
190 | val |= BCM6358_SLED_CLKDIV_8; | ||
191 | break; | ||
192 | case 4: | ||
193 | val |= BCM6358_SLED_CLKDIV_4; | ||
194 | break; | ||
195 | case 2: | ||
196 | val |= BCM6358_SLED_CLKDIV_2; | ||
197 | break; | ||
198 | default: | ||
199 | val |= BCM6358_SLED_CLKDIV_1; | ||
200 | break; | ||
201 | } | ||
202 | bcm6358_led_write(mem + BCM6358_REG_CTRL, val); | ||
203 | |||
204 | for_each_available_child_of_node(np, child) { | ||
205 | int rc; | ||
206 | u32 reg; | ||
207 | |||
208 | if (of_property_read_u32(child, "reg", ®)) | ||
209 | continue; | ||
210 | |||
211 | if (reg >= BCM6358_SLED_MAX_COUNT) { | ||
212 | dev_err(dev, "invalid LED (%u >= %d)\n", reg, | ||
213 | BCM6358_SLED_MAX_COUNT); | ||
214 | continue; | ||
215 | } | ||
216 | |||
217 | rc = bcm6358_led(dev, child, reg, mem, lock); | ||
218 | if (rc < 0) | ||
219 | return rc; | ||
220 | } | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static const struct of_device_id bcm6358_leds_of_match[] = { | ||
226 | { .compatible = "brcm,bcm6358-leds", }, | ||
227 | { }, | ||
228 | }; | ||
229 | |||
230 | static struct platform_driver bcm6358_leds_driver = { | ||
231 | .probe = bcm6358_leds_probe, | ||
232 | .driver = { | ||
233 | .name = "leds-bcm6358", | ||
234 | .of_match_table = bcm6358_leds_of_match, | ||
235 | }, | ||
236 | }; | ||
237 | |||
238 | module_platform_driver(bcm6358_leds_driver); | ||
239 | |||
240 | MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>"); | ||
241 | MODULE_DESCRIPTION("LED driver for BCM6358 controllers"); | ||
242 | MODULE_LICENSE("GPL v2"); | ||
243 | MODULE_ALIAS("platform:leds-bcm6358"); | ||
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c index 06dbe18a2065..b316df4a8c1e 100644 --- a/drivers/leds/leds-cobalt-raq.c +++ b/drivers/leds/leds-cobalt-raq.c | |||
@@ -108,20 +108,8 @@ err_null: | |||
108 | return retval; | 108 | return retval; |
109 | } | 109 | } |
110 | 110 | ||
111 | static int cobalt_raq_led_remove(struct platform_device *pdev) | ||
112 | { | ||
113 | led_classdev_unregister(&raq_power_off_led); | ||
114 | led_classdev_unregister(&raq_web_led); | ||
115 | |||
116 | if (led_port) | ||
117 | led_port = NULL; | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static struct platform_driver cobalt_raq_led_driver = { | 111 | static struct platform_driver cobalt_raq_led_driver = { |
123 | .probe = cobalt_raq_led_probe, | 112 | .probe = cobalt_raq_led_probe, |
124 | .remove = cobalt_raq_led_remove, | ||
125 | .driver = { | 113 | .driver = { |
126 | .name = "cobalt-raq-leds", | 114 | .name = "cobalt-raq-leds", |
127 | }, | 115 | }, |
@@ -131,5 +119,4 @@ static int __init cobalt_raq_led_init(void) | |||
131 | { | 119 | { |
132 | return platform_driver_register(&cobalt_raq_led_driver); | 120 | return platform_driver_register(&cobalt_raq_led_driver); |
133 | } | 121 | } |
134 | 122 | device_initcall(cobalt_raq_led_init); | |
135 | module_init(cobalt_raq_led_init); | ||
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index d2d54d62afee..af1876a3a77c 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/of.h> | ||
19 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
20 | #include <linux/property.h> | 21 | #include <linux/property.h> |
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
@@ -198,8 +199,10 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) | |||
198 | } else { | 199 | } else { |
199 | if (IS_ENABLED(CONFIG_OF) && !led.name && np) | 200 | if (IS_ENABLED(CONFIG_OF) && !led.name && np) |
200 | led.name = np->name; | 201 | led.name = np->name; |
201 | if (!led.name) | 202 | if (!led.name) { |
202 | return ERR_PTR(-EINVAL); | 203 | ret = -EINVAL; |
204 | goto err; | ||
205 | } | ||
203 | } | 206 | } |
204 | fwnode_property_read_string(child, "linux,default-trigger", | 207 | fwnode_property_read_string(child, "linux,default-trigger", |
205 | &led.default_trigger); | 208 | &led.default_trigger); |
@@ -217,18 +220,19 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) | |||
217 | if (fwnode_property_present(child, "retain-state-suspended")) | 220 | if (fwnode_property_present(child, "retain-state-suspended")) |
218 | led.retain_state_suspended = 1; | 221 | led.retain_state_suspended = 1; |
219 | 222 | ||
220 | ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], | 223 | ret = create_gpio_led(&led, &priv->leds[priv->num_leds], |
221 | dev, NULL); | 224 | dev, NULL); |
222 | if (ret < 0) { | 225 | if (ret < 0) { |
223 | fwnode_handle_put(child); | 226 | fwnode_handle_put(child); |
224 | goto err; | 227 | goto err; |
225 | } | 228 | } |
229 | priv->num_leds++; | ||
226 | } | 230 | } |
227 | 231 | ||
228 | return priv; | 232 | return priv; |
229 | 233 | ||
230 | err: | 234 | err: |
231 | for (count = priv->num_leds - 2; count >= 0; count--) | 235 | for (count = priv->num_leds - 1; count >= 0; count--) |
232 | delete_gpio_led(&priv->leds[count]); | 236 | delete_gpio_led(&priv->leds[count]); |
233 | return ERR_PTR(ret); | 237 | return ERR_PTR(ret); |
234 | } | 238 | } |
diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c new file mode 100644 index 000000000000..2ae8c4d17ff8 --- /dev/null +++ b/drivers/leds/leds-ktd2692.c | |||
@@ -0,0 +1,443 @@ | |||
1 | /* | ||
2 | * LED driver : leds-ktd2692.c | ||
3 | * | ||
4 | * Copyright (C) 2015 Samsung Electronics | ||
5 | * Ingi Kim <ingi2.kim@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/delay.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/gpio/consumer.h> | ||
15 | #include <linux/led-class-flash.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/mutex.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | |||
23 | /* Value related the movie mode */ | ||
24 | #define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16 | ||
25 | #define KTD2692_MM_TO_FL_RATIO(x) ((x) / 3) | ||
26 | #define KTD2962_MM_MIN_CURR_THRESHOLD_SCALE 8 | ||
27 | |||
28 | /* Value related the flash mode */ | ||
29 | #define KTD2692_FLASH_MODE_TIMEOUT_LEVELS 8 | ||
30 | #define KTD2692_FLASH_MODE_TIMEOUT_DISABLE 0 | ||
31 | #define KTD2692_FLASH_MODE_CURR_PERCENT(x) (((x) * 16) / 100) | ||
32 | |||
33 | /* Macro for getting offset of flash timeout */ | ||
34 | #define GET_TIMEOUT_OFFSET(timeout, step) ((timeout) / (step)) | ||
35 | |||
36 | /* Base register address */ | ||
37 | #define KTD2692_REG_LVP_BASE 0x00 | ||
38 | #define KTD2692_REG_FLASH_TIMEOUT_BASE 0x20 | ||
39 | #define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE 0x40 | ||
40 | #define KTD2692_REG_MOVIE_CURRENT_BASE 0x60 | ||
41 | #define KTD2692_REG_FLASH_CURRENT_BASE 0x80 | ||
42 | #define KTD2692_REG_MODE_BASE 0xA0 | ||
43 | |||
44 | /* Set bit coding time for expresswire interface */ | ||
45 | #define KTD2692_TIME_RESET_US 700 | ||
46 | #define KTD2692_TIME_DATA_START_TIME_US 10 | ||
47 | #define KTD2692_TIME_HIGH_END_OF_DATA_US 350 | ||
48 | #define KTD2692_TIME_LOW_END_OF_DATA_US 10 | ||
49 | #define KTD2692_TIME_SHORT_BITSET_US 4 | ||
50 | #define KTD2692_TIME_LONG_BITSET_US 12 | ||
51 | |||
52 | /* KTD2692 default length of name */ | ||
53 | #define KTD2692_NAME_LENGTH 20 | ||
54 | |||
55 | enum ktd2692_bitset { | ||
56 | KTD2692_LOW = 0, | ||
57 | KTD2692_HIGH, | ||
58 | }; | ||
59 | |||
60 | /* Movie / Flash Mode Control */ | ||
61 | enum ktd2692_led_mode { | ||
62 | KTD2692_MODE_DISABLE = 0, /* default */ | ||
63 | KTD2692_MODE_MOVIE, | ||
64 | KTD2692_MODE_FLASH, | ||
65 | }; | ||
66 | |||
67 | struct ktd2692_led_config_data { | ||
68 | /* maximum LED current in movie mode */ | ||
69 | u32 movie_max_microamp; | ||
70 | /* maximum LED current in flash mode */ | ||
71 | u32 flash_max_microamp; | ||
72 | /* maximum flash timeout */ | ||
73 | u32 flash_max_timeout; | ||
74 | /* max LED brightness level */ | ||
75 | enum led_brightness max_brightness; | ||
76 | }; | ||
77 | |||
78 | struct ktd2692_context { | ||
79 | /* Related LED Flash class device */ | ||
80 | struct led_classdev_flash fled_cdev; | ||
81 | |||
82 | /* secures access to the device */ | ||
83 | struct mutex lock; | ||
84 | struct regulator *regulator; | ||
85 | struct work_struct work_brightness_set; | ||
86 | |||
87 | struct gpio_desc *aux_gpio; | ||
88 | struct gpio_desc *ctrl_gpio; | ||
89 | |||
90 | enum ktd2692_led_mode mode; | ||
91 | enum led_brightness torch_brightness; | ||
92 | }; | ||
93 | |||
94 | static struct ktd2692_context *fled_cdev_to_led( | ||
95 | struct led_classdev_flash *fled_cdev) | ||
96 | { | ||
97 | return container_of(fled_cdev, struct ktd2692_context, fled_cdev); | ||
98 | } | ||
99 | |||
100 | static void ktd2692_expresswire_start(struct ktd2692_context *led) | ||
101 | { | ||
102 | gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); | ||
103 | udelay(KTD2692_TIME_DATA_START_TIME_US); | ||
104 | } | ||
105 | |||
106 | static void ktd2692_expresswire_reset(struct ktd2692_context *led) | ||
107 | { | ||
108 | gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); | ||
109 | udelay(KTD2692_TIME_RESET_US); | ||
110 | } | ||
111 | |||
112 | static void ktd2692_expresswire_end(struct ktd2692_context *led) | ||
113 | { | ||
114 | gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); | ||
115 | udelay(KTD2692_TIME_LOW_END_OF_DATA_US); | ||
116 | gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); | ||
117 | udelay(KTD2692_TIME_HIGH_END_OF_DATA_US); | ||
118 | } | ||
119 | |||
120 | static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit) | ||
121 | { | ||
122 | /* | ||
123 | * The Low Bit(0) and High Bit(1) is based on a time detection | ||
124 | * algorithm between time low and time high | ||
125 | * Time_(L_LB) : Low time of the Low Bit(0) | ||
126 | * Time_(H_LB) : High time of the LOW Bit(0) | ||
127 | * Time_(L_HB) : Low time of the High Bit(1) | ||
128 | * Time_(H_HB) : High time of the High Bit(1) | ||
129 | * | ||
130 | * It can be simplified to: | ||
131 | * Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB) | ||
132 | * High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB) | ||
133 | * HIGH ___ ____ _.. _________ ___ | ||
134 | * |_________| |_.. |____| |__| | ||
135 | * LOW <L_LB> <H_LB> <L_HB> <H_HB> | ||
136 | * [ Low Bit (0) ] [ High Bit(1) ] | ||
137 | */ | ||
138 | if (bit) { | ||
139 | gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); | ||
140 | udelay(KTD2692_TIME_SHORT_BITSET_US); | ||
141 | gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); | ||
142 | udelay(KTD2692_TIME_LONG_BITSET_US); | ||
143 | } else { | ||
144 | gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); | ||
145 | udelay(KTD2692_TIME_LONG_BITSET_US); | ||
146 | gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); | ||
147 | udelay(KTD2692_TIME_SHORT_BITSET_US); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value) | ||
152 | { | ||
153 | int i; | ||
154 | |||
155 | ktd2692_expresswire_start(led); | ||
156 | for (i = 7; i >= 0; i--) | ||
157 | ktd2692_expresswire_set_bit(led, value & BIT(i)); | ||
158 | ktd2692_expresswire_end(led); | ||
159 | } | ||
160 | |||
161 | static void ktd2692_brightness_set(struct ktd2692_context *led, | ||
162 | enum led_brightness brightness) | ||
163 | { | ||
164 | mutex_lock(&led->lock); | ||
165 | |||
166 | if (brightness == LED_OFF) { | ||
167 | led->mode = KTD2692_MODE_DISABLE; | ||
168 | gpiod_direction_output(led->aux_gpio, KTD2692_LOW); | ||
169 | } else { | ||
170 | ktd2692_expresswire_write(led, brightness | | ||
171 | KTD2692_REG_MOVIE_CURRENT_BASE); | ||
172 | led->mode = KTD2692_MODE_MOVIE; | ||
173 | } | ||
174 | |||
175 | ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); | ||
176 | mutex_unlock(&led->lock); | ||
177 | } | ||
178 | |||
179 | static void ktd2692_brightness_set_work(struct work_struct *work) | ||
180 | { | ||
181 | struct ktd2692_context *led = | ||
182 | container_of(work, struct ktd2692_context, work_brightness_set); | ||
183 | |||
184 | ktd2692_brightness_set(led, led->torch_brightness); | ||
185 | } | ||
186 | |||
187 | static void ktd2692_led_brightness_set(struct led_classdev *led_cdev, | ||
188 | enum led_brightness brightness) | ||
189 | { | ||
190 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
191 | struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); | ||
192 | |||
193 | led->torch_brightness = brightness; | ||
194 | schedule_work(&led->work_brightness_set); | ||
195 | } | ||
196 | |||
197 | static int ktd2692_led_brightness_set_sync(struct led_classdev *led_cdev, | ||
198 | enum led_brightness brightness) | ||
199 | { | ||
200 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
201 | struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); | ||
202 | |||
203 | ktd2692_brightness_set(led, brightness); | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, | ||
209 | bool state) | ||
210 | { | ||
211 | struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); | ||
212 | struct led_flash_setting *timeout = &fled_cdev->timeout; | ||
213 | u32 flash_tm_reg; | ||
214 | |||
215 | mutex_lock(&led->lock); | ||
216 | |||
217 | if (state) { | ||
218 | flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step); | ||
219 | ktd2692_expresswire_write(led, flash_tm_reg | ||
220 | | KTD2692_REG_FLASH_TIMEOUT_BASE); | ||
221 | |||
222 | led->mode = KTD2692_MODE_FLASH; | ||
223 | gpiod_direction_output(led->aux_gpio, KTD2692_HIGH); | ||
224 | } else { | ||
225 | led->mode = KTD2692_MODE_DISABLE; | ||
226 | gpiod_direction_output(led->aux_gpio, KTD2692_LOW); | ||
227 | } | ||
228 | |||
229 | ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); | ||
230 | |||
231 | fled_cdev->led_cdev.brightness = LED_OFF; | ||
232 | led->mode = KTD2692_MODE_DISABLE; | ||
233 | |||
234 | mutex_unlock(&led->lock); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, | ||
240 | u32 timeout) | ||
241 | { | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg) | ||
246 | { | ||
247 | u32 offset, step; | ||
248 | u32 movie_current_microamp; | ||
249 | |||
250 | offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS; | ||
251 | step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp) | ||
252 | / KTD2692_MOVIE_MODE_CURRENT_LEVELS; | ||
253 | |||
254 | do { | ||
255 | movie_current_microamp = step * offset; | ||
256 | offset--; | ||
257 | } while ((movie_current_microamp > cfg->movie_max_microamp) && | ||
258 | (offset > 0)); | ||
259 | |||
260 | cfg->max_brightness = offset; | ||
261 | } | ||
262 | |||
263 | static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev, | ||
264 | struct ktd2692_led_config_data *cfg) | ||
265 | { | ||
266 | struct led_flash_setting *setting; | ||
267 | |||
268 | setting = &fled_cdev->timeout; | ||
269 | setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE; | ||
270 | setting->max = cfg->flash_max_timeout; | ||
271 | setting->step = cfg->flash_max_timeout | ||
272 | / (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1); | ||
273 | setting->val = cfg->flash_max_timeout; | ||
274 | } | ||
275 | |||
276 | static void ktd2692_setup(struct ktd2692_context *led) | ||
277 | { | ||
278 | led->mode = KTD2692_MODE_DISABLE; | ||
279 | ktd2692_expresswire_reset(led); | ||
280 | gpiod_direction_output(led->aux_gpio, KTD2692_LOW); | ||
281 | |||
282 | ktd2692_expresswire_write(led, (KTD2962_MM_MIN_CURR_THRESHOLD_SCALE - 1) | ||
283 | | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE); | ||
284 | ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45) | ||
285 | | KTD2692_REG_FLASH_CURRENT_BASE); | ||
286 | } | ||
287 | |||
288 | static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev, | ||
289 | struct ktd2692_led_config_data *cfg) | ||
290 | { | ||
291 | struct device_node *np = dev->of_node; | ||
292 | struct device_node *child_node; | ||
293 | int ret; | ||
294 | |||
295 | if (!dev->of_node) | ||
296 | return -ENXIO; | ||
297 | |||
298 | led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS); | ||
299 | if (IS_ERR(led->ctrl_gpio)) { | ||
300 | ret = PTR_ERR(led->ctrl_gpio); | ||
301 | dev_err(dev, "cannot get ctrl-gpios %d\n", ret); | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS); | ||
306 | if (IS_ERR(led->aux_gpio)) { | ||
307 | ret = PTR_ERR(led->aux_gpio); | ||
308 | dev_err(dev, "cannot get aux-gpios %d\n", ret); | ||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | led->regulator = devm_regulator_get(dev, "vin"); | ||
313 | if (IS_ERR(led->regulator)) | ||
314 | led->regulator = NULL; | ||
315 | |||
316 | if (led->regulator) { | ||
317 | ret = regulator_enable(led->regulator); | ||
318 | if (ret) | ||
319 | dev_err(dev, "Failed to enable supply: %d\n", ret); | ||
320 | } | ||
321 | |||
322 | child_node = of_get_next_available_child(np, NULL); | ||
323 | if (!child_node) { | ||
324 | dev_err(dev, "No DT child node found for connected LED.\n"); | ||
325 | return -EINVAL; | ||
326 | } | ||
327 | |||
328 | led->fled_cdev.led_cdev.name = | ||
329 | of_get_property(child_node, "label", NULL) ? : child_node->name; | ||
330 | |||
331 | ret = of_property_read_u32(child_node, "led-max-microamp", | ||
332 | &cfg->movie_max_microamp); | ||
333 | if (ret) { | ||
334 | dev_err(dev, "failed to parse led-max-microamp\n"); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | ret = of_property_read_u32(child_node, "flash-max-microamp", | ||
339 | &cfg->flash_max_microamp); | ||
340 | if (ret) { | ||
341 | dev_err(dev, "failed to parse flash-max-microamp\n"); | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | ret = of_property_read_u32(child_node, "flash-max-timeout-us", | ||
346 | &cfg->flash_max_timeout); | ||
347 | if (ret) | ||
348 | dev_err(dev, "failed to parse flash-max-timeout-us\n"); | ||
349 | |||
350 | of_node_put(child_node); | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | static const struct led_flash_ops flash_ops = { | ||
355 | .strobe_set = ktd2692_led_flash_strobe_set, | ||
356 | .timeout_set = ktd2692_led_flash_timeout_set, | ||
357 | }; | ||
358 | |||
359 | static int ktd2692_probe(struct platform_device *pdev) | ||
360 | { | ||
361 | struct ktd2692_context *led; | ||
362 | struct led_classdev *led_cdev; | ||
363 | struct led_classdev_flash *fled_cdev; | ||
364 | struct ktd2692_led_config_data led_cfg; | ||
365 | int ret; | ||
366 | |||
367 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); | ||
368 | if (!led) | ||
369 | return -ENOMEM; | ||
370 | |||
371 | fled_cdev = &led->fled_cdev; | ||
372 | led_cdev = &fled_cdev->led_cdev; | ||
373 | |||
374 | ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg); | ||
375 | if (ret) | ||
376 | return ret; | ||
377 | |||
378 | ktd2692_init_flash_timeout(fled_cdev, &led_cfg); | ||
379 | ktd2692_init_movie_current_max(&led_cfg); | ||
380 | |||
381 | fled_cdev->ops = &flash_ops; | ||
382 | |||
383 | led_cdev->max_brightness = led_cfg.max_brightness; | ||
384 | led_cdev->brightness_set = ktd2692_led_brightness_set; | ||
385 | led_cdev->brightness_set_sync = ktd2692_led_brightness_set_sync; | ||
386 | led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; | ||
387 | |||
388 | mutex_init(&led->lock); | ||
389 | INIT_WORK(&led->work_brightness_set, ktd2692_brightness_set_work); | ||
390 | |||
391 | platform_set_drvdata(pdev, led); | ||
392 | |||
393 | ret = led_classdev_flash_register(&pdev->dev, fled_cdev); | ||
394 | if (ret) { | ||
395 | dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name); | ||
396 | mutex_destroy(&led->lock); | ||
397 | return ret; | ||
398 | } | ||
399 | |||
400 | ktd2692_setup(led); | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static int ktd2692_remove(struct platform_device *pdev) | ||
406 | { | ||
407 | struct ktd2692_context *led = platform_get_drvdata(pdev); | ||
408 | int ret; | ||
409 | |||
410 | led_classdev_flash_unregister(&led->fled_cdev); | ||
411 | cancel_work_sync(&led->work_brightness_set); | ||
412 | |||
413 | if (led->regulator) { | ||
414 | ret = regulator_disable(led->regulator); | ||
415 | if (ret) | ||
416 | dev_err(&pdev->dev, | ||
417 | "Failed to disable supply: %d\n", ret); | ||
418 | } | ||
419 | |||
420 | mutex_destroy(&led->lock); | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static const struct of_device_id ktd2692_match[] = { | ||
426 | { .compatible = "kinetic,ktd2692", }, | ||
427 | { /* sentinel */ }, | ||
428 | }; | ||
429 | |||
430 | static struct platform_driver ktd2692_driver = { | ||
431 | .driver = { | ||
432 | .name = "ktd2692", | ||
433 | .of_match_table = ktd2692_match, | ||
434 | }, | ||
435 | .probe = ktd2692_probe, | ||
436 | .remove = ktd2692_remove, | ||
437 | }; | ||
438 | |||
439 | module_platform_driver(ktd2692_driver); | ||
440 | |||
441 | MODULE_AUTHOR("Ingi Kim <ingi2.kim@samsung.com>"); | ||
442 | MODULE_DESCRIPTION("Kinetic KTD2692 LED driver"); | ||
443 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 9e1716f8098c..584dbbcec659 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #define LP5523_REG_OP_MODE 0x01 | 50 | #define LP5523_REG_OP_MODE 0x01 |
51 | #define LP5523_REG_ENABLE_LEDS_MSB 0x04 | 51 | #define LP5523_REG_ENABLE_LEDS_MSB 0x04 |
52 | #define LP5523_REG_ENABLE_LEDS_LSB 0x05 | 52 | #define LP5523_REG_ENABLE_LEDS_LSB 0x05 |
53 | #define LP5523_REG_LED_CTRL_BASE 0x06 | ||
53 | #define LP5523_REG_LED_PWM_BASE 0x16 | 54 | #define LP5523_REG_LED_PWM_BASE 0x16 |
54 | #define LP5523_REG_LED_CURRENT_BASE 0x26 | 55 | #define LP5523_REG_LED_CURRENT_BASE 0x26 |
55 | #define LP5523_REG_CONFIG 0x36 | 56 | #define LP5523_REG_CONFIG 0x36 |
@@ -57,6 +58,7 @@ | |||
57 | #define LP5523_REG_RESET 0x3D | 58 | #define LP5523_REG_RESET 0x3D |
58 | #define LP5523_REG_LED_TEST_CTRL 0x41 | 59 | #define LP5523_REG_LED_TEST_CTRL 0x41 |
59 | #define LP5523_REG_LED_TEST_ADC 0x42 | 60 | #define LP5523_REG_LED_TEST_ADC 0x42 |
61 | #define LP5523_REG_MASTER_FADER_BASE 0x48 | ||
60 | #define LP5523_REG_CH1_PROG_START 0x4C | 62 | #define LP5523_REG_CH1_PROG_START 0x4C |
61 | #define LP5523_REG_CH2_PROG_START 0x4D | 63 | #define LP5523_REG_CH2_PROG_START 0x4D |
62 | #define LP5523_REG_CH3_PROG_START 0x4E | 64 | #define LP5523_REG_CH3_PROG_START 0x4E |
@@ -78,6 +80,9 @@ | |||
78 | #define LP5523_EXT_CLK_USED 0x08 | 80 | #define LP5523_EXT_CLK_USED 0x08 |
79 | #define LP5523_ENG_STATUS_MASK 0x07 | 81 | #define LP5523_ENG_STATUS_MASK 0x07 |
80 | 82 | ||
83 | #define LP5523_FADER_MAPPING_MASK 0xC0 | ||
84 | #define LP5523_FADER_MAPPING_SHIFT 6 | ||
85 | |||
81 | /* Memory Page Selection */ | 86 | /* Memory Page Selection */ |
82 | #define LP5523_PAGE_ENG1 0 | 87 | #define LP5523_PAGE_ENG1 0 |
83 | #define LP5523_PAGE_ENG2 1 | 88 | #define LP5523_PAGE_ENG2 1 |
@@ -666,6 +671,137 @@ release_lock: | |||
666 | return pos; | 671 | return pos; |
667 | } | 672 | } |
668 | 673 | ||
674 | #define show_fader(nr) \ | ||
675 | static ssize_t show_master_fader##nr(struct device *dev, \ | ||
676 | struct device_attribute *attr, \ | ||
677 | char *buf) \ | ||
678 | { \ | ||
679 | return show_master_fader(dev, attr, buf, nr); \ | ||
680 | } | ||
681 | |||
682 | #define store_fader(nr) \ | ||
683 | static ssize_t store_master_fader##nr(struct device *dev, \ | ||
684 | struct device_attribute *attr, \ | ||
685 | const char *buf, size_t len) \ | ||
686 | { \ | ||
687 | return store_master_fader(dev, attr, buf, len, nr); \ | ||
688 | } | ||
689 | |||
690 | static ssize_t show_master_fader(struct device *dev, | ||
691 | struct device_attribute *attr, | ||
692 | char *buf, int nr) | ||
693 | { | ||
694 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
695 | struct lp55xx_chip *chip = led->chip; | ||
696 | int ret; | ||
697 | u8 val; | ||
698 | |||
699 | mutex_lock(&chip->lock); | ||
700 | ret = lp55xx_read(chip, LP5523_REG_MASTER_FADER_BASE + nr - 1, &val); | ||
701 | mutex_unlock(&chip->lock); | ||
702 | |||
703 | if (ret == 0) | ||
704 | ret = sprintf(buf, "%u\n", val); | ||
705 | |||
706 | return ret; | ||
707 | } | ||
708 | show_fader(1) | ||
709 | show_fader(2) | ||
710 | show_fader(3) | ||
711 | |||
712 | static ssize_t store_master_fader(struct device *dev, | ||
713 | struct device_attribute *attr, | ||
714 | const char *buf, size_t len, int nr) | ||
715 | { | ||
716 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
717 | struct lp55xx_chip *chip = led->chip; | ||
718 | int ret; | ||
719 | unsigned long val; | ||
720 | |||
721 | if (kstrtoul(buf, 0, &val)) | ||
722 | return -EINVAL; | ||
723 | |||
724 | if (val > 0xff) | ||
725 | return -EINVAL; | ||
726 | |||
727 | mutex_lock(&chip->lock); | ||
728 | ret = lp55xx_write(chip, LP5523_REG_MASTER_FADER_BASE + nr - 1, | ||
729 | (u8)val); | ||
730 | mutex_unlock(&chip->lock); | ||
731 | |||
732 | if (ret == 0) | ||
733 | ret = len; | ||
734 | |||
735 | return ret; | ||
736 | } | ||
737 | store_fader(1) | ||
738 | store_fader(2) | ||
739 | store_fader(3) | ||
740 | |||
741 | static ssize_t show_master_fader_leds(struct device *dev, | ||
742 | struct device_attribute *attr, | ||
743 | char *buf) | ||
744 | { | ||
745 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
746 | struct lp55xx_chip *chip = led->chip; | ||
747 | int i, ret, pos = 0; | ||
748 | u8 val; | ||
749 | |||
750 | mutex_lock(&chip->lock); | ||
751 | |||
752 | for (i = 0; i < LP5523_MAX_LEDS; i++) { | ||
753 | ret = lp55xx_read(chip, LP5523_REG_LED_CTRL_BASE + i, &val); | ||
754 | if (ret) | ||
755 | goto leave; | ||
756 | |||
757 | val = (val & LP5523_FADER_MAPPING_MASK) | ||
758 | >> LP5523_FADER_MAPPING_SHIFT; | ||
759 | if (val > 3) { | ||
760 | ret = -EINVAL; | ||
761 | goto leave; | ||
762 | } | ||
763 | buf[pos++] = val + '0'; | ||
764 | } | ||
765 | buf[pos++] = '\n'; | ||
766 | ret = pos; | ||
767 | leave: | ||
768 | mutex_unlock(&chip->lock); | ||
769 | return ret; | ||
770 | } | ||
771 | |||
772 | static ssize_t store_master_fader_leds(struct device *dev, | ||
773 | struct device_attribute *attr, | ||
774 | const char *buf, size_t len) | ||
775 | { | ||
776 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
777 | struct lp55xx_chip *chip = led->chip; | ||
778 | int i, n, ret; | ||
779 | u8 val; | ||
780 | |||
781 | n = min_t(int, len, LP5523_MAX_LEDS); | ||
782 | |||
783 | mutex_lock(&chip->lock); | ||
784 | |||
785 | for (i = 0; i < n; i++) { | ||
786 | if (buf[i] >= '0' && buf[i] <= '3') { | ||
787 | val = (buf[i] - '0') << LP5523_FADER_MAPPING_SHIFT; | ||
788 | ret = lp55xx_update_bits(chip, | ||
789 | LP5523_REG_LED_CTRL_BASE + i, | ||
790 | LP5523_FADER_MAPPING_MASK, | ||
791 | val); | ||
792 | if (ret) | ||
793 | goto leave; | ||
794 | } else { | ||
795 | ret = -EINVAL; | ||
796 | goto leave; | ||
797 | } | ||
798 | } | ||
799 | ret = len; | ||
800 | leave: | ||
801 | mutex_unlock(&chip->lock); | ||
802 | return ret; | ||
803 | } | ||
804 | |||
669 | static void lp5523_led_brightness_work(struct work_struct *work) | 805 | static void lp5523_led_brightness_work(struct work_struct *work) |
670 | { | 806 | { |
671 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, | 807 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, |
@@ -688,6 +824,14 @@ static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load); | |||
688 | static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load); | 824 | static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load); |
689 | static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load); | 825 | static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load); |
690 | static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest); | 826 | static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest); |
827 | static LP55XX_DEV_ATTR_RW(master_fader1, show_master_fader1, | ||
828 | store_master_fader1); | ||
829 | static LP55XX_DEV_ATTR_RW(master_fader2, show_master_fader2, | ||
830 | store_master_fader2); | ||
831 | static LP55XX_DEV_ATTR_RW(master_fader3, show_master_fader3, | ||
832 | store_master_fader3); | ||
833 | static LP55XX_DEV_ATTR_RW(master_fader_leds, show_master_fader_leds, | ||
834 | store_master_fader_leds); | ||
691 | 835 | ||
692 | static struct attribute *lp5523_attributes[] = { | 836 | static struct attribute *lp5523_attributes[] = { |
693 | &dev_attr_engine1_mode.attr, | 837 | &dev_attr_engine1_mode.attr, |
@@ -700,6 +844,10 @@ static struct attribute *lp5523_attributes[] = { | |||
700 | &dev_attr_engine2_leds.attr, | 844 | &dev_attr_engine2_leds.attr, |
701 | &dev_attr_engine3_leds.attr, | 845 | &dev_attr_engine3_leds.attr, |
702 | &dev_attr_selftest.attr, | 846 | &dev_attr_selftest.attr, |
847 | &dev_attr_master_fader1.attr, | ||
848 | &dev_attr_master_fader2.attr, | ||
849 | &dev_attr_master_fader3.attr, | ||
850 | &dev_attr_master_fader_leds.attr, | ||
703 | NULL, | 851 | NULL, |
704 | }; | 852 | }; |
705 | 853 | ||
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 77c26bc32eed..96d51e9879c9 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c | |||
@@ -223,7 +223,7 @@ static int lp55xx_request_firmware(struct lp55xx_chip *chip) | |||
223 | const char *name = chip->cl->name; | 223 | const char *name = chip->cl->name; |
224 | struct device *dev = &chip->cl->dev; | 224 | struct device *dev = &chip->cl->dev; |
225 | 225 | ||
226 | return request_firmware_nowait(THIS_MODULE, true, name, dev, | 226 | return request_firmware_nowait(THIS_MODULE, false, name, dev, |
227 | GFP_KERNEL, chip, lp55xx_firmware_loaded); | 227 | GFP_KERNEL, chip, lp55xx_firmware_loaded); |
228 | } | 228 | } |
229 | 229 | ||
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c new file mode 100644 index 000000000000..b8b0eec7b540 --- /dev/null +++ b/drivers/leds/leds-max77693.c | |||
@@ -0,0 +1,1097 @@ | |||
1 | /* | ||
2 | * LED Flash class driver for the flash cell of max77693 mfd. | ||
3 | * | ||
4 | * Copyright (C) 2015, Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Jacek Anaszewski <j.anaszewski@samsung.com> | ||
7 | * Andrzej Hajda <a.hajda@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/led-class-flash.h> | ||
15 | #include <linux/mfd/max77693.h> | ||
16 | #include <linux/mfd/max77693-private.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/regmap.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/workqueue.h> | ||
23 | #include <media/v4l2-flash-led-class.h> | ||
24 | |||
25 | #define MODE_OFF 0 | ||
26 | #define MODE_FLASH(a) (1 << (a)) | ||
27 | #define MODE_TORCH(a) (1 << (2 + (a))) | ||
28 | #define MODE_FLASH_EXTERNAL(a) (1 << (4 + (a))) | ||
29 | |||
30 | #define MODE_FLASH_MASK (MODE_FLASH(FLED1) | MODE_FLASH(FLED2) | \ | ||
31 | MODE_FLASH_EXTERNAL(FLED1) | \ | ||
32 | MODE_FLASH_EXTERNAL(FLED2)) | ||
33 | #define MODE_TORCH_MASK (MODE_TORCH(FLED1) | MODE_TORCH(FLED2)) | ||
34 | |||
35 | #define FLED1_IOUT (1 << 0) | ||
36 | #define FLED2_IOUT (1 << 1) | ||
37 | |||
38 | enum max77693_fled { | ||
39 | FLED1, | ||
40 | FLED2, | ||
41 | }; | ||
42 | |||
43 | enum max77693_led_mode { | ||
44 | FLASH, | ||
45 | TORCH, | ||
46 | }; | ||
47 | |||
48 | struct max77693_led_config_data { | ||
49 | const char *label[2]; | ||
50 | u32 iout_torch_max[2]; | ||
51 | u32 iout_flash_max[2]; | ||
52 | u32 flash_timeout_max[2]; | ||
53 | u32 num_leds; | ||
54 | u32 boost_mode; | ||
55 | u32 boost_vout; | ||
56 | u32 low_vsys; | ||
57 | }; | ||
58 | |||
59 | struct max77693_sub_led { | ||
60 | /* corresponding FLED output identifier */ | ||
61 | int fled_id; | ||
62 | /* corresponding LED Flash class device */ | ||
63 | struct led_classdev_flash fled_cdev; | ||
64 | /* assures led-triggers compatibility */ | ||
65 | struct work_struct work_brightness_set; | ||
66 | /* V4L2 Flash device */ | ||
67 | struct v4l2_flash *v4l2_flash; | ||
68 | |||
69 | /* brightness cache */ | ||
70 | unsigned int torch_brightness; | ||
71 | /* flash timeout cache */ | ||
72 | unsigned int flash_timeout; | ||
73 | /* flash faults that may have occurred */ | ||
74 | u32 flash_faults; | ||
75 | }; | ||
76 | |||
77 | struct max77693_led_device { | ||
78 | /* parent mfd regmap */ | ||
79 | struct regmap *regmap; | ||
80 | /* platform device data */ | ||
81 | struct platform_device *pdev; | ||
82 | /* secures access to the device */ | ||
83 | struct mutex lock; | ||
84 | |||
85 | /* sub led data */ | ||
86 | struct max77693_sub_led sub_leds[2]; | ||
87 | |||
88 | /* maximum torch current values for FLED outputs */ | ||
89 | u32 iout_torch_max[2]; | ||
90 | /* maximum flash current values for FLED outputs */ | ||
91 | u32 iout_flash_max[2]; | ||
92 | |||
93 | /* current flash timeout cache */ | ||
94 | unsigned int current_flash_timeout; | ||
95 | /* ITORCH register cache */ | ||
96 | u8 torch_iout_reg; | ||
97 | /* mode of fled outputs */ | ||
98 | unsigned int mode_flags; | ||
99 | /* recently strobed fled */ | ||
100 | int strobing_sub_led_id; | ||
101 | /* bitmask of FLED outputs use state (bit 0. - FLED1, bit 1. - FLED2) */ | ||
102 | u8 fled_mask; | ||
103 | /* FLED modes that can be set */ | ||
104 | u8 allowed_modes; | ||
105 | |||
106 | /* arrangement of current outputs */ | ||
107 | bool iout_joint; | ||
108 | }; | ||
109 | |||
110 | static u8 max77693_led_iout_to_reg(u32 ua) | ||
111 | { | ||
112 | if (ua < FLASH_IOUT_MIN) | ||
113 | ua = FLASH_IOUT_MIN; | ||
114 | return (ua - FLASH_IOUT_MIN) / FLASH_IOUT_STEP; | ||
115 | } | ||
116 | |||
117 | static u8 max77693_flash_timeout_to_reg(u32 us) | ||
118 | { | ||
119 | return (us - FLASH_TIMEOUT_MIN) / FLASH_TIMEOUT_STEP; | ||
120 | } | ||
121 | |||
122 | static inline struct max77693_sub_led *flcdev_to_sub_led( | ||
123 | struct led_classdev_flash *fled_cdev) | ||
124 | { | ||
125 | return container_of(fled_cdev, struct max77693_sub_led, fled_cdev); | ||
126 | } | ||
127 | |||
128 | static inline struct max77693_led_device *sub_led_to_led( | ||
129 | struct max77693_sub_led *sub_led) | ||
130 | { | ||
131 | return container_of(sub_led, struct max77693_led_device, | ||
132 | sub_leds[sub_led->fled_id]); | ||
133 | } | ||
134 | |||
135 | static inline u8 max77693_led_vsys_to_reg(u32 mv) | ||
136 | { | ||
137 | return ((mv - MAX_FLASH1_VSYS_MIN) / MAX_FLASH1_VSYS_STEP) << 2; | ||
138 | } | ||
139 | |||
140 | static inline u8 max77693_led_vout_to_reg(u32 mv) | ||
141 | { | ||
142 | return (mv - FLASH_VOUT_MIN) / FLASH_VOUT_STEP + FLASH_VOUT_RMIN; | ||
143 | } | ||
144 | |||
145 | static inline bool max77693_fled_used(struct max77693_led_device *led, | ||
146 | int fled_id) | ||
147 | { | ||
148 | u8 fled_bit = (fled_id == FLED1) ? FLED1_IOUT : FLED2_IOUT; | ||
149 | |||
150 | return led->fled_mask & fled_bit; | ||
151 | } | ||
152 | |||
153 | static int max77693_set_mode_reg(struct max77693_led_device *led, u8 mode) | ||
154 | { | ||
155 | struct regmap *rmap = led->regmap; | ||
156 | int ret, v = 0, i; | ||
157 | |||
158 | for (i = FLED1; i <= FLED2; ++i) { | ||
159 | if (mode & MODE_TORCH(i)) | ||
160 | v |= FLASH_EN_ON << TORCH_EN_SHIFT(i); | ||
161 | |||
162 | if (mode & MODE_FLASH(i)) { | ||
163 | v |= FLASH_EN_ON << FLASH_EN_SHIFT(i); | ||
164 | } else if (mode & MODE_FLASH_EXTERNAL(i)) { | ||
165 | v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(i); | ||
166 | /* | ||
167 | * Enable hw triggering also for torch mode, as some | ||
168 | * camera sensors use torch led to fathom ambient light | ||
169 | * conditions before strobing the flash. | ||
170 | */ | ||
171 | v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(i); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /* Reset the register only prior setting flash modes */ | ||
176 | if (mode & ~(MODE_TORCH(FLED1) | MODE_TORCH(FLED2))) { | ||
177 | ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0); | ||
178 | if (ret < 0) | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v); | ||
183 | } | ||
184 | |||
185 | static int max77693_add_mode(struct max77693_led_device *led, u8 mode) | ||
186 | { | ||
187 | u8 new_mode_flags; | ||
188 | int i, ret; | ||
189 | |||
190 | if (led->iout_joint) | ||
191 | /* Span the mode on FLED2 for joint iouts case */ | ||
192 | mode |= (mode << 1); | ||
193 | |||
194 | /* | ||
195 | * FLASH_EXTERNAL mode activates FLASHEN and TORCHEN pins in the device. | ||
196 | * Corresponding register bit fields interfere with SW triggered modes, | ||
197 | * thus clear them to ensure proper device configuration. | ||
198 | */ | ||
199 | for (i = FLED1; i <= FLED2; ++i) | ||
200 | if (mode & MODE_FLASH_EXTERNAL(i)) | ||
201 | led->mode_flags &= (~MODE_TORCH(i) & ~MODE_FLASH(i)); | ||
202 | |||
203 | new_mode_flags = mode | led->mode_flags; | ||
204 | new_mode_flags &= led->allowed_modes; | ||
205 | |||
206 | if (new_mode_flags ^ led->mode_flags) | ||
207 | led->mode_flags = new_mode_flags; | ||
208 | else | ||
209 | return 0; | ||
210 | |||
211 | ret = max77693_set_mode_reg(led, led->mode_flags); | ||
212 | if (ret < 0) | ||
213 | return ret; | ||
214 | |||
215 | /* | ||
216 | * Clear flash mode flag after setting the mode to avoid spurious flash | ||
217 | * strobing on each subsequent torch mode setting. | ||
218 | */ | ||
219 | if (mode & MODE_FLASH_MASK) | ||
220 | led->mode_flags &= ~mode; | ||
221 | |||
222 | return ret; | ||
223 | } | ||
224 | |||
225 | static int max77693_clear_mode(struct max77693_led_device *led, | ||
226 | u8 mode) | ||
227 | { | ||
228 | if (led->iout_joint) | ||
229 | /* Clear mode also on FLED2 for joint iouts case */ | ||
230 | mode |= (mode << 1); | ||
231 | |||
232 | led->mode_flags &= ~mode; | ||
233 | |||
234 | return max77693_set_mode_reg(led, led->mode_flags); | ||
235 | } | ||
236 | |||
237 | static void max77693_add_allowed_modes(struct max77693_led_device *led, | ||
238 | int fled_id, enum max77693_led_mode mode) | ||
239 | { | ||
240 | if (mode == FLASH) | ||
241 | led->allowed_modes |= (MODE_FLASH(fled_id) | | ||
242 | MODE_FLASH_EXTERNAL(fled_id)); | ||
243 | else | ||
244 | led->allowed_modes |= MODE_TORCH(fled_id); | ||
245 | } | ||
246 | |||
247 | static void max77693_distribute_currents(struct max77693_led_device *led, | ||
248 | int fled_id, enum max77693_led_mode mode, | ||
249 | u32 micro_amp, u32 iout_max[2], u32 iout[2]) | ||
250 | { | ||
251 | if (!led->iout_joint) { | ||
252 | iout[fled_id] = micro_amp; | ||
253 | max77693_add_allowed_modes(led, fled_id, mode); | ||
254 | return; | ||
255 | } | ||
256 | |||
257 | iout[FLED1] = min(micro_amp, iout_max[FLED1]); | ||
258 | iout[FLED2] = micro_amp - iout[FLED1]; | ||
259 | |||
260 | if (mode == FLASH) | ||
261 | led->allowed_modes &= ~MODE_FLASH_MASK; | ||
262 | else | ||
263 | led->allowed_modes &= ~MODE_TORCH_MASK; | ||
264 | |||
265 | max77693_add_allowed_modes(led, FLED1, mode); | ||
266 | |||
267 | if (iout[FLED2]) | ||
268 | max77693_add_allowed_modes(led, FLED2, mode); | ||
269 | } | ||
270 | |||
271 | static int max77693_set_torch_current(struct max77693_led_device *led, | ||
272 | int fled_id, u32 micro_amp) | ||
273 | { | ||
274 | struct regmap *rmap = led->regmap; | ||
275 | u8 iout1_reg = 0, iout2_reg = 0; | ||
276 | u32 iout[2]; | ||
277 | |||
278 | max77693_distribute_currents(led, fled_id, TORCH, micro_amp, | ||
279 | led->iout_torch_max, iout); | ||
280 | |||
281 | if (fled_id == FLED1 || led->iout_joint) { | ||
282 | iout1_reg = max77693_led_iout_to_reg(iout[FLED1]); | ||
283 | led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT2_SHIFT); | ||
284 | } | ||
285 | if (fled_id == FLED2 || led->iout_joint) { | ||
286 | iout2_reg = max77693_led_iout_to_reg(iout[FLED2]); | ||
287 | led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT1_SHIFT); | ||
288 | } | ||
289 | |||
290 | led->torch_iout_reg |= ((iout1_reg << TORCH_IOUT1_SHIFT) | | ||
291 | (iout2_reg << TORCH_IOUT2_SHIFT)); | ||
292 | |||
293 | return regmap_write(rmap, MAX77693_LED_REG_ITORCH, | ||
294 | led->torch_iout_reg); | ||
295 | } | ||
296 | |||
297 | static int max77693_set_flash_current(struct max77693_led_device *led, | ||
298 | int fled_id, | ||
299 | u32 micro_amp) | ||
300 | { | ||
301 | struct regmap *rmap = led->regmap; | ||
302 | u8 iout1_reg, iout2_reg; | ||
303 | u32 iout[2]; | ||
304 | int ret = -EINVAL; | ||
305 | |||
306 | max77693_distribute_currents(led, fled_id, FLASH, micro_amp, | ||
307 | led->iout_flash_max, iout); | ||
308 | |||
309 | if (fled_id == FLED1 || led->iout_joint) { | ||
310 | iout1_reg = max77693_led_iout_to_reg(iout[FLED1]); | ||
311 | ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1, | ||
312 | iout1_reg); | ||
313 | if (ret < 0) | ||
314 | return ret; | ||
315 | } | ||
316 | if (fled_id == FLED2 || led->iout_joint) { | ||
317 | iout2_reg = max77693_led_iout_to_reg(iout[FLED2]); | ||
318 | ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2, | ||
319 | iout2_reg); | ||
320 | } | ||
321 | |||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | static int max77693_set_timeout(struct max77693_led_device *led, u32 microsec) | ||
326 | { | ||
327 | struct regmap *rmap = led->regmap; | ||
328 | u8 v; | ||
329 | int ret; | ||
330 | |||
331 | v = max77693_flash_timeout_to_reg(microsec) | FLASH_TMR_LEVEL; | ||
332 | |||
333 | ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v); | ||
334 | if (ret < 0) | ||
335 | return ret; | ||
336 | |||
337 | led->current_flash_timeout = microsec; | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int max77693_get_strobe_status(struct max77693_led_device *led, | ||
343 | bool *state) | ||
344 | { | ||
345 | struct regmap *rmap = led->regmap; | ||
346 | unsigned int v; | ||
347 | int ret; | ||
348 | |||
349 | ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_STATUS, &v); | ||
350 | if (ret < 0) | ||
351 | return ret; | ||
352 | |||
353 | *state = v & FLASH_STATUS_FLASH_ON; | ||
354 | |||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | static int max77693_get_flash_faults(struct max77693_sub_led *sub_led) | ||
359 | { | ||
360 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
361 | struct regmap *rmap = led->regmap; | ||
362 | unsigned int v; | ||
363 | u8 fault_open_mask, fault_short_mask; | ||
364 | int ret; | ||
365 | |||
366 | sub_led->flash_faults = 0; | ||
367 | |||
368 | if (led->iout_joint) { | ||
369 | fault_open_mask = FLASH_INT_FLED1_OPEN | FLASH_INT_FLED2_OPEN; | ||
370 | fault_short_mask = FLASH_INT_FLED1_SHORT | | ||
371 | FLASH_INT_FLED2_SHORT; | ||
372 | } else { | ||
373 | fault_open_mask = (sub_led->fled_id == FLED1) ? | ||
374 | FLASH_INT_FLED1_OPEN : | ||
375 | FLASH_INT_FLED2_OPEN; | ||
376 | fault_short_mask = (sub_led->fled_id == FLED1) ? | ||
377 | FLASH_INT_FLED1_SHORT : | ||
378 | FLASH_INT_FLED2_SHORT; | ||
379 | } | ||
380 | |||
381 | ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_INT, &v); | ||
382 | if (ret < 0) | ||
383 | return ret; | ||
384 | |||
385 | if (v & fault_open_mask) | ||
386 | sub_led->flash_faults |= LED_FAULT_OVER_VOLTAGE; | ||
387 | if (v & fault_short_mask) | ||
388 | sub_led->flash_faults |= LED_FAULT_SHORT_CIRCUIT; | ||
389 | if (v & FLASH_INT_OVER_CURRENT) | ||
390 | sub_led->flash_faults |= LED_FAULT_OVER_CURRENT; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int max77693_setup(struct max77693_led_device *led, | ||
396 | struct max77693_led_config_data *led_cfg) | ||
397 | { | ||
398 | struct regmap *rmap = led->regmap; | ||
399 | int i, first_led, last_led, ret; | ||
400 | u32 max_flash_curr[2]; | ||
401 | u8 v; | ||
402 | |||
403 | /* | ||
404 | * Initialize only flash current. Torch current doesn't | ||
405 | * require initialization as ITORCH register is written with | ||
406 | * new value each time brightness_set op is called. | ||
407 | */ | ||
408 | if (led->iout_joint) { | ||
409 | first_led = FLED1; | ||
410 | last_led = FLED1; | ||
411 | max_flash_curr[FLED1] = led_cfg->iout_flash_max[FLED1] + | ||
412 | led_cfg->iout_flash_max[FLED2]; | ||
413 | } else { | ||
414 | first_led = max77693_fled_used(led, FLED1) ? FLED1 : FLED2; | ||
415 | last_led = max77693_fled_used(led, FLED2) ? FLED2 : FLED1; | ||
416 | max_flash_curr[FLED1] = led_cfg->iout_flash_max[FLED1]; | ||
417 | max_flash_curr[FLED2] = led_cfg->iout_flash_max[FLED2]; | ||
418 | } | ||
419 | |||
420 | for (i = first_led; i <= last_led; ++i) { | ||
421 | ret = max77693_set_flash_current(led, i, | ||
422 | max_flash_curr[i]); | ||
423 | if (ret < 0) | ||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | v = TORCH_TMR_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL; | ||
428 | ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v); | ||
429 | if (ret < 0) | ||
430 | return ret; | ||
431 | |||
432 | if (led_cfg->low_vsys > 0) | ||
433 | v = max77693_led_vsys_to_reg(led_cfg->low_vsys) | | ||
434 | MAX_FLASH1_MAX_FL_EN; | ||
435 | else | ||
436 | v = 0; | ||
437 | |||
438 | ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH1, v); | ||
439 | if (ret < 0) | ||
440 | return ret; | ||
441 | ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH2, 0); | ||
442 | if (ret < 0) | ||
443 | return ret; | ||
444 | |||
445 | if (led_cfg->boost_mode == MAX77693_LED_BOOST_FIXED) | ||
446 | v = FLASH_BOOST_FIXED; | ||
447 | else | ||
448 | v = led_cfg->boost_mode | led_cfg->boost_mode << 1; | ||
449 | |||
450 | if (max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2)) | ||
451 | v |= FLASH_BOOST_LEDNUM_2; | ||
452 | |||
453 | ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_CNTL, v); | ||
454 | if (ret < 0) | ||
455 | return ret; | ||
456 | |||
457 | v = max77693_led_vout_to_reg(led_cfg->boost_vout); | ||
458 | ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v); | ||
459 | if (ret < 0) | ||
460 | return ret; | ||
461 | |||
462 | return max77693_set_mode_reg(led, MODE_OFF); | ||
463 | } | ||
464 | |||
465 | static int __max77693_led_brightness_set(struct max77693_led_device *led, | ||
466 | int fled_id, enum led_brightness value) | ||
467 | { | ||
468 | int ret; | ||
469 | |||
470 | mutex_lock(&led->lock); | ||
471 | |||
472 | if (value == 0) { | ||
473 | ret = max77693_clear_mode(led, MODE_TORCH(fled_id)); | ||
474 | if (ret < 0) | ||
475 | dev_dbg(&led->pdev->dev, | ||
476 | "Failed to clear torch mode (%d)\n", | ||
477 | ret); | ||
478 | goto unlock; | ||
479 | } | ||
480 | |||
481 | ret = max77693_set_torch_current(led, fled_id, value * TORCH_IOUT_STEP); | ||
482 | if (ret < 0) { | ||
483 | dev_dbg(&led->pdev->dev, | ||
484 | "Failed to set torch current (%d)\n", | ||
485 | ret); | ||
486 | goto unlock; | ||
487 | } | ||
488 | |||
489 | ret = max77693_add_mode(led, MODE_TORCH(fled_id)); | ||
490 | if (ret < 0) | ||
491 | dev_dbg(&led->pdev->dev, | ||
492 | "Failed to set torch mode (%d)\n", | ||
493 | ret); | ||
494 | unlock: | ||
495 | mutex_unlock(&led->lock); | ||
496 | return ret; | ||
497 | } | ||
498 | |||
499 | static void max77693_led_brightness_set_work( | ||
500 | struct work_struct *work) | ||
501 | { | ||
502 | struct max77693_sub_led *sub_led = | ||
503 | container_of(work, struct max77693_sub_led, | ||
504 | work_brightness_set); | ||
505 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
506 | |||
507 | __max77693_led_brightness_set(led, sub_led->fled_id, | ||
508 | sub_led->torch_brightness); | ||
509 | } | ||
510 | |||
511 | /* LED subsystem callbacks */ | ||
512 | |||
513 | static int max77693_led_brightness_set_sync( | ||
514 | struct led_classdev *led_cdev, | ||
515 | enum led_brightness value) | ||
516 | { | ||
517 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
518 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
519 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
520 | |||
521 | return __max77693_led_brightness_set(led, sub_led->fled_id, value); | ||
522 | } | ||
523 | |||
524 | static void max77693_led_brightness_set( | ||
525 | struct led_classdev *led_cdev, | ||
526 | enum led_brightness value) | ||
527 | { | ||
528 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
529 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
530 | |||
531 | sub_led->torch_brightness = value; | ||
532 | schedule_work(&sub_led->work_brightness_set); | ||
533 | } | ||
534 | |||
535 | static int max77693_led_flash_brightness_set( | ||
536 | struct led_classdev_flash *fled_cdev, | ||
537 | u32 brightness) | ||
538 | { | ||
539 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
540 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
541 | int ret; | ||
542 | |||
543 | mutex_lock(&led->lock); | ||
544 | ret = max77693_set_flash_current(led, sub_led->fled_id, brightness); | ||
545 | mutex_unlock(&led->lock); | ||
546 | |||
547 | return ret; | ||
548 | } | ||
549 | |||
550 | static int max77693_led_flash_strobe_set( | ||
551 | struct led_classdev_flash *fled_cdev, | ||
552 | bool state) | ||
553 | { | ||
554 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
555 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
556 | int fled_id = sub_led->fled_id; | ||
557 | int ret; | ||
558 | |||
559 | mutex_lock(&led->lock); | ||
560 | |||
561 | if (!state) { | ||
562 | ret = max77693_clear_mode(led, MODE_FLASH(fled_id)); | ||
563 | goto unlock; | ||
564 | } | ||
565 | |||
566 | if (sub_led->flash_timeout != led->current_flash_timeout) { | ||
567 | ret = max77693_set_timeout(led, sub_led->flash_timeout); | ||
568 | if (ret < 0) | ||
569 | goto unlock; | ||
570 | } | ||
571 | |||
572 | led->strobing_sub_led_id = fled_id; | ||
573 | |||
574 | ret = max77693_add_mode(led, MODE_FLASH(fled_id)); | ||
575 | if (ret < 0) | ||
576 | goto unlock; | ||
577 | |||
578 | ret = max77693_get_flash_faults(sub_led); | ||
579 | |||
580 | unlock: | ||
581 | mutex_unlock(&led->lock); | ||
582 | return ret; | ||
583 | } | ||
584 | |||
585 | static int max77693_led_flash_fault_get( | ||
586 | struct led_classdev_flash *fled_cdev, | ||
587 | u32 *fault) | ||
588 | { | ||
589 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
590 | |||
591 | *fault = sub_led->flash_faults; | ||
592 | |||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static int max77693_led_flash_strobe_get( | ||
597 | struct led_classdev_flash *fled_cdev, | ||
598 | bool *state) | ||
599 | { | ||
600 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
601 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
602 | int ret; | ||
603 | |||
604 | if (!state) | ||
605 | return -EINVAL; | ||
606 | |||
607 | mutex_lock(&led->lock); | ||
608 | |||
609 | ret = max77693_get_strobe_status(led, state); | ||
610 | |||
611 | *state = !!(*state && (led->strobing_sub_led_id == sub_led->fled_id)); | ||
612 | |||
613 | mutex_unlock(&led->lock); | ||
614 | |||
615 | return ret; | ||
616 | } | ||
617 | |||
618 | static int max77693_led_flash_timeout_set( | ||
619 | struct led_classdev_flash *fled_cdev, | ||
620 | u32 timeout) | ||
621 | { | ||
622 | struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); | ||
623 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
624 | |||
625 | mutex_lock(&led->lock); | ||
626 | sub_led->flash_timeout = timeout; | ||
627 | mutex_unlock(&led->lock); | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | static int max77693_led_parse_dt(struct max77693_led_device *led, | ||
633 | struct max77693_led_config_data *cfg, | ||
634 | struct device_node **sub_nodes) | ||
635 | { | ||
636 | struct device *dev = &led->pdev->dev; | ||
637 | struct max77693_sub_led *sub_leds = led->sub_leds; | ||
638 | struct device_node *node = dev->of_node, *child_node; | ||
639 | struct property *prop; | ||
640 | u32 led_sources[2]; | ||
641 | int i, ret, fled_id; | ||
642 | |||
643 | of_property_read_u32(node, "maxim,boost-mode", &cfg->boost_mode); | ||
644 | of_property_read_u32(node, "maxim,boost-mvout", &cfg->boost_vout); | ||
645 | of_property_read_u32(node, "maxim,mvsys-min", &cfg->low_vsys); | ||
646 | |||
647 | for_each_available_child_of_node(node, child_node) { | ||
648 | prop = of_find_property(child_node, "led-sources", NULL); | ||
649 | if (prop) { | ||
650 | const __be32 *srcs = NULL; | ||
651 | |||
652 | for (i = 0; i < ARRAY_SIZE(led_sources); ++i) { | ||
653 | srcs = of_prop_next_u32(prop, srcs, | ||
654 | &led_sources[i]); | ||
655 | if (!srcs) | ||
656 | break; | ||
657 | } | ||
658 | } else { | ||
659 | dev_err(dev, | ||
660 | "led-sources DT property missing\n"); | ||
661 | of_node_put(child_node); | ||
662 | return -EINVAL; | ||
663 | } | ||
664 | |||
665 | if (i == 2) { | ||
666 | fled_id = FLED1; | ||
667 | led->fled_mask = FLED1_IOUT | FLED2_IOUT; | ||
668 | } else if (led_sources[0] == FLED1) { | ||
669 | fled_id = FLED1; | ||
670 | led->fled_mask |= FLED1_IOUT; | ||
671 | } else if (led_sources[0] == FLED2) { | ||
672 | fled_id = FLED2; | ||
673 | led->fled_mask |= FLED2_IOUT; | ||
674 | } else { | ||
675 | dev_err(dev, | ||
676 | "Wrong led-sources DT property value.\n"); | ||
677 | of_node_put(child_node); | ||
678 | return -EINVAL; | ||
679 | } | ||
680 | |||
681 | if (sub_nodes[fled_id]) { | ||
682 | dev_err(dev, | ||
683 | "Conflicting \"led-sources\" DT properties\n"); | ||
684 | return -EINVAL; | ||
685 | } | ||
686 | |||
687 | sub_nodes[fled_id] = child_node; | ||
688 | sub_leds[fled_id].fled_id = fled_id; | ||
689 | |||
690 | cfg->label[fled_id] = | ||
691 | of_get_property(child_node, "label", NULL) ? : | ||
692 | child_node->name; | ||
693 | |||
694 | ret = of_property_read_u32(child_node, "led-max-microamp", | ||
695 | &cfg->iout_torch_max[fled_id]); | ||
696 | if (ret < 0) { | ||
697 | cfg->iout_torch_max[fled_id] = TORCH_IOUT_MIN; | ||
698 | dev_warn(dev, "led-max-microamp DT property missing\n"); | ||
699 | } | ||
700 | |||
701 | ret = of_property_read_u32(child_node, "flash-max-microamp", | ||
702 | &cfg->iout_flash_max[fled_id]); | ||
703 | if (ret < 0) { | ||
704 | cfg->iout_flash_max[fled_id] = FLASH_IOUT_MIN; | ||
705 | dev_warn(dev, | ||
706 | "flash-max-microamp DT property missing\n"); | ||
707 | } | ||
708 | |||
709 | ret = of_property_read_u32(child_node, "flash-max-timeout-us", | ||
710 | &cfg->flash_timeout_max[fled_id]); | ||
711 | if (ret < 0) { | ||
712 | cfg->flash_timeout_max[fled_id] = FLASH_TIMEOUT_MIN; | ||
713 | dev_warn(dev, | ||
714 | "flash-max-timeout-us DT property missing\n"); | ||
715 | } | ||
716 | |||
717 | if (++cfg->num_leds == 2 || | ||
718 | (max77693_fled_used(led, FLED1) && | ||
719 | max77693_fled_used(led, FLED2))) { | ||
720 | of_node_put(child_node); | ||
721 | break; | ||
722 | } | ||
723 | } | ||
724 | |||
725 | if (cfg->num_leds == 0) { | ||
726 | dev_err(dev, "No DT child node found for connected LED(s).\n"); | ||
727 | return -EINVAL; | ||
728 | } | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static void clamp_align(u32 *v, u32 min, u32 max, u32 step) | ||
734 | { | ||
735 | *v = clamp_val(*v, min, max); | ||
736 | if (step > 1) | ||
737 | *v = (*v - min) / step * step + min; | ||
738 | } | ||
739 | |||
740 | static void max77693_align_iout_current(struct max77693_led_device *led, | ||
741 | u32 *iout, u32 min, u32 max, u32 step) | ||
742 | { | ||
743 | int i; | ||
744 | |||
745 | if (led->iout_joint) { | ||
746 | if (iout[FLED1] > min) { | ||
747 | iout[FLED1] /= 2; | ||
748 | iout[FLED2] = iout[FLED1]; | ||
749 | } else { | ||
750 | iout[FLED1] = min; | ||
751 | iout[FLED2] = 0; | ||
752 | return; | ||
753 | } | ||
754 | } | ||
755 | |||
756 | for (i = FLED1; i <= FLED2; ++i) | ||
757 | if (max77693_fled_used(led, i)) | ||
758 | clamp_align(&iout[i], min, max, step); | ||
759 | else | ||
760 | iout[i] = 0; | ||
761 | } | ||
762 | |||
763 | static void max77693_led_validate_configuration(struct max77693_led_device *led, | ||
764 | struct max77693_led_config_data *cfg) | ||
765 | { | ||
766 | u32 flash_iout_max = cfg->boost_mode ? FLASH_IOUT_MAX_2LEDS : | ||
767 | FLASH_IOUT_MAX_1LED; | ||
768 | int i; | ||
769 | |||
770 | if (cfg->num_leds == 1 && | ||
771 | max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2)) | ||
772 | led->iout_joint = true; | ||
773 | |||
774 | cfg->boost_mode = clamp_val(cfg->boost_mode, MAX77693_LED_BOOST_NONE, | ||
775 | MAX77693_LED_BOOST_FIXED); | ||
776 | |||
777 | /* Boost must be enabled if both current outputs are used */ | ||
778 | if ((cfg->boost_mode == MAX77693_LED_BOOST_NONE) && led->iout_joint) | ||
779 | cfg->boost_mode = MAX77693_LED_BOOST_FIXED; | ||
780 | |||
781 | max77693_align_iout_current(led, cfg->iout_torch_max, | ||
782 | TORCH_IOUT_MIN, TORCH_IOUT_MAX, TORCH_IOUT_STEP); | ||
783 | |||
784 | max77693_align_iout_current(led, cfg->iout_flash_max, | ||
785 | FLASH_IOUT_MIN, flash_iout_max, FLASH_IOUT_STEP); | ||
786 | |||
787 | for (i = 0; i < ARRAY_SIZE(cfg->flash_timeout_max); ++i) | ||
788 | clamp_align(&cfg->flash_timeout_max[i], FLASH_TIMEOUT_MIN, | ||
789 | FLASH_TIMEOUT_MAX, FLASH_TIMEOUT_STEP); | ||
790 | |||
791 | clamp_align(&cfg->boost_vout, FLASH_VOUT_MIN, FLASH_VOUT_MAX, | ||
792 | FLASH_VOUT_STEP); | ||
793 | |||
794 | if (cfg->low_vsys) | ||
795 | clamp_align(&cfg->low_vsys, MAX_FLASH1_VSYS_MIN, | ||
796 | MAX_FLASH1_VSYS_MAX, MAX_FLASH1_VSYS_STEP); | ||
797 | } | ||
798 | |||
799 | static int max77693_led_get_configuration(struct max77693_led_device *led, | ||
800 | struct max77693_led_config_data *cfg, | ||
801 | struct device_node **sub_nodes) | ||
802 | { | ||
803 | int ret; | ||
804 | |||
805 | ret = max77693_led_parse_dt(led, cfg, sub_nodes); | ||
806 | if (ret < 0) | ||
807 | return ret; | ||
808 | |||
809 | max77693_led_validate_configuration(led, cfg); | ||
810 | |||
811 | memcpy(led->iout_torch_max, cfg->iout_torch_max, | ||
812 | sizeof(led->iout_torch_max)); | ||
813 | memcpy(led->iout_flash_max, cfg->iout_flash_max, | ||
814 | sizeof(led->iout_flash_max)); | ||
815 | |||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | static const struct led_flash_ops flash_ops = { | ||
820 | .flash_brightness_set = max77693_led_flash_brightness_set, | ||
821 | .strobe_set = max77693_led_flash_strobe_set, | ||
822 | .strobe_get = max77693_led_flash_strobe_get, | ||
823 | .timeout_set = max77693_led_flash_timeout_set, | ||
824 | .fault_get = max77693_led_flash_fault_get, | ||
825 | }; | ||
826 | |||
827 | static void max77693_init_flash_settings(struct max77693_sub_led *sub_led, | ||
828 | struct max77693_led_config_data *led_cfg) | ||
829 | { | ||
830 | struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; | ||
831 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
832 | int fled_id = sub_led->fled_id; | ||
833 | struct led_flash_setting *setting; | ||
834 | |||
835 | /* Init flash intensity setting */ | ||
836 | setting = &fled_cdev->brightness; | ||
837 | setting->min = FLASH_IOUT_MIN; | ||
838 | setting->max = led->iout_joint ? | ||
839 | led_cfg->iout_flash_max[FLED1] + | ||
840 | led_cfg->iout_flash_max[FLED2] : | ||
841 | led_cfg->iout_flash_max[fled_id]; | ||
842 | setting->step = FLASH_IOUT_STEP; | ||
843 | setting->val = setting->max; | ||
844 | |||
845 | /* Init flash timeout setting */ | ||
846 | setting = &fled_cdev->timeout; | ||
847 | setting->min = FLASH_TIMEOUT_MIN; | ||
848 | setting->max = led_cfg->flash_timeout_max[fled_id]; | ||
849 | setting->step = FLASH_TIMEOUT_STEP; | ||
850 | setting->val = setting->max; | ||
851 | } | ||
852 | |||
853 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
854 | |||
855 | static int max77693_led_external_strobe_set( | ||
856 | struct v4l2_flash *v4l2_flash, | ||
857 | bool enable) | ||
858 | { | ||
859 | struct max77693_sub_led *sub_led = | ||
860 | flcdev_to_sub_led(v4l2_flash->fled_cdev); | ||
861 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
862 | int fled_id = sub_led->fled_id; | ||
863 | int ret; | ||
864 | |||
865 | mutex_lock(&led->lock); | ||
866 | |||
867 | if (enable) | ||
868 | ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL(fled_id)); | ||
869 | else | ||
870 | ret = max77693_clear_mode(led, MODE_FLASH_EXTERNAL(fled_id)); | ||
871 | |||
872 | mutex_unlock(&led->lock); | ||
873 | |||
874 | return ret; | ||
875 | } | ||
876 | |||
877 | static void max77693_init_v4l2_flash_config(struct max77693_sub_led *sub_led, | ||
878 | struct max77693_led_config_data *led_cfg, | ||
879 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
880 | { | ||
881 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
882 | struct device *dev = &led->pdev->dev; | ||
883 | struct max77693_dev *iodev = dev_get_drvdata(dev->parent); | ||
884 | struct i2c_client *i2c = iodev->i2c; | ||
885 | struct led_flash_setting *s; | ||
886 | |||
887 | snprintf(v4l2_sd_cfg->dev_name, sizeof(v4l2_sd_cfg->dev_name), | ||
888 | "%s %d-%04x", sub_led->fled_cdev.led_cdev.name, | ||
889 | i2c_adapter_id(i2c->adapter), i2c->addr); | ||
890 | |||
891 | s = &v4l2_sd_cfg->torch_intensity; | ||
892 | s->min = TORCH_IOUT_MIN; | ||
893 | s->max = sub_led->fled_cdev.led_cdev.max_brightness * TORCH_IOUT_STEP; | ||
894 | s->step = TORCH_IOUT_STEP; | ||
895 | s->val = s->max; | ||
896 | |||
897 | /* Init flash faults config */ | ||
898 | v4l2_sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE | | ||
899 | LED_FAULT_SHORT_CIRCUIT | | ||
900 | LED_FAULT_OVER_CURRENT; | ||
901 | |||
902 | v4l2_sd_cfg->has_external_strobe = true; | ||
903 | } | ||
904 | |||
905 | static const struct v4l2_flash_ops v4l2_flash_ops = { | ||
906 | .external_strobe_set = max77693_led_external_strobe_set, | ||
907 | }; | ||
908 | #else | ||
909 | static inline void max77693_init_v4l2_flash_config( | ||
910 | struct max77693_sub_led *sub_led, | ||
911 | struct max77693_led_config_data *led_cfg, | ||
912 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
913 | { | ||
914 | } | ||
915 | static const struct v4l2_flash_ops v4l2_flash_ops; | ||
916 | #endif | ||
917 | |||
918 | static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led, | ||
919 | struct max77693_led_config_data *led_cfg) | ||
920 | { | ||
921 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
922 | int fled_id = sub_led->fled_id; | ||
923 | struct led_classdev_flash *fled_cdev; | ||
924 | struct led_classdev *led_cdev; | ||
925 | |||
926 | /* Initialize LED Flash class device */ | ||
927 | fled_cdev = &sub_led->fled_cdev; | ||
928 | fled_cdev->ops = &flash_ops; | ||
929 | led_cdev = &fled_cdev->led_cdev; | ||
930 | |||
931 | led_cdev->name = led_cfg->label[fled_id]; | ||
932 | |||
933 | led_cdev->brightness_set = max77693_led_brightness_set; | ||
934 | led_cdev->brightness_set_sync = max77693_led_brightness_set_sync; | ||
935 | led_cdev->max_brightness = (led->iout_joint ? | ||
936 | led_cfg->iout_torch_max[FLED1] + | ||
937 | led_cfg->iout_torch_max[FLED2] : | ||
938 | led_cfg->iout_torch_max[fled_id]) / | ||
939 | TORCH_IOUT_STEP; | ||
940 | led_cdev->flags |= LED_DEV_CAP_FLASH; | ||
941 | INIT_WORK(&sub_led->work_brightness_set, | ||
942 | max77693_led_brightness_set_work); | ||
943 | |||
944 | max77693_init_flash_settings(sub_led, led_cfg); | ||
945 | |||
946 | /* Init flash timeout cache */ | ||
947 | sub_led->flash_timeout = fled_cdev->timeout.val; | ||
948 | } | ||
949 | |||
950 | static int max77693_register_led(struct max77693_sub_led *sub_led, | ||
951 | struct max77693_led_config_data *led_cfg, | ||
952 | struct device_node *sub_node) | ||
953 | { | ||
954 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
955 | struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; | ||
956 | struct device *dev = &led->pdev->dev; | ||
957 | struct v4l2_flash_config v4l2_sd_cfg = {}; | ||
958 | int ret; | ||
959 | |||
960 | /* Register in the LED subsystem */ | ||
961 | ret = led_classdev_flash_register(dev, fled_cdev); | ||
962 | if (ret < 0) | ||
963 | return ret; | ||
964 | |||
965 | max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg); | ||
966 | |||
967 | /* Register in the V4L2 subsystem. */ | ||
968 | sub_led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL, | ||
969 | &v4l2_flash_ops, &v4l2_sd_cfg); | ||
970 | if (IS_ERR(sub_led->v4l2_flash)) { | ||
971 | ret = PTR_ERR(sub_led->v4l2_flash); | ||
972 | goto err_v4l2_flash_init; | ||
973 | } | ||
974 | |||
975 | return 0; | ||
976 | |||
977 | err_v4l2_flash_init: | ||
978 | led_classdev_flash_unregister(fled_cdev); | ||
979 | return ret; | ||
980 | } | ||
981 | |||
982 | static int max77693_led_probe(struct platform_device *pdev) | ||
983 | { | ||
984 | struct device *dev = &pdev->dev; | ||
985 | struct max77693_dev *iodev = dev_get_drvdata(dev->parent); | ||
986 | struct max77693_led_device *led; | ||
987 | struct max77693_sub_led *sub_leds; | ||
988 | struct device_node *sub_nodes[2] = {}; | ||
989 | struct max77693_led_config_data led_cfg = {}; | ||
990 | int init_fled_cdev[2], i, ret; | ||
991 | |||
992 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); | ||
993 | if (!led) | ||
994 | return -ENOMEM; | ||
995 | |||
996 | led->pdev = pdev; | ||
997 | led->regmap = iodev->regmap; | ||
998 | led->allowed_modes = MODE_FLASH_MASK; | ||
999 | sub_leds = led->sub_leds; | ||
1000 | |||
1001 | platform_set_drvdata(pdev, led); | ||
1002 | ret = max77693_led_get_configuration(led, &led_cfg, sub_nodes); | ||
1003 | if (ret < 0) | ||
1004 | return ret; | ||
1005 | |||
1006 | ret = max77693_setup(led, &led_cfg); | ||
1007 | if (ret < 0) | ||
1008 | return ret; | ||
1009 | |||
1010 | mutex_init(&led->lock); | ||
1011 | |||
1012 | init_fled_cdev[FLED1] = | ||
1013 | led->iout_joint || max77693_fled_used(led, FLED1); | ||
1014 | init_fled_cdev[FLED2] = | ||
1015 | !led->iout_joint && max77693_fled_used(led, FLED2); | ||
1016 | |||
1017 | for (i = FLED1; i <= FLED2; ++i) { | ||
1018 | if (!init_fled_cdev[i]) | ||
1019 | continue; | ||
1020 | |||
1021 | /* Initialize LED Flash class device */ | ||
1022 | max77693_init_fled_cdev(&sub_leds[i], &led_cfg); | ||
1023 | |||
1024 | /* | ||
1025 | * Register LED Flash class device and corresponding | ||
1026 | * V4L2 Flash device. | ||
1027 | */ | ||
1028 | ret = max77693_register_led(&sub_leds[i], &led_cfg, | ||
1029 | sub_nodes[i]); | ||
1030 | if (ret < 0) { | ||
1031 | /* | ||
1032 | * At this moment FLED1 might have been already | ||
1033 | * registered and it needs to be released. | ||
1034 | */ | ||
1035 | if (i == FLED2) | ||
1036 | goto err_register_led2; | ||
1037 | else | ||
1038 | goto err_register_led1; | ||
1039 | } | ||
1040 | } | ||
1041 | |||
1042 | return 0; | ||
1043 | |||
1044 | err_register_led2: | ||
1045 | /* It is possible than only FLED2 was to be registered */ | ||
1046 | if (!init_fled_cdev[FLED1]) | ||
1047 | goto err_register_led1; | ||
1048 | v4l2_flash_release(sub_leds[FLED1].v4l2_flash); | ||
1049 | led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); | ||
1050 | err_register_led1: | ||
1051 | mutex_destroy(&led->lock); | ||
1052 | |||
1053 | return ret; | ||
1054 | } | ||
1055 | |||
1056 | static int max77693_led_remove(struct platform_device *pdev) | ||
1057 | { | ||
1058 | struct max77693_led_device *led = platform_get_drvdata(pdev); | ||
1059 | struct max77693_sub_led *sub_leds = led->sub_leds; | ||
1060 | |||
1061 | if (led->iout_joint || max77693_fled_used(led, FLED1)) { | ||
1062 | v4l2_flash_release(sub_leds[FLED1].v4l2_flash); | ||
1063 | led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); | ||
1064 | cancel_work_sync(&sub_leds[FLED1].work_brightness_set); | ||
1065 | } | ||
1066 | |||
1067 | if (!led->iout_joint && max77693_fled_used(led, FLED2)) { | ||
1068 | v4l2_flash_release(sub_leds[FLED2].v4l2_flash); | ||
1069 | led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev); | ||
1070 | cancel_work_sync(&sub_leds[FLED2].work_brightness_set); | ||
1071 | } | ||
1072 | |||
1073 | mutex_destroy(&led->lock); | ||
1074 | |||
1075 | return 0; | ||
1076 | } | ||
1077 | |||
1078 | static const struct of_device_id max77693_led_dt_match[] = { | ||
1079 | { .compatible = "maxim,max77693-led" }, | ||
1080 | {}, | ||
1081 | }; | ||
1082 | |||
1083 | static struct platform_driver max77693_led_driver = { | ||
1084 | .probe = max77693_led_probe, | ||
1085 | .remove = max77693_led_remove, | ||
1086 | .driver = { | ||
1087 | .name = "max77693-led", | ||
1088 | .of_match_table = max77693_led_dt_match, | ||
1089 | }, | ||
1090 | }; | ||
1091 | |||
1092 | module_platform_driver(max77693_led_driver); | ||
1093 | |||
1094 | MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); | ||
1095 | MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); | ||
1096 | MODULE_DESCRIPTION("Maxim MAX77693 led flash driver"); | ||
1097 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c new file mode 100644 index 000000000000..de16c29d7895 --- /dev/null +++ b/drivers/leds/leds-tlc591xx.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * Copyright 2014 Belkin Inc. | ||
3 | * Copyright 2015 Andrew Lunn <andrew@lunn.ch> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; version 2 of the License. | ||
8 | */ | ||
9 | |||
10 | #include <linux/i2c.h> | ||
11 | #include <linux/leds.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/of_device.h> | ||
15 | #include <linux/regmap.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/workqueue.h> | ||
18 | |||
19 | #define TLC591XX_MAX_LEDS 16 | ||
20 | |||
21 | #define TLC591XX_REG_MODE1 0x00 | ||
22 | #define MODE1_RESPON_ADDR_MASK 0xF0 | ||
23 | #define MODE1_NORMAL_MODE (0 << 4) | ||
24 | #define MODE1_SPEED_MODE (1 << 4) | ||
25 | |||
26 | #define TLC591XX_REG_MODE2 0x01 | ||
27 | #define MODE2_DIM (0 << 5) | ||
28 | #define MODE2_BLINK (1 << 5) | ||
29 | #define MODE2_OCH_STOP (0 << 3) | ||
30 | #define MODE2_OCH_ACK (1 << 3) | ||
31 | |||
32 | #define TLC591XX_REG_PWM(x) (0x02 + (x)) | ||
33 | |||
34 | #define TLC591XX_REG_GRPPWM 0x12 | ||
35 | #define TLC591XX_REG_GRPFREQ 0x13 | ||
36 | |||
37 | /* LED Driver Output State, determine the source that drives LED outputs */ | ||
38 | #define LEDOUT_OFF 0x0 /* Output LOW */ | ||
39 | #define LEDOUT_ON 0x1 /* Output HI-Z */ | ||
40 | #define LEDOUT_DIM 0x2 /* Dimming */ | ||
41 | #define LEDOUT_BLINK 0x3 /* Blinking */ | ||
42 | #define LEDOUT_MASK 0x3 | ||
43 | |||
44 | #define ldev_to_led(c) container_of(c, struct tlc591xx_led, ldev) | ||
45 | #define work_to_led(work) container_of(work, struct tlc591xx_led, work) | ||
46 | |||
47 | struct tlc591xx_led { | ||
48 | bool active; | ||
49 | unsigned int led_no; | ||
50 | struct led_classdev ldev; | ||
51 | struct work_struct work; | ||
52 | struct tlc591xx_priv *priv; | ||
53 | }; | ||
54 | |||
55 | struct tlc591xx_priv { | ||
56 | struct tlc591xx_led leds[TLC591XX_MAX_LEDS]; | ||
57 | struct regmap *regmap; | ||
58 | unsigned int reg_ledout_offset; | ||
59 | }; | ||
60 | |||
61 | struct tlc591xx { | ||
62 | unsigned int max_leds; | ||
63 | unsigned int reg_ledout_offset; | ||
64 | }; | ||
65 | |||
66 | static const struct tlc591xx tlc59116 = { | ||
67 | .max_leds = 16, | ||
68 | .reg_ledout_offset = 0x14, | ||
69 | }; | ||
70 | |||
71 | static const struct tlc591xx tlc59108 = { | ||
72 | .max_leds = 8, | ||
73 | .reg_ledout_offset = 0x0c, | ||
74 | }; | ||
75 | |||
76 | static int | ||
77 | tlc591xx_set_mode(struct regmap *regmap, u8 mode) | ||
78 | { | ||
79 | int err; | ||
80 | u8 val; | ||
81 | |||
82 | err = regmap_write(regmap, TLC591XX_REG_MODE1, MODE1_NORMAL_MODE); | ||
83 | if (err) | ||
84 | return err; | ||
85 | |||
86 | val = MODE2_OCH_STOP | mode; | ||
87 | |||
88 | return regmap_write(regmap, TLC591XX_REG_MODE2, val); | ||
89 | } | ||
90 | |||
91 | static int | ||
92 | tlc591xx_set_ledout(struct tlc591xx_priv *priv, struct tlc591xx_led *led, | ||
93 | u8 val) | ||
94 | { | ||
95 | unsigned int i = (led->led_no % 4) * 2; | ||
96 | unsigned int mask = LEDOUT_MASK << i; | ||
97 | unsigned int addr = priv->reg_ledout_offset + (led->led_no >> 2); | ||
98 | |||
99 | val = val << i; | ||
100 | |||
101 | return regmap_update_bits(priv->regmap, addr, mask, val); | ||
102 | } | ||
103 | |||
104 | static int | ||
105 | tlc591xx_set_pwm(struct tlc591xx_priv *priv, struct tlc591xx_led *led, | ||
106 | u8 brightness) | ||
107 | { | ||
108 | u8 pwm = TLC591XX_REG_PWM(led->led_no); | ||
109 | |||
110 | return regmap_write(priv->regmap, pwm, brightness); | ||
111 | } | ||
112 | |||
113 | static void | ||
114 | tlc591xx_led_work(struct work_struct *work) | ||
115 | { | ||
116 | struct tlc591xx_led *led = work_to_led(work); | ||
117 | struct tlc591xx_priv *priv = led->priv; | ||
118 | enum led_brightness brightness = led->ldev.brightness; | ||
119 | int err; | ||
120 | |||
121 | switch (brightness) { | ||
122 | case 0: | ||
123 | err = tlc591xx_set_ledout(priv, led, LEDOUT_OFF); | ||
124 | break; | ||
125 | case LED_FULL: | ||
126 | err = tlc591xx_set_ledout(priv, led, LEDOUT_ON); | ||
127 | break; | ||
128 | default: | ||
129 | err = tlc591xx_set_ledout(priv, led, LEDOUT_DIM); | ||
130 | if (!err) | ||
131 | err = tlc591xx_set_pwm(priv, led, brightness); | ||
132 | } | ||
133 | |||
134 | if (err) | ||
135 | dev_err(led->ldev.dev, "Failed setting brightness\n"); | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | tlc591xx_brightness_set(struct led_classdev *led_cdev, | ||
140 | enum led_brightness brightness) | ||
141 | { | ||
142 | struct tlc591xx_led *led = ldev_to_led(led_cdev); | ||
143 | |||
144 | led->ldev.brightness = brightness; | ||
145 | schedule_work(&led->work); | ||
146 | } | ||
147 | |||
148 | static void | ||
149 | tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j) | ||
150 | { | ||
151 | int i = j; | ||
152 | |||
153 | while (--i >= 0) { | ||
154 | if (priv->leds[i].active) { | ||
155 | led_classdev_unregister(&priv->leds[i].ldev); | ||
156 | cancel_work_sync(&priv->leds[i].work); | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | static int | ||
162 | tlc591xx_configure(struct device *dev, | ||
163 | struct tlc591xx_priv *priv, | ||
164 | const struct tlc591xx *tlc591xx) | ||
165 | { | ||
166 | unsigned int i; | ||
167 | int err = 0; | ||
168 | |||
169 | tlc591xx_set_mode(priv->regmap, MODE2_DIM); | ||
170 | for (i = 0; i < TLC591XX_MAX_LEDS; i++) { | ||
171 | struct tlc591xx_led *led = &priv->leds[i]; | ||
172 | |||
173 | if (!led->active) | ||
174 | continue; | ||
175 | |||
176 | led->priv = priv; | ||
177 | led->led_no = i; | ||
178 | led->ldev.brightness_set = tlc591xx_brightness_set; | ||
179 | led->ldev.max_brightness = LED_FULL; | ||
180 | INIT_WORK(&led->work, tlc591xx_led_work); | ||
181 | err = led_classdev_register(dev, &led->ldev); | ||
182 | if (err < 0) { | ||
183 | dev_err(dev, "couldn't register LED %s\n", | ||
184 | led->ldev.name); | ||
185 | goto exit; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | return 0; | ||
190 | |||
191 | exit: | ||
192 | tlc591xx_destroy_devices(priv, i); | ||
193 | return err; | ||
194 | } | ||
195 | |||
196 | static const struct regmap_config tlc591xx_regmap = { | ||
197 | .reg_bits = 8, | ||
198 | .val_bits = 8, | ||
199 | .max_register = 0x1e, | ||
200 | }; | ||
201 | |||
202 | static const struct of_device_id of_tlc591xx_leds_match[] = { | ||
203 | { .compatible = "ti,tlc59116", | ||
204 | .data = &tlc59116 }, | ||
205 | { .compatible = "ti,tlc59108", | ||
206 | .data = &tlc59108 }, | ||
207 | {}, | ||
208 | }; | ||
209 | MODULE_DEVICE_TABLE(of, of_tlc591xx_leds_match); | ||
210 | |||
211 | static int | ||
212 | tlc591xx_probe(struct i2c_client *client, | ||
213 | const struct i2c_device_id *id) | ||
214 | { | ||
215 | struct device_node *np = client->dev.of_node, *child; | ||
216 | struct device *dev = &client->dev; | ||
217 | const struct of_device_id *match; | ||
218 | const struct tlc591xx *tlc591xx; | ||
219 | struct tlc591xx_priv *priv; | ||
220 | int err, count, reg; | ||
221 | |||
222 | match = of_match_device(of_tlc591xx_leds_match, dev); | ||
223 | if (!match) | ||
224 | return -ENODEV; | ||
225 | |||
226 | tlc591xx = match->data; | ||
227 | if (!np) | ||
228 | return -ENODEV; | ||
229 | |||
230 | count = of_get_child_count(np); | ||
231 | if (!count || count > tlc591xx->max_leds) | ||
232 | return -EINVAL; | ||
233 | |||
234 | if (!i2c_check_functionality(client->adapter, | ||
235 | I2C_FUNC_SMBUS_BYTE_DATA)) | ||
236 | return -EIO; | ||
237 | |||
238 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
239 | if (!priv) | ||
240 | return -ENOMEM; | ||
241 | |||
242 | priv->regmap = devm_regmap_init_i2c(client, &tlc591xx_regmap); | ||
243 | if (IS_ERR(priv->regmap)) { | ||
244 | err = PTR_ERR(priv->regmap); | ||
245 | dev_err(dev, "Failed to allocate register map: %d\n", err); | ||
246 | return err; | ||
247 | } | ||
248 | priv->reg_ledout_offset = tlc591xx->reg_ledout_offset; | ||
249 | |||
250 | i2c_set_clientdata(client, priv); | ||
251 | |||
252 | for_each_child_of_node(np, child) { | ||
253 | err = of_property_read_u32(child, "reg", ®); | ||
254 | if (err) | ||
255 | return err; | ||
256 | if (reg < 0 || reg >= tlc591xx->max_leds) | ||
257 | return -EINVAL; | ||
258 | if (priv->leds[reg].active) | ||
259 | return -EINVAL; | ||
260 | priv->leds[reg].active = true; | ||
261 | priv->leds[reg].ldev.name = | ||
262 | of_get_property(child, "label", NULL) ? : child->name; | ||
263 | priv->leds[reg].ldev.default_trigger = | ||
264 | of_get_property(child, "linux,default-trigger", NULL); | ||
265 | } | ||
266 | return tlc591xx_configure(dev, priv, tlc591xx); | ||
267 | } | ||
268 | |||
269 | static int | ||
270 | tlc591xx_remove(struct i2c_client *client) | ||
271 | { | ||
272 | struct tlc591xx_priv *priv = i2c_get_clientdata(client); | ||
273 | |||
274 | tlc591xx_destroy_devices(priv, TLC591XX_MAX_LEDS); | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static const struct i2c_device_id tlc591xx_id[] = { | ||
280 | { "tlc59116" }, | ||
281 | { "tlc59108" }, | ||
282 | {}, | ||
283 | }; | ||
284 | MODULE_DEVICE_TABLE(i2c, tlc591xx_id); | ||
285 | |||
286 | static struct i2c_driver tlc591xx_driver = { | ||
287 | .driver = { | ||
288 | .name = "tlc591xx", | ||
289 | .of_match_table = of_match_ptr(of_tlc591xx_leds_match), | ||
290 | }, | ||
291 | .probe = tlc591xx_probe, | ||
292 | .remove = tlc591xx_remove, | ||
293 | .id_table = tlc591xx_id, | ||
294 | }; | ||
295 | |||
296 | module_i2c_driver(tlc591xx_driver); | ||
297 | |||
298 | MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); | ||
299 | MODULE_LICENSE("GPL"); | ||
300 | MODULE_DESCRIPTION("TLC591XX LED driver"); | ||
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 79efe57c7405..bc89d7ace2c4 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h | |||
@@ -13,7 +13,6 @@ | |||
13 | #ifndef __LEDS_H_INCLUDED | 13 | #ifndef __LEDS_H_INCLUDED |
14 | #define __LEDS_H_INCLUDED | 14 | #define __LEDS_H_INCLUDED |
15 | 15 | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/rwsem.h> | 16 | #include <linux/rwsem.h> |
18 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
19 | 18 | ||
@@ -50,27 +49,4 @@ void led_stop_software_blink(struct led_classdev *led_cdev); | |||
50 | extern struct rw_semaphore leds_list_lock; | 49 | extern struct rw_semaphore leds_list_lock; |
51 | extern struct list_head leds_list; | 50 | extern struct list_head leds_list; |
52 | 51 | ||
53 | #ifdef CONFIG_LEDS_TRIGGERS | ||
54 | void led_trigger_set_default(struct led_classdev *led_cdev); | ||
55 | void led_trigger_set(struct led_classdev *led_cdev, | ||
56 | struct led_trigger *trigger); | ||
57 | void led_trigger_remove(struct led_classdev *led_cdev); | ||
58 | |||
59 | static inline void *led_get_trigger_data(struct led_classdev *led_cdev) | ||
60 | { | ||
61 | return led_cdev->trigger_data; | ||
62 | } | ||
63 | |||
64 | #else | ||
65 | #define led_trigger_set_default(x) do {} while (0) | ||
66 | #define led_trigger_set(x, y) do {} while (0) | ||
67 | #define led_trigger_remove(x) do {} while (0) | ||
68 | #define led_get_trigger_data(x) (NULL) | ||
69 | #endif | ||
70 | |||
71 | ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | ||
72 | const char *buf, size_t count); | ||
73 | ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, | ||
74 | char *buf); | ||
75 | |||
76 | #endif /* __LEDS_H_INCLUDED */ | 52 | #endif /* __LEDS_H_INCLUDED */ |
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index f7a01a72eb9e..b4b022933e29 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-async.c b/drivers/media/v4l2-core/v4l2-async.c index 85a6a34128a8..5bada202b2d3 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c | |||
@@ -22,10 +22,10 @@ | |||
22 | #include <media/v4l2-device.h> | 22 | #include <media/v4l2-device.h> |
23 | #include <media/v4l2-subdev.h> | 23 | #include <media/v4l2-subdev.h> |
24 | 24 | ||
25 | static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) | 25 | static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
26 | { | 26 | { |
27 | #if IS_ENABLED(CONFIG_I2C) | 27 | #if IS_ENABLED(CONFIG_I2C) |
28 | struct i2c_client *client = i2c_verify_client(dev); | 28 | struct i2c_client *client = i2c_verify_client(sd->dev); |
29 | return client && | 29 | return client && |
30 | asd->match.i2c.adapter_id == client->adapter->nr && | 30 | asd->match.i2c.adapter_id == client->adapter->nr && |
31 | asd->match.i2c.address == client->addr; | 31 | asd->match.i2c.address == client->addr; |
@@ -34,14 +34,24 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) | |||
34 | #endif | 34 | #endif |
35 | } | 35 | } |
36 | 36 | ||
37 | static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) | 37 | static bool match_devname(struct v4l2_subdev *sd, |
38 | struct v4l2_async_subdev *asd) | ||
38 | { | 39 | { |
39 | return !strcmp(asd->match.device_name.name, dev_name(dev)); | 40 | return !strcmp(asd->match.device_name.name, dev_name(sd->dev)); |
40 | } | 41 | } |
41 | 42 | ||
42 | static bool match_of(struct device *dev, struct v4l2_async_subdev *asd) | 43 | static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
43 | { | 44 | { |
44 | return dev->of_node == asd->match.of.node; | 45 | return sd->of_node == asd->match.of.node; |
46 | } | ||
47 | |||
48 | static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | ||
49 | { | ||
50 | if (!asd->match.custom.match) | ||
51 | /* Match always */ | ||
52 | return true; | ||
53 | |||
54 | return asd->match.custom.match(sd->dev, asd); | ||
45 | } | 55 | } |
46 | 56 | ||
47 | static LIST_HEAD(subdev_list); | 57 | static LIST_HEAD(subdev_list); |
@@ -51,17 +61,14 @@ static DEFINE_MUTEX(list_lock); | |||
51 | static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, | 61 | static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, |
52 | struct v4l2_subdev *sd) | 62 | struct v4l2_subdev *sd) |
53 | { | 63 | { |
64 | bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *); | ||
54 | struct v4l2_async_subdev *asd; | 65 | struct v4l2_async_subdev *asd; |
55 | bool (*match)(struct device *, struct v4l2_async_subdev *); | ||
56 | 66 | ||
57 | list_for_each_entry(asd, ¬ifier->waiting, list) { | 67 | list_for_each_entry(asd, ¬ifier->waiting, list) { |
58 | /* bus_type has been verified valid before */ | 68 | /* bus_type has been verified valid before */ |
59 | switch (asd->match_type) { | 69 | switch (asd->match_type) { |
60 | case V4L2_ASYNC_MATCH_CUSTOM: | 70 | case V4L2_ASYNC_MATCH_CUSTOM: |
61 | match = asd->match.custom.match; | 71 | match = match_custom; |
62 | if (!match) | ||
63 | /* Match always */ | ||
64 | return asd; | ||
65 | break; | 72 | break; |
66 | case V4L2_ASYNC_MATCH_DEVNAME: | 73 | case V4L2_ASYNC_MATCH_DEVNAME: |
67 | match = match_devname; | 74 | match = match_devname; |
@@ -79,7 +86,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * | |||
79 | } | 86 | } |
80 | 87 | ||
81 | /* match cannot be NULL here */ | 88 | /* match cannot be NULL here */ |
82 | if (match(sd->dev, asd)) | 89 | if (match(sd, asd)) |
83 | return asd; | 90 | return asd; |
84 | } | 91 | } |
85 | 92 | ||
@@ -266,6 +273,14 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) | |||
266 | { | 273 | { |
267 | struct v4l2_async_notifier *notifier; | 274 | struct v4l2_async_notifier *notifier; |
268 | 275 | ||
276 | /* | ||
277 | * No reference taken. The reference is held by the device | ||
278 | * (struct v4l2_subdev.dev), and async sub-device does not | ||
279 | * exist independently of the device at any point of time. | ||
280 | */ | ||
281 | if (!sd->of_node && sd->dev) | ||
282 | sd->of_node = sd->dev->of_node; | ||
283 | |||
269 | mutex_lock(&list_lock); | 284 | mutex_lock(&list_lock); |
270 | 285 | ||
271 | INIT_LIST_HEAD(&sd->async_list); | 286 | INIT_LIST_HEAD(&sd->async_list); |
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/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index fd098169fe87..adac255aee86 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h | |||
@@ -407,6 +407,21 @@ static inline int desc_to_gpio(const struct gpio_desc *desc) | |||
407 | return -EINVAL; | 407 | return -EINVAL; |
408 | } | 408 | } |
409 | 409 | ||
410 | /* Child properties interface */ | ||
411 | struct fwnode_handle; | ||
412 | |||
413 | static inline struct gpio_desc *fwnode_get_named_gpiod( | ||
414 | struct fwnode_handle *fwnode, const char *propname) | ||
415 | { | ||
416 | return ERR_PTR(-ENOSYS); | ||
417 | } | ||
418 | |||
419 | static inline struct gpio_desc *devm_get_gpiod_from_child( | ||
420 | struct device *dev, const char *con_id, struct fwnode_handle *child) | ||
421 | { | ||
422 | return ERR_PTR(-ENOSYS); | ||
423 | } | ||
424 | |||
410 | #endif /* CONFIG_GPIOLIB */ | 425 | #endif /* CONFIG_GPIOLIB */ |
411 | 426 | ||
412 | /* | 427 | /* |
diff --git a/include/linux/leds.h b/include/linux/leds.h index 9a2b000094cf..b122eeafb5dc 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #ifndef __LINUX_LEDS_H_INCLUDED | 12 | #ifndef __LINUX_LEDS_H_INCLUDED |
13 | #define __LINUX_LEDS_H_INCLUDED | 13 | #define __LINUX_LEDS_H_INCLUDED |
14 | 14 | ||
15 | #include <linux/device.h> | ||
15 | #include <linux/list.h> | 16 | #include <linux/list.h> |
16 | #include <linux/mutex.h> | 17 | #include <linux/mutex.h> |
17 | #include <linux/rwsem.h> | 18 | #include <linux/rwsem.h> |
@@ -222,6 +223,11 @@ struct led_trigger { | |||
222 | struct list_head next_trig; | 223 | struct list_head next_trig; |
223 | }; | 224 | }; |
224 | 225 | ||
226 | ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | ||
227 | const char *buf, size_t count); | ||
228 | ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, | ||
229 | char *buf); | ||
230 | |||
225 | /* Registration functions for complex triggers */ | 231 | /* Registration functions for complex triggers */ |
226 | extern int led_trigger_register(struct led_trigger *trigger); | 232 | extern int led_trigger_register(struct led_trigger *trigger); |
227 | extern void led_trigger_unregister(struct led_trigger *trigger); | 233 | extern void led_trigger_unregister(struct led_trigger *trigger); |
@@ -238,6 +244,16 @@ extern void led_trigger_blink_oneshot(struct led_trigger *trigger, | |||
238 | unsigned long *delay_on, | 244 | unsigned long *delay_on, |
239 | unsigned long *delay_off, | 245 | unsigned long *delay_off, |
240 | int invert); | 246 | int invert); |
247 | extern void led_trigger_set_default(struct led_classdev *led_cdev); | ||
248 | extern void led_trigger_set(struct led_classdev *led_cdev, | ||
249 | struct led_trigger *trigger); | ||
250 | extern void led_trigger_remove(struct led_classdev *led_cdev); | ||
251 | |||
252 | static inline void *led_get_trigger_data(struct led_classdev *led_cdev) | ||
253 | { | ||
254 | return led_cdev->trigger_data; | ||
255 | } | ||
256 | |||
241 | /** | 257 | /** |
242 | * led_trigger_rename_static - rename a trigger | 258 | * led_trigger_rename_static - rename a trigger |
243 | * @name: the new trigger name | 259 | * @name: the new trigger name |
@@ -267,6 +283,15 @@ static inline void led_trigger_register_simple(const char *name, | |||
267 | static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {} | 283 | static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {} |
268 | static inline void led_trigger_event(struct led_trigger *trigger, | 284 | static inline void led_trigger_event(struct led_trigger *trigger, |
269 | enum led_brightness event) {} | 285 | enum led_brightness event) {} |
286 | static inline void led_trigger_set_default(struct led_classdev *led_cdev) {} | ||
287 | static inline void led_trigger_set(struct led_classdev *led_cdev, | ||
288 | struct led_trigger *trigger) {} | ||
289 | static inline void led_trigger_remove(struct led_classdev *led_cdev) {} | ||
290 | static inline void *led_get_trigger_data(struct led_classdev *led_cdev) | ||
291 | { | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
270 | #endif /* CONFIG_LEDS_TRIGGERS */ | 295 | #endif /* CONFIG_LEDS_TRIGGERS */ |
271 | 296 | ||
272 | /* Trigger specific functions */ | 297 | /* Trigger specific functions */ |
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 */ | ||
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index dc20102ff600..4e18318eb425 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h | |||
@@ -605,6 +605,8 @@ struct v4l2_subdev { | |||
605 | struct video_device *devnode; | 605 | struct video_device *devnode; |
606 | /* pointer to the physical device, if any */ | 606 | /* pointer to the physical device, if any */ |
607 | struct device *dev; | 607 | struct device *dev; |
608 | /* The device_node of the subdev, usually the same as dev->of_node. */ | ||
609 | struct device_node *of_node; | ||
608 | /* Links this subdev to a global subdev_list or @notifier->done list. */ | 610 | /* Links this subdev to a global subdev_list or @notifier->done list. */ |
609 | struct list_head async_list; | 611 | struct list_head async_list; |
610 | /* Pointer to respective struct v4l2_async_subdev. */ | 612 | /* Pointer to respective struct v4l2_async_subdev. */ |