LEDS for v6.20
- New Support & Features
* Add support for the TI LP5812 4x3 matrix RGB LED driver, including autonomous animation engine
control and extensive scan multiplexing modes.
* Add a new driver for the ams Osram AS3668 4-channel I2C LED controller.
* Extend the is31fl32xx driver to support the is31fl3293 variant, which features 3 channels and
12-bit PWM resolution.
- Improvements & Fixes
* Prevent the ExpressWire KTD2801 chip from entering an undefined state by disabling interrupts
during time-sensitive communication.
* Ensure the Qualcomm LPG driver detects hardware write failures by checking the return value of
regmap_bulk_write() during LUT programming.
* Fix kernel-doc warnings in the lm3692x driver by documenting missing struct members and
standardizing the comment style.
* Update the ExpressWire library to use fsleep() and unexport internal-only functions.
* Improve the is31fl32xx driver by reordering code to eliminate unnecessary forward declarations.
- Cleanups & Refactoring
* Simplify the LP55XX common LED driver by utilizing the for_each_available_child_of_node_scoped()
macro for more concise node iteration.
- Device Tree Bindings Updates
* Add new YAML bindings for the TI LP5860 and LP5812 LED controllers, and the ams Osram AS3668.
* Convert the TI LM3697 white LED driver binding to DT schema format.
* Allow multicolor LED nodes to be named with numeric suffixes (e.g., multi-led-0) to handle
multiple instances without unit addresses.
* Document support for the PMH0101 variant in the Qualcomm LPG PWM and SPMI Flash LED bindings.
* Add the issi,is31fl3293 compatible string to the is31fl32xx binding.
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmmN1yEACgkQUa+KL4f8
d2Fk8w/+NbgkKGY/B8j7f78Tlriy9QvZLXFVwj7SvlKBgMWlH2WwaEEfj7qTQntr
ZheZsY9eU1rAT78zCOuPLIRujLSCurVSaX77YWKvgl3/j3KVC9jHkEA0ShftNGeL
RXR0/dnzrPhfyrRLpIXvBY4QPHG4/CBpxhdvYsaxQ0+0Jl6jN262xdPS4MZjNJB/
MPM01Bo3bXMUE3i8feiGUNqMe0yitHRqw8yfNEUo4nBnOkmT0WbGnatW/ifiWos8
3q+XQDgj36eU9uCnTiXLbT/YMGPC4ZvZxy96MaCLWnGabaVE4XWwtHCzKoHdXpCG
6g8FcAapg2XK+iNqmThbFjIhzk048bIQkDNuM/FGTUNsi7gUDODfAWbT133pB/gx
v8moBUNo9csFCctM4BI4Is/xkmzrEzQQK3OQ7KZw1XymoC66vi0cc6mCzetwevAM
08TTQmtlDdQGFHFrBNWvx0AXwe8uHDv154lE7taEx5d2by+eBi1Eh5nCum4346N0
0jssEjwT78eFuwAV0pZW5rHvH5kchKnoWfTS+zpuPSUKTW5jTkUBoVcywu/YTm0e
X1EkxJxZ/fQJvcL79QSqKnex1jXFC/a+D/81+piJZrQajkGFeutBQ28Wh0ka/n0j
AjyxNAob5nGt65az7bpG26LIcXpXfdTwlmcKA3q5rOU4u0dmvsI=
=IyMQ
-----END PGP SIGNATURE-----
Merge tag 'leds-next-6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
Pull LED updates from Lee Jones:
"New Support & Features:
- Add support for the TI LP5812 4x3 matrix RGB LED driver, including
autonomous animation engine control and extensive scan multiplexing
modes
- Add a new driver for the ams Osram AS3668 4-channel I2C LED
controller
- Extend the is31fl32xx driver to support the is31fl3293 variant,
which features 3 channels and 12-bit PWM resolution
Improvements & Fixes:
- Prevent the ExpressWire KTD2801 chip from entering an undefined
state by disabling interrupts during time-sensitive communication
- Ensure the Qualcomm LPG driver detects hardware write failures by
checking the return value of regmap_bulk_write() during LUT
programming
- Fix kernel-doc warnings in the lm3692x driver by documenting
missing struct members and standardizing the comment style
- Update the ExpressWire library to use fsleep() and unexport
internal-only functions
- Improve the is31fl32xx driver by reordering code to eliminate
unnecessary forward declarations
Cleanups & Refactoring:
- Simplify the LP55XX common LED driver by utilizing the
for_each_available_child_of_node_scoped() macro for more concise
node iteration
Device Tree Bindings Updates:
- Add new YAML bindings for the TI LP5860 and LP5812 LED controllers,
and the ams Osram AS3668
- Convert the TI LM3697 white LED driver binding to DT schema format
- Allow multicolor LED nodes to be named with numeric suffixes (e.g.,
multi-led-0) to handle multiple instances without unit addresses
- Document support for the PMH0101 variant in the Qualcomm LPG PWM
and SPMI Flash LED bindings
- Add the issi,is31fl3293 compatible string to the is31fl32xx
binding"
* tag 'leds-next-6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds:
dt-bindings: leds: Convert ti,lm3697 to DT schema
leds: as3668: Driver for the ams Osram 4-channel i2c LED driver
dt-bindings: leds: Add new as3668 support
docs: leds: Document TI LP5812 LED driver
leds: Add basic support for TI/National Semiconductor LP5812 LED Driver
leds: qcom-lpg: Check the return value of regmap_bulk_write()
dt-bindings: leds: qcom,spmi-flash-led: Add PMH0101 compatible
dt-bindings: leds: leds-qcom-lpg: Add support for PMH0101 PWM
dt-bindings: leds: Allow differently named multicolor LEDs
leds: lp55xx: Simplify with scoped for each OF child loop
dt-bindings: leds: add TI/National Semiconductor LP5812 LED Driver
leds: is31f132xx: Add support for is31fl3293
leds: is31f132xx: Re-order code to remove forward declarations
dt-bindings: leds: Add issi,is31fl3293 to leds-is31fl32xx
leds: expresswire: Fix chip state breakage
dt-bindings: leds: Add LP5860 LED controller
leds: lm3692x: Fix kernel-doc for struct lm3692x_led
master
commit
2228d9cf7a
|
|
@ -0,0 +1,74 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/ams,as3668.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Osram 4-channel i2c LED driver
|
||||
|
||||
maintainers:
|
||||
- Lukas Timmermann <linux@timmermann.space>
|
||||
|
||||
description:
|
||||
This IC can drive up to four separate LEDs.
|
||||
Having four channels suggests it could be used with a single RGBW LED.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ams,as3668
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-3]$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@42 {
|
||||
compatible = "ams,as3668";
|
||||
reg = <0x42>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <0x1>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ description: |
|
|||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^multi-led(@[0-9a-f])?$"
|
||||
pattern: "^multi-led(@[0-9a-f]|-[0-9]+)?$"
|
||||
|
||||
color:
|
||||
description: |
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ Required properties:
|
|||
issi,is31fl3235
|
||||
issi,is31fl3218
|
||||
issi,is31fl3216
|
||||
issi,is31fl3293
|
||||
si-en,sn3218
|
||||
si-en,sn3216
|
||||
- reg: I2C slave address
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
* Texas Instruments - LM3697 Highly Efficient White LED Driver
|
||||
|
||||
The LM3697 11-bit LED driver provides high-
|
||||
performance backlight dimming for 1, 2, or 3 series
|
||||
LED strings while delivering up to 90% efficiency.
|
||||
|
||||
This device is suitable for display and keypad lighting
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
"ti,lm3697"
|
||||
- reg : I2C slave address
|
||||
- #address-cells : 1
|
||||
- #size-cells : 0
|
||||
|
||||
Optional properties:
|
||||
- enable-gpios : GPIO pin to enable/disable the device
|
||||
- vled-supply : LED supply
|
||||
|
||||
Required child properties:
|
||||
- reg : 0 - LED is Controlled by bank A
|
||||
1 - LED is Controlled by bank B
|
||||
- led-sources : Indicates which HVLED string is associated to which
|
||||
control bank. This is a zero based property so
|
||||
HVLED1 = 0, HVLED2 = 1, HVLED3 = 2.
|
||||
Additional information is contained
|
||||
in Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Optional child properties:
|
||||
- ti,brightness-resolution - see Documentation/devicetree/bindings/mfd/ti-lmu.txt
|
||||
- ramp-up-us: see Documentation/devicetree/bindings/mfd/ti-lmu.txt
|
||||
- ramp-down-us: see Documentation/devicetree/bindings/mfd/ti-lmu.txt
|
||||
- label : see Documentation/devicetree/bindings/leds/common.txt
|
||||
- linux,default-trigger :
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Example:
|
||||
|
||||
HVLED string 1 and 3 are controlled by control bank A and HVLED 2 string is
|
||||
controlled by control bank B.
|
||||
|
||||
led-controller@36 {
|
||||
compatible = "ti,lm3697";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x36>;
|
||||
|
||||
enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
|
||||
vled-supply = <&vbatt>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
led-sources = <0 2>;
|
||||
ti,brightness-resolution = <2047>;
|
||||
ramp-up-us = <5000>;
|
||||
ramp-down-us = <1000>;
|
||||
label = "white:first_backlight_cluster";
|
||||
linux,default-trigger = "backlight";
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
led-sources = <1>;
|
||||
ti,brightness-resolution = <255>;
|
||||
ramp-up-us = <500>;
|
||||
ramp-down-us = <1000>;
|
||||
label = "white:second_backlight_cluster";
|
||||
linux,default-trigger = "backlight";
|
||||
};
|
||||
}
|
||||
|
||||
For more product information please see the link below:
|
||||
https://www.ti.com/lit/ds/symlink/lm3697.pdf
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/leds-lp5860.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LED driver for LP5860 RGB LED from Texas Instruments.
|
||||
|
||||
maintainers:
|
||||
- Steffen Trumtrar <kernel@pengutronix.de>
|
||||
|
||||
description: |
|
||||
The LP5860 is multi-channel, I2C and SPI RGB LED Driver that can group RGB LEDs
|
||||
into a LED group or control them individually.
|
||||
|
||||
For more product information please see the link below:
|
||||
https://www.ti.com/lit/ds/symlink/lp5860.pdf
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,lp5860
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^multi-led@[0-9a-f]+$':
|
||||
type: object
|
||||
$ref: leds-class-multicolor.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 198
|
||||
description:
|
||||
This property denotes the LED module number that is used
|
||||
for the child node.
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@0 {
|
||||
compatible = "ti,lp5860";
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
multi-led@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <0x1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -43,6 +43,7 @@ properties:
|
|||
- items:
|
||||
- enum:
|
||||
- qcom,pm8550-pwm
|
||||
- qcom,pmh0101-pwm
|
||||
- const: qcom,pm8350c-pwm
|
||||
- items:
|
||||
- enum:
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ properties:
|
|||
- qcom,pm8150l-flash-led
|
||||
- qcom,pm8350c-flash-led
|
||||
- qcom,pm8550-flash-led
|
||||
- qcom,pmh0101-flash-led
|
||||
- qcom,pmi8998-flash-led
|
||||
- const: qcom,spmi-flash-led
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/ti,lm3697.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI LM3697 Highly Efficient White LED Driver
|
||||
|
||||
maintainers:
|
||||
- Dan Murphy <dmurphy@ti.com>
|
||||
|
||||
description: >
|
||||
The LM3697 11-bit LED driver provides high-performance backlight dimming for
|
||||
1, 2, or 3 series LED strings while delivering up to 90% efficiency.
|
||||
|
||||
This device is suitable for display and keypad lighting.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,lm3697
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
enable-gpios:
|
||||
description: GPIO pin to enable or disable the device.
|
||||
maxItems: 1
|
||||
|
||||
vled-supply:
|
||||
description: LED supply for the device.
|
||||
|
||||
patternProperties:
|
||||
'^led@[01]$':
|
||||
description: LED control bank nodes.
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Control bank selection (0 = bank A, 1 = bank B).
|
||||
maximum: 1
|
||||
|
||||
led-sources:
|
||||
description: >
|
||||
HVLED strings associated with this control bank:
|
||||
|
||||
0 - HVLED1
|
||||
1 - HVLED2
|
||||
2 - HVLED3
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
items:
|
||||
maximum: 2
|
||||
|
||||
ti,brightness-resolution:
|
||||
description: Brightness resolution for the LED string.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 2047
|
||||
|
||||
ramp-up-us:
|
||||
description: Ramp-up time in microseconds.
|
||||
minimum: 117
|
||||
maximum: 2048
|
||||
|
||||
ramp-down-us:
|
||||
description: Ramp-down time in microseconds.
|
||||
minimum: 117
|
||||
maximum: 2048
|
||||
|
||||
required:
|
||||
- reg
|
||||
- led-sources
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@36 {
|
||||
compatible = "ti,lm3697";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x36>;
|
||||
|
||||
enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
|
||||
vled-supply = <&vbatt>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
led-sources = <0 2>;
|
||||
ti,brightness-resolution = <2047>;
|
||||
ramp-up-us = <500>;
|
||||
ramp-down-us = <1000>;
|
||||
label = "white:first_backlight_cluster";
|
||||
linux,default-trigger = "backlight";
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
led-sources = <1>;
|
||||
ti,brightness-resolution = <255>;
|
||||
ramp-up-us = <500>;
|
||||
ramp-down-us = <1000>;
|
||||
label = "white:second_backlight_cluster";
|
||||
linux,default-trigger = "backlight";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/ti,lp5812.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI LP5812 4x3 Matrix RGB LED Driver with Autonomous Control
|
||||
|
||||
maintainers:
|
||||
- Nam Tran <trannamatk@gmail.com>
|
||||
|
||||
description: |
|
||||
The LP5812 is a 4x3 matrix RGB LED driver with I2C interface
|
||||
and autonomous animation engine control.
|
||||
For more product information please see the link below:
|
||||
https://www.ti.com/product/LP5812#tech-docs
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,lp5812
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
ti,scan-mode:
|
||||
description: |
|
||||
Selects the LED scan mode of the LP5812. The device supports
|
||||
three modes:
|
||||
- Direct-drive mode (by default if 'ti,scan-mode' is omitted)
|
||||
drives up to 4 LEDs directly by internal current sinks (LED0-LED3).
|
||||
- TCM-drive mode ("tcm:<n>:<order...>") drives up to 12 LEDs
|
||||
(4 RGB) using 1-4 scan multiplexing. The <n> specifies the number
|
||||
of scans (1-4), and <order...> defines the scan order of the outputs.
|
||||
- Mix-drive mode ("mix:<n>:<direct>:<order...>") combines
|
||||
direct-drive and TCM-drive outputs. The <n> specifies the number
|
||||
of scans, <direct> selects the direct-drive outputs, and <order...>
|
||||
defines the scan order.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
pattern: '^(tcm|mix):[1-4](:[0-3]){1,4}$'
|
||||
|
||||
vcc-supply:
|
||||
description: Regulator providing power to the 'VCC' pin.
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-3]$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
"^multi-led@[4-7]$":
|
||||
type: object
|
||||
$ref: leds-class-multicolor.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 4
|
||||
maximum: 7
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[4-9a-f]$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 4
|
||||
maximum: 15
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@1b {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "ti,lp5812";
|
||||
reg = <0x1b>;
|
||||
ti,scan-mode = "tcm:4:0:1:2:3";
|
||||
vcc-supply = <&vdd_3v3_reg>;
|
||||
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
label = "LED0";
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <0x1>;
|
||||
label = "LED1";
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <0x2>;
|
||||
label = "LED2";
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <0x3>;
|
||||
label = "LED3";
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
multi-led@4 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x4>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
label = "LED_A";
|
||||
|
||||
led@4 {
|
||||
reg = <0x4>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@5 {
|
||||
reg = <0x5>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@6 {
|
||||
reg = <0x6>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
};
|
||||
|
||||
multi-led@5 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x5>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
label = "LED_B";
|
||||
|
||||
led@7 {
|
||||
reg = <0x7>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@8 {
|
||||
reg = <0x8>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@9 {
|
||||
reg = <0x9>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
};
|
||||
|
||||
multi-led@6 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x6>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
label = "LED_C";
|
||||
|
||||
led@a {
|
||||
reg = <0xa>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@b {
|
||||
reg = <0xb>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@c {
|
||||
reg = <0xc>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
};
|
||||
|
||||
multi-led@7 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x7>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
label = "LED_D";
|
||||
|
||||
led@d {
|
||||
reg = <0xd>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@e {
|
||||
reg = <0xe>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
|
||||
led@f {
|
||||
reg = <0xf>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
led-max-microamp = <25500>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -25,6 +25,7 @@ LEDs
|
|||
leds-lp5523
|
||||
leds-lp5562
|
||||
leds-lp55xx
|
||||
leds-lp5812
|
||||
leds-mlxcpld
|
||||
leds-mt6370-rgb
|
||||
leds-sc27xx
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
========================
|
||||
Kernel driver for lp5812
|
||||
========================
|
||||
|
||||
* TI/National Semiconductor LP5812 LED Driver
|
||||
* Datasheet: https://www.ti.com/product/LP5812#tech-docs
|
||||
|
||||
Authors: Jared Zhou <jared-zhou@ti.com>
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The LP5812 is a 4x3 matrix LED driver with support for both manual and
|
||||
autonomous animation control. This driver provides sysfs interfaces to
|
||||
control and configure the LP5812 device and its LED channels.
|
||||
|
||||
Sysfs Interface
|
||||
===============
|
||||
|
||||
This driver uses the standard multicolor LED class interfaces defined
|
||||
in Documentation/ABI/testing/sysfs-class-led-multicolor.rst.
|
||||
|
||||
Each LP5812 LED output appears under ``/sys/class/leds/`` with its
|
||||
assigned label (for example ``LED_A``).
|
||||
|
||||
The following attributes are exposed:
|
||||
- multi_intensity: Per-channel RGB intensity control
|
||||
- brightness: Standard brightness control (0-255)
|
||||
|
||||
Autonomous Control Modes
|
||||
========================
|
||||
|
||||
The driver also supports autonomous control through pattern configuration
|
||||
(e.g., direct, tcmscan, or mixscan modes) defined in the device tree.
|
||||
When configured, the LP5812 can generate transitions and color effects
|
||||
without CPU intervention.
|
||||
|
||||
Refer to the device tree binding document for valid mode strings and
|
||||
configuration examples.
|
||||
|
||||
Example Usage
|
||||
=============
|
||||
|
||||
To control LED_A::
|
||||
# Set RGB intensity (R=50, G=50, B=50)
|
||||
echo 50 50 50 > /sys/class/leds/LED_A/multi_intensity
|
||||
# Set overall brightness to maximum
|
||||
echo 255 > /sys/class/leds/LED_A/brightness
|
||||
18
MAINTAINERS
18
MAINTAINERS
|
|
@ -3806,6 +3806,13 @@ L: linux-leds@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/leds/flash/leds-as3645a.c
|
||||
|
||||
AS3668 LED DRIVER
|
||||
M: Lukas Timmermann <linux@timmermann.space>
|
||||
L: linux-leds@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/leds/ams,as3668.yaml
|
||||
F: drivers/leds/leds-as3668.c
|
||||
|
||||
ASAHI KASEI AK7375 LENS VOICE COIL DRIVER
|
||||
M: Tianshu Qiu <tian.shu.qiu@intel.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
@ -26094,6 +26101,17 @@ S: Supported
|
|||
F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml
|
||||
F: drivers/iio/dac/ti-dac7612.c
|
||||
|
||||
TEXAS INSTRUMENTS' LP5812 RGB LED DRIVER
|
||||
M: Nam Tran <trannamatk@gmail.com>
|
||||
L: linux-leds@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/leds/ti,lp5812.yaml
|
||||
F: Documentation/leds/leds-lp5812.rst
|
||||
F: drivers/leds/rgb/Kconfig
|
||||
F: drivers/leds/rgb/Makefile
|
||||
F: drivers/leds/rgb/leds-lp5812.c
|
||||
F: drivers/leds/rgb/leds-lp5812.h
|
||||
|
||||
TEXAS INSTRUMENTS' LB8864 LED BACKLIGHT DRIVER
|
||||
M: Alexander Sverdlin <alexander.sverdlin@siemens.com>
|
||||
L: linux-leds@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -107,6 +107,19 @@ config LEDS_ARIEL
|
|||
|
||||
Say Y to if your machine is a Dell Wyse 3020 thin client.
|
||||
|
||||
config LEDS_OSRAM_AMS_AS3668
|
||||
tristate "LED support for Osram AMS AS3668"
|
||||
depends on LEDS_CLASS
|
||||
depends on I2C
|
||||
help
|
||||
This option enables support for the Osram AMS AS3668 LED controller.
|
||||
The AS3668 provides up to four LED channels and is controlled via
|
||||
the I2C bus. This driver offers basic brightness control for each
|
||||
channel, without support for blinking or other advanced features.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called leds-as3668.
|
||||
|
||||
config LEDS_AW200XX
|
||||
tristate "LED support for Awinic AW20036/AW20054/AW20072/AW20108"
|
||||
depends on LEDS_CLASS
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
|
|||
obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
|
||||
obj-$(CONFIG_LEDS_APU) += leds-apu.o
|
||||
obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o
|
||||
obj-$(CONFIG_LEDS_AS3668) += leds-as3668.o
|
||||
obj-$(CONFIG_LEDS_AW200XX) += leds-aw200xx.o
|
||||
obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o
|
||||
obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Osram AMS AS3668 LED Driver IC
|
||||
*
|
||||
* Copyright (C) 2025 Lukas Timmermann <linux@timmermann.space>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uleds.h>
|
||||
|
||||
#define AS3668_MAX_LEDS 4
|
||||
|
||||
/* Chip Ident */
|
||||
|
||||
#define AS3668_CHIP_ID1_REG 0x3e
|
||||
#define AS3668_CHIP_ID 0xa5
|
||||
|
||||
/* Current Control */
|
||||
|
||||
#define AS3668_CURR_MODE_REG 0x01
|
||||
#define AS3668_CURR_MODE_OFF 0x0
|
||||
#define AS3668_CURR_MODE_ON 0x1
|
||||
#define AS3668_CURR1_MODE_MASK GENMASK(1, 0)
|
||||
#define AS3668_CURR2_MODE_MASK GENMASK(3, 2)
|
||||
#define AS3668_CURR3_MODE_MASK GENMASK(5, 4)
|
||||
#define AS3668_CURR4_MODE_MASK GENMASK(7, 6)
|
||||
#define AS3668_CURR1_REG 0x02
|
||||
#define AS3668_CURR2_REG 0x03
|
||||
#define AS3668_CURR3_REG 0x04
|
||||
#define AS3668_CURR4_REG 0x05
|
||||
|
||||
#define AS3668_CURR_MODE_PACK(mode) (((mode) << 0) | \
|
||||
((mode) << 2) | \
|
||||
((mode) << 4) | \
|
||||
((mode) << 6))
|
||||
|
||||
struct as3668_led {
|
||||
struct led_classdev cdev;
|
||||
struct as3668 *chip;
|
||||
struct fwnode_handle *fwnode;
|
||||
u8 mode_mask;
|
||||
u8 current_reg;
|
||||
};
|
||||
|
||||
struct as3668 {
|
||||
struct i2c_client *client;
|
||||
struct as3668_led leds[AS3668_MAX_LEDS];
|
||||
};
|
||||
|
||||
static int as3668_channel_mode_set(struct as3668_led *led, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
u8 channel_modes;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(led->chip->client, AS3668_CURR_MODE_REG);
|
||||
if (ret < 0) {
|
||||
dev_err(led->cdev.dev, "failed to read channel modes\n");
|
||||
return ret;
|
||||
}
|
||||
channel_modes = (u8)ret;
|
||||
|
||||
channel_modes &= ~led->mode_mask;
|
||||
channel_modes |= led->mode_mask & (AS3668_CURR_MODE_PACK(mode));
|
||||
|
||||
return i2c_smbus_write_byte_data(led->chip->client, AS3668_CURR_MODE_REG, channel_modes);
|
||||
}
|
||||
|
||||
static enum led_brightness as3668_brightness_get(struct led_classdev *cdev)
|
||||
{
|
||||
struct as3668_led *led = container_of(cdev, struct as3668_led, cdev);
|
||||
|
||||
return i2c_smbus_read_byte_data(led->chip->client, led->current_reg);
|
||||
}
|
||||
|
||||
static void as3668_brightness_set(struct led_classdev *cdev, enum led_brightness brightness)
|
||||
{
|
||||
struct as3668_led *led = container_of(cdev, struct as3668_led, cdev);
|
||||
int err;
|
||||
|
||||
err = as3668_channel_mode_set(led, !!brightness);
|
||||
if (err)
|
||||
dev_err(cdev->dev, "failed to set channel mode: %d\n", err);
|
||||
|
||||
err = i2c_smbus_write_byte_data(led->chip->client, led->current_reg, brightness);
|
||||
if (err)
|
||||
dev_err(cdev->dev, "failed to set brightness: %d\n", err);
|
||||
}
|
||||
|
||||
static int as3668_dt_init(struct as3668 *as3668)
|
||||
{
|
||||
struct device *dev = &as3668->client->dev;
|
||||
struct as3668_led *led;
|
||||
struct led_init_data init_data = {};
|
||||
int err;
|
||||
u32 reg;
|
||||
|
||||
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
|
||||
err = of_property_read_u32(child, "reg", ®);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to read 'reg' property");
|
||||
|
||||
if (reg < 0 || reg >= AS3668_MAX_LEDS)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"unsupported LED: %d\n", reg);
|
||||
|
||||
led = &as3668->leds[reg];
|
||||
led->fwnode = of_fwnode_handle(child);
|
||||
|
||||
led->current_reg = reg + AS3668_CURR1_REG;
|
||||
led->mode_mask = AS3668_CURR1_MODE_MASK << (reg * 2);
|
||||
led->chip = as3668;
|
||||
|
||||
led->cdev.max_brightness = U8_MAX;
|
||||
led->cdev.brightness_get = as3668_brightness_get;
|
||||
led->cdev.brightness_set = as3668_brightness_set;
|
||||
|
||||
init_data.fwnode = led->fwnode;
|
||||
init_data.default_label = ":";
|
||||
|
||||
err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to register LED %d\n", reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int as3668_probe(struct i2c_client *client)
|
||||
{
|
||||
struct as3668 *as3668;
|
||||
int err;
|
||||
u8 chip_id;
|
||||
|
||||
chip_id = i2c_smbus_read_byte_data(client, AS3668_CHIP_ID1_REG);
|
||||
if (chip_id != AS3668_CHIP_ID)
|
||||
return dev_err_probe(&client->dev, -ENODEV,
|
||||
"expected chip ID 0x%02x, got 0x%02x\n",
|
||||
AS3668_CHIP_ID, chip_id);
|
||||
|
||||
as3668 = devm_kzalloc(&client->dev, sizeof(*as3668), GFP_KERNEL);
|
||||
if (!as3668)
|
||||
return -ENOMEM;
|
||||
|
||||
as3668->client = client;
|
||||
|
||||
err = as3668_dt_init(as3668);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set all four channel modes to 'off' */
|
||||
err = i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG,
|
||||
FIELD_PREP(AS3668_CURR1_MODE_MASK, AS3668_CURR_MODE_OFF) |
|
||||
FIELD_PREP(AS3668_CURR2_MODE_MASK, AS3668_CURR_MODE_OFF) |
|
||||
FIELD_PREP(AS3668_CURR3_MODE_MASK, AS3668_CURR_MODE_OFF) |
|
||||
FIELD_PREP(AS3668_CURR4_MODE_MASK, AS3668_CURR_MODE_OFF));
|
||||
|
||||
/* Set initial currents to 0mA */
|
||||
err |= i2c_smbus_write_byte_data(client, AS3668_CURR1_REG, 0);
|
||||
err |= i2c_smbus_write_byte_data(client, AS3668_CURR2_REG, 0);
|
||||
err |= i2c_smbus_write_byte_data(client, AS3668_CURR3_REG, 0);
|
||||
err |= i2c_smbus_write_byte_data(client, AS3668_CURR4_REG, 0);
|
||||
|
||||
if (err)
|
||||
return dev_err_probe(&client->dev, -EIO, "failed to set zero initial current levels\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void as3668_remove(struct i2c_client *client)
|
||||
{
|
||||
i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG, 0);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id as3668_idtable[] = {
|
||||
{ "as3668" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, as3668_idtable);
|
||||
|
||||
static const struct of_device_id as3668_match_table[] = {
|
||||
{ .compatible = "ams,as3668" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, as3668_match_table);
|
||||
|
||||
static struct i2c_driver as3668_driver = {
|
||||
.driver = {
|
||||
.name = "leds_as3668",
|
||||
.of_match_table = as3668_match_table,
|
||||
},
|
||||
.probe = as3668_probe,
|
||||
.remove = as3668_remove,
|
||||
.id_table = as3668_idtable,
|
||||
};
|
||||
module_i2c_driver(as3668_driver);
|
||||
|
||||
MODULE_AUTHOR("Lukas Timmermann <linux@timmermann.space>");
|
||||
MODULE_DESCRIPTION("AS3668 LED driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/leds-expresswire.h>
|
||||
|
|
@ -16,37 +17,41 @@
|
|||
void expresswire_power_off(struct expresswire_common_props *props)
|
||||
{
|
||||
gpiod_set_value_cansleep(props->ctrl_gpio, 0);
|
||||
usleep_range(props->timing.poweroff_us, props->timing.poweroff_us * 2);
|
||||
fsleep(props->timing.poweroff_us);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(expresswire_power_off, "EXPRESSWIRE");
|
||||
|
||||
void expresswire_enable(struct expresswire_common_props *props)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
gpiod_set_value(props->ctrl_gpio, 1);
|
||||
udelay(props->timing.detect_delay_us);
|
||||
gpiod_set_value(props->ctrl_gpio, 0);
|
||||
udelay(props->timing.detect_us);
|
||||
gpiod_set_value(props->ctrl_gpio, 1);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(expresswire_enable, "EXPRESSWIRE");
|
||||
|
||||
void expresswire_start(struct expresswire_common_props *props)
|
||||
static void expresswire_start(struct expresswire_common_props *props)
|
||||
{
|
||||
gpiod_set_value(props->ctrl_gpio, 1);
|
||||
udelay(props->timing.data_start_us);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(expresswire_start, "EXPRESSWIRE");
|
||||
|
||||
void expresswire_end(struct expresswire_common_props *props)
|
||||
static void expresswire_end(struct expresswire_common_props *props)
|
||||
{
|
||||
gpiod_set_value(props->ctrl_gpio, 0);
|
||||
udelay(props->timing.end_of_data_low_us);
|
||||
gpiod_set_value(props->ctrl_gpio, 1);
|
||||
udelay(props->timing.end_of_data_high_us);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(expresswire_end, "EXPRESSWIRE");
|
||||
|
||||
void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
|
||||
static void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
|
||||
{
|
||||
if (bit) {
|
||||
gpiod_set_value(props->ctrl_gpio, 0);
|
||||
|
|
@ -60,13 +65,18 @@ void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
|
|||
udelay(props->timing.short_bitset_us);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(expresswire_set_bit, "EXPRESSWIRE");
|
||||
|
||||
void expresswire_write_u8(struct expresswire_common_props *props, u8 val)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
expresswire_start(props);
|
||||
for (int i = 7; i >= 0; i--)
|
||||
expresswire_set_bit(props, val & BIT(i));
|
||||
expresswire_end(props);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(expresswire_write_u8, "EXPRESSWIRE");
|
||||
|
|
|
|||
|
|
@ -34,10 +34,26 @@
|
|||
|
||||
#define IS31FL32XX_PWM_FREQUENCY_22KHZ 0x01
|
||||
|
||||
/* Registers for IS31FL3293 */
|
||||
#define IS31FL3293_SHUTDOWN_REG 0x01
|
||||
#define IS31FL3293_SHUTDOWN_SSD_DISABLE BIT(0)
|
||||
#define IS31FL3293_SHUTDOWN_EN1 BIT(4)
|
||||
#define IS31FL3293_SHUTDOWN_EN2 BIT(5)
|
||||
#define IS31FL3293_SHUTDOWN_EN3 BIT(6)
|
||||
#define IS31FL3293_GCC_REG 0x03
|
||||
#define IS31FL3293_GCC_LEVEL_MAX 0x3f
|
||||
#define IS31FL3293_CL_REG 0x10
|
||||
#define IS31FL3293_COLOR_UPDATE_REG 0x27
|
||||
#define IS31FL3293_COLOR_UPDATE_MAGIC 0xc5
|
||||
#define IS31FL3293_RESET_REG 0x3c
|
||||
#define IS31FL3293_RESET_MAGIC 0xc5
|
||||
#define IS31FL3293_MAX_MICROAMP 20000
|
||||
|
||||
struct is31fl32xx_priv;
|
||||
struct is31fl32xx_led_data {
|
||||
struct led_classdev cdev;
|
||||
u8 channel; /* 1-based, max priv->cdef->channels */
|
||||
u32 max_microamp;
|
||||
struct is31fl32xx_priv *priv;
|
||||
};
|
||||
|
||||
|
|
@ -53,6 +69,7 @@ struct is31fl32xx_priv {
|
|||
* @channels : Number of LED channels
|
||||
* @shutdown_reg : address of Shutdown register (optional)
|
||||
* @pwm_update_reg : address of PWM Update register
|
||||
* @pwm_update_value : value to write to PWM Update register
|
||||
* @global_control_reg : address of Global Control register (optional)
|
||||
* @reset_reg : address of Reset register (optional)
|
||||
* @output_frequency_setting_reg: address of output frequency register (optional)
|
||||
|
|
@ -60,6 +77,7 @@ struct is31fl32xx_priv {
|
|||
* @pwm_registers_reversed: : true if PWM registers count down instead of up
|
||||
* @led_control_register_base : address of first LED control register (optional)
|
||||
* @enable_bits_per_led_control_register: number of LEDs enable bits in each
|
||||
* @brightness_steps : number of brightness steps supported by the chip
|
||||
* @reset_func : pointer to reset function
|
||||
* @sw_shutdown_func : pointer to software shutdown function
|
||||
*
|
||||
|
|
@ -77,6 +95,7 @@ struct is31fl32xx_chipdef {
|
|||
u8 channels;
|
||||
u8 shutdown_reg;
|
||||
u8 pwm_update_reg;
|
||||
u8 pwm_update_value;
|
||||
u8 global_control_reg;
|
||||
u8 reset_reg;
|
||||
u8 output_frequency_setting_reg;
|
||||
|
|
@ -84,76 +103,11 @@ struct is31fl32xx_chipdef {
|
|||
bool pwm_registers_reversed;
|
||||
u8 led_control_register_base;
|
||||
u8 enable_bits_per_led_control_register;
|
||||
u16 brightness_steps;
|
||||
int (*reset_func)(struct is31fl32xx_priv *priv);
|
||||
int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable);
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3236_cdef = {
|
||||
.channels = 36,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x26,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3236a_cdef = {
|
||||
.channels = 36,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = 0x4b,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x26,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3235_cdef = {
|
||||
.channels = 28,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x05,
|
||||
.led_control_register_base = 0x2a,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3218_cdef = {
|
||||
.channels = 18,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x16,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = 0x17,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x13,
|
||||
.enable_bits_per_led_control_register = 6,
|
||||
};
|
||||
|
||||
static int is31fl3216_reset(struct is31fl32xx_priv *priv);
|
||||
static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
|
||||
bool enable);
|
||||
static const struct is31fl32xx_chipdef is31fl3216_cdef = {
|
||||
.channels = 16,
|
||||
.shutdown_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_update_reg = 0xB0,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = IS31FL32XX_REG_NONE,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x10,
|
||||
.pwm_registers_reversed = true,
|
||||
.led_control_register_base = 0x01,
|
||||
.enable_bits_per_led_control_register = 8,
|
||||
.reset_func = is31fl3216_reset,
|
||||
.sw_shutdown_func = is31fl3216_software_shutdown,
|
||||
};
|
||||
|
||||
static int is31fl32xx_write(struct is31fl32xx_priv *priv, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -218,6 +172,62 @@ static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
|
|||
return is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom Reset function for IS31FL3293. We need to set the global current limit
|
||||
* and write to the color update register once.
|
||||
*/
|
||||
static int is31fl3293_reset(struct is31fl32xx_priv *priv)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
ret = is31fl32xx_write(priv, IS31FL3293_RESET_REG,
|
||||
IS31FL3293_RESET_MAGIC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set the global current limit to maximum */
|
||||
ret = is31fl32xx_write(priv, IS31FL3293_GCC_REG,
|
||||
IS31FL3293_GCC_LEVEL_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < priv->num_leds; i++) {
|
||||
struct is31fl32xx_led_data *led_data = &priv->leds[i];
|
||||
int current_level_reg = IS31FL3293_CL_REG + led_data->channel - 1;
|
||||
int microamp = max(led_data->max_microamp, IS31FL3293_MAX_MICROAMP);
|
||||
int current_level = (microamp * 0xff) / IS31FL3293_MAX_MICROAMP;
|
||||
|
||||
ret = is31fl32xx_write(priv, current_level_reg, current_level);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = is31fl32xx_write(priv, IS31FL3293_COLOR_UPDATE_REG,
|
||||
IS31FL3293_COLOR_UPDATE_MAGIC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom Software-Shutdown function for IS31FL3293 because the SHUTDOWN
|
||||
* register of this device also has bits to enable the channels.
|
||||
*/
|
||||
static int is31fl3293_software_shutdown(struct is31fl32xx_priv *priv,
|
||||
bool enable)
|
||||
{
|
||||
u8 value = 0;
|
||||
|
||||
if (!enable)
|
||||
value = IS31FL3293_SHUTDOWN_SSD_DISABLE |
|
||||
IS31FL3293_SHUTDOWN_EN1 |
|
||||
IS31FL3293_SHUTDOWN_EN2 |
|
||||
IS31FL3293_SHUTDOWN_EN3;
|
||||
|
||||
return is31fl32xx_write(priv, IS31FL3293_SHUTDOWN_REG, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: A mutex is not needed in this function because:
|
||||
* - All referenced data is read-only after probe()
|
||||
|
|
@ -256,13 +266,36 @@ static int is31fl32xx_brightness_set(struct led_classdev *led_cdev,
|
|||
else
|
||||
pwm_register_offset = led_data->channel - 1;
|
||||
|
||||
ret = is31fl32xx_write(led_data->priv,
|
||||
cdef->pwm_register_base + pwm_register_offset,
|
||||
brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
switch (cdef->brightness_steps) {
|
||||
case 256:
|
||||
ret = is31fl32xx_write(led_data->priv,
|
||||
cdef->pwm_register_base + pwm_register_offset,
|
||||
brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0);
|
||||
break;
|
||||
case 4096:
|
||||
/* IS31FL329x devices use two registers to store 12 bits of brightness */
|
||||
pwm_register_offset *= 2;
|
||||
|
||||
ret = is31fl32xx_write(led_data->priv,
|
||||
cdef->pwm_register_base + pwm_register_offset,
|
||||
brightness & 0xff);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = is31fl32xx_write(led_data->priv,
|
||||
cdef->pwm_register_base + pwm_register_offset + 1,
|
||||
(brightness >> 8) & 0xf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg,
|
||||
cdef->pwm_update_value);
|
||||
}
|
||||
|
||||
static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv)
|
||||
|
|
@ -361,6 +394,8 @@ static int is31fl32xx_parse_child_dt(const struct device *dev,
|
|||
}
|
||||
led_data->channel = reg;
|
||||
|
||||
of_property_read_u32(child, "led-max-microamp", &led_data->max_microamp);
|
||||
|
||||
cdev->brightness_set_blocking = is31fl32xx_brightness_set;
|
||||
|
||||
return 0;
|
||||
|
|
@ -405,6 +440,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
const struct is31fl32xx_led_data *other_led_data;
|
||||
|
||||
led_data->priv = priv;
|
||||
led_data->cdev.max_brightness = priv->cdef->brightness_steps - 1;
|
||||
|
||||
ret = is31fl32xx_parse_child_dt(dev, child, led_data);
|
||||
if (ret)
|
||||
|
|
@ -435,8 +471,89 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
|
||||
return 0;
|
||||
}
|
||||
static const struct is31fl32xx_chipdef is31fl3236_cdef = {
|
||||
.channels = 36,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x26,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3236a_cdef = {
|
||||
.channels = 36,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = 0x4b,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x26,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
.brightness_steps = 256,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3235_cdef = {
|
||||
.channels = 28,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x05,
|
||||
.led_control_register_base = 0x2a,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
.brightness_steps = 256,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3218_cdef = {
|
||||
.channels = 18,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x16,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = 0x17,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x13,
|
||||
.enable_bits_per_led_control_register = 6,
|
||||
.brightness_steps = 256,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3216_cdef = {
|
||||
.channels = 16,
|
||||
.shutdown_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_update_reg = 0xB0,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = IS31FL32XX_REG_NONE,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x10,
|
||||
.pwm_registers_reversed = true,
|
||||
.led_control_register_base = 0x01,
|
||||
.enable_bits_per_led_control_register = 8,
|
||||
.reset_func = is31fl3216_reset,
|
||||
.sw_shutdown_func = is31fl3216_software_shutdown,
|
||||
.brightness_steps = 256,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3293_cdef = {
|
||||
.channels = 3,
|
||||
.shutdown_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_update_reg = 0x28,
|
||||
.pwm_update_value = 0xc5,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x19,
|
||||
.led_control_register_base = IS31FL32XX_REG_NONE,
|
||||
.brightness_steps = 4096,
|
||||
.reset_func = is31fl3293_reset,
|
||||
.sw_shutdown_func = is31fl3293_software_shutdown,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_is31fl32xx_match[] = {
|
||||
{ .compatible = "issi,is31fl3293", .data = &is31fl3293_cdef, },
|
||||
{ .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
|
||||
{ .compatible = "issi,is31fl3236a", .data = &is31fl3236a_cdef, },
|
||||
{ .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
|
||||
|
|
@ -472,11 +589,11 @@ static int is31fl32xx_probe(struct i2c_client *client)
|
|||
priv->cdef = cdef;
|
||||
i2c_set_clientdata(client, priv);
|
||||
|
||||
ret = is31fl32xx_init_regs(priv);
|
||||
ret = is31fl32xx_parse_dt(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = is31fl32xx_parse_dt(dev, priv);
|
||||
ret = is31fl32xx_init_regs(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -499,6 +616,7 @@ static void is31fl32xx_remove(struct i2c_client *client)
|
|||
* even though it is not used for DeviceTree based instantiation.
|
||||
*/
|
||||
static const struct i2c_device_id is31fl32xx_id[] = {
|
||||
{ "is31fl3293" },
|
||||
{ "is31fl3236" },
|
||||
{ "is31fl3236a" },
|
||||
{ "is31fl3235" },
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@
|
|||
* @regulator: LED supply regulator pointer
|
||||
* @led_enable: LED sync to be enabled
|
||||
* @model_id: Current device model ID enumerated
|
||||
* @boost_ctrl: Cached configuration for the boost control register
|
||||
* @brightness_ctrl: Cached configuration for brightness/brightness control
|
||||
* @enabled: Cached enable state of the device
|
||||
*/
|
||||
struct lm3692x_led {
|
||||
struct mutex lock;
|
||||
|
|
|
|||
|
|
@ -1204,7 +1204,6 @@ static struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
|
|||
struct device_node *np,
|
||||
struct lp55xx_chip *chip)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct lp55xx_led_config *cfg;
|
||||
int num_channels;
|
||||
|
|
@ -1229,12 +1228,10 @@ static struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
|
|||
pdata->num_channels = num_channels;
|
||||
cfg->max_channel = chip->cfg->max_channel;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
ret = lp55xx_parse_logical_led(child, cfg, i);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,19 @@ config LEDS_KTD202X
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called leds-ktd202x.
|
||||
|
||||
config LEDS_LP5812
|
||||
tristate "LED support for Texas Instruments LP5812"
|
||||
depends on I2C
|
||||
help
|
||||
If you say Y here you get support for TI LP5812 LED driver.
|
||||
The LP5812 is a 4x3 matrix RGB LED driver with autonomous
|
||||
animation engine control.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called leds-lp5812.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LEDS_NCP5623
|
||||
tristate "LED support for NCP5623"
|
||||
depends on I2C
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o
|
||||
obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o
|
||||
obj-$(CONFIG_LEDS_LP5812) += leds-lp5812.o
|
||||
obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o
|
||||
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
|
||||
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,642 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* LP5812 LED driver
|
||||
*
|
||||
* Copyright (C) 2025 Texas Instruments
|
||||
*
|
||||
* Author: Jared Zhou <jared-zhou@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "leds-lp5812.h"
|
||||
|
||||
static const struct lp5812_mode_mapping chip_mode_map[] = {
|
||||
{"direct_mode", 0, 0, 0, 0, 0, 0},
|
||||
{"tcm:1:0", 1, 0, 0, 0, 0, 0},
|
||||
{"tcm:1:1", 1, 1, 0, 0, 0, 0},
|
||||
{"tcm:1:2", 1, 2, 0, 0, 0, 0},
|
||||
{"tcm:1:3", 1, 3, 0, 0, 0, 0},
|
||||
{"tcm:2:0:1", 2, 0, 1, 0, 0, 0},
|
||||
{"tcm:2:0:2", 2, 0, 2, 0, 0, 0},
|
||||
{"tcm:2:0:3", 2, 0, 3, 0, 0, 0},
|
||||
{"tcm:2:1:2", 2, 1, 2, 0, 0, 0},
|
||||
{"tcm:2:1:3", 2, 1, 3, 0, 0, 0},
|
||||
{"tcm:2:2:3", 2, 2, 3, 0, 0, 0},
|
||||
{"tcm:3:0:1:2", 3, 0, 1, 2, 0, 0},
|
||||
{"tcm:3:0:1:3", 3, 0, 1, 3, 0, 0},
|
||||
{"tcm:3:0:2:3", 3, 0, 2, 3, 0, 0},
|
||||
{"tcm:4:0:1:2:3", 4, 0, 1, 2, 3, 0},
|
||||
{"mix:1:0:1", 5, 1, 0, 0, 0, 0},
|
||||
{"mix:1:0:2", 5, 2, 0, 0, 0, 0},
|
||||
{"mix:1:0:3", 5, 3, 0, 0, 0, 0},
|
||||
{"mix:1:1:0", 5, 0, 0, 0, 0, 1},
|
||||
{"mix:1:1:2", 5, 2, 0, 0, 0, 1},
|
||||
{"mix:1:1:3", 5, 3, 0, 0, 0, 1},
|
||||
{"mix:1:2:0", 5, 0, 0, 0, 0, 2},
|
||||
{"mix:1:2:1", 5, 1, 0, 0, 0, 2},
|
||||
{"mix:1:2:3", 5, 3, 0, 0, 0, 2},
|
||||
{"mix:1:3:0", 5, 0, 0, 0, 0, 3},
|
||||
{"mix:1:3:1", 5, 1, 0, 0, 0, 3},
|
||||
{"mix:1:3:2", 5, 2, 0, 0, 0, 3},
|
||||
{"mix:2:0:1:2", 6, 1, 2, 0, 0, 0},
|
||||
{"mix:2:0:1:3", 6, 1, 3, 0, 0, 0},
|
||||
{"mix:2:0:2:3", 6, 2, 3, 0, 0, 0},
|
||||
{"mix:2:1:0:2", 6, 0, 2, 0, 0, 1},
|
||||
{"mix:2:1:0:3", 6, 0, 3, 0, 0, 1},
|
||||
{"mix:2:1:2:3", 6, 2, 3, 0, 0, 1},
|
||||
{"mix:2:2:0:1", 6, 0, 1, 0, 0, 2},
|
||||
{"mix:2:2:0:3", 6, 0, 3, 0, 0, 2},
|
||||
{"mix:2:2:1:3", 6, 1, 3, 0, 0, 2},
|
||||
{"mix:2:3:0:1", 6, 0, 1, 0, 0, 3},
|
||||
{"mix:2:3:0:2", 6, 0, 2, 0, 0, 3},
|
||||
{"mix:2:3:1:2", 6, 1, 2, 0, 0, 3},
|
||||
{"mix:3:0:1:2:3", 7, 1, 2, 3, 0, 0},
|
||||
{"mix:3:1:0:2:3", 7, 0, 2, 3, 0, 1},
|
||||
{"mix:3:2:0:1:3", 7, 0, 1, 3, 0, 2},
|
||||
{"mix:3:3:0:1:2", 7, 0, 1, 2, 0, 3}
|
||||
};
|
||||
|
||||
static int lp5812_write(struct lp5812_chip *chip, u16 reg, u8 val)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
struct i2c_msg msg;
|
||||
u8 buf[LP5812_DATA_LENGTH];
|
||||
u8 reg_addr_bit8_9;
|
||||
int ret;
|
||||
|
||||
/* Extract register address bits 9 and 8 for Address Byte 1 */
|
||||
reg_addr_bit8_9 = (reg >> LP5812_REG_ADDR_HIGH_SHIFT) & LP5812_REG_ADDR_BIT_8_9_MASK;
|
||||
|
||||
/* Prepare payload: Address Byte 2 (bits [7:0]) and value to write */
|
||||
buf[LP5812_DATA_BYTE_0_IDX] = (u8)(reg & LP5812_REG_ADDR_LOW_MASK);
|
||||
buf[LP5812_DATA_BYTE_1_IDX] = val;
|
||||
|
||||
/* Construct I2C message for a write operation */
|
||||
msg.addr = (chip->client->addr << LP5812_CHIP_ADDR_SHIFT) | reg_addr_bit8_9;
|
||||
msg.flags = 0;
|
||||
msg.len = sizeof(buf);
|
||||
msg.buf = buf;
|
||||
|
||||
ret = i2c_transfer(chip->client->adapter, &msg, 1);
|
||||
if (ret == 1)
|
||||
return 0;
|
||||
|
||||
dev_err(dev, "I2C write error, ret=%d\n", ret);
|
||||
return ret < 0 ? ret : -EIO;
|
||||
}
|
||||
|
||||
static int lp5812_read(struct lp5812_chip *chip, u16 reg, u8 *val)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
struct i2c_msg msgs[LP5812_READ_MSG_LENGTH];
|
||||
u8 ret_val;
|
||||
u8 reg_addr_bit8_9;
|
||||
u8 converted_reg;
|
||||
int ret;
|
||||
|
||||
/* Extract register address bits 9 and 8 for Address Byte 1 */
|
||||
reg_addr_bit8_9 = (reg >> LP5812_REG_ADDR_HIGH_SHIFT) & LP5812_REG_ADDR_BIT_8_9_MASK;
|
||||
|
||||
/* Lower 8 bits go in Address Byte 2 */
|
||||
converted_reg = (u8)(reg & LP5812_REG_ADDR_LOW_MASK);
|
||||
|
||||
/* Prepare I2C write message to set register address */
|
||||
msgs[LP5812_MSG_0_IDX].addr =
|
||||
(chip->client->addr << LP5812_CHIP_ADDR_SHIFT) | reg_addr_bit8_9;
|
||||
msgs[LP5812_MSG_0_IDX].flags = 0;
|
||||
msgs[LP5812_MSG_0_IDX].len = 1;
|
||||
msgs[LP5812_MSG_0_IDX].buf = &converted_reg;
|
||||
|
||||
/* Prepare I2C read message to retrieve register value */
|
||||
msgs[LP5812_MSG_1_IDX].addr =
|
||||
(chip->client->addr << LP5812_CHIP_ADDR_SHIFT) | reg_addr_bit8_9;
|
||||
msgs[LP5812_MSG_1_IDX].flags = I2C_M_RD;
|
||||
msgs[LP5812_MSG_1_IDX].len = 1;
|
||||
msgs[LP5812_MSG_1_IDX].buf = &ret_val;
|
||||
|
||||
ret = i2c_transfer(chip->client->adapter, msgs, LP5812_READ_MSG_LENGTH);
|
||||
if (ret == LP5812_READ_MSG_LENGTH) {
|
||||
*val = ret_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(dev, "I2C read error, ret=%d\n", ret);
|
||||
*val = 0;
|
||||
return ret < 0 ? ret : -EIO;
|
||||
}
|
||||
|
||||
static int lp5812_read_tsd_config_status(struct lp5812_chip *chip, u8 *reg_val)
|
||||
{
|
||||
return lp5812_read(chip, LP5812_TSD_CONFIG_STATUS, reg_val);
|
||||
}
|
||||
|
||||
static int lp5812_update_regs_config(struct lp5812_chip *chip)
|
||||
{
|
||||
u8 reg_val;
|
||||
int ret;
|
||||
|
||||
ret = lp5812_write(chip, LP5812_CMD_UPDATE, LP5812_UPDATE_CMD_VAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp5812_read_tsd_config_status(chip, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return reg_val & LP5812_CFG_ERR_STATUS_MASK;
|
||||
}
|
||||
|
||||
static ssize_t parse_drive_mode(struct lp5812_chip *chip, const char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
chip->drive_mode.bits.mix_sel_led_0 = false;
|
||||
chip->drive_mode.bits.mix_sel_led_1 = false;
|
||||
chip->drive_mode.bits.mix_sel_led_2 = false;
|
||||
chip->drive_mode.bits.mix_sel_led_3 = false;
|
||||
|
||||
if (sysfs_streq(str, LP5812_MODE_DIRECT_NAME)) {
|
||||
chip->drive_mode.bits.led_mode = LP5812_MODE_DIRECT_VALUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chip_mode_map); i++) {
|
||||
if (!sysfs_streq(str, chip_mode_map[i].mode_name))
|
||||
continue;
|
||||
|
||||
chip->drive_mode.bits.led_mode = chip_mode_map[i].mode;
|
||||
chip->scan_order.bits.order0 = chip_mode_map[i].scan_order_0;
|
||||
chip->scan_order.bits.order1 = chip_mode_map[i].scan_order_1;
|
||||
chip->scan_order.bits.order2 = chip_mode_map[i].scan_order_2;
|
||||
chip->scan_order.bits.order3 = chip_mode_map[i].scan_order_3;
|
||||
|
||||
switch (chip_mode_map[i].selection_led) {
|
||||
case LP5812_MODE_MIX_SELECT_LED_0:
|
||||
chip->drive_mode.bits.mix_sel_led_0 = true;
|
||||
break;
|
||||
case LP5812_MODE_MIX_SELECT_LED_1:
|
||||
chip->drive_mode.bits.mix_sel_led_1 = true;
|
||||
break;
|
||||
case LP5812_MODE_MIX_SELECT_LED_2:
|
||||
chip->drive_mode.bits.mix_sel_led_2 = true;
|
||||
break;
|
||||
case LP5812_MODE_MIX_SELECT_LED_3:
|
||||
chip->drive_mode.bits.mix_sel_led_3 = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int lp5812_set_drive_mode_scan_order(struct lp5812_chip *chip)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
val = chip->drive_mode.val;
|
||||
ret = lp5812_write(chip, LP5812_DEV_CONFIG1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = chip->scan_order.val;
|
||||
ret = lp5812_write(chip, LP5812_DEV_CONFIG2, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5812_set_led_mode(struct lp5812_chip *chip, int led_number,
|
||||
enum control_mode mode)
|
||||
{
|
||||
u8 reg_val;
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Select device configuration register.
|
||||
* Reg3 for LED_0–LED_3, LED_A0–A2, LED_B0
|
||||
* Reg4 for LED_B1–B2, LED_C0–C2, LED_D0–D2
|
||||
*/
|
||||
if (led_number < LP5812_NUMBER_LED_IN_REG)
|
||||
reg = LP5812_DEV_CONFIG3;
|
||||
else
|
||||
reg = LP5812_DEV_CONFIG4;
|
||||
|
||||
ret = lp5812_read(chip, reg, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mode == LP5812_MODE_MANUAL)
|
||||
reg_val &= ~(LP5812_ENABLE << (led_number % LP5812_NUMBER_LED_IN_REG));
|
||||
else
|
||||
reg_val |= (LP5812_ENABLE << (led_number % LP5812_NUMBER_LED_IN_REG));
|
||||
|
||||
ret = lp5812_write(chip, reg, reg_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp5812_update_regs_config(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5812_manual_dc_pwm_control(struct lp5812_chip *chip, int led_number,
|
||||
u8 val, enum dimming_type dimming_type)
|
||||
{
|
||||
u16 led_base_reg;
|
||||
int ret;
|
||||
|
||||
if (dimming_type == LP5812_DIMMING_ANALOG)
|
||||
led_base_reg = LP5812_MANUAL_DC_BASE;
|
||||
else
|
||||
led_base_reg = LP5812_MANUAL_PWM_BASE;
|
||||
|
||||
ret = lp5812_write(chip, led_base_reg + led_number, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5812_multicolor_brightness(struct lp5812_led *led)
|
||||
{
|
||||
struct lp5812_chip *chip = led->chip;
|
||||
int ret, i;
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
for (i = 0; i < led->mc_cdev.num_colors; i++) {
|
||||
ret = lp5812_manual_dc_pwm_control(chip, led->mc_cdev.subled_info[i].channel,
|
||||
led->mc_cdev.subled_info[i].brightness,
|
||||
LP5812_DIMMING_PWM);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp5812_led_brightness(struct lp5812_led *led)
|
||||
{
|
||||
struct lp5812_chip *chip = led->chip;
|
||||
struct lp5812_led_config *led_cfg;
|
||||
int ret;
|
||||
|
||||
led_cfg = &chip->led_config[led->chan_nr];
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
ret = lp5812_manual_dc_pwm_control(chip, led_cfg->led_id[0],
|
||||
led->brightness, LP5812_DIMMING_PWM);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5812_set_brightness(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct lp5812_led *led = container_of(cdev, struct lp5812_led, cdev);
|
||||
|
||||
led->brightness = (u8)brightness;
|
||||
|
||||
return lp5812_led_brightness(led);
|
||||
}
|
||||
|
||||
static int lp5812_set_mc_brightness(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
|
||||
struct lp5812_led *led = container_of(mc_dev, struct lp5812_led, mc_cdev);
|
||||
|
||||
led_mc_calc_color_components(&led->mc_cdev, brightness);
|
||||
|
||||
return lp5812_multicolor_brightness(led);
|
||||
}
|
||||
|
||||
static int lp5812_init_led(struct lp5812_led *led, struct lp5812_chip *chip, int chan)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
struct mc_subled *mc_led_info;
|
||||
struct led_classdev *led_cdev;
|
||||
int i, ret;
|
||||
|
||||
if (chip->led_config[chan].name) {
|
||||
led->cdev.name = chip->led_config[chan].name;
|
||||
} else {
|
||||
led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:channel%d",
|
||||
chip->label ? : chip->client->name, chan);
|
||||
if (!led->cdev.name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!chip->led_config[chan].is_sc_led) {
|
||||
mc_led_info = devm_kcalloc(dev, chip->led_config[chan].num_colors,
|
||||
sizeof(*mc_led_info), GFP_KERNEL);
|
||||
if (!mc_led_info)
|
||||
return -ENOMEM;
|
||||
|
||||
led_cdev = &led->mc_cdev.led_cdev;
|
||||
led_cdev->name = led->cdev.name;
|
||||
led_cdev->brightness_set_blocking = lp5812_set_mc_brightness;
|
||||
led->mc_cdev.num_colors = chip->led_config[chan].num_colors;
|
||||
|
||||
for (i = 0; i < led->mc_cdev.num_colors; i++) {
|
||||
mc_led_info[i].color_index = chip->led_config[chan].color_id[i];
|
||||
mc_led_info[i].channel = chip->led_config[chan].led_id[i];
|
||||
}
|
||||
|
||||
led->mc_cdev.subled_info = mc_led_info;
|
||||
} else {
|
||||
led->cdev.brightness_set_blocking = lp5812_set_brightness;
|
||||
}
|
||||
|
||||
led->chan_nr = chan;
|
||||
|
||||
if (chip->led_config[chan].is_sc_led) {
|
||||
ret = devm_led_classdev_register(dev, &led->cdev);
|
||||
if (ret == 0)
|
||||
led->cdev.dev->platform_data = led;
|
||||
} else {
|
||||
ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev);
|
||||
if (ret == 0)
|
||||
led->mc_cdev.led_cdev.dev->platform_data = led;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5812_register_leds(struct lp5812_led *leds, struct lp5812_chip *chip)
|
||||
{
|
||||
struct lp5812_led *led;
|
||||
int num_channels = chip->num_channels;
|
||||
u8 reg_val;
|
||||
u16 reg;
|
||||
int ret, i, j;
|
||||
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
led = &leds[i];
|
||||
ret = lp5812_init_led(led, chip, i);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
|
||||
led->chip = chip;
|
||||
|
||||
for (j = 0; j < chip->led_config[i].num_colors; j++) {
|
||||
ret = lp5812_write(chip,
|
||||
LP5812_AUTO_DC_BASE + chip->led_config[i].led_id[j],
|
||||
chip->led_config[i].max_current[j]);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
|
||||
ret = lp5812_manual_dc_pwm_control(chip, chip->led_config[i].led_id[j],
|
||||
chip->led_config[i].max_current[j],
|
||||
LP5812_DIMMING_ANALOG);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
|
||||
ret = lp5812_set_led_mode(chip, chip->led_config[i].led_id[j],
|
||||
LP5812_MODE_MANUAL);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
|
||||
reg = (chip->led_config[i].led_id[j] < LP5812_NUMBER_LED_IN_REG) ?
|
||||
LP5812_LED_EN_1 : LP5812_LED_EN_2;
|
||||
|
||||
ret = lp5812_read(chip, reg, ®_val);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
|
||||
reg_val |= (LP5812_ENABLE << (chip->led_config[i].led_id[j] %
|
||||
LP5812_NUMBER_LED_IN_REG));
|
||||
|
||||
ret = lp5812_write(chip, reg, reg_val);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_init_led:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5812_init_device(struct lp5812_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usleep_range(LP5812_WAIT_DEVICE_STABLE_MIN, LP5812_WAIT_DEVICE_STABLE_MAX);
|
||||
|
||||
ret = lp5812_write(chip, LP5812_REG_ENABLE, LP5812_ENABLE);
|
||||
if (ret) {
|
||||
dev_err(&chip->client->dev, "failed to enable LP5812 device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lp5812_write(chip, LP5812_DEV_CONFIG12, LP5812_LSD_LOD_START_UP);
|
||||
if (ret) {
|
||||
dev_err(&chip->client->dev, "failed to configure device safety thresholds\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = parse_drive_mode(chip, chip->scan_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp5812_set_drive_mode_scan_order(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp5812_update_regs_config(chip);
|
||||
if (ret) {
|
||||
dev_err(&chip->client->dev, "failed to apply configuration updates\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lp5812_deinit_device(struct lp5812_chip *chip)
|
||||
{
|
||||
lp5812_write(chip, LP5812_LED_EN_1, LP5812_DISABLE);
|
||||
lp5812_write(chip, LP5812_LED_EN_2, LP5812_DISABLE);
|
||||
lp5812_write(chip, LP5812_REG_ENABLE, LP5812_DISABLE);
|
||||
}
|
||||
|
||||
static int lp5812_parse_led_channel(struct device_node *np,
|
||||
struct lp5812_led_config *cfg,
|
||||
int color_number)
|
||||
{
|
||||
int color_id, reg, ret;
|
||||
u32 max_cur;
|
||||
|
||||
ret = of_property_read_u32(np, "reg", ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg->led_id[color_number] = reg;
|
||||
|
||||
ret = of_property_read_u32(np, "led-max-microamp", &max_cur);
|
||||
if (ret)
|
||||
max_cur = 0;
|
||||
/* Convert microamps to driver units */
|
||||
cfg->max_current[color_number] = max_cur / 100;
|
||||
|
||||
ret = of_property_read_u32(np, "color", &color_id);
|
||||
if (ret)
|
||||
color_id = 0;
|
||||
cfg->color_id[color_number] = color_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp5812_parse_led(struct device_node *np,
|
||||
struct lp5812_led_config *cfg,
|
||||
int led_index)
|
||||
{
|
||||
int num_colors, ret;
|
||||
|
||||
of_property_read_string(np, "label", &cfg[led_index].name);
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &cfg[led_index].chan_nr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_colors = 0;
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
ret = lp5812_parse_led_channel(child, &cfg[led_index], num_colors);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_colors++;
|
||||
}
|
||||
|
||||
if (num_colors == 0) {
|
||||
ret = lp5812_parse_led_channel(np, &cfg[led_index], 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_colors = 1;
|
||||
cfg[led_index].is_sc_led = true;
|
||||
} else {
|
||||
cfg[led_index].is_sc_led = false;
|
||||
}
|
||||
|
||||
cfg[led_index].num_colors = num_colors;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp5812_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct lp5812_chip *chip)
|
||||
{
|
||||
struct lp5812_led_config *cfg;
|
||||
int num_channels, i = 0, ret;
|
||||
|
||||
num_channels = of_get_available_child_count(np);
|
||||
if (num_channels == 0) {
|
||||
dev_err(dev, "no LED channels\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->led_config = &cfg[0];
|
||||
chip->num_channels = num_channels;
|
||||
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
ret = lp5812_parse_led(child, cfg, i);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
i++;
|
||||
}
|
||||
|
||||
ret = of_property_read_string(np, "ti,scan-mode", &chip->scan_mode);
|
||||
if (ret)
|
||||
chip->scan_mode = LP5812_MODE_DIRECT_NAME;
|
||||
|
||||
of_property_read_string(np, "label", &chip->label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp5812_probe(struct i2c_client *client)
|
||||
{
|
||||
struct lp5812_chip *chip;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
struct lp5812_led *leds;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = lp5812_of_probe(&client->dev, np, chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
leds = devm_kcalloc(&client->dev, chip->num_channels, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
mutex_init(&chip->lock);
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
ret = lp5812_init_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp5812_register_leds(leds, chip);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
lp5812_deinit_device(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lp5812_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lp5812_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
lp5812_deinit_device(chip);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_lp5812_match[] = {
|
||||
{ .compatible = "ti,lp5812" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_lp5812_match);
|
||||
|
||||
static struct i2c_driver lp5812_driver = {
|
||||
.driver = {
|
||||
.name = "lp5812",
|
||||
.of_match_table = of_lp5812_match,
|
||||
},
|
||||
.probe = lp5812_probe,
|
||||
.remove = lp5812_remove,
|
||||
};
|
||||
module_i2c_driver(lp5812_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments LP5812 LED Driver");
|
||||
MODULE_AUTHOR("Jared Zhou <jared-zhou@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* LP5812 Driver Header
|
||||
*
|
||||
* Copyright (C) 2025 Texas Instruments
|
||||
*
|
||||
* Author: Jared Zhou <jared-zhou@ti.com>
|
||||
*/
|
||||
|
||||
#ifndef _LP5812_H_
|
||||
#define _LP5812_H_
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define LP5812_REG_ENABLE 0x0000
|
||||
#define LP5812_REG_RESET 0x0023
|
||||
#define LP5812_DEV_CONFIG0 0x0001
|
||||
#define LP5812_DEV_CONFIG1 0x0002
|
||||
#define LP5812_DEV_CONFIG2 0x0003
|
||||
#define LP5812_DEV_CONFIG3 0x0004
|
||||
#define LP5812_DEV_CONFIG4 0x0005
|
||||
#define LP5812_DEV_CONFIG5 0x0006
|
||||
#define LP5812_DEV_CONFIG6 0x0007
|
||||
#define LP5812_DEV_CONFIG7 0x0008
|
||||
#define LP5812_DEV_CONFIG8 0x0009
|
||||
#define LP5812_DEV_CONFIG9 0x000A
|
||||
#define LP5812_DEV_CONFIG10 0x000B
|
||||
#define LP5812_DEV_CONFIG11 0x000c
|
||||
#define LP5812_DEV_CONFIG12 0x000D
|
||||
#define LP5812_CMD_UPDATE 0x0010
|
||||
#define LP5812_LED_EN_1 0x0020
|
||||
#define LP5812_LED_EN_2 0x0021
|
||||
#define LP5812_FAULT_CLEAR 0x0022
|
||||
#define LP5812_MANUAL_DC_BASE 0x0030
|
||||
#define LP5812_AUTO_DC_BASE 0x0050
|
||||
#define LP5812_MANUAL_PWM_BASE 0x0040
|
||||
|
||||
#define LP5812_TSD_CONFIG_STATUS 0x0300
|
||||
#define LP5812_LOD_STATUS 0x0301
|
||||
#define LP5812_LSD_STATUS 0x0303
|
||||
|
||||
#define LP5812_ENABLE 0x01
|
||||
#define LP5812_DISABLE 0x00
|
||||
#define FAULT_CLEAR_ALL 0x07
|
||||
#define TSD_CLEAR_VAL 0x04
|
||||
#define LSD_CLEAR_VAL 0x02
|
||||
#define LOD_CLEAR_VAL 0x01
|
||||
#define LP5812_RESET 0x66
|
||||
#define LP5812_DEV_CONFIG12_DEFAULT 0x08
|
||||
|
||||
#define LP5812_UPDATE_CMD_VAL 0x55
|
||||
#define LP5812_REG_ADDR_HIGH_SHIFT 8
|
||||
#define LP5812_REG_ADDR_BIT_8_9_MASK 0x03
|
||||
#define LP5812_REG_ADDR_LOW_MASK 0xFF
|
||||
#define LP5812_CHIP_ADDR_SHIFT 2
|
||||
#define LP5812_DATA_LENGTH 2
|
||||
#define LP5812_DATA_BYTE_0_IDX 0
|
||||
#define LP5812_DATA_BYTE_1_IDX 1
|
||||
|
||||
#define LP5812_READ_MSG_LENGTH 2
|
||||
#define LP5812_MSG_0_IDX 0
|
||||
#define LP5812_MSG_1_IDX 1
|
||||
#define LP5812_CFG_ERR_STATUS_MASK 0x01
|
||||
#define LP5812_CFG_TSD_STATUS_SHIFT 1
|
||||
#define LP5812_CFG_TSD_STATUS_MASK 0x01
|
||||
|
||||
#define LP5812_FAULT_CLEAR_LOD 0
|
||||
#define LP5812_FAULT_CLEAR_LSD 1
|
||||
#define LP5812_FAULT_CLEAR_TSD 2
|
||||
#define LP5812_FAULT_CLEAR_ALL 3
|
||||
#define LP5812_NUMBER_LED_IN_REG 8
|
||||
|
||||
#define LP5812_WAIT_DEVICE_STABLE_MIN 1000
|
||||
#define LP5812_WAIT_DEVICE_STABLE_MAX 1100
|
||||
|
||||
#define LP5812_LSD_LOD_START_UP 0x0B
|
||||
#define LP5812_MODE_NAME_MAX_LEN 20
|
||||
#define LP5812_MODE_DIRECT_NAME "direct_mode"
|
||||
#define LP5812_MODE_DIRECT_VALUE 0
|
||||
#define LP5812_MODE_MIX_SELECT_LED_0 0
|
||||
#define LP5812_MODE_MIX_SELECT_LED_1 1
|
||||
#define LP5812_MODE_MIX_SELECT_LED_2 2
|
||||
#define LP5812_MODE_MIX_SELECT_LED_3 3
|
||||
|
||||
enum control_mode {
|
||||
LP5812_MODE_MANUAL = 0,
|
||||
LP5812_MODE_AUTONOMOUS
|
||||
};
|
||||
|
||||
enum dimming_type {
|
||||
LP5812_DIMMING_ANALOG,
|
||||
LP5812_DIMMING_PWM
|
||||
};
|
||||
|
||||
union lp5812_scan_order {
|
||||
struct {
|
||||
u8 order0:2;
|
||||
u8 order1:2;
|
||||
u8 order2:2;
|
||||
u8 order3:2;
|
||||
} bits;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
union lp5812_drive_mode {
|
||||
struct {
|
||||
u8 mix_sel_led_0:1;
|
||||
u8 mix_sel_led_1:1;
|
||||
u8 mix_sel_led_2:1;
|
||||
u8 mix_sel_led_3:1;
|
||||
u8 led_mode:3;
|
||||
u8 pwm_fre:1;
|
||||
} bits;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
struct lp5812_reg {
|
||||
u16 addr;
|
||||
union {
|
||||
u8 val;
|
||||
u8 mask;
|
||||
u8 shift;
|
||||
};
|
||||
};
|
||||
|
||||
struct lp5812_mode_mapping {
|
||||
char mode_name[LP5812_MODE_NAME_MAX_LEN];
|
||||
u8 mode;
|
||||
u8 scan_order_0;
|
||||
u8 scan_order_1;
|
||||
u8 scan_order_2;
|
||||
u8 scan_order_3;
|
||||
u8 selection_led;
|
||||
};
|
||||
|
||||
struct lp5812_led_config {
|
||||
bool is_sc_led;
|
||||
const char *name;
|
||||
u8 color_id[LED_COLOR_ID_MAX];
|
||||
u32 max_current[LED_COLOR_ID_MAX];
|
||||
int chan_nr;
|
||||
int num_colors;
|
||||
int led_id[LED_COLOR_ID_MAX];
|
||||
};
|
||||
|
||||
struct lp5812_chip {
|
||||
u8 num_channels;
|
||||
struct i2c_client *client;
|
||||
struct mutex lock; /* Protects register access */
|
||||
struct lp5812_led_config *led_config;
|
||||
const char *label;
|
||||
const char *scan_mode;
|
||||
union lp5812_scan_order scan_order;
|
||||
union lp5812_drive_mode drive_mode;
|
||||
};
|
||||
|
||||
struct lp5812_led {
|
||||
u8 brightness;
|
||||
int chan_nr;
|
||||
struct led_classdev cdev;
|
||||
struct led_classdev_mc mc_cdev;
|
||||
struct lp5812_chip *chip;
|
||||
};
|
||||
|
||||
#endif /*_LP5812_H_*/
|
||||
|
|
@ -369,7 +369,7 @@ static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern,
|
|||
{
|
||||
unsigned int idx;
|
||||
u16 val;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size,
|
||||
0, len, 0);
|
||||
|
|
@ -379,8 +379,10 @@ static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern,
|
|||
for (i = 0; i < len; i++) {
|
||||
val = pattern[i].brightness;
|
||||
|
||||
regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i),
|
||||
&val, sizeof(val));
|
||||
ret = regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i),
|
||||
&val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bitmap_set(lpg->lut_bitmap, idx, len);
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ struct expresswire_common_props {
|
|||
|
||||
void expresswire_power_off(struct expresswire_common_props *props);
|
||||
void expresswire_enable(struct expresswire_common_props *props);
|
||||
void expresswire_start(struct expresswire_common_props *props);
|
||||
void expresswire_end(struct expresswire_common_props *props);
|
||||
void expresswire_set_bit(struct expresswire_common_props *props, bool bit);
|
||||
void expresswire_write_u8(struct expresswire_common_props *props, u8 val);
|
||||
|
||||
#endif /* _LEDS_EXPRESSWIRE_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue