LED updates for 5.19. Most significant here is driver for Qualcomm
LPG. Apparently it drives backlight on some boards, so it is quite important for some people. -----BEGIN PGP SIGNATURE----- iF0EABECAB0WIQRPfPO7r0eAhk010v0w5/Bqldv68gUCYpZ0rAAKCRAw5/Bqldv6 8pH4AJ9cxxetf4/p1NtPPUl0eucqDXQlcACgnCWjGLrrjgkkY45OThDLPLbuplg= =ipf/ -----END PGP SIGNATURE----- Merge tag 'leds-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds Pull LED updates from Pavel Machek: "Most significant here is the driver for Qualcomm LPG. Apparently it drives backlight on some boards, so it is quite important for some people" * tag 'leds-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds: leds: qcom-lpg: Require pattern to follow documentation leds: lp50xx: Remove duplicated error reporting in .remove() leds: qcom-lpg: add missing PWM dependency leds: ktd2692: Make aux-gpios optional dt-bindings: leds: convert ktd2692 bindings to yaml leds: ktd2692: Avoid duplicate error messages on probe deferral leds: is31fl32xx: Improve error reporting in .remove() leds: Move pwm-multicolor driver into rgb directory leds: Add PWM multicolor driver dt-bindings: leds: Add multicolor PWM LED bindings dt-bindings: leds: Optional multi-led unit address leds: regulator: Make probeable from device tree leds: regulator: Add dev helper variable dt-bindings: leds: Add regulator-led binding leds: pca9532: Make pca9532_destroy_devices() return void leds: Add pm8350c support to Qualcomm LPG driver dt-bindings: leds: Add pm8350c pmic support leds: Add driver for Qualcomm LPG dt-bindings: leds: Add Qualcomm Light Pulse Generator binding
This commit is contained in:
Коммит
2a5699b0de
|
@ -0,0 +1,87 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/kinetic,ktd2692.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: KTD2692 Flash LED Driver from Kinetic Technologies
|
||||
|
||||
maintainers:
|
||||
- Markuss Broks <markuss.broks@gmail.com>
|
||||
|
||||
description: |
|
||||
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.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: kinetic,ktd2692
|
||||
|
||||
ctrl-gpios:
|
||||
maxItems: 1
|
||||
description: Specifier of the GPIO connected to CTRL pin.
|
||||
|
||||
aux-gpios:
|
||||
maxItems: 1
|
||||
description: Specifier of the GPIO connected to CTRL pin.
|
||||
|
||||
vin-supply:
|
||||
description: LED supply (2.7V to 5.5V).
|
||||
|
||||
led:
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
description: Properties for the LED.
|
||||
properties:
|
||||
function: true
|
||||
color: true
|
||||
flash-max-timeout-us:
|
||||
description: Flash LED maximum timeout.
|
||||
|
||||
led-max-microamp:
|
||||
maximum: 300000
|
||||
description: Minimum Threshold for Timer protection
|
||||
is defined internally (Maximum 300mA).
|
||||
|
||||
flash-max-microamp:
|
||||
maximum: 300000
|
||||
description: Flash LED maximum current
|
||||
Formula - I(uA) = 15000000 / Rset.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- ctrl-gpios
|
||||
- led
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
ktd2692 {
|
||||
compatible = "kinetic,ktd2692";
|
||||
ctrl-gpios = <&gpc0 1 0>;
|
||||
aux-gpios = <&gpc0 2 0>;
|
||||
vin-supply = <&vbat>;
|
||||
|
||||
led {
|
||||
function = LED_FUNCTION_FLASH;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
flash-max-timeout-us = <250000>;
|
||||
flash-max-microamp = <150000>;
|
||||
led-max-microamp = <25000>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -20,7 +20,7 @@ description: |
|
|||
within this documentation directory.
|
||||
|
||||
patternProperties:
|
||||
"^multi-led@([0-9a-f])$":
|
||||
"^multi-led(@[0-9a-f])?$":
|
||||
type: object
|
||||
description: Represents the LEDs that are to be grouped.
|
||||
properties:
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
* 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,79 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/leds-pwm-multicolor.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Multi-color LEDs connected to PWM
|
||||
|
||||
maintainers:
|
||||
- Sven Schwermer <sven.schwermer@disruptive-technologies.com>
|
||||
|
||||
description: |
|
||||
This driver combines several monochrome PWM LEDs into one multi-color
|
||||
LED using the multicolor LED class.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: pwm-leds-multicolor
|
||||
|
||||
multi-led:
|
||||
type: object
|
||||
|
||||
patternProperties:
|
||||
"^led-[0-9a-z]+$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
pwms:
|
||||
maxItems: 1
|
||||
|
||||
pwm-names: true
|
||||
|
||||
color: true
|
||||
|
||||
required:
|
||||
- pwms
|
||||
- color
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: leds-class-multicolor.yaml#
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
led-controller {
|
||||
compatible = "pwm-leds-multicolor";
|
||||
|
||||
multi-led {
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
max-brightness = <65535>;
|
||||
|
||||
led-red {
|
||||
pwms = <&pwm1 0 1000000>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led-green {
|
||||
pwms = <&pwm2 0 1000000>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led-blue {
|
||||
pwms = <&pwm3 0 1000000>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,174 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/leds-qcom-lpg.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Light Pulse Generator
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description: >
|
||||
The Qualcomm Light Pulse Generator consists of three different hardware blocks;
|
||||
a ramp generator with lookup table, the light pulse generator and a three
|
||||
channel current sink. These blocks are found in a wide range of Qualcomm PMICs.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,pm8150b-lpg
|
||||
- qcom,pm8150l-lpg
|
||||
- qcom,pm8350c-pwm
|
||||
- qcom,pm8916-pwm
|
||||
- qcom,pm8941-lpg
|
||||
- qcom,pm8994-lpg
|
||||
- qcom,pmc8180c-lpg
|
||||
- qcom,pmi8994-lpg
|
||||
- qcom,pmi8998-lpg
|
||||
|
||||
"#pwm-cells":
|
||||
const: 2
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
qcom,power-source:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
power-source used to drive the output, as defined in the datasheet.
|
||||
Should be specified if the TRILED block is present
|
||||
enum: [0, 1, 3]
|
||||
|
||||
qcom,dtest:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
description: >
|
||||
A list of integer pairs, where each pair represent the dtest line the
|
||||
particular channel should be connected to and the flags denoting how the
|
||||
value should be outputed, as defined in the datasheet. The number of
|
||||
pairs should be the same as the number of channels.
|
||||
items:
|
||||
items:
|
||||
- description: dtest line to attach
|
||||
- description: flags for the attachment
|
||||
|
||||
multi-led:
|
||||
type: object
|
||||
$ref: leds-class-multicolor.yaml#
|
||||
properties:
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-9a-f]$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-9a-f]$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
|
||||
properties:
|
||||
reg: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
led-controller {
|
||||
compatible = "qcom,pmi8994-lpg";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
qcom,power-source = <1>;
|
||||
|
||||
qcom,dtest = <0 0>,
|
||||
<0 0>,
|
||||
<0 0>,
|
||||
<4 1>;
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
function-enumerator = <1>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
function-enumerator = <0>;
|
||||
default-state = "on";
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
function-enumerator = <2>;
|
||||
};
|
||||
|
||||
led@4 {
|
||||
reg = <4>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
function-enumerator = <3>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
led-controller {
|
||||
compatible = "qcom,pmi8994-lpg";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
qcom,power-source = <1>;
|
||||
|
||||
multi-led {
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
pwm-controller {
|
||||
compatible = "qcom,pm8916-pwm";
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
...
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/regulator-led.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Device Tree Bindings for Regulator LEDs
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
description: |
|
||||
Regulator LEDs are powered by a single regulator such that they can
|
||||
be turned on or off by enabling or disabling the regulator. The available
|
||||
brightness settings will be inferred from the available voltages on the
|
||||
regulator, and any constraints on the voltage or current will need to be
|
||||
specified on the regulator.
|
||||
|
||||
allOf:
|
||||
- $ref: common.yaml#
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: '^led.*$'
|
||||
|
||||
compatible:
|
||||
const: regulator-led
|
||||
|
||||
vled-supply:
|
||||
description:
|
||||
The regulator controlling the current to the LED.
|
||||
|
||||
function: true
|
||||
color: true
|
||||
linux,default-trigger: true
|
||||
default-state: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vled-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
led-heartbeat {
|
||||
compatible = "regulator-led";
|
||||
vled-supply = <®ulator>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
linux,default-trigger = "heartbeat";
|
||||
};
|
||||
...
|
|
@ -0,0 +1,78 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==============================
|
||||
Kernel driver for Qualcomm LPG
|
||||
==============================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The Qualcomm LPG can be found in a variety of Qualcomm PMICs and consists of a
|
||||
number of PWM channels, a programmable pattern lookup table and a RGB LED
|
||||
current sink.
|
||||
|
||||
To facilitate the various use cases, the LPG channels can be exposed as
|
||||
individual LEDs, grouped together as RGB LEDs or otherwise be accessed as PWM
|
||||
channels. The output of each PWM channel is routed to other hardware
|
||||
blocks, such as the RGB current sink, GPIO pins etc.
|
||||
|
||||
The each PWM channel can operate with a period between 27us and 384 seconds and
|
||||
has a 9 bit resolution of the duty cycle.
|
||||
|
||||
In order to provide support for status notifications with the CPU subsystem in
|
||||
deeper idle states the LPG provides pattern support. This consists of a shared
|
||||
lookup table of brightness values and per channel properties to select the
|
||||
range within the table to use, the rate and if the pattern should repeat.
|
||||
|
||||
The pattern for a channel can be programmed using the "pattern" trigger, using
|
||||
the hw_pattern attribute.
|
||||
|
||||
/sys/class/leds/<led>/hw_pattern
|
||||
--------------------------------
|
||||
|
||||
Specify a hardware pattern for a Qualcomm LPG LED.
|
||||
|
||||
The pattern is a series of brightness and hold-time pairs, with the hold-time
|
||||
expressed in milliseconds. The hold time is a property of the pattern and must
|
||||
therefor be identical for each element in the pattern (except for the pauses
|
||||
described below). As the LPG hardware is not able to perform the linear
|
||||
transitions expected by the leds-trigger-pattern format, each entry in the
|
||||
pattern must be followed a zero-length entry of the same brightness.
|
||||
|
||||
Simple pattern::
|
||||
|
||||
"255 500 255 0 0 500 0 0"
|
||||
|
||||
^
|
||||
|
|
||||
255 +----+ +----+
|
||||
| | | | ...
|
||||
0 | +----+ +----
|
||||
+---------------------->
|
||||
0 5 10 15 time (100ms)
|
||||
|
||||
The LPG supports specifying a longer hold-time for the first and last element
|
||||
in the pattern, the so called "low pause" and "high pause".
|
||||
|
||||
Low-pause pattern::
|
||||
|
||||
"255 1000 255 0 0 500 0 0 255 500 255 0 0 500 0 0"
|
||||
|
||||
^
|
||||
|
|
||||
255 +--------+ +----+ +----+ +--------+
|
||||
| | | | | | | | ...
|
||||
0 | +----+ +----+ +----+ +----
|
||||
+----------------------------->
|
||||
0 5 10 15 20 25 time (100ms)
|
||||
|
||||
Similarily, the last entry can be stretched by using a higher hold-time on the
|
||||
last entry.
|
||||
|
||||
In order to save space in the shared lookup table the LPG supports "ping-pong"
|
||||
mode, in which case each run through the pattern is performed by first running
|
||||
the pattern forward, then backwards. This mode is automatically used by the
|
||||
driver when the given pattern is a palindrome. In this case the "high pause"
|
||||
denotes the wait time before the pattern is run in reverse and as such the
|
||||
specified hold-time of the middle item in the pattern is allowed to have a
|
||||
different hold-time.
|
|
@ -869,6 +869,9 @@ source "drivers/leds/blink/Kconfig"
|
|||
comment "Flash and Torch LED drivers"
|
||||
source "drivers/leds/flash/Kconfig"
|
||||
|
||||
comment "RGB LED drivers"
|
||||
source "drivers/leds/rgb/Kconfig"
|
||||
|
||||
comment "LED Triggers"
|
||||
source "drivers/leds/trigger/Kconfig"
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ obj-$(CONFIG_LEDS_USER) += uleds.o
|
|||
# Flash and Torch LED Drivers
|
||||
obj-$(CONFIG_LEDS_CLASS_FLASH) += flash/
|
||||
|
||||
# RGB LED Drivers
|
||||
obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += rgb/
|
||||
|
||||
# LED Triggers
|
||||
obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
|
||||
|
||||
|
|
|
@ -279,17 +279,12 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
|
|||
|
||||
led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
|
||||
ret = PTR_ERR_OR_ZERO(led->ctrl_gpio);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot get ctrl-gpios %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "cannot get ctrl-gpios\n");
|
||||
|
||||
led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS);
|
||||
ret = PTR_ERR_OR_ZERO(led->aux_gpio);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot get aux-gpios %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
led->aux_gpio = devm_gpiod_get_optional(dev, "aux", GPIOD_ASIS);
|
||||
if (IS_ERR(led->aux_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(led->aux_gpio), "cannot get aux-gpios\n");
|
||||
|
||||
led->regulator = devm_regulator_get(dev, "vin");
|
||||
if (IS_ERR(led->regulator))
|
||||
|
|
|
@ -460,8 +460,14 @@ static int is31fl32xx_probe(struct i2c_client *client,
|
|||
static int is31fl32xx_remove(struct i2c_client *client)
|
||||
{
|
||||
struct is31fl32xx_priv *priv = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return is31fl32xx_reset_regs(priv);
|
||||
ret = is31fl32xx_reset_regs(priv);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "Failed to reset registers on removal (%pe)\n",
|
||||
ERR_PTR(ret));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -569,10 +569,8 @@ static int lp50xx_remove(struct i2c_client *client)
|
|||
int ret;
|
||||
|
||||
ret = lp50xx_enable_disable(led, 0);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
dev_err(led->dev, "Failed to disable chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (led->regulator) {
|
||||
ret = regulator_disable(led->regulator);
|
||||
|
|
|
@ -318,13 +318,10 @@ static int pca9532_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
|
|||
}
|
||||
#endif /* CONFIG_LEDS_PCA9532_GPIO */
|
||||
|
||||
static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
|
||||
static void pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
|
||||
{
|
||||
int i = n_devs;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
while (--i >= 0) {
|
||||
switch (data->leds[i].type) {
|
||||
case PCA9532_TYPE_NONE:
|
||||
|
@ -346,8 +343,6 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
|
|||
if (data->gpio.parent)
|
||||
gpiochip_remove(&data->gpio);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca9532_configure(struct i2c_client *client,
|
||||
|
@ -555,7 +550,9 @@ static int pca9532_remove(struct i2c_client *client)
|
|||
{
|
||||
struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
|
||||
return pca9532_destroy_devices(data, data->chip_info->num_leds);
|
||||
pca9532_destroy_devices(data, data->chip_info->num_leds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_i2c_driver(pca9532_driver);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/leds.h>
|
||||
|
@ -123,34 +124,37 @@ static int regulator_led_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct led_regulator_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct led_init_data init_data = {};
|
||||
struct regulator_led *led;
|
||||
struct regulator *vcc;
|
||||
int ret = 0;
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vcc = devm_regulator_get_exclusive(&pdev->dev, "vled");
|
||||
vcc = devm_regulator_get_exclusive(dev, "vled");
|
||||
if (IS_ERR(vcc)) {
|
||||
dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
|
||||
dev_err(dev, "Cannot get vcc\n");
|
||||
return PTR_ERR(vcc);
|
||||
}
|
||||
|
||||
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
|
||||
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||
if (led == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
init_data.fwnode = dev->fwnode;
|
||||
|
||||
led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);
|
||||
if (pdata->brightness > led->cdev.max_brightness) {
|
||||
dev_err(&pdev->dev, "Invalid default brightness %d\n",
|
||||
/* Legacy platform data label assignment */
|
||||
if (pdata) {
|
||||
if (pdata->brightness > led->cdev.max_brightness) {
|
||||
dev_err(dev, "Invalid default brightness %d\n",
|
||||
pdata->brightness);
|
||||
return -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
led->cdev.brightness = pdata->brightness;
|
||||
init_data.default_label = pdata->name;
|
||||
}
|
||||
|
||||
led->cdev.brightness_set_blocking = regulator_led_brightness_set;
|
||||
led->cdev.name = pdata->name;
|
||||
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
led->vcc = vcc;
|
||||
|
||||
|
@ -162,16 +166,10 @@ static int regulator_led_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, led);
|
||||
|
||||
ret = led_classdev_register(&pdev->dev, &led->cdev);
|
||||
ret = led_classdev_register_ext(dev, &led->cdev, &init_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* to expose the default value to userspace */
|
||||
led->cdev.brightness = pdata->brightness;
|
||||
|
||||
/* Set the default led status */
|
||||
regulator_led_brightness_set(&led->cdev, led->cdev.brightness);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -184,10 +182,17 @@ static int regulator_led_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id regulator_led_of_match[] = {
|
||||
{ .compatible = "regulator-led", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, regulator_led_of_match);
|
||||
|
||||
static struct platform_driver regulator_led_driver = {
|
||||
.driver = {
|
||||
.name = "leds-regulator",
|
||||
},
|
||||
.name = "leds-regulator",
|
||||
.of_match_table = regulator_led_of_match,
|
||||
},
|
||||
.probe = regulator_led_probe,
|
||||
.remove = regulator_led_remove,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
if LEDS_CLASS_MULTICOLOR
|
||||
|
||||
config LEDS_PWM_MULTICOLOR
|
||||
tristate "PWM driven multi-color LED Support"
|
||||
depends on PWM
|
||||
help
|
||||
This option enables support for PWM driven monochrome LEDs that are
|
||||
grouped into multicolor LEDs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called leds-pwm-multicolor.
|
||||
|
||||
config LEDS_QCOM_LPG
|
||||
tristate "LED support for Qualcomm LPG"
|
||||
depends on OF
|
||||
depends on PWM
|
||||
depends on SPMI
|
||||
help
|
||||
This option enables support for the Light Pulse Generator found in a
|
||||
wide variety of Qualcomm PMICs. The LPG consists of a number of PWM
|
||||
channels and typically a shared pattern lookup table and a current
|
||||
sink, intended to drive RGB LEDs. Each channel can either be used as
|
||||
a LED, grouped to represent a RGB LED or exposed as PWM channels.
|
||||
|
||||
If compiled as a module, the module will be named leds-qcom-lpg.
|
||||
|
||||
endif # LEDS_CLASS_MULTICOLOR
|
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
|
||||
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
|
|
@ -0,0 +1,186 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* PWM-based multi-color LED control
|
||||
*
|
||||
* Copyright 2022 Sven Schwermer <sven.schwermer@disruptive-technologies.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
struct pwm_led {
|
||||
struct pwm_device *pwm;
|
||||
struct pwm_state state;
|
||||
};
|
||||
|
||||
struct pwm_mc_led {
|
||||
struct led_classdev_mc mc_cdev;
|
||||
struct mutex lock;
|
||||
struct pwm_led leds[];
|
||||
};
|
||||
|
||||
static int led_pwm_mc_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
|
||||
struct pwm_mc_led *priv = container_of(mc_cdev, struct pwm_mc_led, mc_cdev);
|
||||
unsigned long long duty;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
led_mc_calc_color_components(mc_cdev, brightness);
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
for (i = 0; i < mc_cdev->num_colors; i++) {
|
||||
duty = priv->leds[i].state.period;
|
||||
duty *= mc_cdev->subled_info[i].brightness;
|
||||
do_div(duty, cdev->max_brightness);
|
||||
|
||||
priv->leds[i].state.duty_cycle = duty;
|
||||
priv->leds[i].state.enabled = duty > 0;
|
||||
ret = pwm_apply_state(priv->leds[i].pwm,
|
||||
&priv->leds[i].state);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iterate_subleds(struct device *dev, struct pwm_mc_led *priv,
|
||||
struct fwnode_handle *mcnode)
|
||||
{
|
||||
struct mc_subled *subled = priv->mc_cdev.subled_info;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct pwm_led *pwmled;
|
||||
u32 color;
|
||||
int ret;
|
||||
|
||||
/* iterate over the nodes inside the multi-led node */
|
||||
fwnode_for_each_child_node(mcnode, fwnode) {
|
||||
pwmled = &priv->leds[priv->mc_cdev.num_colors];
|
||||
pwmled->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL);
|
||||
if (IS_ERR(pwmled->pwm)) {
|
||||
ret = PTR_ERR(pwmled->pwm);
|
||||
dev_err(dev, "unable to request PWM: %d\n", ret);
|
||||
goto release_fwnode;
|
||||
}
|
||||
pwm_init_state(pwmled->pwm, &pwmled->state);
|
||||
|
||||
ret = fwnode_property_read_u32(fwnode, "color", &color);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot read color: %d\n", ret);
|
||||
goto release_fwnode;
|
||||
}
|
||||
|
||||
subled[priv->mc_cdev.num_colors].color_index = color;
|
||||
priv->mc_cdev.num_colors++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
release_fwnode:
|
||||
fwnode_handle_put(fwnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int led_pwm_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fwnode_handle *mcnode, *fwnode;
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
struct mc_subled *subled;
|
||||
struct pwm_mc_led *priv;
|
||||
int count = 0;
|
||||
int ret = 0;
|
||||
|
||||
mcnode = device_get_named_child_node(&pdev->dev, "multi-led");
|
||||
if (!mcnode)
|
||||
return dev_err_probe(&pdev->dev, -ENODEV,
|
||||
"expected multi-led node\n");
|
||||
|
||||
/* count the nodes inside the multi-led node */
|
||||
fwnode_for_each_child_node(mcnode, fwnode)
|
||||
count++;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, count),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto release_mcnode;
|
||||
}
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
subled = devm_kcalloc(&pdev->dev, count, sizeof(*subled), GFP_KERNEL);
|
||||
if (!subled) {
|
||||
ret = -ENOMEM;
|
||||
goto release_mcnode;
|
||||
}
|
||||
priv->mc_cdev.subled_info = subled;
|
||||
|
||||
/* init the multicolor's LED class device */
|
||||
cdev = &priv->mc_cdev.led_cdev;
|
||||
fwnode_property_read_u32(mcnode, "max-brightness",
|
||||
&cdev->max_brightness);
|
||||
cdev->flags = LED_CORE_SUSPENDRESUME;
|
||||
cdev->brightness_set_blocking = led_pwm_mc_set;
|
||||
|
||||
ret = iterate_subleds(&pdev->dev, priv, mcnode);
|
||||
if (ret)
|
||||
goto release_mcnode;
|
||||
|
||||
init_data.fwnode = mcnode;
|
||||
ret = devm_led_classdev_multicolor_register_ext(&pdev->dev,
|
||||
&priv->mc_cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register multicolor PWM led for %s: %d\n",
|
||||
cdev->name, ret);
|
||||
goto release_mcnode;
|
||||
}
|
||||
|
||||
ret = led_pwm_mc_set(cdev, cdev->brightness);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to set led PWM value for %s: %d",
|
||||
cdev->name, ret);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
|
||||
release_mcnode:
|
||||
fwnode_handle_put(mcnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_pwm_leds_mc_match[] = {
|
||||
{ .compatible = "pwm-leds-multicolor", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_pwm_leds_mc_match);
|
||||
|
||||
static struct platform_driver led_pwm_mc_driver = {
|
||||
.probe = led_pwm_mc_probe,
|
||||
.driver = {
|
||||
.name = "leds_pwm_multicolor",
|
||||
.of_match_table = of_pwm_leds_mc_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(led_pwm_mc_driver);
|
||||
|
||||
MODULE_AUTHOR("Sven Schwermer <sven.schwermer@disruptive-technologies.com>");
|
||||
MODULE_DESCRIPTION("multi-color PWM LED driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:leds-pwm-multicolor");
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче