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 ...
This commit is contained in:
Коммит
13d45f79a2
|
@ -0,0 +1,73 @@
|
||||||
|
* Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs
|
||||||
|
|
||||||
|
The device is controlled through two pins: FL_EN and EN_SET. The pins when,
|
||||||
|
asserted high, enable flash strobe and movie mode (max 1/2 of flash current)
|
||||||
|
respectively. In order to add a capability of selecting the strobe signal source
|
||||||
|
(e.g. CPU or camera sensor) there is an additional switch required, independent
|
||||||
|
of the flash chip. The switch is controlled with pin control.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible : Must be "skyworks,aat1290".
|
||||||
|
- flen-gpios : Must be device tree identifier of the flash device FL_EN pin.
|
||||||
|
- enset-gpios : Must be device tree identifier of the flash device EN_SET pin.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- pinctrl-names : Must contain entries: "default", "host", "isp". Entries
|
||||||
|
"default" and "host" must refer to the same pin configuration
|
||||||
|
node, which sets the host as a strobe signal provider. Entry
|
||||||
|
"isp" must refer to the pin configuration node, which sets the
|
||||||
|
ISP as a strobe signal provider.
|
||||||
|
|
||||||
|
A discrete LED element connected to the device must be represented by a child
|
||||||
|
node - see Documentation/devicetree/bindings/leds/common.txt.
|
||||||
|
|
||||||
|
Required properties of the LED child node:
|
||||||
|
- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
Maximum flash LED supply current can be calculated using
|
||||||
|
following formula: I = 1A * 162kohm / Rset.
|
||||||
|
- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
Maximum flash timeout can be calculated using following
|
||||||
|
formula: T = 8.82 * 10^9 * Ct.
|
||||||
|
|
||||||
|
Optional properties of the LED child node:
|
||||||
|
- label : see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
Example (by Ct = 220nF, Rset = 160kohm and exynos4412-trats2 board with
|
||||||
|
a switch that allows for routing strobe signal either from the host or from
|
||||||
|
the camera sensor):
|
||||||
|
|
||||||
|
#include "exynos4412.dtsi"
|
||||||
|
|
||||||
|
aat1290 {
|
||||||
|
compatible = "skyworks,aat1290";
|
||||||
|
flen-gpios = <&gpj1 1 GPIO_ACTIVE_HIGH>;
|
||||||
|
enset-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
||||||
|
pinctrl-names = "default", "host", "isp";
|
||||||
|
pinctrl-0 = <&camera_flash_host>;
|
||||||
|
pinctrl-1 = <&camera_flash_host>;
|
||||||
|
pinctrl-2 = <&camera_flash_isp>;
|
||||||
|
|
||||||
|
camera_flash: flash-led {
|
||||||
|
label = "aat1290-flash";
|
||||||
|
led-max-microamp = <520833>;
|
||||||
|
flash-max-microamp = <1012500>;
|
||||||
|
flash-timeout-us = <1940000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&pinctrl_0 {
|
||||||
|
camera_flash_host: camera-flash-host {
|
||||||
|
samsung,pins = "gpj1-0";
|
||||||
|
samsung,pin-function = <1>;
|
||||||
|
samsung,pin-val = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
camera_flash_isp: camera-flash-isp {
|
||||||
|
samsung,pins = "gpj1-0";
|
||||||
|
samsung,pin-function = <1>;
|
||||||
|
samsung,pin-val = <1>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,309 @@
|
||||||
|
LEDs connected to Broadcom BCM6328 controller
|
||||||
|
|
||||||
|
This controller is present on BCM6318, BCM6328, BCM6362 and BCM63268.
|
||||||
|
In these SoCs it's possible to control LEDs both as GPIOs or by hardware.
|
||||||
|
However, on some devices there are Serial LEDs (LEDs connected to a 74x164
|
||||||
|
controller), which can either be controlled by software (exporting the 74x164
|
||||||
|
as spi-gpio. See Documentation/devicetree/bindings/gpio/gpio-74x164.txt), or
|
||||||
|
by hardware using this driver.
|
||||||
|
Some of these Serial LEDs are hardware controlled (e.g. ethernet LEDs) and
|
||||||
|
exporting the 74x164 as spi-gpio prevents those LEDs to be hardware
|
||||||
|
controlled, so the only chance to keep them working is by using this driver.
|
||||||
|
|
||||||
|
BCM6328 LED controller has a HWDIS register, which controls whether a LED
|
||||||
|
should be controlled by a hardware signal instead of the MODE register value,
|
||||||
|
with 0 meaning hardware control enabled and 1 hardware control disabled. This
|
||||||
|
is usually 1:1 for hardware to LED signals, but through the activity/link
|
||||||
|
registers you have some limited control over rerouting the LEDs (as
|
||||||
|
explained later in brcm,link-signal-sources). Even if a LED is hardware
|
||||||
|
controlled you are still able to make it blink or light it up if it isn't,
|
||||||
|
but you can't turn it off if the hardware decides to light it up. For this
|
||||||
|
reason, hardware controlled LEDs aren't registered as LED class devices.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : should be "brcm,bcm6328-leds".
|
||||||
|
- #address-cells : must be 1.
|
||||||
|
- #size-cells : must be 0.
|
||||||
|
- reg : BCM6328 LED controller address and size.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- brcm,serial-leds : Boolean, enables Serial LEDs.
|
||||||
|
Default : false
|
||||||
|
|
||||||
|
Each LED is represented as a sub-node of the brcm,bcm6328-leds device.
|
||||||
|
|
||||||
|
LED sub-node required properties:
|
||||||
|
- reg : LED pin number (only LEDs 0 to 23 are valid).
|
||||||
|
|
||||||
|
LED sub-node optional properties:
|
||||||
|
a) Optional properties for sub-nodes related to software controlled LEDs:
|
||||||
|
- label : see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
- active-low : Boolean, makes LED active low.
|
||||||
|
Default : false
|
||||||
|
- default-state : see
|
||||||
|
Documentation/devicetree/bindings/leds/leds-gpio.txt
|
||||||
|
- linux,default-trigger : see
|
||||||
|
Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
b) Optional properties for sub-nodes related to hardware controlled LEDs:
|
||||||
|
- brcm,hardware-controlled : Boolean, makes this LED hardware controlled.
|
||||||
|
Default : false
|
||||||
|
- brcm,link-signal-sources : An array of hardware link
|
||||||
|
signal sources. Up to four link hardware signals can get muxed into
|
||||||
|
these LEDs. Only valid for LEDs 0 to 7, where LED signals 0 to 3 may
|
||||||
|
be muxed to LEDs 0 to 3, and signals 4 to 7 may be muxed to LEDs
|
||||||
|
4 to 7. A signal can be muxed to more than one LED, and one LED can
|
||||||
|
have more than one source signal.
|
||||||
|
- brcm,activity-signal-sources : An array of hardware activity
|
||||||
|
signal sources. Up to four activity hardware signals can get muxed into
|
||||||
|
these LEDs. Only valid for LEDs 0 to 7, where LED signals 0 to 3 may
|
||||||
|
be muxed to LEDs 0 to 3, and signals 4 to 7 may be muxed to LEDs
|
||||||
|
4 to 7. A signal can be muxed to more than one LED, and one LED can
|
||||||
|
have more than one source signal.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Scenario 1 : BCM6328 with 4 EPHY LEDs
|
||||||
|
leds0: led-controller@10000800 {
|
||||||
|
compatible = "brcm,bcm6328-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x10000800 0x24>;
|
||||||
|
|
||||||
|
alarm_red@2 {
|
||||||
|
reg = <2>;
|
||||||
|
active-low;
|
||||||
|
label = "red:alarm";
|
||||||
|
};
|
||||||
|
inet_green@3 {
|
||||||
|
reg = <3>;
|
||||||
|
active-low;
|
||||||
|
label = "green:inet";
|
||||||
|
};
|
||||||
|
power_green@4 {
|
||||||
|
reg = <4>;
|
||||||
|
active-low;
|
||||||
|
label = "green:power";
|
||||||
|
default-state = "on";
|
||||||
|
};
|
||||||
|
ephy0_spd@17 {
|
||||||
|
reg = <17>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy1_spd@18 {
|
||||||
|
reg = <18>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy2_spd@19 {
|
||||||
|
reg = <19>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy3_spd@20 {
|
||||||
|
reg = <20>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 2 : BCM63268 with Serial/GPHY0 LEDs
|
||||||
|
leds0: led-controller@10001900 {
|
||||||
|
compatible = "brcm,bcm6328-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x10001900 0x24>;
|
||||||
|
brcm,serial-leds;
|
||||||
|
|
||||||
|
gphy0_spd0@0 {
|
||||||
|
reg = <0>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <0>;
|
||||||
|
};
|
||||||
|
gphy0_spd1@1 {
|
||||||
|
reg = <1>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <1>;
|
||||||
|
};
|
||||||
|
inet_red@2 {
|
||||||
|
reg = <2>;
|
||||||
|
active-low;
|
||||||
|
label = "red:inet";
|
||||||
|
};
|
||||||
|
dsl_green@3 {
|
||||||
|
reg = <3>;
|
||||||
|
active-low;
|
||||||
|
label = "green:dsl";
|
||||||
|
};
|
||||||
|
usb_green@4 {
|
||||||
|
reg = <4>;
|
||||||
|
active-low;
|
||||||
|
label = "green:usb";
|
||||||
|
};
|
||||||
|
wps_green@7 {
|
||||||
|
reg = <7>;
|
||||||
|
active-low;
|
||||||
|
label = "green:wps";
|
||||||
|
};
|
||||||
|
inet_green@8 {
|
||||||
|
reg = <8>;
|
||||||
|
active-low;
|
||||||
|
label = "green:inet";
|
||||||
|
};
|
||||||
|
ephy0_act@9 {
|
||||||
|
reg = <9>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy1_act@10 {
|
||||||
|
reg = <10>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy2_act@11 {
|
||||||
|
reg = <11>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
gphy0_act@12 {
|
||||||
|
reg = <12>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy0_spd@13 {
|
||||||
|
reg = <13>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy1_spd@14 {
|
||||||
|
reg = <14>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
ephy2_spd@15 {
|
||||||
|
reg = <15>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
};
|
||||||
|
power_green@20 {
|
||||||
|
reg = <20>;
|
||||||
|
active-low;
|
||||||
|
label = "green:power";
|
||||||
|
default-state = "on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 3 : BCM6362 with 1 LED for each EPHY
|
||||||
|
leds0: led-controller@10001900 {
|
||||||
|
compatible = "brcm,bcm6328-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x10001900 0x24>;
|
||||||
|
|
||||||
|
usb@0 {
|
||||||
|
reg = <0>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <0>;
|
||||||
|
brcm,activity-signal-sources = <0>;
|
||||||
|
/* USB link/activity routed to USB LED */
|
||||||
|
};
|
||||||
|
inet@1 {
|
||||||
|
reg = <1>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,activity-signal-sources = <1>;
|
||||||
|
/* INET activity routed to INET LED */
|
||||||
|
};
|
||||||
|
ephy0@4 {
|
||||||
|
reg = <4>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <4>;
|
||||||
|
/* EPHY0 link routed to EPHY0 LED */
|
||||||
|
};
|
||||||
|
ephy1@5 {
|
||||||
|
reg = <5>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <5>;
|
||||||
|
/* EPHY1 link routed to EPHY1 LED */
|
||||||
|
};
|
||||||
|
ephy2@6 {
|
||||||
|
reg = <6>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <6>;
|
||||||
|
/* EPHY2 link routed to EPHY2 LED */
|
||||||
|
};
|
||||||
|
ephy3@7 {
|
||||||
|
reg = <7>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <7>;
|
||||||
|
/* EPHY3 link routed to EPHY3 LED */
|
||||||
|
};
|
||||||
|
power_green@20 {
|
||||||
|
reg = <20>;
|
||||||
|
active-low;
|
||||||
|
label = "green:power";
|
||||||
|
default-state = "on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 4 : BCM6362 with 1 LED for all EPHYs
|
||||||
|
leds0: led-controller@10001900 {
|
||||||
|
compatible = "brcm,bcm6328-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x10001900 0x24>;
|
||||||
|
|
||||||
|
usb@0 {
|
||||||
|
reg = <0>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <0 1>;
|
||||||
|
brcm,activity-signal-sources = <0 1>;
|
||||||
|
/* USB/INET link/activity routed to USB LED */
|
||||||
|
};
|
||||||
|
ephy@4 {
|
||||||
|
reg = <4>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <4 5 6 7>;
|
||||||
|
/* EPHY0/1/2/3 link routed to EPHY0 LED */
|
||||||
|
};
|
||||||
|
power_green@20 {
|
||||||
|
reg = <20>;
|
||||||
|
active-low;
|
||||||
|
label = "green:power";
|
||||||
|
default-state = "on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 5 : BCM6362 with EPHY LEDs swapped
|
||||||
|
leds0: led-controller@10001900 {
|
||||||
|
compatible = "brcm,bcm6328-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x10001900 0x24>;
|
||||||
|
|
||||||
|
usb@0 {
|
||||||
|
reg = <0>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <0>;
|
||||||
|
brcm,activity-signal-sources = <0 1>;
|
||||||
|
/* USB link/act and INET act routed to USB LED */
|
||||||
|
};
|
||||||
|
ephy0@4 {
|
||||||
|
reg = <4>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <7>;
|
||||||
|
/* EPHY3 link routed to EPHY0 LED */
|
||||||
|
};
|
||||||
|
ephy1@5 {
|
||||||
|
reg = <5>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <6>;
|
||||||
|
/* EPHY2 link routed to EPHY1 LED */
|
||||||
|
};
|
||||||
|
ephy2@6 {
|
||||||
|
reg = <6>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <5>;
|
||||||
|
/* EPHY1 link routed to EPHY2 LED */
|
||||||
|
};
|
||||||
|
ephy3@7 {
|
||||||
|
reg = <7>;
|
||||||
|
brcm,hardware-controlled;
|
||||||
|
brcm,link-signal-sources = <4>;
|
||||||
|
/* EPHY0 link routed to EPHY3 LED */
|
||||||
|
};
|
||||||
|
power_green@20 {
|
||||||
|
reg = <20>;
|
||||||
|
active-low;
|
||||||
|
label = "green:power";
|
||||||
|
default-state = "on";
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,145 @@
|
||||||
|
LEDs connected to Broadcom BCM6358 controller
|
||||||
|
|
||||||
|
This controller is present on BCM6358 and BCM6368.
|
||||||
|
In these SoCs there are Serial LEDs (LEDs connected to a 74x164 controller),
|
||||||
|
which can either be controlled by software (exporting the 74x164 as spi-gpio.
|
||||||
|
See Documentation/devicetree/bindings/gpio/gpio-74x164.txt), or
|
||||||
|
by hardware using this driver.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : should be "brcm,bcm6358-leds".
|
||||||
|
- #address-cells : must be 1.
|
||||||
|
- #size-cells : must be 0.
|
||||||
|
- reg : BCM6358 LED controller address and size.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- brcm,clk-div : SCK signal divider. Possible values are 1, 2, 4 and 8.
|
||||||
|
Default : 1
|
||||||
|
- brcm,clk-dat-low : Boolean, makes clock and data signals active low.
|
||||||
|
Default : false
|
||||||
|
|
||||||
|
Each LED is represented as a sub-node of the brcm,bcm6358-leds device.
|
||||||
|
|
||||||
|
LED sub-node required properties:
|
||||||
|
- reg : LED pin number (only LEDs 0 to 31 are valid).
|
||||||
|
|
||||||
|
LED sub-node optional properties:
|
||||||
|
- label : see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
- active-low : Boolean, makes LED active low.
|
||||||
|
Default : false
|
||||||
|
- default-state : see
|
||||||
|
Documentation/devicetree/bindings/leds/leds-gpio.txt
|
||||||
|
- linux,default-trigger : see
|
||||||
|
Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Scenario 1 : BCM6358
|
||||||
|
leds0: led-controller@fffe00d0 {
|
||||||
|
compatible = "brcm,bcm6358-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0xfffe00d0 0x8>;
|
||||||
|
|
||||||
|
alarm_white {
|
||||||
|
reg = <0>;
|
||||||
|
active-low;
|
||||||
|
label = "white:alarm";
|
||||||
|
};
|
||||||
|
tv_white {
|
||||||
|
reg = <2>;
|
||||||
|
active-low;
|
||||||
|
label = "white:tv";
|
||||||
|
};
|
||||||
|
tel_white {
|
||||||
|
reg = <3>;
|
||||||
|
active-low;
|
||||||
|
label = "white:tel";
|
||||||
|
};
|
||||||
|
adsl_white {
|
||||||
|
reg = <4>;
|
||||||
|
active-low;
|
||||||
|
label = "white:adsl";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 2 : BCM6368
|
||||||
|
leds0: led-controller@100000d0 {
|
||||||
|
compatible = "brcm,bcm6358-leds";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x100000d0 0x8>;
|
||||||
|
brcm,pol-low;
|
||||||
|
brcm,clk-div = <4>;
|
||||||
|
|
||||||
|
power_red {
|
||||||
|
reg = <0>;
|
||||||
|
active-low;
|
||||||
|
label = "red:power";
|
||||||
|
};
|
||||||
|
power_green {
|
||||||
|
reg = <1>;
|
||||||
|
active-low;
|
||||||
|
label = "green:power";
|
||||||
|
default-state = "on";
|
||||||
|
};
|
||||||
|
power_blue {
|
||||||
|
reg = <2>;
|
||||||
|
label = "blue:power";
|
||||||
|
};
|
||||||
|
broadband_red {
|
||||||
|
reg = <3>;
|
||||||
|
active-low;
|
||||||
|
label = "red:broadband";
|
||||||
|
};
|
||||||
|
broadband_green {
|
||||||
|
reg = <4>;
|
||||||
|
label = "green:broadband";
|
||||||
|
};
|
||||||
|
broadband_blue {
|
||||||
|
reg = <5>;
|
||||||
|
active-low;
|
||||||
|
label = "blue:broadband";
|
||||||
|
};
|
||||||
|
wireless_red {
|
||||||
|
reg = <6>;
|
||||||
|
active-low;
|
||||||
|
label = "red:wireless";
|
||||||
|
};
|
||||||
|
wireless_green {
|
||||||
|
reg = <7>;
|
||||||
|
active-low;
|
||||||
|
label = "green:wireless";
|
||||||
|
};
|
||||||
|
wireless_blue {
|
||||||
|
reg = <8>;
|
||||||
|
label = "blue:wireless";
|
||||||
|
};
|
||||||
|
phone_red {
|
||||||
|
reg = <9>;
|
||||||
|
active-low;
|
||||||
|
label = "red:phone";
|
||||||
|
};
|
||||||
|
phone_green {
|
||||||
|
reg = <10>;
|
||||||
|
active-low;
|
||||||
|
label = "green:phone";
|
||||||
|
};
|
||||||
|
phone_blue {
|
||||||
|
reg = <11>;
|
||||||
|
label = "blue:phone";
|
||||||
|
};
|
||||||
|
upgrading_red {
|
||||||
|
reg = <12>;
|
||||||
|
active-low;
|
||||||
|
label = "red:upgrading";
|
||||||
|
};
|
||||||
|
upgrading_green {
|
||||||
|
reg = <13>;
|
||||||
|
active-low;
|
||||||
|
label = "green:upgrading";
|
||||||
|
};
|
||||||
|
upgrading_blue {
|
||||||
|
reg = <14>;
|
||||||
|
label = "blue:upgrading";
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,50 @@
|
||||||
|
* Kinetic Technologies - KTD2692 Flash LED Driver
|
||||||
|
|
||||||
|
KTD2692 is the ideal power solution for high-power flash LEDs.
|
||||||
|
It uses ExpressWire single-wire programming for maximum flexibility.
|
||||||
|
|
||||||
|
The ExpressWire interface through CTRL pin can control LED on/off and
|
||||||
|
enable/disable the IC, Movie(max 1/3 of Flash current) / Flash mode current,
|
||||||
|
Flash timeout, LVP(low voltage protection).
|
||||||
|
|
||||||
|
Also, When the AUX pin is pulled high while CTRL pin is high,
|
||||||
|
LED current will be ramped up to the flash-mode current level.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be "kinetic,ktd2692".
|
||||||
|
- ctrl-gpios : Specifier of the GPIO connected to CTRL pin.
|
||||||
|
- aux-gpios : Specifier of the GPIO connected to AUX pin.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- vin-supply : "vin" LED supply (2.7V to 5.5V).
|
||||||
|
See Documentation/devicetree/bindings/regulator/regulator.txt
|
||||||
|
|
||||||
|
A discrete LED element connected to the device must be represented by a child
|
||||||
|
node - See Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
Required properties for flash LED child nodes:
|
||||||
|
See Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
- led-max-microamp : Minimum Threshold for Timer protection
|
||||||
|
is defined internally (Maximum 300mA).
|
||||||
|
- flash-max-microamp : Flash LED maximum current
|
||||||
|
Formula : I(mA) = 15000 / Rset.
|
||||||
|
- flash-max-timeout-us : Flash LED maximum timeout.
|
||||||
|
|
||||||
|
Optional properties for flash LED child nodes:
|
||||||
|
- label : See Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
ktd2692 {
|
||||||
|
compatible = "kinetic,ktd2692";
|
||||||
|
ctrl-gpios = <&gpc0 1 0>;
|
||||||
|
aux-gpios = <&gpc0 2 0>;
|
||||||
|
vin-supply = <&vbat>;
|
||||||
|
|
||||||
|
flash-led {
|
||||||
|
label = "ktd2692-flash";
|
||||||
|
led-max-microamp = <300000>;
|
||||||
|
flash-max-microamp = <1500000>;
|
||||||
|
flash-max-timeout-us = <1835000>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
LEDs connected to tlc59116 or tlc59108
|
||||||
|
|
||||||
|
Required properties
|
||||||
|
- compatible: should be "ti,tlc59116" or "ti,tlc59108"
|
||||||
|
- #address-cells: must be 1
|
||||||
|
- #size-cells: must be 0
|
||||||
|
- reg: typically 0x68
|
||||||
|
|
||||||
|
Each led is represented as a sub-node of the ti,tlc59116.
|
||||||
|
See Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
LED sub-node properties:
|
||||||
|
- reg: number of LED line, 0 to 15 or 0 to 7
|
||||||
|
- label: (optional) name of LED
|
||||||
|
- linux,default-trigger : (optional)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
tlc59116@68 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
compatible = "ti,tlc59116";
|
||||||
|
reg = <0x68>;
|
||||||
|
|
||||||
|
wan@0 {
|
||||||
|
label = "wrt1900ac:amber:wan";
|
||||||
|
reg = <0x0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
2g@2 {
|
||||||
|
label = "wrt1900ac:white:2g";
|
||||||
|
reg = <0x2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
alive@9 {
|
||||||
|
label = "wrt1900ac:green:alive";
|
||||||
|
reg = <0x9>;
|
||||||
|
linux,default_trigger = "heartbeat";
|
||||||
|
};
|
||||||
|
};
|
|
@ -114,6 +114,7 @@ isee ISEE 2007 S.L.
|
||||||
isil Intersil
|
isil Intersil
|
||||||
karo Ka-Ro electronics GmbH
|
karo Ka-Ro electronics GmbH
|
||||||
keymile Keymile GmbH
|
keymile Keymile GmbH
|
||||||
|
kinetic Kinetic Technologies
|
||||||
lacie LaCie
|
lacie LaCie
|
||||||
lantiq Lantiq Semiconductor
|
lantiq Lantiq Semiconductor
|
||||||
lenovo Lenovo Group Ltd.
|
lenovo Lenovo Group Ltd.
|
||||||
|
|
|
@ -20,3 +20,54 @@ Following sysfs attributes are exposed for controlling flash LED devices:
|
||||||
- max_flash_timeout
|
- max_flash_timeout
|
||||||
- flash_strobe
|
- flash_strobe
|
||||||
- flash_fault
|
- flash_fault
|
||||||
|
|
||||||
|
|
||||||
|
V4L2 flash wrapper for flash LEDs
|
||||||
|
=================================
|
||||||
|
|
||||||
|
A LED subsystem driver can be controlled also from the level of VideoForLinux2
|
||||||
|
subsystem. In order to enable this CONFIG_V4L2_FLASH_LED_CLASS symbol has to
|
||||||
|
be defined in the kernel config.
|
||||||
|
|
||||||
|
The driver must call the v4l2_flash_init function to get registered in the
|
||||||
|
V4L2 subsystem. The function takes six arguments:
|
||||||
|
- dev : flash device, e.g. an I2C device
|
||||||
|
- of_node : of_node of the LED, may be NULL if the same as device's
|
||||||
|
- fled_cdev : LED flash class device to wrap
|
||||||
|
- iled_cdev : LED flash class device representing indicator LED associated with
|
||||||
|
fled_cdev, may be NULL
|
||||||
|
- ops : V4L2 specific ops
|
||||||
|
* external_strobe_set - defines the source of the flash LED strobe -
|
||||||
|
V4L2_CID_FLASH_STROBE control or external source, typically
|
||||||
|
a sensor, which makes it possible to synchronise the flash
|
||||||
|
strobe start with exposure start,
|
||||||
|
* intensity_to_led_brightness and led_brightness_to_intensity - perform
|
||||||
|
enum led_brightness <-> V4L2 intensity conversion in a device
|
||||||
|
specific manner - they can be used for devices with non-linear
|
||||||
|
LED current scale.
|
||||||
|
- config : configuration for V4L2 Flash sub-device
|
||||||
|
* dev_name - the name of the media entity, unique in the system,
|
||||||
|
* flash_faults - bitmask of flash faults that the LED flash class
|
||||||
|
device can report; corresponding LED_FAULT* bit definitions are
|
||||||
|
available in <linux/led-class-flash.h>,
|
||||||
|
* torch_intensity - constraints for the LED in TORCH mode
|
||||||
|
in microamperes,
|
||||||
|
* indicator_intensity - constraints for the indicator LED
|
||||||
|
in microamperes,
|
||||||
|
* has_external_strobe - determines whether the flash strobe source
|
||||||
|
can be switched to external,
|
||||||
|
|
||||||
|
On remove the v4l2_flash_release function has to be called, which takes one
|
||||||
|
argument - struct v4l2_flash pointer returned previously by v4l2_flash_init.
|
||||||
|
This function can be safely called with NULL or error pointer argument.
|
||||||
|
|
||||||
|
Please refer to drivers/leds/leds-max77693.c for an exemplary usage of the
|
||||||
|
v4l2 flash wrapper.
|
||||||
|
|
||||||
|
Once the V4L2 sub-device is registered by the driver which created the Media
|
||||||
|
controller device, the sub-device node acts just as a node of a native V4L2
|
||||||
|
flash API device would. The calls are simply routed to the LED flash API.
|
||||||
|
|
||||||
|
Opening the V4L2 flash sub-device makes the LED subsystem sysfs interface
|
||||||
|
unavailable. The interface is re-enabled after the V4L2 flash sub-device
|
||||||
|
is closed.
|
||||||
|
|
|
@ -49,6 +49,36 @@ There are two ways to run LED patterns.
|
||||||
2) Firmware interface - LP55xx common interface
|
2) Firmware interface - LP55xx common interface
|
||||||
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
||||||
|
|
||||||
|
LP5523 has three master faders. If a channel is mapped to one of
|
||||||
|
the master faders, its output is dimmed based on the value of the master
|
||||||
|
fader.
|
||||||
|
|
||||||
|
For example,
|
||||||
|
|
||||||
|
echo "123000123" > master_fader_leds
|
||||||
|
|
||||||
|
creates the following channel-fader mappings:
|
||||||
|
|
||||||
|
channel 0,6 to master_fader1
|
||||||
|
channel 1,7 to master_fader2
|
||||||
|
channel 2,8 to master_fader3
|
||||||
|
|
||||||
|
Then, to have 25% of the original output on channel 0,6:
|
||||||
|
|
||||||
|
echo 64 > master_fader1
|
||||||
|
|
||||||
|
To have 0% of the original output (i.e. no output) channel 1,7:
|
||||||
|
|
||||||
|
echo 0 > master_fader2
|
||||||
|
|
||||||
|
To have 100% of the original output (i.e. no dimming) on channel 2,8:
|
||||||
|
|
||||||
|
echo 255 > master_fader3
|
||||||
|
|
||||||
|
To clear all master fader controls:
|
||||||
|
|
||||||
|
echo "000000000" > master_fader_leds
|
||||||
|
|
||||||
Selftest uses always the current from the platform data.
|
Selftest uses always the current from the platform data.
|
||||||
|
|
||||||
Each channel contains led current settings.
|
Each channel contains led current settings.
|
||||||
|
|
|
@ -39,6 +39,32 @@ config LEDS_88PM860X
|
||||||
This option enables support for on-chip LED drivers found on Marvell
|
This option enables support for on-chip LED drivers found on Marvell
|
||||||
Semiconductor 88PM8606 PMIC.
|
Semiconductor 88PM8606 PMIC.
|
||||||
|
|
||||||
|
config LEDS_AAT1290
|
||||||
|
tristate "LED support for the AAT1290"
|
||||||
|
depends on LEDS_CLASS_FLASH
|
||||||
|
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
|
||||||
|
depends on GPIOLIB
|
||||||
|
depends on OF
|
||||||
|
depends on PINCTRL
|
||||||
|
help
|
||||||
|
This option enables support for the LEDs on the AAT1290.
|
||||||
|
|
||||||
|
config LEDS_BCM6328
|
||||||
|
tristate "LED Support for Broadcom BCM6328"
|
||||||
|
depends on LEDS_CLASS
|
||||||
|
depends on OF
|
||||||
|
help
|
||||||
|
This option enables support for LEDs connected to the BCM6328
|
||||||
|
LED HW controller accessed via MMIO registers.
|
||||||
|
|
||||||
|
config LEDS_BCM6358
|
||||||
|
tristate "LED Support for Broadcom BCM6358"
|
||||||
|
depends on LEDS_CLASS
|
||||||
|
depends on OF
|
||||||
|
help
|
||||||
|
This option enables support for LEDs connected to the BCM6358
|
||||||
|
LED HW controller accessed via MMIO registers.
|
||||||
|
|
||||||
config LEDS_LM3530
|
config LEDS_LM3530
|
||||||
tristate "LCD Backlight driver for LM3530"
|
tristate "LCD Backlight driver for LM3530"
|
||||||
depends on LEDS_CLASS
|
depends on LEDS_CLASS
|
||||||
|
@ -179,7 +205,7 @@ config LEDS_PCA9532_GPIO
|
||||||
config LEDS_GPIO
|
config LEDS_GPIO
|
||||||
tristate "LED Support for GPIO connected LEDs"
|
tristate "LED Support for GPIO connected LEDs"
|
||||||
depends on LEDS_CLASS
|
depends on LEDS_CLASS
|
||||||
depends on GPIOLIB
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This option enables support for the LEDs connected to GPIO
|
This option enables support for the LEDs connected to GPIO
|
||||||
outputs. To be useful the particular board must have LEDs
|
outputs. To be useful the particular board must have LEDs
|
||||||
|
@ -203,6 +229,7 @@ config LEDS_LP55XX_COMMON
|
||||||
tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
|
tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
|
||||||
depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
|
depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
|
||||||
select FW_LOADER
|
select FW_LOADER
|
||||||
|
select FW_LOADER_USER_HELPER_FALLBACK
|
||||||
help
|
help
|
||||||
This option supports common operations for LP5521/5523/55231/5562/8501
|
This option supports common operations for LP5521/5523/55231/5562/8501
|
||||||
devices.
|
devices.
|
||||||
|
@ -464,6 +491,25 @@ config LEDS_TCA6507
|
||||||
LED driver chips accessed via the I2C bus.
|
LED driver chips accessed via the I2C bus.
|
||||||
Driver support brightness control and hardware-assisted blinking.
|
Driver support brightness control and hardware-assisted blinking.
|
||||||
|
|
||||||
|
config LEDS_TLC591XX
|
||||||
|
tristate "LED driver for TLC59108 and TLC59116 controllers"
|
||||||
|
depends on LEDS_CLASS && I2C
|
||||||
|
select REGMAP_I2C
|
||||||
|
help
|
||||||
|
This option enables support for Texas Instruments TLC59108
|
||||||
|
and TLC59116 LED controllers.
|
||||||
|
|
||||||
|
config LEDS_MAX77693
|
||||||
|
tristate "LED support for MAX77693 Flash"
|
||||||
|
depends on LEDS_CLASS_FLASH
|
||||||
|
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
|
||||||
|
depends on MFD_MAX77693
|
||||||
|
depends on OF
|
||||||
|
help
|
||||||
|
This option enables support for the flash part of the MAX77693
|
||||||
|
multifunction device. It has build in control for two leds in flash
|
||||||
|
and torch mode.
|
||||||
|
|
||||||
config LEDS_MAX8997
|
config LEDS_MAX8997
|
||||||
tristate "LED support for MAX8997 PMIC"
|
tristate "LED support for MAX8997 PMIC"
|
||||||
depends on LEDS_CLASS && MFD_MAX8997
|
depends on LEDS_CLASS && MFD_MAX8997
|
||||||
|
@ -495,6 +541,15 @@ config LEDS_MENF21BMC
|
||||||
This driver can also be built as a module. If so the module
|
This driver can also be built as a module. If so the module
|
||||||
will be called leds-menf21bmc.
|
will be called leds-menf21bmc.
|
||||||
|
|
||||||
|
config LEDS_KTD2692
|
||||||
|
tristate "LED support for KTD2692 flash LED controller"
|
||||||
|
depends on LEDS_CLASS_FLASH && GPIOLIB && OF
|
||||||
|
help
|
||||||
|
This option enables support for KTD2692 LED flash connected
|
||||||
|
through ExpressWire interface.
|
||||||
|
|
||||||
|
Say Y to enable this driver.
|
||||||
|
|
||||||
comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
|
comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
|
||||||
|
|
||||||
config LEDS_BLINKM
|
config LEDS_BLINKM
|
||||||
|
|
|
@ -7,6 +7,9 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
|
||||||
|
|
||||||
# LED Platform Drivers
|
# LED Platform Drivers
|
||||||
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
|
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
|
||||||
|
obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o
|
||||||
|
obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
|
||||||
|
obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o
|
||||||
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
|
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
|
||||||
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
|
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
|
||||||
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
|
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
|
||||||
|
@ -31,6 +34,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
|
||||||
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
|
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
|
||||||
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
|
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
|
||||||
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
|
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
|
||||||
|
obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o
|
||||||
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
|
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
|
||||||
obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
|
obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
|
||||||
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
|
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
|
||||||
|
@ -52,6 +56,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
|
||||||
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
|
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
|
||||||
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
|
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
|
||||||
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
|
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
|
||||||
|
obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o
|
||||||
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
|
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
|
||||||
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
|
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
|
||||||
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
|
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
|
||||||
|
@ -59,6 +64,7 @@ obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
|
||||||
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
|
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
|
||||||
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
|
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
|
||||||
obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o
|
obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o
|
||||||
|
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
|
||||||
|
|
||||||
# LED SPI Drivers
|
# LED SPI Drivers
|
||||||
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
|
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
|
||||||
|
|
|
@ -121,6 +121,11 @@ static void led_timer_function(unsigned long data)
|
||||||
brightness = led_get_brightness(led_cdev);
|
brightness = led_get_brightness(led_cdev);
|
||||||
if (!brightness) {
|
if (!brightness) {
|
||||||
/* Time to switch the LED on. */
|
/* Time to switch the LED on. */
|
||||||
|
if (led_cdev->delayed_set_value) {
|
||||||
|
led_cdev->blink_brightness =
|
||||||
|
led_cdev->delayed_set_value;
|
||||||
|
led_cdev->delayed_set_value = 0;
|
||||||
|
}
|
||||||
brightness = led_cdev->blink_brightness;
|
brightness = led_cdev->blink_brightness;
|
||||||
delay = led_cdev->blink_delay_on;
|
delay = led_cdev->blink_delay_on;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -119,10 +119,11 @@ void led_set_brightness(struct led_classdev *led_cdev,
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* delay brightness setting if need to stop soft-blink timer */
|
/* delay brightness if soft-blink is active */
|
||||||
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
|
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
|
||||||
led_cdev->delayed_set_value = brightness;
|
led_cdev->delayed_set_value = brightness;
|
||||||
schedule_work(&led_cdev->set_brightness_work);
|
if (brightness == LED_OFF)
|
||||||
|
schedule_work(&led_cdev->set_brightness_work);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,576 @@
|
||||||
|
/*
|
||||||
|
* LED Flash class driver for the AAT1290
|
||||||
|
* 1.5A Step-Up Current Regulator for Flash LEDs
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015, Samsung Electronics Co., Ltd.
|
||||||
|
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* version 2 as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/led-class-flash.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <media/v4l2-flash-led-class.h>
|
||||||
|
|
||||||
|
#define AAT1290_MOVIE_MODE_CURRENT_ADDR 17
|
||||||
|
#define AAT1290_MAX_MM_CURR_PERCENT_0 16
|
||||||
|
#define AAT1290_MAX_MM_CURR_PERCENT_100 1
|
||||||
|
|
||||||
|
#define AAT1290_FLASH_SAFETY_TIMER_ADDR 18
|
||||||
|
|
||||||
|
#define AAT1290_MOVIE_MODE_CONFIG_ADDR 19
|
||||||
|
#define AAT1290_MOVIE_MODE_OFF 1
|
||||||
|
#define AAT1290_MOVIE_MODE_ON 3
|
||||||
|
|
||||||
|
#define AAT1290_MM_CURRENT_RATIO_ADDR 20
|
||||||
|
#define AAT1290_MM_TO_FL_1_92 1
|
||||||
|
|
||||||
|
#define AAT1290_MM_TO_FL_RATIO 1000 / 1920
|
||||||
|
#define AAT1290_MAX_MM_CURRENT(fl_max) (fl_max * AAT1290_MM_TO_FL_RATIO)
|
||||||
|
|
||||||
|
#define AAT1290_LATCH_TIME_MIN_US 500
|
||||||
|
#define AAT1290_LATCH_TIME_MAX_US 1000
|
||||||
|
#define AAT1290_EN_SET_TICK_TIME_US 1
|
||||||
|
#define AAT1290_FLEN_OFF_DELAY_TIME_US 10
|
||||||
|
#define AAT1290_FLASH_TM_NUM_LEVELS 16
|
||||||
|
#define AAT1290_MM_CURRENT_SCALE_SIZE 15
|
||||||
|
|
||||||
|
|
||||||
|
struct aat1290_led_config_data {
|
||||||
|
/* maximum LED current in movie mode */
|
||||||
|
u32 max_mm_current;
|
||||||
|
/* maximum LED current in flash mode */
|
||||||
|
u32 max_flash_current;
|
||||||
|
/* maximum flash timeout */
|
||||||
|
u32 max_flash_tm;
|
||||||
|
/* external strobe capability */
|
||||||
|
bool has_external_strobe;
|
||||||
|
/* max LED brightness level */
|
||||||
|
enum led_brightness max_brightness;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct aat1290_led {
|
||||||
|
/* platform device data */
|
||||||
|
struct platform_device *pdev;
|
||||||
|
/* secures access to the device */
|
||||||
|
struct mutex lock;
|
||||||
|
|
||||||
|
/* corresponding LED Flash class device */
|
||||||
|
struct led_classdev_flash fled_cdev;
|
||||||
|
/* V4L2 Flash device */
|
||||||
|
struct v4l2_flash *v4l2_flash;
|
||||||
|
|
||||||
|
/* FLEN pin */
|
||||||
|
struct gpio_desc *gpio_fl_en;
|
||||||
|
/* EN|SET pin */
|
||||||
|
struct gpio_desc *gpio_en_set;
|
||||||
|
/* movie mode current scale */
|
||||||
|
int *mm_current_scale;
|
||||||
|
/* device mode */
|
||||||
|
bool movie_mode;
|
||||||
|
|
||||||
|
/* brightness cache */
|
||||||
|
unsigned int torch_brightness;
|
||||||
|
/* assures led-triggers compatibility */
|
||||||
|
struct work_struct work_brightness_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct aat1290_led *fled_cdev_to_led(
|
||||||
|
struct led_classdev_flash *fled_cdev)
|
||||||
|
{
|
||||||
|
return container_of(fled_cdev, struct aat1290_led, fled_cdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
gpiod_direction_output(led->gpio_fl_en, 0);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 0);
|
||||||
|
|
||||||
|
udelay(AAT1290_FLEN_OFF_DELAY_TIME_US);
|
||||||
|
|
||||||
|
/* write address */
|
||||||
|
for (i = 0; i < addr; ++i) {
|
||||||
|
udelay(AAT1290_EN_SET_TICK_TIME_US);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 0);
|
||||||
|
udelay(AAT1290_EN_SET_TICK_TIME_US);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US);
|
||||||
|
|
||||||
|
/* write data */
|
||||||
|
for (i = 0; i < value; ++i) {
|
||||||
|
udelay(AAT1290_EN_SET_TICK_TIME_US);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 0);
|
||||||
|
udelay(AAT1290_EN_SET_TICK_TIME_US);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_set_flash_safety_timer(struct aat1290_led *led,
|
||||||
|
unsigned int micro_sec)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = &led->fled_cdev;
|
||||||
|
struct led_flash_setting *flash_tm = &fled_cdev->timeout;
|
||||||
|
int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS -
|
||||||
|
(micro_sec / flash_tm->step) + 1;
|
||||||
|
|
||||||
|
aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR,
|
||||||
|
flash_tm_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_brightness_set(struct aat1290_led *led,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
mutex_lock(&led->lock);
|
||||||
|
|
||||||
|
if (brightness == 0) {
|
||||||
|
gpiod_direction_output(led->gpio_fl_en, 0);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 0);
|
||||||
|
led->movie_mode = false;
|
||||||
|
} else {
|
||||||
|
if (!led->movie_mode) {
|
||||||
|
aat1290_as2cwire_write(led,
|
||||||
|
AAT1290_MM_CURRENT_RATIO_ADDR,
|
||||||
|
AAT1290_MM_TO_FL_1_92);
|
||||||
|
led->movie_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR,
|
||||||
|
AAT1290_MAX_MM_CURR_PERCENT_0 - brightness);
|
||||||
|
aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR,
|
||||||
|
AAT1290_MOVIE_MODE_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&led->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LED subsystem callbacks */
|
||||||
|
|
||||||
|
static void aat1290_brightness_set_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct aat1290_led *led =
|
||||||
|
container_of(work, struct aat1290_led, work_brightness_set);
|
||||||
|
|
||||||
|
aat1290_brightness_set(led, led->torch_brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_led_brightness_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
|
||||||
|
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
|
||||||
|
led->torch_brightness = brightness;
|
||||||
|
schedule_work(&led->work_brightness_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_brightness_set_sync(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
|
||||||
|
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
|
||||||
|
aat1290_brightness_set(led, brightness);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_flash_strobe_set(struct led_classdev_flash *fled_cdev,
|
||||||
|
bool state)
|
||||||
|
|
||||||
|
{
|
||||||
|
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
struct led_classdev *led_cdev = &fled_cdev->led_cdev;
|
||||||
|
struct led_flash_setting *timeout = &fled_cdev->timeout;
|
||||||
|
|
||||||
|
mutex_lock(&led->lock);
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
aat1290_set_flash_safety_timer(led, timeout->val);
|
||||||
|
gpiod_direction_output(led->gpio_fl_en, 1);
|
||||||
|
} else {
|
||||||
|
gpiod_direction_output(led->gpio_fl_en, 0);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To reenter movie mode after a flash event the part must be cycled
|
||||||
|
* off and back on to reset the movie mode and reprogrammed via the
|
||||||
|
* AS2Cwire. Therefore the brightness and movie_mode properties needs
|
||||||
|
* to be updated here to reflect the actual state.
|
||||||
|
*/
|
||||||
|
led_cdev->brightness = 0;
|
||||||
|
led->movie_mode = false;
|
||||||
|
|
||||||
|
mutex_unlock(&led->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
|
||||||
|
u32 timeout)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Don't do anything - flash timeout is cached in the led-class-flash
|
||||||
|
* core and will be applied in the strobe_set op, as writing the
|
||||||
|
* safety timer register spuriously turns the torch mode on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_parse_dt(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *cfg,
|
||||||
|
struct device_node **sub_node)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
|
||||||
|
struct device *dev = &led->pdev->dev;
|
||||||
|
struct device_node *child_node;
|
||||||
|
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
|
||||||
|
struct pinctrl *pinctrl;
|
||||||
|
#endif
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
led->gpio_fl_en = devm_gpiod_get(dev, "flen", GPIOD_ASIS);
|
||||||
|
if (IS_ERR(led->gpio_fl_en)) {
|
||||||
|
ret = PTR_ERR(led->gpio_fl_en);
|
||||||
|
dev_err(dev, "Unable to claim gpio \"flen\".\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
led->gpio_en_set = devm_gpiod_get(dev, "enset", GPIOD_ASIS);
|
||||||
|
if (IS_ERR(led->gpio_en_set)) {
|
||||||
|
ret = PTR_ERR(led->gpio_en_set);
|
||||||
|
dev_err(dev, "Unable to claim gpio \"enset\".\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
|
||||||
|
pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev);
|
||||||
|
if (IS_ERR(pinctrl)) {
|
||||||
|
cfg->has_external_strobe = false;
|
||||||
|
dev_info(dev,
|
||||||
|
"No support for external strobe detected.\n");
|
||||||
|
} else {
|
||||||
|
cfg->has_external_strobe = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
child_node = of_get_next_available_child(dev->of_node, NULL);
|
||||||
|
if (!child_node) {
|
||||||
|
dev_err(dev, "No DT child node found for connected LED.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_cdev->name = of_get_property(child_node, "label", NULL) ? :
|
||||||
|
child_node->name;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child_node, "led-max-microamp",
|
||||||
|
&cfg->max_mm_current);
|
||||||
|
/*
|
||||||
|
* led-max-microamp will default to 1/20 of flash-max-microamp
|
||||||
|
* in case it is missing.
|
||||||
|
*/
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev,
|
||||||
|
"led-max-microamp DT property missing\n");
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child_node, "flash-max-microamp",
|
||||||
|
&cfg->max_flash_current);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev,
|
||||||
|
"flash-max-microamp DT property missing\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child_node, "flash-max-timeout-us",
|
||||||
|
&cfg->max_flash_tm);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev,
|
||||||
|
"flash-max-timeout-us DT property missing\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
of_node_put(child_node);
|
||||||
|
|
||||||
|
*sub_node = child_node;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_led_validate_mm_current(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *cfg)
|
||||||
|
{
|
||||||
|
int i, b = 0, e = AAT1290_MM_CURRENT_SCALE_SIZE;
|
||||||
|
|
||||||
|
while (e - b > 1) {
|
||||||
|
i = b + (e - b) / 2;
|
||||||
|
if (cfg->max_mm_current < led->mm_current_scale[i])
|
||||||
|
e = i;
|
||||||
|
else
|
||||||
|
b = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg->max_mm_current = led->mm_current_scale[b];
|
||||||
|
cfg->max_brightness = b + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_mm_current_scale(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *cfg)
|
||||||
|
{
|
||||||
|
int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56,
|
||||||
|
63, 71, 79, 89, 100 };
|
||||||
|
int i, max_mm_current =
|
||||||
|
AAT1290_MAX_MM_CURRENT(cfg->max_flash_current);
|
||||||
|
|
||||||
|
led->mm_current_scale = devm_kzalloc(&led->pdev->dev,
|
||||||
|
sizeof(max_mm_current_percent),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!led->mm_current_scale)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i)
|
||||||
|
led->mm_current_scale[i] = max_mm_current *
|
||||||
|
max_mm_current_percent[i] / 100;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_get_configuration(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *cfg,
|
||||||
|
struct device_node **sub_node)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = aat1290_led_parse_dt(led, cfg, sub_node);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* Init non-linear movie mode current scale basing
|
||||||
|
* on the max flash current from led configuration.
|
||||||
|
*/
|
||||||
|
ret = init_mm_current_scale(led, cfg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
aat1290_led_validate_mm_current(led, cfg);
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
|
||||||
|
#else
|
||||||
|
devm_kfree(&led->pdev->dev, led->mm_current_scale);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_init_flash_timeout(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *cfg)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = &led->fled_cdev;
|
||||||
|
struct led_flash_setting *setting;
|
||||||
|
|
||||||
|
/* Init flash timeout setting */
|
||||||
|
setting = &fled_cdev->timeout;
|
||||||
|
setting->min = cfg->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS;
|
||||||
|
setting->max = cfg->max_flash_tm;
|
||||||
|
setting->step = setting->min;
|
||||||
|
setting->val = setting->max;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
|
||||||
|
static enum led_brightness aat1290_intensity_to_brightness(
|
||||||
|
struct v4l2_flash *v4l2_flash,
|
||||||
|
s32 intensity)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i)
|
||||||
|
if (intensity >= led->mm_current_scale[i])
|
||||||
|
return i + 1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
|
||||||
|
return led->mm_current_scale[brightness - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev);
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct led_classdev *led_cdev = &fled_cdev->led_cdev;
|
||||||
|
struct pinctrl *pinctrl;
|
||||||
|
|
||||||
|
gpiod_direction_output(led->gpio_fl_en, 0);
|
||||||
|
gpiod_direction_output(led->gpio_en_set, 0);
|
||||||
|
|
||||||
|
led->movie_mode = false;
|
||||||
|
led_cdev->brightness = 0;
|
||||||
|
|
||||||
|
pinctrl = devm_pinctrl_get_select(&led->pdev->dev,
|
||||||
|
enable ? "isp" : "host");
|
||||||
|
if (IS_ERR(pinctrl)) {
|
||||||
|
dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n");
|
||||||
|
return PTR_ERR(pinctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *led_cfg,
|
||||||
|
struct v4l2_flash_config *v4l2_sd_cfg)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
|
||||||
|
struct led_flash_setting *s;
|
||||||
|
|
||||||
|
strlcpy(v4l2_sd_cfg->dev_name, led_cdev->name,
|
||||||
|
sizeof(v4l2_sd_cfg->dev_name));
|
||||||
|
|
||||||
|
s = &v4l2_sd_cfg->torch_intensity;
|
||||||
|
s->min = led->mm_current_scale[0];
|
||||||
|
s->max = led_cfg->max_mm_current;
|
||||||
|
s->step = 1;
|
||||||
|
s->val = s->max;
|
||||||
|
|
||||||
|
v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_flash_ops v4l2_flash_ops = {
|
||||||
|
.external_strobe_set = aat1290_led_external_strobe_set,
|
||||||
|
.intensity_to_led_brightness = aat1290_intensity_to_brightness,
|
||||||
|
.led_brightness_to_intensity = aat1290_brightness_to_intensity,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
|
||||||
|
struct aat1290_led_config_data *led_cfg,
|
||||||
|
struct v4l2_flash_config *v4l2_sd_cfg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static const struct v4l2_flash_ops v4l2_flash_ops;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct led_flash_ops flash_ops = {
|
||||||
|
.strobe_set = aat1290_led_flash_strobe_set,
|
||||||
|
.timeout_set = aat1290_led_flash_timeout_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int aat1290_led_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct device_node *sub_node = NULL;
|
||||||
|
struct aat1290_led *led;
|
||||||
|
struct led_classdev *led_cdev;
|
||||||
|
struct led_classdev_flash *fled_cdev;
|
||||||
|
struct aat1290_led_config_data led_cfg = {};
|
||||||
|
struct v4l2_flash_config v4l2_sd_cfg = {};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||||
|
if (!led)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
led->pdev = pdev;
|
||||||
|
platform_set_drvdata(pdev, led);
|
||||||
|
|
||||||
|
fled_cdev = &led->fled_cdev;
|
||||||
|
fled_cdev->ops = &flash_ops;
|
||||||
|
led_cdev = &fled_cdev->led_cdev;
|
||||||
|
|
||||||
|
ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mutex_init(&led->lock);
|
||||||
|
|
||||||
|
/* Initialize LED Flash class device */
|
||||||
|
led_cdev->brightness_set = aat1290_led_brightness_set;
|
||||||
|
led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync;
|
||||||
|
led_cdev->max_brightness = led_cfg.max_brightness;
|
||||||
|
led_cdev->flags |= LED_DEV_CAP_FLASH;
|
||||||
|
INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
|
||||||
|
|
||||||
|
aat1290_init_flash_timeout(led, &led_cfg);
|
||||||
|
|
||||||
|
/* Register LED Flash class device */
|
||||||
|
ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_flash_register;
|
||||||
|
|
||||||
|
aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg);
|
||||||
|
|
||||||
|
/* Create V4L2 Flash subdev. */
|
||||||
|
led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL,
|
||||||
|
&v4l2_flash_ops, &v4l2_sd_cfg);
|
||||||
|
if (IS_ERR(led->v4l2_flash)) {
|
||||||
|
ret = PTR_ERR(led->v4l2_flash);
|
||||||
|
goto error_v4l2_flash_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_v4l2_flash_init:
|
||||||
|
led_classdev_flash_unregister(fled_cdev);
|
||||||
|
err_flash_register:
|
||||||
|
mutex_destroy(&led->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aat1290_led_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct aat1290_led *led = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
v4l2_flash_release(led->v4l2_flash);
|
||||||
|
led_classdev_flash_unregister(&led->fled_cdev);
|
||||||
|
cancel_work_sync(&led->work_brightness_set);
|
||||||
|
|
||||||
|
mutex_destroy(&led->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id aat1290_led_dt_match[] = {
|
||||||
|
{ .compatible = "skyworks,aat1290" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver aat1290_led_driver = {
|
||||||
|
.probe = aat1290_led_probe,
|
||||||
|
.remove = aat1290_led_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "aat1290",
|
||||||
|
.of_match_table = aat1290_led_dt_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(aat1290_led_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
|
||||||
|
MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,413 @@
|
||||||
|
/*
|
||||||
|
* Driver for BCM6328 memory-mapped LEDs, based on leds-syscon.c
|
||||||
|
*
|
||||||
|
* Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
|
||||||
|
* Copyright 2015 Jonas Gorski <jogo@openwrt.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#define BCM6328_REG_INIT 0x00
|
||||||
|
#define BCM6328_REG_MODE_HI 0x04
|
||||||
|
#define BCM6328_REG_MODE_LO 0x08
|
||||||
|
#define BCM6328_REG_HWDIS 0x0c
|
||||||
|
#define BCM6328_REG_STROBE 0x10
|
||||||
|
#define BCM6328_REG_LNKACTSEL_HI 0x14
|
||||||
|
#define BCM6328_REG_LNKACTSEL_LO 0x18
|
||||||
|
#define BCM6328_REG_RBACK 0x1c
|
||||||
|
#define BCM6328_REG_SERMUX 0x20
|
||||||
|
|
||||||
|
#define BCM6328_LED_MAX_COUNT 24
|
||||||
|
#define BCM6328_LED_DEF_DELAY 500
|
||||||
|
#define BCM6328_LED_INTERVAL_MS 20
|
||||||
|
|
||||||
|
#define BCM6328_LED_INTV_MASK 0x3f
|
||||||
|
#define BCM6328_LED_FAST_INTV_SHIFT 6
|
||||||
|
#define BCM6328_LED_FAST_INTV_MASK (BCM6328_LED_INTV_MASK << \
|
||||||
|
BCM6328_LED_FAST_INTV_SHIFT)
|
||||||
|
#define BCM6328_SERIAL_LED_EN BIT(12)
|
||||||
|
#define BCM6328_SERIAL_LED_MUX BIT(13)
|
||||||
|
#define BCM6328_SERIAL_LED_CLK_NPOL BIT(14)
|
||||||
|
#define BCM6328_SERIAL_LED_DATA_PPOL BIT(15)
|
||||||
|
#define BCM6328_SERIAL_LED_SHIFT_DIR BIT(16)
|
||||||
|
#define BCM6328_LED_SHIFT_TEST BIT(30)
|
||||||
|
#define BCM6328_LED_TEST BIT(31)
|
||||||
|
|
||||||
|
#define BCM6328_LED_MODE_MASK 3
|
||||||
|
#define BCM6328_LED_MODE_OFF 0
|
||||||
|
#define BCM6328_LED_MODE_FAST 1
|
||||||
|
#define BCM6328_LED_MODE_BLINK 2
|
||||||
|
#define BCM6328_LED_MODE_ON 3
|
||||||
|
#define BCM6328_LED_SHIFT(X) ((X) << 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct bcm6328_led - state container for bcm6328 based LEDs
|
||||||
|
* @cdev: LED class device for this LED
|
||||||
|
* @mem: memory resource
|
||||||
|
* @lock: memory lock
|
||||||
|
* @pin: LED pin number
|
||||||
|
* @blink_leds: blinking LEDs
|
||||||
|
* @blink_delay: blinking delay
|
||||||
|
* @active_low: LED is active low
|
||||||
|
*/
|
||||||
|
struct bcm6328_led {
|
||||||
|
struct led_classdev cdev;
|
||||||
|
void __iomem *mem;
|
||||||
|
spinlock_t *lock;
|
||||||
|
unsigned long pin;
|
||||||
|
unsigned long *blink_leds;
|
||||||
|
unsigned long *blink_delay;
|
||||||
|
bool active_low;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm6328_led_write(void __iomem *reg, unsigned long data)
|
||||||
|
{
|
||||||
|
iowrite32be(data, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long bcm6328_led_read(void __iomem *reg)
|
||||||
|
{
|
||||||
|
return ioread32be(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LEDMode 64 bits / 24 LEDs
|
||||||
|
* bits [31:0] -> LEDs 8-23
|
||||||
|
* bits [47:32] -> LEDs 0-7
|
||||||
|
* bits [63:48] -> unused
|
||||||
|
*/
|
||||||
|
static unsigned long bcm6328_pin2shift(unsigned long pin)
|
||||||
|
{
|
||||||
|
if (pin < 8)
|
||||||
|
return pin + 16; /* LEDs 0-7 (bits 47:32) */
|
||||||
|
else
|
||||||
|
return pin - 8; /* LEDs 8-23 (bits 31:0) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm6328_led_mode(struct bcm6328_led *led, unsigned long value)
|
||||||
|
{
|
||||||
|
void __iomem *mode;
|
||||||
|
unsigned long val, shift;
|
||||||
|
|
||||||
|
shift = bcm6328_pin2shift(led->pin);
|
||||||
|
if (shift / 16)
|
||||||
|
mode = led->mem + BCM6328_REG_MODE_HI;
|
||||||
|
else
|
||||||
|
mode = led->mem + BCM6328_REG_MODE_LO;
|
||||||
|
|
||||||
|
val = bcm6328_led_read(mode);
|
||||||
|
val &= ~(BCM6328_LED_MODE_MASK << BCM6328_LED_SHIFT(shift % 16));
|
||||||
|
val |= (value << BCM6328_LED_SHIFT(shift % 16));
|
||||||
|
bcm6328_led_write(mode, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm6328_led_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct bcm6328_led *led =
|
||||||
|
container_of(led_cdev, struct bcm6328_led, cdev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(led->lock, flags);
|
||||||
|
*(led->blink_leds) &= ~BIT(led->pin);
|
||||||
|
if ((led->active_low && value == LED_OFF) ||
|
||||||
|
(!led->active_low && value != LED_OFF))
|
||||||
|
bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
|
||||||
|
else
|
||||||
|
bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
|
||||||
|
spin_unlock_irqrestore(led->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm6328_blink_set(struct led_classdev *led_cdev,
|
||||||
|
unsigned long *delay_on, unsigned long *delay_off)
|
||||||
|
{
|
||||||
|
struct bcm6328_led *led =
|
||||||
|
container_of(led_cdev, struct bcm6328_led, cdev);
|
||||||
|
unsigned long delay, flags;
|
||||||
|
|
||||||
|
if (!*delay_on)
|
||||||
|
*delay_on = BCM6328_LED_DEF_DELAY;
|
||||||
|
if (!*delay_off)
|
||||||
|
*delay_off = BCM6328_LED_DEF_DELAY;
|
||||||
|
|
||||||
|
if (*delay_on != *delay_off) {
|
||||||
|
dev_dbg(led_cdev->dev,
|
||||||
|
"fallback to soft blinking (delay_on != delay_off)\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay = *delay_on / BCM6328_LED_INTERVAL_MS;
|
||||||
|
if (delay == 0)
|
||||||
|
delay = 1;
|
||||||
|
else if (delay > BCM6328_LED_INTV_MASK) {
|
||||||
|
dev_dbg(led_cdev->dev,
|
||||||
|
"fallback to soft blinking (delay > %ums)\n",
|
||||||
|
BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(led->lock, flags);
|
||||||
|
if (*(led->blink_leds) == 0 ||
|
||||||
|
*(led->blink_leds) == BIT(led->pin) ||
|
||||||
|
*(led->blink_delay) == delay) {
|
||||||
|
unsigned long val;
|
||||||
|
|
||||||
|
*(led->blink_leds) |= BIT(led->pin);
|
||||||
|
*(led->blink_delay) = delay;
|
||||||
|
|
||||||
|
val = bcm6328_led_read(led->mem + BCM6328_REG_INIT);
|
||||||
|
val &= ~BCM6328_LED_FAST_INTV_MASK;
|
||||||
|
val |= (delay << BCM6328_LED_FAST_INTV_SHIFT);
|
||||||
|
bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);
|
||||||
|
|
||||||
|
bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(led->lock, flags);
|
||||||
|
} else {
|
||||||
|
spin_unlock_irqrestore(led->lock, flags);
|
||||||
|
dev_dbg(led_cdev->dev,
|
||||||
|
"fallback to soft blinking (delay already set)\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg,
|
||||||
|
void __iomem *mem, spinlock_t *lock)
|
||||||
|
{
|
||||||
|
int i, cnt;
|
||||||
|
unsigned long flags, val;
|
||||||
|
|
||||||
|
spin_lock_irqsave(lock, flags);
|
||||||
|
val = bcm6328_led_read(mem + BCM6328_REG_HWDIS);
|
||||||
|
val &= ~BIT(reg);
|
||||||
|
bcm6328_led_write(mem + BCM6328_REG_HWDIS, val);
|
||||||
|
spin_unlock_irqrestore(lock, flags);
|
||||||
|
|
||||||
|
/* Only LEDs 0-7 can be activity/link controlled */
|
||||||
|
if (reg >= 8)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cnt = of_property_count_elems_of_size(nc, "brcm,link-signal-sources",
|
||||||
|
sizeof(u32));
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
u32 sel;
|
||||||
|
void __iomem *addr;
|
||||||
|
|
||||||
|
if (reg < 4)
|
||||||
|
addr = mem + BCM6328_REG_LNKACTSEL_LO;
|
||||||
|
else
|
||||||
|
addr = mem + BCM6328_REG_LNKACTSEL_HI;
|
||||||
|
|
||||||
|
of_property_read_u32_index(nc, "brcm,link-signal-sources", i,
|
||||||
|
&sel);
|
||||||
|
|
||||||
|
if (reg / 4 != sel / 4) {
|
||||||
|
dev_warn(dev, "invalid link signal source\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(lock, flags);
|
||||||
|
val = bcm6328_led_read(addr);
|
||||||
|
val |= (BIT(reg) << (((sel % 4) * 4) + 16));
|
||||||
|
bcm6328_led_write(addr, val);
|
||||||
|
spin_unlock_irqrestore(lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt = of_property_count_elems_of_size(nc,
|
||||||
|
"brcm,activity-signal-sources",
|
||||||
|
sizeof(u32));
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
u32 sel;
|
||||||
|
void __iomem *addr;
|
||||||
|
|
||||||
|
if (reg < 4)
|
||||||
|
addr = mem + BCM6328_REG_LNKACTSEL_LO;
|
||||||
|
else
|
||||||
|
addr = mem + BCM6328_REG_LNKACTSEL_HI;
|
||||||
|
|
||||||
|
of_property_read_u32_index(nc, "brcm,activity-signal-sources",
|
||||||
|
i, &sel);
|
||||||
|
|
||||||
|
if (reg / 4 != sel / 4) {
|
||||||
|
dev_warn(dev, "invalid activity signal source\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(lock, flags);
|
||||||
|
val = bcm6328_led_read(addr);
|
||||||
|
val |= (BIT(reg) << ((sel % 4) * 4));
|
||||||
|
bcm6328_led_write(addr, val);
|
||||||
|
spin_unlock_irqrestore(lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
|
||||||
|
void __iomem *mem, spinlock_t *lock,
|
||||||
|
unsigned long *blink_leds, unsigned long *blink_delay)
|
||||||
|
{
|
||||||
|
struct bcm6328_led *led;
|
||||||
|
unsigned long flags;
|
||||||
|
const char *state;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||||
|
if (!led)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
led->pin = reg;
|
||||||
|
led->mem = mem;
|
||||||
|
led->lock = lock;
|
||||||
|
led->blink_leds = blink_leds;
|
||||||
|
led->blink_delay = blink_delay;
|
||||||
|
|
||||||
|
if (of_property_read_bool(nc, "active-low"))
|
||||||
|
led->active_low = true;
|
||||||
|
|
||||||
|
led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
|
||||||
|
led->cdev.default_trigger = of_get_property(nc,
|
||||||
|
"linux,default-trigger",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!of_property_read_string(nc, "default-state", &state)) {
|
||||||
|
spin_lock_irqsave(lock, flags);
|
||||||
|
if (!strcmp(state, "on")) {
|
||||||
|
led->cdev.brightness = LED_FULL;
|
||||||
|
bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
|
||||||
|
} else if (!strcmp(state, "keep")) {
|
||||||
|
void __iomem *mode;
|
||||||
|
unsigned long val, shift;
|
||||||
|
|
||||||
|
shift = bcm6328_pin2shift(led->pin);
|
||||||
|
if (shift / 16)
|
||||||
|
mode = mem + BCM6328_REG_MODE_HI;
|
||||||
|
else
|
||||||
|
mode = mem + BCM6328_REG_MODE_LO;
|
||||||
|
|
||||||
|
val = bcm6328_led_read(mode) >> (shift % 16);
|
||||||
|
val &= BCM6328_LED_MODE_MASK;
|
||||||
|
if (val == BCM6328_LED_MODE_ON)
|
||||||
|
led->cdev.brightness = LED_FULL;
|
||||||
|
else {
|
||||||
|
led->cdev.brightness = LED_OFF;
|
||||||
|
bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
led->cdev.brightness = LED_OFF;
|
||||||
|
bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
led->cdev.brightness_set = bcm6328_led_set;
|
||||||
|
led->cdev.blink_set = bcm6328_blink_set;
|
||||||
|
|
||||||
|
rc = led_classdev_register(dev, &led->cdev);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
dev_dbg(dev, "registered LED %s\n", led->cdev.name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm6328_leds_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct device_node *child;
|
||||||
|
struct resource *mem_r;
|
||||||
|
void __iomem *mem;
|
||||||
|
spinlock_t *lock;
|
||||||
|
unsigned long val, *blink_leds, *blink_delay;
|
||||||
|
|
||||||
|
mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!mem_r)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mem = devm_ioremap_resource(dev, mem_r);
|
||||||
|
if (IS_ERR(mem))
|
||||||
|
return PTR_ERR(mem);
|
||||||
|
|
||||||
|
lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
|
||||||
|
if (!lock)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
blink_leds = devm_kzalloc(dev, sizeof(*blink_leds), GFP_KERNEL);
|
||||||
|
if (!blink_leds)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
blink_delay = devm_kzalloc(dev, sizeof(*blink_delay), GFP_KERNEL);
|
||||||
|
if (!blink_delay)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(lock);
|
||||||
|
|
||||||
|
bcm6328_led_write(mem + BCM6328_REG_HWDIS, ~0);
|
||||||
|
bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_HI, 0);
|
||||||
|
bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0);
|
||||||
|
|
||||||
|
val = bcm6328_led_read(mem + BCM6328_REG_INIT);
|
||||||
|
val &= ~BCM6328_SERIAL_LED_EN;
|
||||||
|
if (of_property_read_bool(np, "brcm,serial-leds"))
|
||||||
|
val |= BCM6328_SERIAL_LED_EN;
|
||||||
|
bcm6328_led_write(mem + BCM6328_REG_INIT, val);
|
||||||
|
|
||||||
|
for_each_available_child_of_node(np, child) {
|
||||||
|
int rc;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
if (of_property_read_u32(child, "reg", ®))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (reg >= BCM6328_LED_MAX_COUNT) {
|
||||||
|
dev_err(dev, "invalid LED (>= %d)\n",
|
||||||
|
BCM6328_LED_MAX_COUNT);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_bool(child, "brcm,hardware-controlled"))
|
||||||
|
rc = bcm6328_hwled(dev, child, reg, mem, lock);
|
||||||
|
else
|
||||||
|
rc = bcm6328_led(dev, child, reg, mem, lock,
|
||||||
|
blink_leds, blink_delay);
|
||||||
|
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id bcm6328_leds_of_match[] = {
|
||||||
|
{ .compatible = "brcm,bcm6328-leds", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver bcm6328_leds_driver = {
|
||||||
|
.probe = bcm6328_leds_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "leds-bcm6328",
|
||||||
|
.of_match_table = bcm6328_leds_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(bcm6328_leds_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
|
||||||
|
MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
|
||||||
|
MODULE_DESCRIPTION("LED driver for BCM6328 controllers");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:leds-bcm6328");
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
* Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
|
||||||
|
*
|
||||||
|
* Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#define BCM6358_REG_MODE 0x0
|
||||||
|
#define BCM6358_REG_CTRL 0x4
|
||||||
|
|
||||||
|
#define BCM6358_SLED_CLKDIV_MASK 3
|
||||||
|
#define BCM6358_SLED_CLKDIV_1 0
|
||||||
|
#define BCM6358_SLED_CLKDIV_2 1
|
||||||
|
#define BCM6358_SLED_CLKDIV_4 2
|
||||||
|
#define BCM6358_SLED_CLKDIV_8 3
|
||||||
|
|
||||||
|
#define BCM6358_SLED_POLARITY BIT(2)
|
||||||
|
#define BCM6358_SLED_BUSY BIT(3)
|
||||||
|
|
||||||
|
#define BCM6358_SLED_MAX_COUNT 32
|
||||||
|
#define BCM6358_SLED_WAIT 100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct bcm6358_led - state container for bcm6358 based LEDs
|
||||||
|
* @cdev: LED class device for this LED
|
||||||
|
* @mem: memory resource
|
||||||
|
* @lock: memory lock
|
||||||
|
* @pin: LED pin number
|
||||||
|
* @active_low: LED is active low
|
||||||
|
*/
|
||||||
|
struct bcm6358_led {
|
||||||
|
struct led_classdev cdev;
|
||||||
|
void __iomem *mem;
|
||||||
|
spinlock_t *lock;
|
||||||
|
unsigned long pin;
|
||||||
|
bool active_low;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm6358_led_write(void __iomem *reg, unsigned long data)
|
||||||
|
{
|
||||||
|
iowrite32be(data, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long bcm6358_led_read(void __iomem *reg)
|
||||||
|
{
|
||||||
|
return ioread32be(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long bcm6358_led_busy(void __iomem *mem)
|
||||||
|
{
|
||||||
|
unsigned long val;
|
||||||
|
|
||||||
|
while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
|
||||||
|
BCM6358_SLED_BUSY)
|
||||||
|
udelay(BCM6358_SLED_WAIT);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
|
||||||
|
{
|
||||||
|
unsigned long val;
|
||||||
|
|
||||||
|
bcm6358_led_busy(led->mem);
|
||||||
|
|
||||||
|
val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
|
||||||
|
if ((led->active_low && value == LED_OFF) ||
|
||||||
|
(!led->active_low && value != LED_OFF))
|
||||||
|
val |= BIT(led->pin);
|
||||||
|
else
|
||||||
|
val &= ~(BIT(led->pin));
|
||||||
|
bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm6358_led_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct bcm6358_led *led =
|
||||||
|
container_of(led_cdev, struct bcm6358_led, cdev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(led->lock, flags);
|
||||||
|
bcm6358_led_mode(led, value);
|
||||||
|
spin_unlock_irqrestore(led->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
|
||||||
|
void __iomem *mem, spinlock_t *lock)
|
||||||
|
{
|
||||||
|
struct bcm6358_led *led;
|
||||||
|
unsigned long flags;
|
||||||
|
const char *state;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||||
|
if (!led)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
led->pin = reg;
|
||||||
|
led->mem = mem;
|
||||||
|
led->lock = lock;
|
||||||
|
|
||||||
|
if (of_property_read_bool(nc, "active-low"))
|
||||||
|
led->active_low = true;
|
||||||
|
|
||||||
|
led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
|
||||||
|
led->cdev.default_trigger = of_get_property(nc,
|
||||||
|
"linux,default-trigger",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
spin_lock_irqsave(lock, flags);
|
||||||
|
if (!of_property_read_string(nc, "default-state", &state)) {
|
||||||
|
if (!strcmp(state, "on")) {
|
||||||
|
led->cdev.brightness = LED_FULL;
|
||||||
|
} else if (!strcmp(state, "keep")) {
|
||||||
|
unsigned long val;
|
||||||
|
|
||||||
|
bcm6358_led_busy(led->mem);
|
||||||
|
|
||||||
|
val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
|
||||||
|
val &= BIT(led->pin);
|
||||||
|
if ((led->active_low && !val) ||
|
||||||
|
(!led->active_low && val))
|
||||||
|
led->cdev.brightness = LED_FULL;
|
||||||
|
else
|
||||||
|
led->cdev.brightness = LED_OFF;
|
||||||
|
} else {
|
||||||
|
led->cdev.brightness = LED_OFF;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
led->cdev.brightness = LED_OFF;
|
||||||
|
}
|
||||||
|
bcm6358_led_mode(led, led->cdev.brightness);
|
||||||
|
spin_unlock_irqrestore(lock, flags);
|
||||||
|
|
||||||
|
led->cdev.brightness_set = bcm6358_led_set;
|
||||||
|
|
||||||
|
rc = led_classdev_register(dev, &led->cdev);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
dev_dbg(dev, "registered LED %s\n", led->cdev.name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm6358_leds_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct device_node *child;
|
||||||
|
struct resource *mem_r;
|
||||||
|
void __iomem *mem;
|
||||||
|
spinlock_t *lock; /* memory lock */
|
||||||
|
unsigned long val;
|
||||||
|
u32 clk_div;
|
||||||
|
|
||||||
|
mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!mem_r)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mem = devm_ioremap_resource(dev, mem_r);
|
||||||
|
if (IS_ERR(mem))
|
||||||
|
return PTR_ERR(mem);
|
||||||
|
|
||||||
|
lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
|
||||||
|
if (!lock)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(lock);
|
||||||
|
|
||||||
|
val = bcm6358_led_busy(mem);
|
||||||
|
val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
|
||||||
|
if (of_property_read_bool(np, "brcm,clk-dat-low"))
|
||||||
|
val |= BCM6358_SLED_POLARITY;
|
||||||
|
of_property_read_u32(np, "brcm,clk-div", &clk_div);
|
||||||
|
switch (clk_div) {
|
||||||
|
case 8:
|
||||||
|
val |= BCM6358_SLED_CLKDIV_8;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
val |= BCM6358_SLED_CLKDIV_4;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
val |= BCM6358_SLED_CLKDIV_2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val |= BCM6358_SLED_CLKDIV_1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
|
||||||
|
|
||||||
|
for_each_available_child_of_node(np, child) {
|
||||||
|
int rc;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
if (of_property_read_u32(child, "reg", ®))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (reg >= BCM6358_SLED_MAX_COUNT) {
|
||||||
|
dev_err(dev, "invalid LED (%u >= %d)\n", reg,
|
||||||
|
BCM6358_SLED_MAX_COUNT);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = bcm6358_led(dev, child, reg, mem, lock);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id bcm6358_leds_of_match[] = {
|
||||||
|
{ .compatible = "brcm,bcm6358-leds", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver bcm6358_leds_driver = {
|
||||||
|
.probe = bcm6358_leds_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "leds-bcm6358",
|
||||||
|
.of_match_table = bcm6358_leds_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(bcm6358_leds_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:leds-bcm6358");
|
|
@ -108,20 +108,8 @@ err_null:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cobalt_raq_led_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
led_classdev_unregister(&raq_power_off_led);
|
|
||||||
led_classdev_unregister(&raq_web_led);
|
|
||||||
|
|
||||||
if (led_port)
|
|
||||||
led_port = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver cobalt_raq_led_driver = {
|
static struct platform_driver cobalt_raq_led_driver = {
|
||||||
.probe = cobalt_raq_led_probe,
|
.probe = cobalt_raq_led_probe,
|
||||||
.remove = cobalt_raq_led_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "cobalt-raq-leds",
|
.name = "cobalt-raq-leds",
|
||||||
},
|
},
|
||||||
|
@ -131,5 +119,4 @@ static int __init cobalt_raq_led_init(void)
|
||||||
{
|
{
|
||||||
return platform_driver_register(&cobalt_raq_led_driver);
|
return platform_driver_register(&cobalt_raq_led_driver);
|
||||||
}
|
}
|
||||||
|
device_initcall(cobalt_raq_led_init);
|
||||||
module_init(cobalt_raq_led_init);
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -198,8 +199,10 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
||||||
} else {
|
} else {
|
||||||
if (IS_ENABLED(CONFIG_OF) && !led.name && np)
|
if (IS_ENABLED(CONFIG_OF) && !led.name && np)
|
||||||
led.name = np->name;
|
led.name = np->name;
|
||||||
if (!led.name)
|
if (!led.name) {
|
||||||
return ERR_PTR(-EINVAL);
|
ret = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fwnode_property_read_string(child, "linux,default-trigger",
|
fwnode_property_read_string(child, "linux,default-trigger",
|
||||||
&led.default_trigger);
|
&led.default_trigger);
|
||||||
|
@ -217,18 +220,19 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
||||||
if (fwnode_property_present(child, "retain-state-suspended"))
|
if (fwnode_property_present(child, "retain-state-suspended"))
|
||||||
led.retain_state_suspended = 1;
|
led.retain_state_suspended = 1;
|
||||||
|
|
||||||
ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
|
ret = create_gpio_led(&led, &priv->leds[priv->num_leds],
|
||||||
dev, NULL);
|
dev, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fwnode_handle_put(child);
|
fwnode_handle_put(child);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
priv->num_leds++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return priv;
|
return priv;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
for (count = priv->num_leds - 2; count >= 0; count--)
|
for (count = priv->num_leds - 1; count >= 0; count--)
|
||||||
delete_gpio_led(&priv->leds[count]);
|
delete_gpio_led(&priv->leds[count]);
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,443 @@
|
||||||
|
/*
|
||||||
|
* LED driver : leds-ktd2692.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Samsung Electronics
|
||||||
|
* Ingi Kim <ingi2.kim@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/led-class-flash.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
/* Value related the movie mode */
|
||||||
|
#define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16
|
||||||
|
#define KTD2692_MM_TO_FL_RATIO(x) ((x) / 3)
|
||||||
|
#define KTD2962_MM_MIN_CURR_THRESHOLD_SCALE 8
|
||||||
|
|
||||||
|
/* Value related the flash mode */
|
||||||
|
#define KTD2692_FLASH_MODE_TIMEOUT_LEVELS 8
|
||||||
|
#define KTD2692_FLASH_MODE_TIMEOUT_DISABLE 0
|
||||||
|
#define KTD2692_FLASH_MODE_CURR_PERCENT(x) (((x) * 16) / 100)
|
||||||
|
|
||||||
|
/* Macro for getting offset of flash timeout */
|
||||||
|
#define GET_TIMEOUT_OFFSET(timeout, step) ((timeout) / (step))
|
||||||
|
|
||||||
|
/* Base register address */
|
||||||
|
#define KTD2692_REG_LVP_BASE 0x00
|
||||||
|
#define KTD2692_REG_FLASH_TIMEOUT_BASE 0x20
|
||||||
|
#define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE 0x40
|
||||||
|
#define KTD2692_REG_MOVIE_CURRENT_BASE 0x60
|
||||||
|
#define KTD2692_REG_FLASH_CURRENT_BASE 0x80
|
||||||
|
#define KTD2692_REG_MODE_BASE 0xA0
|
||||||
|
|
||||||
|
/* Set bit coding time for expresswire interface */
|
||||||
|
#define KTD2692_TIME_RESET_US 700
|
||||||
|
#define KTD2692_TIME_DATA_START_TIME_US 10
|
||||||
|
#define KTD2692_TIME_HIGH_END_OF_DATA_US 350
|
||||||
|
#define KTD2692_TIME_LOW_END_OF_DATA_US 10
|
||||||
|
#define KTD2692_TIME_SHORT_BITSET_US 4
|
||||||
|
#define KTD2692_TIME_LONG_BITSET_US 12
|
||||||
|
|
||||||
|
/* KTD2692 default length of name */
|
||||||
|
#define KTD2692_NAME_LENGTH 20
|
||||||
|
|
||||||
|
enum ktd2692_bitset {
|
||||||
|
KTD2692_LOW = 0,
|
||||||
|
KTD2692_HIGH,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Movie / Flash Mode Control */
|
||||||
|
enum ktd2692_led_mode {
|
||||||
|
KTD2692_MODE_DISABLE = 0, /* default */
|
||||||
|
KTD2692_MODE_MOVIE,
|
||||||
|
KTD2692_MODE_FLASH,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ktd2692_led_config_data {
|
||||||
|
/* maximum LED current in movie mode */
|
||||||
|
u32 movie_max_microamp;
|
||||||
|
/* maximum LED current in flash mode */
|
||||||
|
u32 flash_max_microamp;
|
||||||
|
/* maximum flash timeout */
|
||||||
|
u32 flash_max_timeout;
|
||||||
|
/* max LED brightness level */
|
||||||
|
enum led_brightness max_brightness;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ktd2692_context {
|
||||||
|
/* Related LED Flash class device */
|
||||||
|
struct led_classdev_flash fled_cdev;
|
||||||
|
|
||||||
|
/* secures access to the device */
|
||||||
|
struct mutex lock;
|
||||||
|
struct regulator *regulator;
|
||||||
|
struct work_struct work_brightness_set;
|
||||||
|
|
||||||
|
struct gpio_desc *aux_gpio;
|
||||||
|
struct gpio_desc *ctrl_gpio;
|
||||||
|
|
||||||
|
enum ktd2692_led_mode mode;
|
||||||
|
enum led_brightness torch_brightness;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ktd2692_context *fled_cdev_to_led(
|
||||||
|
struct led_classdev_flash *fled_cdev)
|
||||||
|
{
|
||||||
|
return container_of(fled_cdev, struct ktd2692_context, fled_cdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_expresswire_start(struct ktd2692_context *led)
|
||||||
|
{
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
|
||||||
|
udelay(KTD2692_TIME_DATA_START_TIME_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_expresswire_reset(struct ktd2692_context *led)
|
||||||
|
{
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
|
||||||
|
udelay(KTD2692_TIME_RESET_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_expresswire_end(struct ktd2692_context *led)
|
||||||
|
{
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
|
||||||
|
udelay(KTD2692_TIME_LOW_END_OF_DATA_US);
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
|
||||||
|
udelay(KTD2692_TIME_HIGH_END_OF_DATA_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The Low Bit(0) and High Bit(1) is based on a time detection
|
||||||
|
* algorithm between time low and time high
|
||||||
|
* Time_(L_LB) : Low time of the Low Bit(0)
|
||||||
|
* Time_(H_LB) : High time of the LOW Bit(0)
|
||||||
|
* Time_(L_HB) : Low time of the High Bit(1)
|
||||||
|
* Time_(H_HB) : High time of the High Bit(1)
|
||||||
|
*
|
||||||
|
* It can be simplified to:
|
||||||
|
* Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB)
|
||||||
|
* High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB)
|
||||||
|
* HIGH ___ ____ _.. _________ ___
|
||||||
|
* |_________| |_.. |____| |__|
|
||||||
|
* LOW <L_LB> <H_LB> <L_HB> <H_HB>
|
||||||
|
* [ Low Bit (0) ] [ High Bit(1) ]
|
||||||
|
*/
|
||||||
|
if (bit) {
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
|
||||||
|
udelay(KTD2692_TIME_SHORT_BITSET_US);
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
|
||||||
|
udelay(KTD2692_TIME_LONG_BITSET_US);
|
||||||
|
} else {
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
|
||||||
|
udelay(KTD2692_TIME_LONG_BITSET_US);
|
||||||
|
gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
|
||||||
|
udelay(KTD2692_TIME_SHORT_BITSET_US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ktd2692_expresswire_start(led);
|
||||||
|
for (i = 7; i >= 0; i--)
|
||||||
|
ktd2692_expresswire_set_bit(led, value & BIT(i));
|
||||||
|
ktd2692_expresswire_end(led);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_brightness_set(struct ktd2692_context *led,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
mutex_lock(&led->lock);
|
||||||
|
|
||||||
|
if (brightness == LED_OFF) {
|
||||||
|
led->mode = KTD2692_MODE_DISABLE;
|
||||||
|
gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
|
||||||
|
} else {
|
||||||
|
ktd2692_expresswire_write(led, brightness |
|
||||||
|
KTD2692_REG_MOVIE_CURRENT_BASE);
|
||||||
|
led->mode = KTD2692_MODE_MOVIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
|
||||||
|
mutex_unlock(&led->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_brightness_set_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct ktd2692_context *led =
|
||||||
|
container_of(work, struct ktd2692_context, work_brightness_set);
|
||||||
|
|
||||||
|
ktd2692_brightness_set(led, led->torch_brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_led_brightness_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
|
||||||
|
struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
|
||||||
|
led->torch_brightness = brightness;
|
||||||
|
schedule_work(&led->work_brightness_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ktd2692_led_brightness_set_sync(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
|
||||||
|
struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
|
||||||
|
ktd2692_brightness_set(led, brightness);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev,
|
||||||
|
bool state)
|
||||||
|
{
|
||||||
|
struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
|
||||||
|
struct led_flash_setting *timeout = &fled_cdev->timeout;
|
||||||
|
u32 flash_tm_reg;
|
||||||
|
|
||||||
|
mutex_lock(&led->lock);
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step);
|
||||||
|
ktd2692_expresswire_write(led, flash_tm_reg
|
||||||
|
| KTD2692_REG_FLASH_TIMEOUT_BASE);
|
||||||
|
|
||||||
|
led->mode = KTD2692_MODE_FLASH;
|
||||||
|
gpiod_direction_output(led->aux_gpio, KTD2692_HIGH);
|
||||||
|
} else {
|
||||||
|
led->mode = KTD2692_MODE_DISABLE;
|
||||||
|
gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
|
||||||
|
|
||||||
|
fled_cdev->led_cdev.brightness = LED_OFF;
|
||||||
|
led->mode = KTD2692_MODE_DISABLE;
|
||||||
|
|
||||||
|
mutex_unlock(&led->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
|
||||||
|
u32 timeout)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg)
|
||||||
|
{
|
||||||
|
u32 offset, step;
|
||||||
|
u32 movie_current_microamp;
|
||||||
|
|
||||||
|
offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS;
|
||||||
|
step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp)
|
||||||
|
/ KTD2692_MOVIE_MODE_CURRENT_LEVELS;
|
||||||
|
|
||||||
|
do {
|
||||||
|
movie_current_microamp = step * offset;
|
||||||
|
offset--;
|
||||||
|
} while ((movie_current_microamp > cfg->movie_max_microamp) &&
|
||||||
|
(offset > 0));
|
||||||
|
|
||||||
|
cfg->max_brightness = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev,
|
||||||
|
struct ktd2692_led_config_data *cfg)
|
||||||
|
{
|
||||||
|
struct led_flash_setting *setting;
|
||||||
|
|
||||||
|
setting = &fled_cdev->timeout;
|
||||||
|
setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE;
|
||||||
|
setting->max = cfg->flash_max_timeout;
|
||||||
|
setting->step = cfg->flash_max_timeout
|
||||||
|
/ (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1);
|
||||||
|
setting->val = cfg->flash_max_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ktd2692_setup(struct ktd2692_context *led)
|
||||||
|
{
|
||||||
|
led->mode = KTD2692_MODE_DISABLE;
|
||||||
|
ktd2692_expresswire_reset(led);
|
||||||
|
gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
|
||||||
|
|
||||||
|
ktd2692_expresswire_write(led, (KTD2962_MM_MIN_CURR_THRESHOLD_SCALE - 1)
|
||||||
|
| KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE);
|
||||||
|
ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45)
|
||||||
|
| KTD2692_REG_FLASH_CURRENT_BASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
|
||||||
|
struct ktd2692_led_config_data *cfg)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
struct device_node *child_node;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!dev->of_node)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
|
||||||
|
if (IS_ERR(led->ctrl_gpio)) {
|
||||||
|
ret = PTR_ERR(led->ctrl_gpio);
|
||||||
|
dev_err(dev, "cannot get ctrl-gpios %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS);
|
||||||
|
if (IS_ERR(led->aux_gpio)) {
|
||||||
|
ret = PTR_ERR(led->aux_gpio);
|
||||||
|
dev_err(dev, "cannot get aux-gpios %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
led->regulator = devm_regulator_get(dev, "vin");
|
||||||
|
if (IS_ERR(led->regulator))
|
||||||
|
led->regulator = NULL;
|
||||||
|
|
||||||
|
if (led->regulator) {
|
||||||
|
ret = regulator_enable(led->regulator);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "Failed to enable supply: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
child_node = of_get_next_available_child(np, NULL);
|
||||||
|
if (!child_node) {
|
||||||
|
dev_err(dev, "No DT child node found for connected LED.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
led->fled_cdev.led_cdev.name =
|
||||||
|
of_get_property(child_node, "label", NULL) ? : child_node->name;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child_node, "led-max-microamp",
|
||||||
|
&cfg->movie_max_microamp);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to parse led-max-microamp\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child_node, "flash-max-microamp",
|
||||||
|
&cfg->flash_max_microamp);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to parse flash-max-microamp\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child_node, "flash-max-timeout-us",
|
||||||
|
&cfg->flash_max_timeout);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "failed to parse flash-max-timeout-us\n");
|
||||||
|
|
||||||
|
of_node_put(child_node);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct led_flash_ops flash_ops = {
|
||||||
|
.strobe_set = ktd2692_led_flash_strobe_set,
|
||||||
|
.timeout_set = ktd2692_led_flash_timeout_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ktd2692_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct ktd2692_context *led;
|
||||||
|
struct led_classdev *led_cdev;
|
||||||
|
struct led_classdev_flash *fled_cdev;
|
||||||
|
struct ktd2692_led_config_data led_cfg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
|
||||||
|
if (!led)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
fled_cdev = &led->fled_cdev;
|
||||||
|
led_cdev = &fled_cdev->led_cdev;
|
||||||
|
|
||||||
|
ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ktd2692_init_flash_timeout(fled_cdev, &led_cfg);
|
||||||
|
ktd2692_init_movie_current_max(&led_cfg);
|
||||||
|
|
||||||
|
fled_cdev->ops = &flash_ops;
|
||||||
|
|
||||||
|
led_cdev->max_brightness = led_cfg.max_brightness;
|
||||||
|
led_cdev->brightness_set = ktd2692_led_brightness_set;
|
||||||
|
led_cdev->brightness_set_sync = ktd2692_led_brightness_set_sync;
|
||||||
|
led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
|
||||||
|
|
||||||
|
mutex_init(&led->lock);
|
||||||
|
INIT_WORK(&led->work_brightness_set, ktd2692_brightness_set_work);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, led);
|
||||||
|
|
||||||
|
ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name);
|
||||||
|
mutex_destroy(&led->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ktd2692_setup(led);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ktd2692_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct ktd2692_context *led = platform_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
led_classdev_flash_unregister(&led->fled_cdev);
|
||||||
|
cancel_work_sync(&led->work_brightness_set);
|
||||||
|
|
||||||
|
if (led->regulator) {
|
||||||
|
ret = regulator_disable(led->regulator);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Failed to disable supply: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_destroy(&led->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id ktd2692_match[] = {
|
||||||
|
{ .compatible = "kinetic,ktd2692", },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver ktd2692_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "ktd2692",
|
||||||
|
.of_match_table = ktd2692_match,
|
||||||
|
},
|
||||||
|
.probe = ktd2692_probe,
|
||||||
|
.remove = ktd2692_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(ktd2692_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Ingi Kim <ingi2.kim@samsung.com>");
|
||||||
|
MODULE_DESCRIPTION("Kinetic KTD2692 LED driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -50,6 +50,7 @@
|
||||||
#define LP5523_REG_OP_MODE 0x01
|
#define LP5523_REG_OP_MODE 0x01
|
||||||
#define LP5523_REG_ENABLE_LEDS_MSB 0x04
|
#define LP5523_REG_ENABLE_LEDS_MSB 0x04
|
||||||
#define LP5523_REG_ENABLE_LEDS_LSB 0x05
|
#define LP5523_REG_ENABLE_LEDS_LSB 0x05
|
||||||
|
#define LP5523_REG_LED_CTRL_BASE 0x06
|
||||||
#define LP5523_REG_LED_PWM_BASE 0x16
|
#define LP5523_REG_LED_PWM_BASE 0x16
|
||||||
#define LP5523_REG_LED_CURRENT_BASE 0x26
|
#define LP5523_REG_LED_CURRENT_BASE 0x26
|
||||||
#define LP5523_REG_CONFIG 0x36
|
#define LP5523_REG_CONFIG 0x36
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
#define LP5523_REG_RESET 0x3D
|
#define LP5523_REG_RESET 0x3D
|
||||||
#define LP5523_REG_LED_TEST_CTRL 0x41
|
#define LP5523_REG_LED_TEST_CTRL 0x41
|
||||||
#define LP5523_REG_LED_TEST_ADC 0x42
|
#define LP5523_REG_LED_TEST_ADC 0x42
|
||||||
|
#define LP5523_REG_MASTER_FADER_BASE 0x48
|
||||||
#define LP5523_REG_CH1_PROG_START 0x4C
|
#define LP5523_REG_CH1_PROG_START 0x4C
|
||||||
#define LP5523_REG_CH2_PROG_START 0x4D
|
#define LP5523_REG_CH2_PROG_START 0x4D
|
||||||
#define LP5523_REG_CH3_PROG_START 0x4E
|
#define LP5523_REG_CH3_PROG_START 0x4E
|
||||||
|
@ -78,6 +80,9 @@
|
||||||
#define LP5523_EXT_CLK_USED 0x08
|
#define LP5523_EXT_CLK_USED 0x08
|
||||||
#define LP5523_ENG_STATUS_MASK 0x07
|
#define LP5523_ENG_STATUS_MASK 0x07
|
||||||
|
|
||||||
|
#define LP5523_FADER_MAPPING_MASK 0xC0
|
||||||
|
#define LP5523_FADER_MAPPING_SHIFT 6
|
||||||
|
|
||||||
/* Memory Page Selection */
|
/* Memory Page Selection */
|
||||||
#define LP5523_PAGE_ENG1 0
|
#define LP5523_PAGE_ENG1 0
|
||||||
#define LP5523_PAGE_ENG2 1
|
#define LP5523_PAGE_ENG2 1
|
||||||
|
@ -666,6 +671,137 @@ release_lock:
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define show_fader(nr) \
|
||||||
|
static ssize_t show_master_fader##nr(struct device *dev, \
|
||||||
|
struct device_attribute *attr, \
|
||||||
|
char *buf) \
|
||||||
|
{ \
|
||||||
|
return show_master_fader(dev, attr, buf, nr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define store_fader(nr) \
|
||||||
|
static ssize_t store_master_fader##nr(struct device *dev, \
|
||||||
|
struct device_attribute *attr, \
|
||||||
|
const char *buf, size_t len) \
|
||||||
|
{ \
|
||||||
|
return store_master_fader(dev, attr, buf, len, nr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t show_master_fader(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf, int nr)
|
||||||
|
{
|
||||||
|
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct lp55xx_chip *chip = led->chip;
|
||||||
|
int ret;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
mutex_lock(&chip->lock);
|
||||||
|
ret = lp55xx_read(chip, LP5523_REG_MASTER_FADER_BASE + nr - 1, &val);
|
||||||
|
mutex_unlock(&chip->lock);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
ret = sprintf(buf, "%u\n", val);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
show_fader(1)
|
||||||
|
show_fader(2)
|
||||||
|
show_fader(3)
|
||||||
|
|
||||||
|
static ssize_t store_master_fader(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t len, int nr)
|
||||||
|
{
|
||||||
|
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct lp55xx_chip *chip = led->chip;
|
||||||
|
int ret;
|
||||||
|
unsigned long val;
|
||||||
|
|
||||||
|
if (kstrtoul(buf, 0, &val))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (val > 0xff)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&chip->lock);
|
||||||
|
ret = lp55xx_write(chip, LP5523_REG_MASTER_FADER_BASE + nr - 1,
|
||||||
|
(u8)val);
|
||||||
|
mutex_unlock(&chip->lock);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
ret = len;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
store_fader(1)
|
||||||
|
store_fader(2)
|
||||||
|
store_fader(3)
|
||||||
|
|
||||||
|
static ssize_t show_master_fader_leds(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct lp55xx_chip *chip = led->chip;
|
||||||
|
int i, ret, pos = 0;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
mutex_lock(&chip->lock);
|
||||||
|
|
||||||
|
for (i = 0; i < LP5523_MAX_LEDS; i++) {
|
||||||
|
ret = lp55xx_read(chip, LP5523_REG_LED_CTRL_BASE + i, &val);
|
||||||
|
if (ret)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
val = (val & LP5523_FADER_MAPPING_MASK)
|
||||||
|
>> LP5523_FADER_MAPPING_SHIFT;
|
||||||
|
if (val > 3) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
buf[pos++] = val + '0';
|
||||||
|
}
|
||||||
|
buf[pos++] = '\n';
|
||||||
|
ret = pos;
|
||||||
|
leave:
|
||||||
|
mutex_unlock(&chip->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t store_master_fader_leds(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct lp55xx_chip *chip = led->chip;
|
||||||
|
int i, n, ret;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
n = min_t(int, len, LP5523_MAX_LEDS);
|
||||||
|
|
||||||
|
mutex_lock(&chip->lock);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (buf[i] >= '0' && buf[i] <= '3') {
|
||||||
|
val = (buf[i] - '0') << LP5523_FADER_MAPPING_SHIFT;
|
||||||
|
ret = lp55xx_update_bits(chip,
|
||||||
|
LP5523_REG_LED_CTRL_BASE + i,
|
||||||
|
LP5523_FADER_MAPPING_MASK,
|
||||||
|
val);
|
||||||
|
if (ret)
|
||||||
|
goto leave;
|
||||||
|
} else {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = len;
|
||||||
|
leave:
|
||||||
|
mutex_unlock(&chip->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void lp5523_led_brightness_work(struct work_struct *work)
|
static void lp5523_led_brightness_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
|
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
|
||||||
|
@ -688,6 +824,14 @@ static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
|
||||||
static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
|
static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
|
||||||
static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
|
static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
|
||||||
static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);
|
static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);
|
||||||
|
static LP55XX_DEV_ATTR_RW(master_fader1, show_master_fader1,
|
||||||
|
store_master_fader1);
|
||||||
|
static LP55XX_DEV_ATTR_RW(master_fader2, show_master_fader2,
|
||||||
|
store_master_fader2);
|
||||||
|
static LP55XX_DEV_ATTR_RW(master_fader3, show_master_fader3,
|
||||||
|
store_master_fader3);
|
||||||
|
static LP55XX_DEV_ATTR_RW(master_fader_leds, show_master_fader_leds,
|
||||||
|
store_master_fader_leds);
|
||||||
|
|
||||||
static struct attribute *lp5523_attributes[] = {
|
static struct attribute *lp5523_attributes[] = {
|
||||||
&dev_attr_engine1_mode.attr,
|
&dev_attr_engine1_mode.attr,
|
||||||
|
@ -700,6 +844,10 @@ static struct attribute *lp5523_attributes[] = {
|
||||||
&dev_attr_engine2_leds.attr,
|
&dev_attr_engine2_leds.attr,
|
||||||
&dev_attr_engine3_leds.attr,
|
&dev_attr_engine3_leds.attr,
|
||||||
&dev_attr_selftest.attr,
|
&dev_attr_selftest.attr,
|
||||||
|
&dev_attr_master_fader1.attr,
|
||||||
|
&dev_attr_master_fader2.attr,
|
||||||
|
&dev_attr_master_fader3.attr,
|
||||||
|
&dev_attr_master_fader_leds.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ static int lp55xx_request_firmware(struct lp55xx_chip *chip)
|
||||||
const char *name = chip->cl->name;
|
const char *name = chip->cl->name;
|
||||||
struct device *dev = &chip->cl->dev;
|
struct device *dev = &chip->cl->dev;
|
||||||
|
|
||||||
return request_firmware_nowait(THIS_MODULE, true, name, dev,
|
return request_firmware_nowait(THIS_MODULE, false, name, dev,
|
||||||
GFP_KERNEL, chip, lp55xx_firmware_loaded);
|
GFP_KERNEL, chip, lp55xx_firmware_loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Belkin Inc.
|
||||||
|
* Copyright 2015 Andrew Lunn <andrew@lunn.ch>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#define TLC591XX_MAX_LEDS 16
|
||||||
|
|
||||||
|
#define TLC591XX_REG_MODE1 0x00
|
||||||
|
#define MODE1_RESPON_ADDR_MASK 0xF0
|
||||||
|
#define MODE1_NORMAL_MODE (0 << 4)
|
||||||
|
#define MODE1_SPEED_MODE (1 << 4)
|
||||||
|
|
||||||
|
#define TLC591XX_REG_MODE2 0x01
|
||||||
|
#define MODE2_DIM (0 << 5)
|
||||||
|
#define MODE2_BLINK (1 << 5)
|
||||||
|
#define MODE2_OCH_STOP (0 << 3)
|
||||||
|
#define MODE2_OCH_ACK (1 << 3)
|
||||||
|
|
||||||
|
#define TLC591XX_REG_PWM(x) (0x02 + (x))
|
||||||
|
|
||||||
|
#define TLC591XX_REG_GRPPWM 0x12
|
||||||
|
#define TLC591XX_REG_GRPFREQ 0x13
|
||||||
|
|
||||||
|
/* LED Driver Output State, determine the source that drives LED outputs */
|
||||||
|
#define LEDOUT_OFF 0x0 /* Output LOW */
|
||||||
|
#define LEDOUT_ON 0x1 /* Output HI-Z */
|
||||||
|
#define LEDOUT_DIM 0x2 /* Dimming */
|
||||||
|
#define LEDOUT_BLINK 0x3 /* Blinking */
|
||||||
|
#define LEDOUT_MASK 0x3
|
||||||
|
|
||||||
|
#define ldev_to_led(c) container_of(c, struct tlc591xx_led, ldev)
|
||||||
|
#define work_to_led(work) container_of(work, struct tlc591xx_led, work)
|
||||||
|
|
||||||
|
struct tlc591xx_led {
|
||||||
|
bool active;
|
||||||
|
unsigned int led_no;
|
||||||
|
struct led_classdev ldev;
|
||||||
|
struct work_struct work;
|
||||||
|
struct tlc591xx_priv *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tlc591xx_priv {
|
||||||
|
struct tlc591xx_led leds[TLC591XX_MAX_LEDS];
|
||||||
|
struct regmap *regmap;
|
||||||
|
unsigned int reg_ledout_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tlc591xx {
|
||||||
|
unsigned int max_leds;
|
||||||
|
unsigned int reg_ledout_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct tlc591xx tlc59116 = {
|
||||||
|
.max_leds = 16,
|
||||||
|
.reg_ledout_offset = 0x14,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct tlc591xx tlc59108 = {
|
||||||
|
.max_leds = 8,
|
||||||
|
.reg_ledout_offset = 0x0c,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlc591xx_set_mode(struct regmap *regmap, u8 mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
err = regmap_write(regmap, TLC591XX_REG_MODE1, MODE1_NORMAL_MODE);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
val = MODE2_OCH_STOP | mode;
|
||||||
|
|
||||||
|
return regmap_write(regmap, TLC591XX_REG_MODE2, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlc591xx_set_ledout(struct tlc591xx_priv *priv, struct tlc591xx_led *led,
|
||||||
|
u8 val)
|
||||||
|
{
|
||||||
|
unsigned int i = (led->led_no % 4) * 2;
|
||||||
|
unsigned int mask = LEDOUT_MASK << i;
|
||||||
|
unsigned int addr = priv->reg_ledout_offset + (led->led_no >> 2);
|
||||||
|
|
||||||
|
val = val << i;
|
||||||
|
|
||||||
|
return regmap_update_bits(priv->regmap, addr, mask, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlc591xx_set_pwm(struct tlc591xx_priv *priv, struct tlc591xx_led *led,
|
||||||
|
u8 brightness)
|
||||||
|
{
|
||||||
|
u8 pwm = TLC591XX_REG_PWM(led->led_no);
|
||||||
|
|
||||||
|
return regmap_write(priv->regmap, pwm, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tlc591xx_led_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct tlc591xx_led *led = work_to_led(work);
|
||||||
|
struct tlc591xx_priv *priv = led->priv;
|
||||||
|
enum led_brightness brightness = led->ldev.brightness;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (brightness) {
|
||||||
|
case 0:
|
||||||
|
err = tlc591xx_set_ledout(priv, led, LEDOUT_OFF);
|
||||||
|
break;
|
||||||
|
case LED_FULL:
|
||||||
|
err = tlc591xx_set_ledout(priv, led, LEDOUT_ON);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = tlc591xx_set_ledout(priv, led, LEDOUT_DIM);
|
||||||
|
if (!err)
|
||||||
|
err = tlc591xx_set_pwm(priv, led, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
dev_err(led->ldev.dev, "Failed setting brightness\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tlc591xx_brightness_set(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct tlc591xx_led *led = ldev_to_led(led_cdev);
|
||||||
|
|
||||||
|
led->ldev.brightness = brightness;
|
||||||
|
schedule_work(&led->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j)
|
||||||
|
{
|
||||||
|
int i = j;
|
||||||
|
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (priv->leds[i].active) {
|
||||||
|
led_classdev_unregister(&priv->leds[i].ldev);
|
||||||
|
cancel_work_sync(&priv->leds[i].work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlc591xx_configure(struct device *dev,
|
||||||
|
struct tlc591xx_priv *priv,
|
||||||
|
const struct tlc591xx *tlc591xx)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
tlc591xx_set_mode(priv->regmap, MODE2_DIM);
|
||||||
|
for (i = 0; i < TLC591XX_MAX_LEDS; i++) {
|
||||||
|
struct tlc591xx_led *led = &priv->leds[i];
|
||||||
|
|
||||||
|
if (!led->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
led->priv = priv;
|
||||||
|
led->led_no = i;
|
||||||
|
led->ldev.brightness_set = tlc591xx_brightness_set;
|
||||||
|
led->ldev.max_brightness = LED_FULL;
|
||||||
|
INIT_WORK(&led->work, tlc591xx_led_work);
|
||||||
|
err = led_classdev_register(dev, &led->ldev);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(dev, "couldn't register LED %s\n",
|
||||||
|
led->ldev.name);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
tlc591xx_destroy_devices(priv, i);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config tlc591xx_regmap = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
.max_register = 0x1e,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id of_tlc591xx_leds_match[] = {
|
||||||
|
{ .compatible = "ti,tlc59116",
|
||||||
|
.data = &tlc59116 },
|
||||||
|
{ .compatible = "ti,tlc59108",
|
||||||
|
.data = &tlc59108 },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, of_tlc591xx_leds_match);
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlc591xx_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct device_node *np = client->dev.of_node, *child;
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
const struct tlc591xx *tlc591xx;
|
||||||
|
struct tlc591xx_priv *priv;
|
||||||
|
int err, count, reg;
|
||||||
|
|
||||||
|
match = of_match_device(of_tlc591xx_leds_match, dev);
|
||||||
|
if (!match)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
tlc591xx = match->data;
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
count = of_get_child_count(np);
|
||||||
|
if (!count || count > tlc591xx->max_leds)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_BYTE_DATA))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->regmap = devm_regmap_init_i2c(client, &tlc591xx_regmap);
|
||||||
|
if (IS_ERR(priv->regmap)) {
|
||||||
|
err = PTR_ERR(priv->regmap);
|
||||||
|
dev_err(dev, "Failed to allocate register map: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
priv->reg_ledout_offset = tlc591xx->reg_ledout_offset;
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, priv);
|
||||||
|
|
||||||
|
for_each_child_of_node(np, child) {
|
||||||
|
err = of_property_read_u32(child, "reg", ®);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
if (reg < 0 || reg >= tlc591xx->max_leds)
|
||||||
|
return -EINVAL;
|
||||||
|
if (priv->leds[reg].active)
|
||||||
|
return -EINVAL;
|
||||||
|
priv->leds[reg].active = true;
|
||||||
|
priv->leds[reg].ldev.name =
|
||||||
|
of_get_property(child, "label", NULL) ? : child->name;
|
||||||
|
priv->leds[reg].ldev.default_trigger =
|
||||||
|
of_get_property(child, "linux,default-trigger", NULL);
|
||||||
|
}
|
||||||
|
return tlc591xx_configure(dev, priv, tlc591xx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlc591xx_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct tlc591xx_priv *priv = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
tlc591xx_destroy_devices(priv, TLC591XX_MAX_LEDS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id tlc591xx_id[] = {
|
||||||
|
{ "tlc59116" },
|
||||||
|
{ "tlc59108" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, tlc591xx_id);
|
||||||
|
|
||||||
|
static struct i2c_driver tlc591xx_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "tlc591xx",
|
||||||
|
.of_match_table = of_match_ptr(of_tlc591xx_leds_match),
|
||||||
|
},
|
||||||
|
.probe = tlc591xx_probe,
|
||||||
|
.remove = tlc591xx_remove,
|
||||||
|
.id_table = tlc591xx_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(tlc591xx_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("TLC591XX LED driver");
|
|
@ -13,7 +13,6 @@
|
||||||
#ifndef __LEDS_H_INCLUDED
|
#ifndef __LEDS_H_INCLUDED
|
||||||
#define __LEDS_H_INCLUDED
|
#define __LEDS_H_INCLUDED
|
||||||
|
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
|
|
||||||
|
@ -50,27 +49,4 @@ void led_stop_software_blink(struct led_classdev *led_cdev);
|
||||||
extern struct rw_semaphore leds_list_lock;
|
extern struct rw_semaphore leds_list_lock;
|
||||||
extern struct list_head leds_list;
|
extern struct list_head leds_list;
|
||||||
|
|
||||||
#ifdef CONFIG_LEDS_TRIGGERS
|
|
||||||
void led_trigger_set_default(struct led_classdev *led_cdev);
|
|
||||||
void led_trigger_set(struct led_classdev *led_cdev,
|
|
||||||
struct led_trigger *trigger);
|
|
||||||
void led_trigger_remove(struct led_classdev *led_cdev);
|
|
||||||
|
|
||||||
static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
|
|
||||||
{
|
|
||||||
return led_cdev->trigger_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define led_trigger_set_default(x) do {} while (0)
|
|
||||||
#define led_trigger_set(x, y) do {} while (0)
|
|
||||||
#define led_trigger_remove(x) do {} while (0)
|
|
||||||
#define led_get_trigger_data(x) (NULL)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count);
|
|
||||||
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf);
|
|
||||||
|
|
||||||
#endif /* __LEDS_H_INCLUDED */
|
#endif /* __LEDS_H_INCLUDED */
|
||||||
|
|
|
@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
|
||||||
tristate
|
tristate
|
||||||
depends on VIDEOBUF2_CORE
|
depends on VIDEOBUF2_CORE
|
||||||
|
|
||||||
|
# Used by LED subsystem flash drivers
|
||||||
|
config V4L2_FLASH_LED_CLASS
|
||||||
|
tristate "V4L2 flash API for LED flash class devices"
|
||||||
|
depends on VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on LEDS_CLASS_FLASH
|
||||||
|
---help---
|
||||||
|
Say Y here to enable V4L2 flash API support for LED flash
|
||||||
|
class drivers.
|
||||||
|
|
||||||
|
When in doubt, say N.
|
||||||
|
|
||||||
# Used by drivers that need Videobuf modules
|
# Used by drivers that need Videobuf modules
|
||||||
config VIDEOBUF_GEN
|
config VIDEOBUF_GEN
|
||||||
tristate
|
tristate
|
||||||
|
|
|
@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
|
||||||
|
|
||||||
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
|
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
|
||||||
|
|
||||||
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
|
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
|
||||||
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
|
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
|
||||||
obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
|
obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
|
static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
|
||||||
{
|
{
|
||||||
#if IS_ENABLED(CONFIG_I2C)
|
#if IS_ENABLED(CONFIG_I2C)
|
||||||
struct i2c_client *client = i2c_verify_client(dev);
|
struct i2c_client *client = i2c_verify_client(sd->dev);
|
||||||
return client &&
|
return client &&
|
||||||
asd->match.i2c.adapter_id == client->adapter->nr &&
|
asd->match.i2c.adapter_id == client->adapter->nr &&
|
||||||
asd->match.i2c.address == client->addr;
|
asd->match.i2c.address == client->addr;
|
||||||
|
@ -34,14 +34,24 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd)
|
static bool match_devname(struct v4l2_subdev *sd,
|
||||||
|
struct v4l2_async_subdev *asd)
|
||||||
{
|
{
|
||||||
return !strcmp(asd->match.device_name.name, dev_name(dev));
|
return !strcmp(asd->match.device_name.name, dev_name(sd->dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool match_of(struct device *dev, struct v4l2_async_subdev *asd)
|
static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
|
||||||
{
|
{
|
||||||
return dev->of_node == asd->match.of.node;
|
return sd->of_node == asd->match.of.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
|
||||||
|
{
|
||||||
|
if (!asd->match.custom.match)
|
||||||
|
/* Match always */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return asd->match.custom.match(sd->dev, asd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LIST_HEAD(subdev_list);
|
static LIST_HEAD(subdev_list);
|
||||||
|
@ -51,17 +61,14 @@ static DEFINE_MUTEX(list_lock);
|
||||||
static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
|
static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
|
||||||
struct v4l2_subdev *sd)
|
struct v4l2_subdev *sd)
|
||||||
{
|
{
|
||||||
|
bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
|
||||||
struct v4l2_async_subdev *asd;
|
struct v4l2_async_subdev *asd;
|
||||||
bool (*match)(struct device *, struct v4l2_async_subdev *);
|
|
||||||
|
|
||||||
list_for_each_entry(asd, ¬ifier->waiting, list) {
|
list_for_each_entry(asd, ¬ifier->waiting, list) {
|
||||||
/* bus_type has been verified valid before */
|
/* bus_type has been verified valid before */
|
||||||
switch (asd->match_type) {
|
switch (asd->match_type) {
|
||||||
case V4L2_ASYNC_MATCH_CUSTOM:
|
case V4L2_ASYNC_MATCH_CUSTOM:
|
||||||
match = asd->match.custom.match;
|
match = match_custom;
|
||||||
if (!match)
|
|
||||||
/* Match always */
|
|
||||||
return asd;
|
|
||||||
break;
|
break;
|
||||||
case V4L2_ASYNC_MATCH_DEVNAME:
|
case V4L2_ASYNC_MATCH_DEVNAME:
|
||||||
match = match_devname;
|
match = match_devname;
|
||||||
|
@ -79,7 +86,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
|
||||||
}
|
}
|
||||||
|
|
||||||
/* match cannot be NULL here */
|
/* match cannot be NULL here */
|
||||||
if (match(sd->dev, asd))
|
if (match(sd, asd))
|
||||||
return asd;
|
return asd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +273,14 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
|
||||||
{
|
{
|
||||||
struct v4l2_async_notifier *notifier;
|
struct v4l2_async_notifier *notifier;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No reference taken. The reference is held by the device
|
||||||
|
* (struct v4l2_subdev.dev), and async sub-device does not
|
||||||
|
* exist independently of the device at any point of time.
|
||||||
|
*/
|
||||||
|
if (!sd->of_node && sd->dev)
|
||||||
|
sd->of_node = sd->dev->of_node;
|
||||||
|
|
||||||
mutex_lock(&list_lock);
|
mutex_lock(&list_lock);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&sd->async_list);
|
INIT_LIST_HEAD(&sd->async_list);
|
||||||
|
|
|
@ -0,0 +1,710 @@
|
||||||
|
/*
|
||||||
|
* V4L2 flash LED sub-device registration helpers.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Samsung Electronics Co., Ltd
|
||||||
|
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/led-class-flash.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <media/v4l2-flash-led-class.h>
|
||||||
|
|
||||||
|
#define has_flash_op(v4l2_flash, op) \
|
||||||
|
(v4l2_flash && v4l2_flash->ops->op)
|
||||||
|
|
||||||
|
#define call_flash_op(v4l2_flash, op, arg) \
|
||||||
|
(has_flash_op(v4l2_flash, op) ? \
|
||||||
|
v4l2_flash->ops->op(v4l2_flash, arg) : \
|
||||||
|
-EINVAL)
|
||||||
|
|
||||||
|
enum ctrl_init_data_id {
|
||||||
|
LED_MODE,
|
||||||
|
TORCH_INTENSITY,
|
||||||
|
FLASH_INTENSITY,
|
||||||
|
INDICATOR_INTENSITY,
|
||||||
|
FLASH_TIMEOUT,
|
||||||
|
STROBE_SOURCE,
|
||||||
|
/*
|
||||||
|
* Only above values are applicable to
|
||||||
|
* the 'ctrls' array in the struct v4l2_flash.
|
||||||
|
*/
|
||||||
|
FLASH_STROBE,
|
||||||
|
STROBE_STOP,
|
||||||
|
STROBE_STATUS,
|
||||||
|
FLASH_FAULT,
|
||||||
|
NUM_FLASH_CTRLS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum led_brightness __intensity_to_led_brightness(
|
||||||
|
struct v4l2_ctrl *ctrl, s32 intensity)
|
||||||
|
{
|
||||||
|
intensity -= ctrl->minimum;
|
||||||
|
intensity /= (u32) ctrl->step;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicator LEDs, unlike torch LEDs, are turned on/off basing on
|
||||||
|
* the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
|
||||||
|
* Therefore it must be possible to set it to 0 level which in
|
||||||
|
* the LED subsystem reflects LED_OFF state.
|
||||||
|
*/
|
||||||
|
if (ctrl->minimum)
|
||||||
|
++intensity;
|
||||||
|
|
||||||
|
return intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Indicator LEDs, unlike torch LEDs, are turned on/off basing on
|
||||||
|
* the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
|
||||||
|
* Do not decrement brightness read from the LED subsystem for
|
||||||
|
* indicator LED as it may equal 0. For torch LEDs this function
|
||||||
|
* is called only when V4L2_FLASH_LED_MODE_TORCH is set and the
|
||||||
|
* brightness read is guaranteed to be greater than 0. In the mode
|
||||||
|
* V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used.
|
||||||
|
*/
|
||||||
|
if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
|
||||||
|
--brightness;
|
||||||
|
|
||||||
|
return (brightness * ctrl->step) + ctrl->minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
|
||||||
|
struct v4l2_ctrl *ctrl)
|
||||||
|
{
|
||||||
|
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
|
||||||
|
enum led_brightness brightness;
|
||||||
|
|
||||||
|
if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
|
||||||
|
brightness = call_flash_op(v4l2_flash,
|
||||||
|
intensity_to_led_brightness,
|
||||||
|
ctrl->val);
|
||||||
|
else
|
||||||
|
brightness = __intensity_to_led_brightness(ctrl, ctrl->val);
|
||||||
|
/*
|
||||||
|
* In case a LED Flash class driver provides ops for custom
|
||||||
|
* brightness <-> intensity conversion, it also must have defined
|
||||||
|
* related v4l2 control step == 1. In such a case a backward conversion
|
||||||
|
* from led brightness to v4l2 intensity is required to find out the
|
||||||
|
* the aligned intensity value.
|
||||||
|
*/
|
||||||
|
if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
|
||||||
|
ctrl->val = call_flash_op(v4l2_flash,
|
||||||
|
led_brightness_to_intensity,
|
||||||
|
brightness);
|
||||||
|
|
||||||
|
if (ctrl == ctrls[TORCH_INTENSITY]) {
|
||||||
|
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
|
||||||
|
brightness);
|
||||||
|
} else {
|
||||||
|
led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
|
||||||
|
brightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
|
||||||
|
struct v4l2_ctrl *ctrl)
|
||||||
|
{
|
||||||
|
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
|
||||||
|
struct led_classdev *led_cdev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (ctrl == ctrls[TORCH_INTENSITY]) {
|
||||||
|
/*
|
||||||
|
* Update torch brightness only if in TORCH_MODE. In other modes
|
||||||
|
* torch led is turned off, which would spuriously inform the
|
||||||
|
* user space that V4L2_CID_FLASH_TORCH_INTENSITY control value
|
||||||
|
* has changed to 0.
|
||||||
|
*/
|
||||||
|
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
|
||||||
|
return 0;
|
||||||
|
led_cdev = &v4l2_flash->fled_cdev->led_cdev;
|
||||||
|
} else {
|
||||||
|
led_cdev = &v4l2_flash->iled_cdev->led_cdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = led_update_brightness(led_cdev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
|
||||||
|
ctrl->val = call_flash_op(v4l2_flash,
|
||||||
|
led_brightness_to_intensity,
|
||||||
|
led_cdev->brightness);
|
||||||
|
else
|
||||||
|
ctrl->val = __led_brightness_to_intensity(ctrl,
|
||||||
|
led_cdev->brightness);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
|
||||||
|
{
|
||||||
|
struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
bool is_strobing;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (c->id) {
|
||||||
|
case V4L2_CID_FLASH_TORCH_INTENSITY:
|
||||||
|
case V4L2_CID_FLASH_INDICATOR_INTENSITY:
|
||||||
|
return v4l2_flash_update_led_brightness(v4l2_flash, c);
|
||||||
|
case V4L2_CID_FLASH_INTENSITY:
|
||||||
|
ret = led_update_flash_brightness(fled_cdev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* No conversion is needed as LED Flash class also uses
|
||||||
|
* microamperes for flash intensity units.
|
||||||
|
*/
|
||||||
|
c->val = fled_cdev->brightness.val;
|
||||||
|
return 0;
|
||||||
|
case V4L2_CID_FLASH_STROBE_STATUS:
|
||||||
|
ret = led_get_flash_strobe(fled_cdev, &is_strobing);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
c->val = is_strobing;
|
||||||
|
return 0;
|
||||||
|
case V4L2_CID_FLASH_FAULT:
|
||||||
|
/* LED faults map directly to V4L2 flash faults */
|
||||||
|
return led_get_flash_fault(fled_cdev, &c->val);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls)
|
||||||
|
{
|
||||||
|
return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) ||
|
||||||
|
(ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val !=
|
||||||
|
V4L2_FLASH_STROBE_SOURCE_SOFTWARE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
|
||||||
|
{
|
||||||
|
struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct led_classdev *led_cdev = &fled_cdev->led_cdev;
|
||||||
|
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
|
||||||
|
bool external_strobe;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
switch (c->id) {
|
||||||
|
case V4L2_CID_FLASH_LED_MODE:
|
||||||
|
switch (c->val) {
|
||||||
|
case V4L2_FLASH_LED_MODE_NONE:
|
||||||
|
led_set_brightness(led_cdev, LED_OFF);
|
||||||
|
return led_set_flash_strobe(fled_cdev, false);
|
||||||
|
case V4L2_FLASH_LED_MODE_FLASH:
|
||||||
|
/* Turn the torch LED off */
|
||||||
|
led_set_brightness(led_cdev, LED_OFF);
|
||||||
|
if (ctrls[STROBE_SOURCE]) {
|
||||||
|
external_strobe = (ctrls[STROBE_SOURCE]->val ==
|
||||||
|
V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
|
||||||
|
|
||||||
|
ret = call_flash_op(v4l2_flash,
|
||||||
|
external_strobe_set,
|
||||||
|
external_strobe);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
case V4L2_FLASH_LED_MODE_TORCH:
|
||||||
|
if (ctrls[STROBE_SOURCE]) {
|
||||||
|
ret = call_flash_op(v4l2_flash,
|
||||||
|
external_strobe_set,
|
||||||
|
false);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Stop flash strobing */
|
||||||
|
ret = led_set_flash_strobe(fled_cdev, false);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
v4l2_flash_set_led_brightness(v4l2_flash,
|
||||||
|
ctrls[TORCH_INTENSITY]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case V4L2_CID_FLASH_STROBE_SOURCE:
|
||||||
|
external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
|
||||||
|
/*
|
||||||
|
* For some hardware arrangements setting strobe source may
|
||||||
|
* affect torch mode. Therefore, if not in the flash mode,
|
||||||
|
* cache only this setting. It will be applied upon switching
|
||||||
|
* to flash mode.
|
||||||
|
*/
|
||||||
|
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return call_flash_op(v4l2_flash, external_strobe_set,
|
||||||
|
external_strobe);
|
||||||
|
case V4L2_CID_FLASH_STROBE:
|
||||||
|
if (__software_strobe_mode_inactive(ctrls))
|
||||||
|
return -EBUSY;
|
||||||
|
return led_set_flash_strobe(fled_cdev, true);
|
||||||
|
case V4L2_CID_FLASH_STROBE_STOP:
|
||||||
|
if (__software_strobe_mode_inactive(ctrls))
|
||||||
|
return -EBUSY;
|
||||||
|
return led_set_flash_strobe(fled_cdev, false);
|
||||||
|
case V4L2_CID_FLASH_TIMEOUT:
|
||||||
|
/*
|
||||||
|
* No conversion is needed as LED Flash class also uses
|
||||||
|
* microseconds for flash timeout units.
|
||||||
|
*/
|
||||||
|
return led_set_flash_timeout(fled_cdev, c->val);
|
||||||
|
case V4L2_CID_FLASH_INTENSITY:
|
||||||
|
/*
|
||||||
|
* No conversion is needed as LED Flash class also uses
|
||||||
|
* microamperes for flash intensity units.
|
||||||
|
*/
|
||||||
|
return led_set_flash_brightness(fled_cdev, c->val);
|
||||||
|
case V4L2_CID_FLASH_TORCH_INTENSITY:
|
||||||
|
case V4L2_CID_FLASH_INDICATOR_INTENSITY:
|
||||||
|
v4l2_flash_set_led_brightness(v4l2_flash, c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = {
|
||||||
|
.g_volatile_ctrl = v4l2_flash_g_volatile_ctrl,
|
||||||
|
.s_ctrl = v4l2_flash_s_ctrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s,
|
||||||
|
struct v4l2_ctrl_config *c)
|
||||||
|
{
|
||||||
|
c->min = s->min;
|
||||||
|
c->max = s->max;
|
||||||
|
c->step = s->step;
|
||||||
|
c->def = s->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
|
||||||
|
struct v4l2_flash_config *flash_cfg,
|
||||||
|
struct v4l2_flash_ctrl_data *ctrl_init_data)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops;
|
||||||
|
struct led_classdev *led_cdev = &fled_cdev->led_cdev;
|
||||||
|
struct v4l2_ctrl_config *ctrl_cfg;
|
||||||
|
u32 mask;
|
||||||
|
|
||||||
|
/* Init FLASH_FAULT ctrl data */
|
||||||
|
if (flash_cfg->flash_faults) {
|
||||||
|
ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT;
|
||||||
|
ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
|
||||||
|
ctrl_cfg->max = flash_cfg->flash_faults;
|
||||||
|
ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
|
||||||
|
V4L2_CTRL_FLAG_READ_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init FLASH_LED_MODE ctrl data */
|
||||||
|
mask = 1 << V4L2_FLASH_LED_MODE_NONE |
|
||||||
|
1 << V4L2_FLASH_LED_MODE_TORCH;
|
||||||
|
if (led_cdev->flags & LED_DEV_CAP_FLASH)
|
||||||
|
mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
|
||||||
|
|
||||||
|
ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE;
|
||||||
|
ctrl_cfg = &ctrl_init_data[LED_MODE].config;
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
|
||||||
|
ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
|
||||||
|
ctrl_cfg->menu_skip_mask = ~mask;
|
||||||
|
ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
|
||||||
|
ctrl_cfg->flags = 0;
|
||||||
|
|
||||||
|
/* Init TORCH_INTENSITY ctrl data */
|
||||||
|
ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY;
|
||||||
|
ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
|
||||||
|
__lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg);
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
|
||||||
|
ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
|
||||||
|
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
|
||||||
|
|
||||||
|
/* Init INDICATOR_INTENSITY ctrl data */
|
||||||
|
if (v4l2_flash->iled_cdev) {
|
||||||
|
ctrl_init_data[INDICATOR_INTENSITY].cid =
|
||||||
|
V4L2_CID_FLASH_INDICATOR_INTENSITY;
|
||||||
|
ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
|
||||||
|
__lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity,
|
||||||
|
ctrl_cfg);
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
|
||||||
|
ctrl_cfg->min = 0;
|
||||||
|
ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
|
||||||
|
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Init FLASH_STROBE ctrl data */
|
||||||
|
ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE;
|
||||||
|
ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
|
||||||
|
|
||||||
|
/* Init STROBE_STOP ctrl data */
|
||||||
|
ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP;
|
||||||
|
ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
|
||||||
|
|
||||||
|
/* Init FLASH_STROBE_SOURCE ctrl data */
|
||||||
|
if (flash_cfg->has_external_strobe) {
|
||||||
|
mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) |
|
||||||
|
(1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
|
||||||
|
ctrl_init_data[STROBE_SOURCE].cid =
|
||||||
|
V4L2_CID_FLASH_STROBE_SOURCE;
|
||||||
|
ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
|
||||||
|
ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
|
||||||
|
ctrl_cfg->menu_skip_mask = ~mask;
|
||||||
|
ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init STROBE_STATUS ctrl data */
|
||||||
|
if (fled_cdev_ops->strobe_get) {
|
||||||
|
ctrl_init_data[STROBE_STATUS].cid =
|
||||||
|
V4L2_CID_FLASH_STROBE_STATUS;
|
||||||
|
ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
|
||||||
|
ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
|
||||||
|
V4L2_CTRL_FLAG_READ_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init FLASH_TIMEOUT ctrl data */
|
||||||
|
if (fled_cdev_ops->timeout_set) {
|
||||||
|
ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT;
|
||||||
|
ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
|
||||||
|
__lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg);
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init FLASH_INTENSITY ctrl data */
|
||||||
|
if (fled_cdev_ops->flash_brightness_set) {
|
||||||
|
ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY;
|
||||||
|
ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
|
||||||
|
__lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg);
|
||||||
|
ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
|
||||||
|
ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
|
||||||
|
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
|
||||||
|
struct v4l2_flash_config *flash_cfg)
|
||||||
|
|
||||||
|
{
|
||||||
|
struct v4l2_flash_ctrl_data *ctrl_init_data;
|
||||||
|
struct v4l2_ctrl *ctrl;
|
||||||
|
struct v4l2_ctrl_config *ctrl_cfg;
|
||||||
|
int i, ret, num_ctrls = 0;
|
||||||
|
|
||||||
|
v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
|
||||||
|
sizeof(*v4l2_flash->ctrls) *
|
||||||
|
(STROBE_SOURCE + 1), GFP_KERNEL);
|
||||||
|
if (!v4l2_flash->ctrls)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* allocate memory dynamically so as not to exceed stack frame size */
|
||||||
|
ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!ctrl_init_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
__fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_FLASH_CTRLS; ++i)
|
||||||
|
if (ctrl_init_data[i].cid)
|
||||||
|
++num_ctrls;
|
||||||
|
|
||||||
|
v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
|
||||||
|
ctrl_cfg = &ctrl_init_data[i].config;
|
||||||
|
if (!ctrl_init_data[i].cid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
|
||||||
|
ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
|
||||||
|
ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
|
||||||
|
&v4l2_flash_ctrl_ops,
|
||||||
|
ctrl_cfg->id,
|
||||||
|
ctrl_cfg->max,
|
||||||
|
ctrl_cfg->menu_skip_mask,
|
||||||
|
ctrl_cfg->def);
|
||||||
|
else
|
||||||
|
ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
|
||||||
|
&v4l2_flash_ctrl_ops,
|
||||||
|
ctrl_cfg->id,
|
||||||
|
ctrl_cfg->min,
|
||||||
|
ctrl_cfg->max,
|
||||||
|
ctrl_cfg->step,
|
||||||
|
ctrl_cfg->def);
|
||||||
|
|
||||||
|
if (ctrl)
|
||||||
|
ctrl->flags |= ctrl_cfg->flags;
|
||||||
|
|
||||||
|
if (i <= STROBE_SOURCE)
|
||||||
|
v4l2_flash->ctrls[i] = ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(ctrl_init_data);
|
||||||
|
|
||||||
|
if (v4l2_flash->hdl.error) {
|
||||||
|
ret = v4l2_flash->hdl.error;
|
||||||
|
goto error_free_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
|
||||||
|
|
||||||
|
v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_free_handler:
|
||||||
|
v4l2_ctrl_handler_free(&v4l2_flash->hdl);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash)
|
||||||
|
{
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]);
|
||||||
|
|
||||||
|
if (ctrls[INDICATOR_INTENSITY])
|
||||||
|
v4l2_flash_set_led_brightness(v4l2_flash,
|
||||||
|
ctrls[INDICATOR_INTENSITY]);
|
||||||
|
|
||||||
|
if (ctrls[FLASH_TIMEOUT]) {
|
||||||
|
ret = led_set_flash_timeout(fled_cdev,
|
||||||
|
ctrls[FLASH_TIMEOUT]->val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctrls[FLASH_INTENSITY]) {
|
||||||
|
ret = led_set_flash_brightness(fled_cdev,
|
||||||
|
ctrls[FLASH_INTENSITY]->val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For some hardware arrangements setting strobe source may affect
|
||||||
|
* torch mode. Synchronize strobe source setting only if not in torch
|
||||||
|
* mode. For torch mode case it will get synchronized upon switching
|
||||||
|
* to flash mode.
|
||||||
|
*/
|
||||||
|
if (ctrls[STROBE_SOURCE] &&
|
||||||
|
ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
|
||||||
|
ret = call_flash_op(v4l2_flash, external_strobe_set,
|
||||||
|
ctrls[STROBE_SOURCE]->val);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* V4L2 subdev internal operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||||
|
{
|
||||||
|
struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct led_classdev *led_cdev = &fled_cdev->led_cdev;
|
||||||
|
struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
|
||||||
|
struct led_classdev *led_cdev_ind = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!v4l2_fh_is_singular(&fh->vfh))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mutex_lock(&led_cdev->led_access);
|
||||||
|
|
||||||
|
led_sysfs_disable(led_cdev);
|
||||||
|
led_trigger_remove(led_cdev);
|
||||||
|
|
||||||
|
mutex_unlock(&led_cdev->led_access);
|
||||||
|
|
||||||
|
if (iled_cdev) {
|
||||||
|
led_cdev_ind = &iled_cdev->led_cdev;
|
||||||
|
|
||||||
|
mutex_lock(&led_cdev_ind->led_access);
|
||||||
|
|
||||||
|
led_sysfs_disable(led_cdev_ind);
|
||||||
|
led_trigger_remove(led_cdev_ind);
|
||||||
|
|
||||||
|
mutex_unlock(&led_cdev_ind->led_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = __sync_device_with_v4l2_controls(v4l2_flash);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_sync_device;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
out_sync_device:
|
||||||
|
mutex_lock(&led_cdev->led_access);
|
||||||
|
led_sysfs_enable(led_cdev);
|
||||||
|
mutex_unlock(&led_cdev->led_access);
|
||||||
|
|
||||||
|
if (led_cdev_ind) {
|
||||||
|
mutex_lock(&led_cdev_ind->led_access);
|
||||||
|
led_sysfs_enable(led_cdev_ind);
|
||||||
|
mutex_unlock(&led_cdev_ind->led_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||||
|
{
|
||||||
|
struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
|
||||||
|
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
|
||||||
|
struct led_classdev *led_cdev = &fled_cdev->led_cdev;
|
||||||
|
struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!v4l2_fh_is_singular(&fh->vfh))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mutex_lock(&led_cdev->led_access);
|
||||||
|
|
||||||
|
if (v4l2_flash->ctrls[STROBE_SOURCE])
|
||||||
|
ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
|
||||||
|
V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
|
||||||
|
led_sysfs_enable(led_cdev);
|
||||||
|
|
||||||
|
mutex_unlock(&led_cdev->led_access);
|
||||||
|
|
||||||
|
if (iled_cdev) {
|
||||||
|
struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev;
|
||||||
|
|
||||||
|
mutex_lock(&led_cdev_ind->led_access);
|
||||||
|
led_sysfs_enable(led_cdev_ind);
|
||||||
|
mutex_unlock(&led_cdev_ind->led_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
|
||||||
|
.open = v4l2_flash_open,
|
||||||
|
.close = v4l2_flash_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
|
||||||
|
.queryctrl = v4l2_subdev_queryctrl,
|
||||||
|
.querymenu = v4l2_subdev_querymenu,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
|
||||||
|
.core = &v4l2_flash_core_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct v4l2_flash *v4l2_flash_init(
|
||||||
|
struct device *dev, struct device_node *of_node,
|
||||||
|
struct led_classdev_flash *fled_cdev,
|
||||||
|
struct led_classdev_flash *iled_cdev,
|
||||||
|
const struct v4l2_flash_ops *ops,
|
||||||
|
struct v4l2_flash_config *config)
|
||||||
|
{
|
||||||
|
struct v4l2_flash *v4l2_flash;
|
||||||
|
struct led_classdev *led_cdev;
|
||||||
|
struct v4l2_subdev *sd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!fled_cdev || !ops || !config)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
led_cdev = &fled_cdev->led_cdev;
|
||||||
|
|
||||||
|
v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!v4l2_flash)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
sd = &v4l2_flash->sd;
|
||||||
|
v4l2_flash->fled_cdev = fled_cdev;
|
||||||
|
v4l2_flash->iled_cdev = iled_cdev;
|
||||||
|
v4l2_flash->ops = ops;
|
||||||
|
sd->dev = dev;
|
||||||
|
sd->of_node = of_node;
|
||||||
|
v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
|
||||||
|
sd->internal_ops = &v4l2_flash_subdev_internal_ops;
|
||||||
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||||
|
strlcpy(sd->name, config->dev_name, sizeof(sd->name));
|
||||||
|
|
||||||
|
ret = media_entity_init(&sd->entity, 0, NULL, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
|
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
|
||||||
|
|
||||||
|
ret = v4l2_flash_init_controls(v4l2_flash, config);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_init_controls;
|
||||||
|
|
||||||
|
if (sd->of_node)
|
||||||
|
of_node_get(sd->of_node);
|
||||||
|
else
|
||||||
|
of_node_get(led_cdev->dev->of_node);
|
||||||
|
|
||||||
|
ret = v4l2_async_register_subdev(sd);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_async_register_sd;
|
||||||
|
|
||||||
|
return v4l2_flash;
|
||||||
|
|
||||||
|
err_async_register_sd:
|
||||||
|
of_node_put(led_cdev->dev->of_node);
|
||||||
|
v4l2_ctrl_handler_free(sd->ctrl_handler);
|
||||||
|
err_init_controls:
|
||||||
|
media_entity_cleanup(&sd->entity);
|
||||||
|
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(v4l2_flash_init);
|
||||||
|
|
||||||
|
void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
|
||||||
|
{
|
||||||
|
struct v4l2_subdev *sd;
|
||||||
|
struct led_classdev *led_cdev;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(v4l2_flash))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sd = &v4l2_flash->sd;
|
||||||
|
led_cdev = &v4l2_flash->fled_cdev->led_cdev;
|
||||||
|
|
||||||
|
v4l2_async_unregister_subdev(sd);
|
||||||
|
|
||||||
|
if (sd->of_node)
|
||||||
|
of_node_put(sd->of_node);
|
||||||
|
else
|
||||||
|
of_node_put(led_cdev->dev->of_node);
|
||||||
|
|
||||||
|
v4l2_ctrl_handler_free(sd->ctrl_handler);
|
||||||
|
media_entity_cleanup(&sd->entity);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(v4l2_flash_release);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
|
||||||
|
MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -407,6 +407,21 @@ static inline int desc_to_gpio(const struct gpio_desc *desc)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Child properties interface */
|
||||||
|
struct fwnode_handle;
|
||||||
|
|
||||||
|
static inline struct gpio_desc *fwnode_get_named_gpiod(
|
||||||
|
struct fwnode_handle *fwnode, const char *propname)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENOSYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct gpio_desc *devm_get_gpiod_from_child(
|
||||||
|
struct device *dev, const char *con_id, struct fwnode_handle *child)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENOSYS);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_GPIOLIB */
|
#endif /* CONFIG_GPIOLIB */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#ifndef __LINUX_LEDS_H_INCLUDED
|
#ifndef __LINUX_LEDS_H_INCLUDED
|
||||||
#define __LINUX_LEDS_H_INCLUDED
|
#define __LINUX_LEDS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
|
@ -222,6 +223,11 @@ struct led_trigger {
|
||||||
struct list_head next_trig;
|
struct list_head next_trig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count);
|
||||||
|
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
char *buf);
|
||||||
|
|
||||||
/* Registration functions for complex triggers */
|
/* Registration functions for complex triggers */
|
||||||
extern int led_trigger_register(struct led_trigger *trigger);
|
extern int led_trigger_register(struct led_trigger *trigger);
|
||||||
extern void led_trigger_unregister(struct led_trigger *trigger);
|
extern void led_trigger_unregister(struct led_trigger *trigger);
|
||||||
|
@ -238,6 +244,16 @@ extern void led_trigger_blink_oneshot(struct led_trigger *trigger,
|
||||||
unsigned long *delay_on,
|
unsigned long *delay_on,
|
||||||
unsigned long *delay_off,
|
unsigned long *delay_off,
|
||||||
int invert);
|
int invert);
|
||||||
|
extern void led_trigger_set_default(struct led_classdev *led_cdev);
|
||||||
|
extern void led_trigger_set(struct led_classdev *led_cdev,
|
||||||
|
struct led_trigger *trigger);
|
||||||
|
extern void led_trigger_remove(struct led_classdev *led_cdev);
|
||||||
|
|
||||||
|
static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
return led_cdev->trigger_data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* led_trigger_rename_static - rename a trigger
|
* led_trigger_rename_static - rename a trigger
|
||||||
* @name: the new trigger name
|
* @name: the new trigger name
|
||||||
|
@ -267,6 +283,15 @@ static inline void led_trigger_register_simple(const char *name,
|
||||||
static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
|
static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
|
||||||
static inline void led_trigger_event(struct led_trigger *trigger,
|
static inline void led_trigger_event(struct led_trigger *trigger,
|
||||||
enum led_brightness event) {}
|
enum led_brightness event) {}
|
||||||
|
static inline void led_trigger_set_default(struct led_classdev *led_cdev) {}
|
||||||
|
static inline void led_trigger_set(struct led_classdev *led_cdev,
|
||||||
|
struct led_trigger *trigger) {}
|
||||||
|
static inline void led_trigger_remove(struct led_classdev *led_cdev) {}
|
||||||
|
static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_LEDS_TRIGGERS */
|
#endif /* CONFIG_LEDS_TRIGGERS */
|
||||||
|
|
||||||
/* Trigger specific functions */
|
/* Trigger specific functions */
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* V4L2 flash LED sub-device registration helpers.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Samsung Electronics Co., Ltd
|
||||||
|
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _V4L2_FLASH_H
|
||||||
|
#define _V4L2_FLASH_H
|
||||||
|
|
||||||
|
#include <media/v4l2-ctrls.h>
|
||||||
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
struct led_classdev_flash;
|
||||||
|
struct led_classdev;
|
||||||
|
struct v4l2_flash;
|
||||||
|
enum led_brightness;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct v4l2_flash_ctrl_data - flash control initialization data, filled
|
||||||
|
* basing on the features declared by the LED flash
|
||||||
|
* class driver in the v4l2_flash_config
|
||||||
|
* @config: initialization data for a control
|
||||||
|
* @cid: contains v4l2 flash control id if the config
|
||||||
|
* field was initialized, 0 otherwise
|
||||||
|
*/
|
||||||
|
struct v4l2_flash_ctrl_data {
|
||||||
|
struct v4l2_ctrl_config config;
|
||||||
|
u32 cid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct v4l2_flash_ops {
|
||||||
|
/* setup strobing the flash by hardware pin state assertion */
|
||||||
|
int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
|
||||||
|
bool enable);
|
||||||
|
/* convert intensity to brightness in a device specific manner */
|
||||||
|
enum led_brightness (*intensity_to_led_brightness)
|
||||||
|
(struct v4l2_flash *v4l2_flash, s32 intensity);
|
||||||
|
/* convert brightness to intensity in a device specific manner */
|
||||||
|
s32 (*led_brightness_to_intensity)
|
||||||
|
(struct v4l2_flash *v4l2_flash, enum led_brightness);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct v4l2_flash_config - V4L2 Flash sub-device initialization data
|
||||||
|
* @dev_name: the name of the media entity,
|
||||||
|
unique in the system
|
||||||
|
* @torch_intensity: constraints for the LED in torch mode
|
||||||
|
* @indicator_intensity: constraints for the indicator LED
|
||||||
|
* @flash_faults: bitmask of flash faults that the LED flash class
|
||||||
|
device can report; corresponding LED_FAULT* bit
|
||||||
|
definitions are available in the header file
|
||||||
|
<linux/led-class-flash.h>
|
||||||
|
* @has_external_strobe: external strobe capability
|
||||||
|
*/
|
||||||
|
struct v4l2_flash_config {
|
||||||
|
char dev_name[32];
|
||||||
|
struct led_flash_setting torch_intensity;
|
||||||
|
struct led_flash_setting indicator_intensity;
|
||||||
|
u32 flash_faults;
|
||||||
|
unsigned int has_external_strobe:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct v4l2_flash - Flash sub-device context
|
||||||
|
* @fled_cdev: LED flash class device controlled by this sub-device
|
||||||
|
* @iled_cdev: LED class device representing indicator LED associated
|
||||||
|
* with the LED flash class device
|
||||||
|
* @ops: V4L2 specific flash ops
|
||||||
|
* @sd: V4L2 sub-device
|
||||||
|
* @hdl: flash controls handler
|
||||||
|
* @ctrls: array of pointers to controls, whose values define
|
||||||
|
* the sub-device state
|
||||||
|
*/
|
||||||
|
struct v4l2_flash {
|
||||||
|
struct led_classdev_flash *fled_cdev;
|
||||||
|
struct led_classdev_flash *iled_cdev;
|
||||||
|
const struct v4l2_flash_ops *ops;
|
||||||
|
|
||||||
|
struct v4l2_subdev sd;
|
||||||
|
struct v4l2_ctrl_handler hdl;
|
||||||
|
struct v4l2_ctrl **ctrls;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash(
|
||||||
|
struct v4l2_subdev *sd)
|
||||||
|
{
|
||||||
|
return container_of(sd, struct v4l2_flash, sd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c)
|
||||||
|
{
|
||||||
|
return container_of(c->handler, struct v4l2_flash, hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
|
||||||
|
/**
|
||||||
|
* v4l2_flash_init - initialize V4L2 flash led sub-device
|
||||||
|
* @dev: flash device, e.g. an I2C device
|
||||||
|
* @of_node: of_node of the LED, may be NULL if the same as device's
|
||||||
|
* @fled_cdev: LED flash class device to wrap
|
||||||
|
* @iled_cdev: LED flash class device representing indicator LED associated
|
||||||
|
* with fled_cdev, may be NULL
|
||||||
|
* @flash_ops: V4L2 Flash device ops
|
||||||
|
* @config: initialization data for V4L2 Flash sub-device
|
||||||
|
*
|
||||||
|
* Create V4L2 Flash sub-device wrapping given LED subsystem device.
|
||||||
|
*
|
||||||
|
* Returns: A valid pointer, or, when an error occurs, the return
|
||||||
|
* value is encoded using ERR_PTR(). Use IS_ERR() to check and
|
||||||
|
* PTR_ERR() to obtain the numeric return value.
|
||||||
|
*/
|
||||||
|
struct v4l2_flash *v4l2_flash_init(
|
||||||
|
struct device *dev, struct device_node *of_node,
|
||||||
|
struct led_classdev_flash *fled_cdev,
|
||||||
|
struct led_classdev_flash *iled_cdev,
|
||||||
|
const struct v4l2_flash_ops *ops,
|
||||||
|
struct v4l2_flash_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v4l2_flash_release - release V4L2 Flash sub-device
|
||||||
|
* @flash: the V4L2 Flash sub-device to release
|
||||||
|
*
|
||||||
|
* Release V4L2 Flash sub-device.
|
||||||
|
*/
|
||||||
|
void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
|
||||||
|
|
||||||
|
#else
|
||||||
|
static inline struct v4l2_flash *v4l2_flash_init(
|
||||||
|
struct device *dev, struct device_node *of_node,
|
||||||
|
struct led_classdev_flash *fled_cdev,
|
||||||
|
struct led_classdev_flash *iled_cdev,
|
||||||
|
const struct v4l2_flash_ops *ops,
|
||||||
|
struct v4l2_flash_config *config)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
|
||||||
|
|
||||||
|
#endif /* _V4L2_FLASH_H */
|
|
@ -605,6 +605,8 @@ struct v4l2_subdev {
|
||||||
struct video_device *devnode;
|
struct video_device *devnode;
|
||||||
/* pointer to the physical device, if any */
|
/* pointer to the physical device, if any */
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
/* The device_node of the subdev, usually the same as dev->of_node. */
|
||||||
|
struct device_node *of_node;
|
||||||
/* Links this subdev to a global subdev_list or @notifier->done list. */
|
/* Links this subdev to a global subdev_list or @notifier->done list. */
|
||||||
struct list_head async_list;
|
struct list_head async_list;
|
||||||
/* Pointer to respective struct v4l2_async_subdev. */
|
/* Pointer to respective struct v4l2_async_subdev. */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче