Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+1892 -166
+74
Documentation/devicetree/bindings/leds/ams,as3668.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/ams,as3668.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Osram 4-channel i2c LED driver 8 + 9 + maintainers: 10 + - Lukas Timmermann <linux@timmermann.space> 11 + 12 + description: 13 + This IC can drive up to four separate LEDs. 14 + Having four channels suggests it could be used with a single RGBW LED. 15 + 16 + properties: 17 + compatible: 18 + const: ams,as3668 19 + 20 + reg: 21 + maxItems: 1 22 + 23 + "#address-cells": 24 + const: 1 25 + 26 + "#size-cells": 27 + const: 0 28 + 29 + patternProperties: 30 + "^led@[0-3]$": 31 + type: object 32 + $ref: common.yaml# 33 + unevaluatedProperties: false 34 + 35 + properties: 36 + reg: 37 + maxItems: 1 38 + 39 + required: 40 + - compatible 41 + - reg 42 + - "#address-cells" 43 + - "#size-cells" 44 + 45 + additionalProperties: false 46 + 47 + examples: 48 + - | 49 + #include <dt-bindings/leds/common.h> 50 + 51 + i2c { 52 + #address-cells = <1>; 53 + #size-cells = <0>; 54 + 55 + led-controller@42 { 56 + compatible = "ams,as3668"; 57 + reg = <0x42>; 58 + #address-cells = <1>; 59 + #size-cells = <0>; 60 + 61 + led@0 { 62 + reg = <0x0>; 63 + function = LED_FUNCTION_STATUS; 64 + color = <LED_COLOR_ID_RED>; 65 + }; 66 + 67 + led@1 { 68 + reg = <0x1>; 69 + function = LED_FUNCTION_STATUS; 70 + color = <LED_COLOR_ID_GREEN>; 71 + }; 72 + }; 73 + }; 74 +
+1 -1
Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml
··· 21 21 22 22 properties: 23 23 $nodename: 24 - pattern: "^multi-led(@[0-9a-f])?$" 24 + pattern: "^multi-led(@[0-9a-f]|-[0-9]+)?$" 25 25 26 26 color: 27 27 description: |
+1
Documentation/devicetree/bindings/leds/leds-is31fl32xx.txt
··· 10 10 issi,is31fl3235 11 11 issi,is31fl3218 12 12 issi,is31fl3216 13 + issi,is31fl3293 13 14 si-en,sn3218 14 15 si-en,sn3216 15 16 - reg: I2C slave address
-73
Documentation/devicetree/bindings/leds/leds-lm3697.txt
··· 1 - * Texas Instruments - LM3697 Highly Efficient White LED Driver 2 - 3 - The LM3697 11-bit LED driver provides high- 4 - performance backlight dimming for 1, 2, or 3 series 5 - LED strings while delivering up to 90% efficiency. 6 - 7 - This device is suitable for display and keypad lighting 8 - 9 - Required properties: 10 - - compatible: 11 - "ti,lm3697" 12 - - reg : I2C slave address 13 - - #address-cells : 1 14 - - #size-cells : 0 15 - 16 - Optional properties: 17 - - enable-gpios : GPIO pin to enable/disable the device 18 - - vled-supply : LED supply 19 - 20 - Required child properties: 21 - - reg : 0 - LED is Controlled by bank A 22 - 1 - LED is Controlled by bank B 23 - - led-sources : Indicates which HVLED string is associated to which 24 - control bank. This is a zero based property so 25 - HVLED1 = 0, HVLED2 = 1, HVLED3 = 2. 26 - Additional information is contained 27 - in Documentation/devicetree/bindings/leds/common.txt 28 - 29 - Optional child properties: 30 - - ti,brightness-resolution - see Documentation/devicetree/bindings/mfd/ti-lmu.txt 31 - - ramp-up-us: see Documentation/devicetree/bindings/mfd/ti-lmu.txt 32 - - ramp-down-us: see Documentation/devicetree/bindings/mfd/ti-lmu.txt 33 - - label : see Documentation/devicetree/bindings/leds/common.txt 34 - - linux,default-trigger : 35 - see Documentation/devicetree/bindings/leds/common.txt 36 - 37 - Example: 38 - 39 - HVLED string 1 and 3 are controlled by control bank A and HVLED 2 string is 40 - controlled by control bank B. 41 - 42 - led-controller@36 { 43 - compatible = "ti,lm3697"; 44 - #address-cells = <1>; 45 - #size-cells = <0>; 46 - reg = <0x36>; 47 - 48 - enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; 49 - vled-supply = <&vbatt>; 50 - 51 - led@0 { 52 - reg = <0>; 53 - led-sources = <0 2>; 54 - ti,brightness-resolution = <2047>; 55 - ramp-up-us = <5000>; 56 - ramp-down-us = <1000>; 57 - label = "white:first_backlight_cluster"; 58 - linux,default-trigger = "backlight"; 59 - }; 60 - 61 - led@1 { 62 - reg = <1>; 63 - led-sources = <1>; 64 - ti,brightness-resolution = <255>; 65 - ramp-up-us = <500>; 66 - ramp-down-us = <1000>; 67 - label = "white:second_backlight_cluster"; 68 - linux,default-trigger = "backlight"; 69 - }; 70 - } 71 - 72 - For more product information please see the link below: 73 - https://www.ti.com/lit/ds/symlink/lm3697.pdf
+111
Documentation/devicetree/bindings/leds/leds-lp5860.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/leds-lp5860.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: LED driver for LP5860 RGB LED from Texas Instruments. 8 + 9 + maintainers: 10 + - Steffen Trumtrar <kernel@pengutronix.de> 11 + 12 + description: | 13 + The LP5860 is multi-channel, I2C and SPI RGB LED Driver that can group RGB LEDs 14 + into a LED group or control them individually. 15 + 16 + For more product information please see the link below: 17 + https://www.ti.com/lit/ds/symlink/lp5860.pdf 18 + 19 + allOf: 20 + - $ref: /schemas/spi/spi-peripheral-props.yaml# 21 + 22 + properties: 23 + compatible: 24 + enum: 25 + - ti,lp5860 26 + 27 + reg: 28 + maxItems: 1 29 + 30 + '#address-cells': 31 + const: 1 32 + 33 + '#size-cells': 34 + const: 0 35 + 36 + patternProperties: 37 + '^multi-led@[0-9a-f]+$': 38 + type: object 39 + $ref: leds-class-multicolor.yaml# 40 + unevaluatedProperties: false 41 + 42 + properties: 43 + reg: 44 + minimum: 0 45 + maximum: 198 46 + description: 47 + This property denotes the LED module number that is used 48 + for the child node. 49 + 50 + '#address-cells': 51 + const: 1 52 + 53 + '#size-cells': 54 + const: 0 55 + 56 + patternProperties: 57 + "^led@[0-9a-f]+$": 58 + type: object 59 + $ref: common.yaml# 60 + unevaluatedProperties: false 61 + 62 + properties: 63 + reg: 64 + maxItems: 1 65 + 66 + required: 67 + - reg 68 + 69 + required: 70 + - compatible 71 + - reg 72 + 73 + unevaluatedProperties: false 74 + 75 + examples: 76 + - | 77 + #include <dt-bindings/leds/common.h> 78 + 79 + spi { 80 + #address-cells = <1>; 81 + #size-cells = <0>; 82 + 83 + led-controller@0 { 84 + compatible = "ti,lp5860"; 85 + reg = <0x0>; 86 + #address-cells = <1>; 87 + #size-cells = <0>; 88 + 89 + multi-led@0 { 90 + #address-cells = <1>; 91 + #size-cells = <0>; 92 + reg = <0x0>; 93 + color = <LED_COLOR_ID_RGB>; 94 + 95 + led@0 { 96 + reg = <0x0>; 97 + color = <LED_COLOR_ID_RED>; 98 + }; 99 + 100 + led@1 { 101 + reg = <0x1>; 102 + color = <LED_COLOR_ID_GREEN>; 103 + }; 104 + 105 + led@2 { 106 + reg = <0x2>; 107 + color = <LED_COLOR_ID_BLUE>; 108 + }; 109 + }; 110 + }; 111 + };
+1
Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
··· 43 43 - items: 44 44 - enum: 45 45 - qcom,pm8550-pwm 46 + - qcom,pmh0101-pwm 46 47 - const: qcom,pm8350c-pwm 47 48 - items: 48 49 - enum:
+1
Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
··· 29 29 - qcom,pm8150l-flash-led 30 30 - qcom,pm8350c-flash-led 31 31 - qcom,pm8550-flash-led 32 + - qcom,pmh0101-flash-led 32 33 - qcom,pmi8998-flash-led 33 34 - const: qcom,spmi-flash-led 34 35
+125
Documentation/devicetree/bindings/leds/ti,lm3697.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/ti,lm3697.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: TI LM3697 Highly Efficient White LED Driver 8 + 9 + maintainers: 10 + - Dan Murphy <dmurphy@ti.com> 11 + 12 + description: > 13 + The LM3697 11-bit LED driver provides high-performance backlight dimming for 14 + 1, 2, or 3 series LED strings while delivering up to 90% efficiency. 15 + 16 + This device is suitable for display and keypad lighting. 17 + 18 + properties: 19 + compatible: 20 + const: ti,lm3697 21 + 22 + reg: 23 + maxItems: 1 24 + 25 + '#address-cells': 26 + const: 1 27 + 28 + '#size-cells': 29 + const: 0 30 + 31 + enable-gpios: 32 + description: GPIO pin to enable or disable the device. 33 + maxItems: 1 34 + 35 + vled-supply: 36 + description: LED supply for the device. 37 + 38 + patternProperties: 39 + '^led@[01]$': 40 + description: LED control bank nodes. 41 + $ref: common.yaml# 42 + unevaluatedProperties: false 43 + 44 + properties: 45 + reg: 46 + description: Control bank selection (0 = bank A, 1 = bank B). 47 + maximum: 1 48 + 49 + led-sources: 50 + description: > 51 + HVLED strings associated with this control bank: 52 + 53 + 0 - HVLED1 54 + 1 - HVLED2 55 + 2 - HVLED3 56 + minItems: 1 57 + maxItems: 3 58 + items: 59 + maximum: 2 60 + 61 + ti,brightness-resolution: 62 + description: Brightness resolution for the LED string. 63 + $ref: /schemas/types.yaml#/definitions/uint32 64 + maximum: 2047 65 + 66 + ramp-up-us: 67 + description: Ramp-up time in microseconds. 68 + minimum: 117 69 + maximum: 2048 70 + 71 + ramp-down-us: 72 + description: Ramp-down time in microseconds. 73 + minimum: 117 74 + maximum: 2048 75 + 76 + required: 77 + - reg 78 + - led-sources 79 + 80 + required: 81 + - compatible 82 + - reg 83 + - '#address-cells' 84 + - '#size-cells' 85 + 86 + additionalProperties: false 87 + 88 + examples: 89 + - | 90 + #include <dt-bindings/gpio/gpio.h> 91 + 92 + i2c { 93 + #address-cells = <1>; 94 + #size-cells = <0>; 95 + 96 + led-controller@36 { 97 + compatible = "ti,lm3697"; 98 + #address-cells = <1>; 99 + #size-cells = <0>; 100 + reg = <0x36>; 101 + 102 + enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; 103 + vled-supply = <&vbatt>; 104 + 105 + led@0 { 106 + reg = <0>; 107 + led-sources = <0 2>; 108 + ti,brightness-resolution = <2047>; 109 + ramp-up-us = <500>; 110 + ramp-down-us = <1000>; 111 + label = "white:first_backlight_cluster"; 112 + linux,default-trigger = "backlight"; 113 + }; 114 + 115 + led@1 { 116 + reg = <1>; 117 + led-sources = <1>; 118 + ti,brightness-resolution = <255>; 119 + ramp-up-us = <500>; 120 + ramp-down-us = <1000>; 121 + label = "white:second_backlight_cluster"; 122 + linux,default-trigger = "backlight"; 123 + }; 124 + }; 125 + };
+246
Documentation/devicetree/bindings/leds/ti,lp5812.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/ti,lp5812.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: TI LP5812 4x3 Matrix RGB LED Driver with Autonomous Control 8 + 9 + maintainers: 10 + - Nam Tran <trannamatk@gmail.com> 11 + 12 + description: | 13 + The LP5812 is a 4x3 matrix RGB LED driver with I2C interface 14 + and autonomous animation engine control. 15 + For more product information please see the link below: 16 + https://www.ti.com/product/LP5812#tech-docs 17 + 18 + properties: 19 + compatible: 20 + const: ti,lp5812 21 + 22 + reg: 23 + maxItems: 1 24 + 25 + ti,scan-mode: 26 + description: | 27 + Selects the LED scan mode of the LP5812. The device supports 28 + three modes: 29 + - Direct-drive mode (by default if 'ti,scan-mode' is omitted) 30 + drives up to 4 LEDs directly by internal current sinks (LED0-LED3). 31 + - TCM-drive mode ("tcm:<n>:<order...>") drives up to 12 LEDs 32 + (4 RGB) using 1-4 scan multiplexing. The <n> specifies the number 33 + of scans (1-4), and <order...> defines the scan order of the outputs. 34 + - Mix-drive mode ("mix:<n>:<direct>:<order...>") combines 35 + direct-drive and TCM-drive outputs. The <n> specifies the number 36 + of scans, <direct> selects the direct-drive outputs, and <order...> 37 + defines the scan order. 38 + $ref: /schemas/types.yaml#/definitions/string 39 + pattern: '^(tcm|mix):[1-4](:[0-3]){1,4}$' 40 + 41 + vcc-supply: 42 + description: Regulator providing power to the 'VCC' pin. 43 + 44 + "#address-cells": 45 + const: 1 46 + 47 + "#size-cells": 48 + const: 0 49 + 50 + patternProperties: 51 + "^led@[0-3]$": 52 + type: object 53 + $ref: common.yaml# 54 + unevaluatedProperties: false 55 + 56 + properties: 57 + reg: 58 + minimum: 0 59 + maximum: 3 60 + 61 + required: 62 + - reg 63 + 64 + "^multi-led@[4-7]$": 65 + type: object 66 + $ref: leds-class-multicolor.yaml# 67 + unevaluatedProperties: false 68 + 69 + properties: 70 + reg: 71 + minimum: 4 72 + maximum: 7 73 + 74 + "#address-cells": 75 + const: 1 76 + 77 + "#size-cells": 78 + const: 0 79 + 80 + patternProperties: 81 + "^led@[4-9a-f]$": 82 + type: object 83 + $ref: common.yaml# 84 + unevaluatedProperties: false 85 + 86 + properties: 87 + reg: 88 + minimum: 4 89 + maximum: 15 90 + 91 + required: 92 + - reg 93 + 94 + required: 95 + - compatible 96 + - reg 97 + 98 + additionalProperties: false 99 + 100 + examples: 101 + - | 102 + #include <dt-bindings/leds/common.h> 103 + 104 + i2c { 105 + #address-cells = <1>; 106 + #size-cells = <0>; 107 + 108 + led-controller@1b { 109 + #address-cells = <1>; 110 + #size-cells = <0>; 111 + compatible = "ti,lp5812"; 112 + reg = <0x1b>; 113 + ti,scan-mode = "tcm:4:0:1:2:3"; 114 + vcc-supply = <&vdd_3v3_reg>; 115 + 116 + led@0 { 117 + reg = <0x0>; 118 + label = "LED0"; 119 + led-max-microamp = <25500>; 120 + }; 121 + 122 + led@1 { 123 + reg = <0x1>; 124 + label = "LED1"; 125 + led-max-microamp = <25500>; 126 + }; 127 + 128 + led@2 { 129 + reg = <0x2>; 130 + label = "LED2"; 131 + led-max-microamp = <25500>; 132 + }; 133 + 134 + led@3 { 135 + reg = <0x3>; 136 + label = "LED3"; 137 + led-max-microamp = <25500>; 138 + }; 139 + 140 + multi-led@4 { 141 + #address-cells = <1>; 142 + #size-cells = <0>; 143 + reg = <0x4>; 144 + color = <LED_COLOR_ID_RGB>; 145 + label = "LED_A"; 146 + 147 + led@4 { 148 + reg = <0x4>; 149 + color = <LED_COLOR_ID_GREEN>; 150 + led-max-microamp = <25500>; 151 + }; 152 + 153 + led@5 { 154 + reg = <0x5>; 155 + color = <LED_COLOR_ID_RED>; 156 + led-max-microamp = <25500>; 157 + }; 158 + 159 + led@6 { 160 + reg = <0x6>; 161 + color = <LED_COLOR_ID_BLUE>; 162 + led-max-microamp = <25500>; 163 + }; 164 + }; 165 + 166 + multi-led@5 { 167 + #address-cells = <1>; 168 + #size-cells = <0>; 169 + reg = <0x5>; 170 + color = <LED_COLOR_ID_RGB>; 171 + label = "LED_B"; 172 + 173 + led@7 { 174 + reg = <0x7>; 175 + color = <LED_COLOR_ID_GREEN>; 176 + led-max-microamp = <25500>; 177 + }; 178 + 179 + led@8 { 180 + reg = <0x8>; 181 + color = <LED_COLOR_ID_RED>; 182 + led-max-microamp = <25500>; 183 + }; 184 + 185 + led@9 { 186 + reg = <0x9>; 187 + color = <LED_COLOR_ID_BLUE>; 188 + led-max-microamp = <25500>; 189 + }; 190 + }; 191 + 192 + multi-led@6 { 193 + #address-cells = <1>; 194 + #size-cells = <0>; 195 + reg = <0x6>; 196 + color = <LED_COLOR_ID_RGB>; 197 + label = "LED_C"; 198 + 199 + led@a { 200 + reg = <0xa>; 201 + color = <LED_COLOR_ID_GREEN>; 202 + led-max-microamp = <25500>; 203 + }; 204 + 205 + led@b { 206 + reg = <0xb>; 207 + color = <LED_COLOR_ID_RED>; 208 + led-max-microamp = <25500>; 209 + }; 210 + 211 + led@c { 212 + reg = <0xc>; 213 + color = <LED_COLOR_ID_BLUE>; 214 + led-max-microamp = <25500>; 215 + }; 216 + }; 217 + 218 + multi-led@7 { 219 + #address-cells = <1>; 220 + #size-cells = <0>; 221 + reg = <0x7>; 222 + color = <LED_COLOR_ID_RGB>; 223 + label = "LED_D"; 224 + 225 + led@d { 226 + reg = <0xd>; 227 + color = <LED_COLOR_ID_GREEN>; 228 + led-max-microamp = <25500>; 229 + }; 230 + 231 + led@e { 232 + reg = <0xe>; 233 + color = <LED_COLOR_ID_RED>; 234 + led-max-microamp = <25500>; 235 + }; 236 + 237 + led@f { 238 + reg = <0xf>; 239 + color = <LED_COLOR_ID_BLUE>; 240 + led-max-microamp = <25500>; 241 + }; 242 + }; 243 + }; 244 + }; 245 + 246 + ...
+1
Documentation/leds/index.rst
··· 25 25 leds-lp5523 26 26 leds-lp5562 27 27 leds-lp55xx 28 + leds-lp5812 28 29 leds-mlxcpld 29 30 leds-mt6370-rgb 30 31 leds-sc27xx
+50
Documentation/leds/leds-lp5812.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ======================== 4 + Kernel driver for lp5812 5 + ======================== 6 + 7 + * TI/National Semiconductor LP5812 LED Driver 8 + * Datasheet: https://www.ti.com/product/LP5812#tech-docs 9 + 10 + Authors: Jared Zhou <jared-zhou@ti.com> 11 + 12 + Description 13 + =========== 14 + 15 + The LP5812 is a 4x3 matrix LED driver with support for both manual and 16 + autonomous animation control. This driver provides sysfs interfaces to 17 + control and configure the LP5812 device and its LED channels. 18 + 19 + Sysfs Interface 20 + =============== 21 + 22 + This driver uses the standard multicolor LED class interfaces defined 23 + in Documentation/ABI/testing/sysfs-class-led-multicolor.rst. 24 + 25 + Each LP5812 LED output appears under ``/sys/class/leds/`` with its 26 + assigned label (for example ``LED_A``). 27 + 28 + The following attributes are exposed: 29 + - multi_intensity: Per-channel RGB intensity control 30 + - brightness: Standard brightness control (0-255) 31 + 32 + Autonomous Control Modes 33 + ======================== 34 + 35 + The driver also supports autonomous control through pattern configuration 36 + (e.g., direct, tcmscan, or mixscan modes) defined in the device tree. 37 + When configured, the LP5812 can generate transitions and color effects 38 + without CPU intervention. 39 + 40 + Refer to the device tree binding document for valid mode strings and 41 + configuration examples. 42 + 43 + Example Usage 44 + ============= 45 + 46 + To control LED_A:: 47 + # Set RGB intensity (R=50, G=50, B=50) 48 + echo 50 50 50 > /sys/class/leds/LED_A/multi_intensity 49 + # Set overall brightness to maximum 50 + echo 255 > /sys/class/leds/LED_A/brightness
+18
MAINTAINERS
··· 3806 3806 S: Maintained 3807 3807 F: drivers/leds/flash/leds-as3645a.c 3808 3808 3809 + AS3668 LED DRIVER 3810 + M: Lukas Timmermann <linux@timmermann.space> 3811 + L: linux-leds@vger.kernel.org 3812 + S: Maintained 3813 + F: Documentation/devicetree/bindings/leds/ams,as3668.yaml 3814 + F: drivers/leds/leds-as3668.c 3815 + 3809 3816 ASAHI KASEI AK7375 LENS VOICE COIL DRIVER 3810 3817 M: Tianshu Qiu <tian.shu.qiu@intel.com> 3811 3818 L: linux-media@vger.kernel.org ··· 26100 26093 S: Supported 26101 26094 F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml 26102 26095 F: drivers/iio/dac/ti-dac7612.c 26096 + 26097 + TEXAS INSTRUMENTS' LP5812 RGB LED DRIVER 26098 + M: Nam Tran <trannamatk@gmail.com> 26099 + L: linux-leds@vger.kernel.org 26100 + S: Maintained 26101 + F: Documentation/devicetree/bindings/leds/ti,lp5812.yaml 26102 + F: Documentation/leds/leds-lp5812.rst 26103 + F: drivers/leds/rgb/Kconfig 26104 + F: drivers/leds/rgb/Makefile 26105 + F: drivers/leds/rgb/leds-lp5812.c 26106 + F: drivers/leds/rgb/leds-lp5812.h 26103 26107 26104 26108 TEXAS INSTRUMENTS' LB8864 LED BACKLIGHT DRIVER 26105 26109 M: Alexander Sverdlin <alexander.sverdlin@siemens.com>
+13
drivers/leds/Kconfig
··· 107 107 108 108 Say Y to if your machine is a Dell Wyse 3020 thin client. 109 109 110 + config LEDS_OSRAM_AMS_AS3668 111 + tristate "LED support for Osram AMS AS3668" 112 + depends on LEDS_CLASS 113 + depends on I2C 114 + help 115 + This option enables support for the Osram AMS AS3668 LED controller. 116 + The AS3668 provides up to four LED channels and is controlled via 117 + the I2C bus. This driver offers basic brightness control for each 118 + channel, without support for blinking or other advanced features. 119 + 120 + To compile this driver as a module, choose M here: the module 121 + will be called leds-as3668. 122 + 110 123 config LEDS_AW200XX 111 124 tristate "LED support for Awinic AW20036/AW20054/AW20072/AW20108" 112 125 depends on LEDS_CLASS
+1
drivers/leds/Makefile
··· 15 15 obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o 16 16 obj-$(CONFIG_LEDS_APU) += leds-apu.o 17 17 obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o 18 + obj-$(CONFIG_LEDS_AS3668) += leds-as3668.o 18 19 obj-$(CONFIG_LEDS_AW200XX) += leds-aw200xx.o 19 20 obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o 20 21 obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
+202
drivers/leds/leds-as3668.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Osram AMS AS3668 LED Driver IC 4 + * 5 + * Copyright (C) 2025 Lukas Timmermann <linux@timmermann.space> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/i2c.h> 10 + #include <linux/leds.h> 11 + #include <linux/module.h> 12 + #include <linux/uleds.h> 13 + 14 + #define AS3668_MAX_LEDS 4 15 + 16 + /* Chip Ident */ 17 + 18 + #define AS3668_CHIP_ID1_REG 0x3e 19 + #define AS3668_CHIP_ID 0xa5 20 + 21 + /* Current Control */ 22 + 23 + #define AS3668_CURR_MODE_REG 0x01 24 + #define AS3668_CURR_MODE_OFF 0x0 25 + #define AS3668_CURR_MODE_ON 0x1 26 + #define AS3668_CURR1_MODE_MASK GENMASK(1, 0) 27 + #define AS3668_CURR2_MODE_MASK GENMASK(3, 2) 28 + #define AS3668_CURR3_MODE_MASK GENMASK(5, 4) 29 + #define AS3668_CURR4_MODE_MASK GENMASK(7, 6) 30 + #define AS3668_CURR1_REG 0x02 31 + #define AS3668_CURR2_REG 0x03 32 + #define AS3668_CURR3_REG 0x04 33 + #define AS3668_CURR4_REG 0x05 34 + 35 + #define AS3668_CURR_MODE_PACK(mode) (((mode) << 0) | \ 36 + ((mode) << 2) | \ 37 + ((mode) << 4) | \ 38 + ((mode) << 6)) 39 + 40 + struct as3668_led { 41 + struct led_classdev cdev; 42 + struct as3668 *chip; 43 + struct fwnode_handle *fwnode; 44 + u8 mode_mask; 45 + u8 current_reg; 46 + }; 47 + 48 + struct as3668 { 49 + struct i2c_client *client; 50 + struct as3668_led leds[AS3668_MAX_LEDS]; 51 + }; 52 + 53 + static int as3668_channel_mode_set(struct as3668_led *led, u8 mode) 54 + { 55 + int ret; 56 + u8 channel_modes; 57 + 58 + ret = i2c_smbus_read_byte_data(led->chip->client, AS3668_CURR_MODE_REG); 59 + if (ret < 0) { 60 + dev_err(led->cdev.dev, "failed to read channel modes\n"); 61 + return ret; 62 + } 63 + channel_modes = (u8)ret; 64 + 65 + channel_modes &= ~led->mode_mask; 66 + channel_modes |= led->mode_mask & (AS3668_CURR_MODE_PACK(mode)); 67 + 68 + return i2c_smbus_write_byte_data(led->chip->client, AS3668_CURR_MODE_REG, channel_modes); 69 + } 70 + 71 + static enum led_brightness as3668_brightness_get(struct led_classdev *cdev) 72 + { 73 + struct as3668_led *led = container_of(cdev, struct as3668_led, cdev); 74 + 75 + return i2c_smbus_read_byte_data(led->chip->client, led->current_reg); 76 + } 77 + 78 + static void as3668_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) 79 + { 80 + struct as3668_led *led = container_of(cdev, struct as3668_led, cdev); 81 + int err; 82 + 83 + err = as3668_channel_mode_set(led, !!brightness); 84 + if (err) 85 + dev_err(cdev->dev, "failed to set channel mode: %d\n", err); 86 + 87 + err = i2c_smbus_write_byte_data(led->chip->client, led->current_reg, brightness); 88 + if (err) 89 + dev_err(cdev->dev, "failed to set brightness: %d\n", err); 90 + } 91 + 92 + static int as3668_dt_init(struct as3668 *as3668) 93 + { 94 + struct device *dev = &as3668->client->dev; 95 + struct as3668_led *led; 96 + struct led_init_data init_data = {}; 97 + int err; 98 + u32 reg; 99 + 100 + for_each_available_child_of_node_scoped(dev_of_node(dev), child) { 101 + err = of_property_read_u32(child, "reg", &reg); 102 + if (err) 103 + return dev_err_probe(dev, err, "failed to read 'reg' property"); 104 + 105 + if (reg < 0 || reg >= AS3668_MAX_LEDS) 106 + return dev_err_probe(dev, -EINVAL, 107 + "unsupported LED: %d\n", reg); 108 + 109 + led = &as3668->leds[reg]; 110 + led->fwnode = of_fwnode_handle(child); 111 + 112 + led->current_reg = reg + AS3668_CURR1_REG; 113 + led->mode_mask = AS3668_CURR1_MODE_MASK << (reg * 2); 114 + led->chip = as3668; 115 + 116 + led->cdev.max_brightness = U8_MAX; 117 + led->cdev.brightness_get = as3668_brightness_get; 118 + led->cdev.brightness_set = as3668_brightness_set; 119 + 120 + init_data.fwnode = led->fwnode; 121 + init_data.default_label = ":"; 122 + 123 + err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); 124 + if (err) 125 + return dev_err_probe(dev, err, "failed to register LED %d\n", reg); 126 + } 127 + 128 + return 0; 129 + } 130 + 131 + static int as3668_probe(struct i2c_client *client) 132 + { 133 + struct as3668 *as3668; 134 + int err; 135 + u8 chip_id; 136 + 137 + chip_id = i2c_smbus_read_byte_data(client, AS3668_CHIP_ID1_REG); 138 + if (chip_id != AS3668_CHIP_ID) 139 + return dev_err_probe(&client->dev, -ENODEV, 140 + "expected chip ID 0x%02x, got 0x%02x\n", 141 + AS3668_CHIP_ID, chip_id); 142 + 143 + as3668 = devm_kzalloc(&client->dev, sizeof(*as3668), GFP_KERNEL); 144 + if (!as3668) 145 + return -ENOMEM; 146 + 147 + as3668->client = client; 148 + 149 + err = as3668_dt_init(as3668); 150 + if (err) 151 + return err; 152 + 153 + /* Set all four channel modes to 'off' */ 154 + err = i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG, 155 + FIELD_PREP(AS3668_CURR1_MODE_MASK, AS3668_CURR_MODE_OFF) | 156 + FIELD_PREP(AS3668_CURR2_MODE_MASK, AS3668_CURR_MODE_OFF) | 157 + FIELD_PREP(AS3668_CURR3_MODE_MASK, AS3668_CURR_MODE_OFF) | 158 + FIELD_PREP(AS3668_CURR4_MODE_MASK, AS3668_CURR_MODE_OFF)); 159 + 160 + /* Set initial currents to 0mA */ 161 + err |= i2c_smbus_write_byte_data(client, AS3668_CURR1_REG, 0); 162 + err |= i2c_smbus_write_byte_data(client, AS3668_CURR2_REG, 0); 163 + err |= i2c_smbus_write_byte_data(client, AS3668_CURR3_REG, 0); 164 + err |= i2c_smbus_write_byte_data(client, AS3668_CURR4_REG, 0); 165 + 166 + if (err) 167 + return dev_err_probe(&client->dev, -EIO, "failed to set zero initial current levels\n"); 168 + 169 + return 0; 170 + } 171 + 172 + static void as3668_remove(struct i2c_client *client) 173 + { 174 + i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG, 0); 175 + } 176 + 177 + static const struct i2c_device_id as3668_idtable[] = { 178 + { "as3668" }, 179 + { } 180 + }; 181 + MODULE_DEVICE_TABLE(i2c, as3668_idtable); 182 + 183 + static const struct of_device_id as3668_match_table[] = { 184 + { .compatible = "ams,as3668" }, 185 + { } 186 + }; 187 + MODULE_DEVICE_TABLE(of, as3668_match_table); 188 + 189 + static struct i2c_driver as3668_driver = { 190 + .driver = { 191 + .name = "leds_as3668", 192 + .of_match_table = as3668_match_table, 193 + }, 194 + .probe = as3668_probe, 195 + .remove = as3668_remove, 196 + .id_table = as3668_idtable, 197 + }; 198 + module_i2c_driver(as3668_driver); 199 + 200 + MODULE_AUTHOR("Lukas Timmermann <linux@timmermann.space>"); 201 + MODULE_DESCRIPTION("AS3668 LED driver"); 202 + MODULE_LICENSE("GPL");
+17 -7
drivers/leds/leds-expresswire.c
··· 9 9 #include <linux/delay.h> 10 10 #include <linux/export.h> 11 11 #include <linux/gpio/consumer.h> 12 + #include <linux/irqflags.h> 12 13 #include <linux/types.h> 13 14 14 15 #include <linux/leds-expresswire.h> ··· 17 16 void expresswire_power_off(struct expresswire_common_props *props) 18 17 { 19 18 gpiod_set_value_cansleep(props->ctrl_gpio, 0); 20 - usleep_range(props->timing.poweroff_us, props->timing.poweroff_us * 2); 19 + fsleep(props->timing.poweroff_us); 21 20 } 22 21 EXPORT_SYMBOL_NS_GPL(expresswire_power_off, "EXPRESSWIRE"); 23 22 24 23 void expresswire_enable(struct expresswire_common_props *props) 25 24 { 25 + unsigned long flags; 26 + 27 + local_irq_save(flags); 28 + 26 29 gpiod_set_value(props->ctrl_gpio, 1); 27 30 udelay(props->timing.detect_delay_us); 28 31 gpiod_set_value(props->ctrl_gpio, 0); 29 32 udelay(props->timing.detect_us); 30 33 gpiod_set_value(props->ctrl_gpio, 1); 34 + 35 + local_irq_restore(flags); 31 36 } 32 37 EXPORT_SYMBOL_NS_GPL(expresswire_enable, "EXPRESSWIRE"); 33 38 34 - void expresswire_start(struct expresswire_common_props *props) 39 + static void expresswire_start(struct expresswire_common_props *props) 35 40 { 36 41 gpiod_set_value(props->ctrl_gpio, 1); 37 42 udelay(props->timing.data_start_us); 38 43 } 39 - EXPORT_SYMBOL_NS_GPL(expresswire_start, "EXPRESSWIRE"); 40 44 41 - void expresswire_end(struct expresswire_common_props *props) 45 + static void expresswire_end(struct expresswire_common_props *props) 42 46 { 43 47 gpiod_set_value(props->ctrl_gpio, 0); 44 48 udelay(props->timing.end_of_data_low_us); 45 49 gpiod_set_value(props->ctrl_gpio, 1); 46 50 udelay(props->timing.end_of_data_high_us); 47 51 } 48 - EXPORT_SYMBOL_NS_GPL(expresswire_end, "EXPRESSWIRE"); 49 52 50 - void expresswire_set_bit(struct expresswire_common_props *props, bool bit) 53 + static void expresswire_set_bit(struct expresswire_common_props *props, bool bit) 51 54 { 52 55 if (bit) { 53 56 gpiod_set_value(props->ctrl_gpio, 0); ··· 65 60 udelay(props->timing.short_bitset_us); 66 61 } 67 62 } 68 - EXPORT_SYMBOL_NS_GPL(expresswire_set_bit, "EXPRESSWIRE"); 69 63 70 64 void expresswire_write_u8(struct expresswire_common_props *props, u8 val) 71 65 { 66 + unsigned long flags; 67 + 68 + local_irq_save(flags); 69 + 72 70 expresswire_start(props); 73 71 for (int i = 7; i >= 0; i--) 74 72 expresswire_set_bit(props, val & BIT(i)); 75 73 expresswire_end(props); 74 + 75 + local_irq_restore(flags); 76 76 } 77 77 EXPORT_SYMBOL_NS_GPL(expresswire_write_u8, "EXPRESSWIRE");
+192 -74
drivers/leds/leds-is31fl32xx.c
··· 34 34 35 35 #define IS31FL32XX_PWM_FREQUENCY_22KHZ 0x01 36 36 37 + /* Registers for IS31FL3293 */ 38 + #define IS31FL3293_SHUTDOWN_REG 0x01 39 + #define IS31FL3293_SHUTDOWN_SSD_DISABLE BIT(0) 40 + #define IS31FL3293_SHUTDOWN_EN1 BIT(4) 41 + #define IS31FL3293_SHUTDOWN_EN2 BIT(5) 42 + #define IS31FL3293_SHUTDOWN_EN3 BIT(6) 43 + #define IS31FL3293_GCC_REG 0x03 44 + #define IS31FL3293_GCC_LEVEL_MAX 0x3f 45 + #define IS31FL3293_CL_REG 0x10 46 + #define IS31FL3293_COLOR_UPDATE_REG 0x27 47 + #define IS31FL3293_COLOR_UPDATE_MAGIC 0xc5 48 + #define IS31FL3293_RESET_REG 0x3c 49 + #define IS31FL3293_RESET_MAGIC 0xc5 50 + #define IS31FL3293_MAX_MICROAMP 20000 51 + 37 52 struct is31fl32xx_priv; 38 53 struct is31fl32xx_led_data { 39 54 struct led_classdev cdev; 40 55 u8 channel; /* 1-based, max priv->cdef->channels */ 56 + u32 max_microamp; 41 57 struct is31fl32xx_priv *priv; 42 58 }; 43 59 ··· 69 53 * @channels : Number of LED channels 70 54 * @shutdown_reg : address of Shutdown register (optional) 71 55 * @pwm_update_reg : address of PWM Update register 56 + * @pwm_update_value : value to write to PWM Update register 72 57 * @global_control_reg : address of Global Control register (optional) 73 58 * @reset_reg : address of Reset register (optional) 74 59 * @output_frequency_setting_reg: address of output frequency register (optional) ··· 77 60 * @pwm_registers_reversed: : true if PWM registers count down instead of up 78 61 * @led_control_register_base : address of first LED control register (optional) 79 62 * @enable_bits_per_led_control_register: number of LEDs enable bits in each 63 + * @brightness_steps : number of brightness steps supported by the chip 80 64 * @reset_func : pointer to reset function 81 65 * @sw_shutdown_func : pointer to software shutdown function 82 66 * ··· 95 77 u8 channels; 96 78 u8 shutdown_reg; 97 79 u8 pwm_update_reg; 80 + u8 pwm_update_value; 98 81 u8 global_control_reg; 99 82 u8 reset_reg; 100 83 u8 output_frequency_setting_reg; ··· 103 84 bool pwm_registers_reversed; 104 85 u8 led_control_register_base; 105 86 u8 enable_bits_per_led_control_register; 87 + u16 brightness_steps; 106 88 int (*reset_func)(struct is31fl32xx_priv *priv); 107 89 int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable); 108 - }; 109 - 110 - static const struct is31fl32xx_chipdef is31fl3236_cdef = { 111 - .channels = 36, 112 - .shutdown_reg = 0x00, 113 - .pwm_update_reg = 0x25, 114 - .global_control_reg = 0x4a, 115 - .reset_reg = 0x4f, 116 - .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 117 - .pwm_register_base = 0x01, 118 - .led_control_register_base = 0x26, 119 - .enable_bits_per_led_control_register = 1, 120 - }; 121 - 122 - static const struct is31fl32xx_chipdef is31fl3236a_cdef = { 123 - .channels = 36, 124 - .shutdown_reg = 0x00, 125 - .pwm_update_reg = 0x25, 126 - .global_control_reg = 0x4a, 127 - .reset_reg = 0x4f, 128 - .output_frequency_setting_reg = 0x4b, 129 - .pwm_register_base = 0x01, 130 - .led_control_register_base = 0x26, 131 - .enable_bits_per_led_control_register = 1, 132 - }; 133 - 134 - static const struct is31fl32xx_chipdef is31fl3235_cdef = { 135 - .channels = 28, 136 - .shutdown_reg = 0x00, 137 - .pwm_update_reg = 0x25, 138 - .global_control_reg = 0x4a, 139 - .reset_reg = 0x4f, 140 - .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 141 - .pwm_register_base = 0x05, 142 - .led_control_register_base = 0x2a, 143 - .enable_bits_per_led_control_register = 1, 144 - }; 145 - 146 - static const struct is31fl32xx_chipdef is31fl3218_cdef = { 147 - .channels = 18, 148 - .shutdown_reg = 0x00, 149 - .pwm_update_reg = 0x16, 150 - .global_control_reg = IS31FL32XX_REG_NONE, 151 - .reset_reg = 0x17, 152 - .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 153 - .pwm_register_base = 0x01, 154 - .led_control_register_base = 0x13, 155 - .enable_bits_per_led_control_register = 6, 156 - }; 157 - 158 - static int is31fl3216_reset(struct is31fl32xx_priv *priv); 159 - static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv, 160 - bool enable); 161 - static const struct is31fl32xx_chipdef is31fl3216_cdef = { 162 - .channels = 16, 163 - .shutdown_reg = IS31FL32XX_REG_NONE, 164 - .pwm_update_reg = 0xB0, 165 - .global_control_reg = IS31FL32XX_REG_NONE, 166 - .reset_reg = IS31FL32XX_REG_NONE, 167 - .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 168 - .pwm_register_base = 0x10, 169 - .pwm_registers_reversed = true, 170 - .led_control_register_base = 0x01, 171 - .enable_bits_per_led_control_register = 8, 172 - .reset_func = is31fl3216_reset, 173 - .sw_shutdown_func = is31fl3216_software_shutdown, 174 90 }; 175 91 176 92 static int is31fl32xx_write(struct is31fl32xx_priv *priv, u8 reg, u8 val) ··· 173 219 } 174 220 175 221 /* 222 + * Custom Reset function for IS31FL3293. We need to set the global current limit 223 + * and write to the color update register once. 224 + */ 225 + static int is31fl3293_reset(struct is31fl32xx_priv *priv) 226 + { 227 + int i, ret; 228 + 229 + ret = is31fl32xx_write(priv, IS31FL3293_RESET_REG, 230 + IS31FL3293_RESET_MAGIC); 231 + if (ret) 232 + return ret; 233 + 234 + /* Set the global current limit to maximum */ 235 + ret = is31fl32xx_write(priv, IS31FL3293_GCC_REG, 236 + IS31FL3293_GCC_LEVEL_MAX); 237 + if (ret) 238 + return ret; 239 + 240 + for (i = 0; i < priv->num_leds; i++) { 241 + struct is31fl32xx_led_data *led_data = &priv->leds[i]; 242 + int current_level_reg = IS31FL3293_CL_REG + led_data->channel - 1; 243 + int microamp = max(led_data->max_microamp, IS31FL3293_MAX_MICROAMP); 244 + int current_level = (microamp * 0xff) / IS31FL3293_MAX_MICROAMP; 245 + 246 + ret = is31fl32xx_write(priv, current_level_reg, current_level); 247 + if (ret) 248 + return ret; 249 + } 250 + 251 + ret = is31fl32xx_write(priv, IS31FL3293_COLOR_UPDATE_REG, 252 + IS31FL3293_COLOR_UPDATE_MAGIC); 253 + if (ret) 254 + return ret; 255 + 256 + return 0; 257 + } 258 + 259 + /* 260 + * Custom Software-Shutdown function for IS31FL3293 because the SHUTDOWN 261 + * register of this device also has bits to enable the channels. 262 + */ 263 + static int is31fl3293_software_shutdown(struct is31fl32xx_priv *priv, 264 + bool enable) 265 + { 266 + u8 value = 0; 267 + 268 + if (!enable) 269 + value = IS31FL3293_SHUTDOWN_SSD_DISABLE | 270 + IS31FL3293_SHUTDOWN_EN1 | 271 + IS31FL3293_SHUTDOWN_EN2 | 272 + IS31FL3293_SHUTDOWN_EN3; 273 + 274 + return is31fl32xx_write(priv, IS31FL3293_SHUTDOWN_REG, value); 275 + } 276 + 277 + /* 176 278 * NOTE: A mutex is not needed in this function because: 177 279 * - All referenced data is read-only after probe() 178 280 * - The I2C core has a mutex on to protect the bus ··· 266 256 else 267 257 pwm_register_offset = led_data->channel - 1; 268 258 269 - ret = is31fl32xx_write(led_data->priv, 270 - cdef->pwm_register_base + pwm_register_offset, 271 - brightness); 272 - if (ret) 273 - return ret; 259 + switch (cdef->brightness_steps) { 260 + case 256: 261 + ret = is31fl32xx_write(led_data->priv, 262 + cdef->pwm_register_base + pwm_register_offset, 263 + brightness); 264 + if (ret) 265 + return ret; 274 266 275 - return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0); 267 + break; 268 + case 4096: 269 + /* IS31FL329x devices use two registers to store 12 bits of brightness */ 270 + pwm_register_offset *= 2; 271 + 272 + ret = is31fl32xx_write(led_data->priv, 273 + cdef->pwm_register_base + pwm_register_offset, 274 + brightness & 0xff); 275 + if (ret) 276 + return ret; 277 + 278 + ret = is31fl32xx_write(led_data->priv, 279 + cdef->pwm_register_base + pwm_register_offset + 1, 280 + (brightness >> 8) & 0xf); 281 + if (ret) 282 + return ret; 283 + 284 + break; 285 + } 286 + 287 + return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 288 + cdef->pwm_update_value); 276 289 } 277 290 278 291 static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv) ··· 394 361 } 395 362 led_data->channel = reg; 396 363 364 + of_property_read_u32(child, "led-max-microamp", &led_data->max_microamp); 365 + 397 366 cdev->brightness_set_blocking = is31fl32xx_brightness_set; 398 367 399 368 return 0; ··· 440 405 const struct is31fl32xx_led_data *other_led_data; 441 406 442 407 led_data->priv = priv; 408 + led_data->cdev.max_brightness = priv->cdef->brightness_steps - 1; 443 409 444 410 ret = is31fl32xx_parse_child_dt(dev, child, led_data); 445 411 if (ret) ··· 471 435 472 436 return 0; 473 437 } 438 + static const struct is31fl32xx_chipdef is31fl3236_cdef = { 439 + .channels = 36, 440 + .shutdown_reg = 0x00, 441 + .pwm_update_reg = 0x25, 442 + .global_control_reg = 0x4a, 443 + .reset_reg = 0x4f, 444 + .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 445 + .pwm_register_base = 0x01, 446 + .led_control_register_base = 0x26, 447 + .enable_bits_per_led_control_register = 1, 448 + }; 449 + 450 + static const struct is31fl32xx_chipdef is31fl3236a_cdef = { 451 + .channels = 36, 452 + .shutdown_reg = 0x00, 453 + .pwm_update_reg = 0x25, 454 + .global_control_reg = 0x4a, 455 + .reset_reg = 0x4f, 456 + .output_frequency_setting_reg = 0x4b, 457 + .pwm_register_base = 0x01, 458 + .led_control_register_base = 0x26, 459 + .enable_bits_per_led_control_register = 1, 460 + .brightness_steps = 256, 461 + }; 462 + 463 + static const struct is31fl32xx_chipdef is31fl3235_cdef = { 464 + .channels = 28, 465 + .shutdown_reg = 0x00, 466 + .pwm_update_reg = 0x25, 467 + .global_control_reg = 0x4a, 468 + .reset_reg = 0x4f, 469 + .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 470 + .pwm_register_base = 0x05, 471 + .led_control_register_base = 0x2a, 472 + .enable_bits_per_led_control_register = 1, 473 + .brightness_steps = 256, 474 + }; 475 + 476 + static const struct is31fl32xx_chipdef is31fl3218_cdef = { 477 + .channels = 18, 478 + .shutdown_reg = 0x00, 479 + .pwm_update_reg = 0x16, 480 + .global_control_reg = IS31FL32XX_REG_NONE, 481 + .reset_reg = 0x17, 482 + .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 483 + .pwm_register_base = 0x01, 484 + .led_control_register_base = 0x13, 485 + .enable_bits_per_led_control_register = 6, 486 + .brightness_steps = 256, 487 + }; 488 + 489 + static const struct is31fl32xx_chipdef is31fl3216_cdef = { 490 + .channels = 16, 491 + .shutdown_reg = IS31FL32XX_REG_NONE, 492 + .pwm_update_reg = 0xB0, 493 + .global_control_reg = IS31FL32XX_REG_NONE, 494 + .reset_reg = IS31FL32XX_REG_NONE, 495 + .output_frequency_setting_reg = IS31FL32XX_REG_NONE, 496 + .pwm_register_base = 0x10, 497 + .pwm_registers_reversed = true, 498 + .led_control_register_base = 0x01, 499 + .enable_bits_per_led_control_register = 8, 500 + .reset_func = is31fl3216_reset, 501 + .sw_shutdown_func = is31fl3216_software_shutdown, 502 + .brightness_steps = 256, 503 + }; 504 + 505 + static const struct is31fl32xx_chipdef is31fl3293_cdef = { 506 + .channels = 3, 507 + .shutdown_reg = IS31FL32XX_REG_NONE, 508 + .pwm_update_reg = 0x28, 509 + .pwm_update_value = 0xc5, 510 + .global_control_reg = IS31FL32XX_REG_NONE, 511 + .reset_reg = IS31FL32XX_REG_NONE, 512 + .pwm_register_base = 0x19, 513 + .led_control_register_base = IS31FL32XX_REG_NONE, 514 + .brightness_steps = 4096, 515 + .reset_func = is31fl3293_reset, 516 + .sw_shutdown_func = is31fl3293_software_shutdown, 517 + }; 474 518 475 519 static const struct of_device_id of_is31fl32xx_match[] = { 520 + { .compatible = "issi,is31fl3293", .data = &is31fl3293_cdef, }, 476 521 { .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, }, 477 522 { .compatible = "issi,is31fl3236a", .data = &is31fl3236a_cdef, }, 478 523 { .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, }, ··· 589 472 priv->cdef = cdef; 590 473 i2c_set_clientdata(client, priv); 591 474 592 - ret = is31fl32xx_init_regs(priv); 475 + ret = is31fl32xx_parse_dt(dev, priv); 593 476 if (ret) 594 477 return ret; 595 478 596 - ret = is31fl32xx_parse_dt(dev, priv); 479 + ret = is31fl32xx_init_regs(priv); 597 480 if (ret) 598 481 return ret; 599 482 ··· 616 499 * even though it is not used for DeviceTree based instantiation. 617 500 */ 618 501 static const struct i2c_device_id is31fl32xx_id[] = { 502 + { "is31fl3293" }, 619 503 { "is31fl3236" }, 620 504 { "is31fl3236a" }, 621 505 { "is31fl3235" },
+3
drivers/leds/leds-lm3692x.c
··· 104 104 * @regulator: LED supply regulator pointer 105 105 * @led_enable: LED sync to be enabled 106 106 * @model_id: Current device model ID enumerated 107 + * @boost_ctrl: Cached configuration for the boost control register 108 + * @brightness_ctrl: Cached configuration for brightness/brightness control 109 + * @enabled: Cached enable state of the device 107 110 */ 108 111 struct lm3692x_led { 109 112 struct mutex lock;
+2 -5
drivers/leds/leds-lp55xx-common.c
··· 1204 1204 struct device_node *np, 1205 1205 struct lp55xx_chip *chip) 1206 1206 { 1207 - struct device_node *child; 1208 1207 struct lp55xx_platform_data *pdata; 1209 1208 struct lp55xx_led_config *cfg; 1210 1209 int num_channels; ··· 1228 1229 pdata->num_channels = num_channels; 1229 1230 cfg->max_channel = chip->cfg->max_channel; 1230 1231 1231 - for_each_available_child_of_node(np, child) { 1232 + for_each_available_child_of_node_scoped(np, child) { 1232 1233 ret = lp55xx_parse_logical_led(child, cfg, i); 1233 - if (ret) { 1234 - of_node_put(child); 1234 + if (ret) 1235 1235 return ERR_PTR(-EINVAL); 1236 - } 1237 1236 i++; 1238 1237 } 1239 1238
+13
drivers/leds/rgb/Kconfig
··· 26 26 To compile this driver as a module, choose M here: the module 27 27 will be called leds-ktd202x. 28 28 29 + config LEDS_LP5812 30 + tristate "LED support for Texas Instruments LP5812" 31 + depends on I2C 32 + help 33 + If you say Y here you get support for TI LP5812 LED driver. 34 + The LP5812 is a 4x3 matrix RGB LED driver with autonomous 35 + animation engine control. 36 + 37 + To compile this driver as a module, choose M here: the 38 + module will be called leds-lp5812. 39 + 40 + If unsure, say N. 41 + 29 42 config LEDS_NCP5623 30 43 tristate "LED support for NCP5623" 31 44 depends on I2C
+1
drivers/leds/rgb/Makefile
··· 2 2 3 3 obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o 4 4 obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o 5 + obj-$(CONFIG_LEDS_LP5812) += leds-lp5812.o 5 6 obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o 6 7 obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o 7 8 obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
+642
drivers/leds/rgb/leds-lp5812.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * LP5812 LED driver 4 + * 5 + * Copyright (C) 2025 Texas Instruments 6 + * 7 + * Author: Jared Zhou <jared-zhou@ti.com> 8 + */ 9 + 10 + #include <linux/delay.h> 11 + #include <linux/i2c.h> 12 + #include <linux/init.h> 13 + #include <linux/kernel.h> 14 + #include <linux/led-class-multicolor.h> 15 + #include <linux/leds.h> 16 + #include <linux/module.h> 17 + #include <linux/mutex.h> 18 + #include <linux/sysfs.h> 19 + #include <linux/types.h> 20 + 21 + #include "leds-lp5812.h" 22 + 23 + static const struct lp5812_mode_mapping chip_mode_map[] = { 24 + {"direct_mode", 0, 0, 0, 0, 0, 0}, 25 + {"tcm:1:0", 1, 0, 0, 0, 0, 0}, 26 + {"tcm:1:1", 1, 1, 0, 0, 0, 0}, 27 + {"tcm:1:2", 1, 2, 0, 0, 0, 0}, 28 + {"tcm:1:3", 1, 3, 0, 0, 0, 0}, 29 + {"tcm:2:0:1", 2, 0, 1, 0, 0, 0}, 30 + {"tcm:2:0:2", 2, 0, 2, 0, 0, 0}, 31 + {"tcm:2:0:3", 2, 0, 3, 0, 0, 0}, 32 + {"tcm:2:1:2", 2, 1, 2, 0, 0, 0}, 33 + {"tcm:2:1:3", 2, 1, 3, 0, 0, 0}, 34 + {"tcm:2:2:3", 2, 2, 3, 0, 0, 0}, 35 + {"tcm:3:0:1:2", 3, 0, 1, 2, 0, 0}, 36 + {"tcm:3:0:1:3", 3, 0, 1, 3, 0, 0}, 37 + {"tcm:3:0:2:3", 3, 0, 2, 3, 0, 0}, 38 + {"tcm:4:0:1:2:3", 4, 0, 1, 2, 3, 0}, 39 + {"mix:1:0:1", 5, 1, 0, 0, 0, 0}, 40 + {"mix:1:0:2", 5, 2, 0, 0, 0, 0}, 41 + {"mix:1:0:3", 5, 3, 0, 0, 0, 0}, 42 + {"mix:1:1:0", 5, 0, 0, 0, 0, 1}, 43 + {"mix:1:1:2", 5, 2, 0, 0, 0, 1}, 44 + {"mix:1:1:3", 5, 3, 0, 0, 0, 1}, 45 + {"mix:1:2:0", 5, 0, 0, 0, 0, 2}, 46 + {"mix:1:2:1", 5, 1, 0, 0, 0, 2}, 47 + {"mix:1:2:3", 5, 3, 0, 0, 0, 2}, 48 + {"mix:1:3:0", 5, 0, 0, 0, 0, 3}, 49 + {"mix:1:3:1", 5, 1, 0, 0, 0, 3}, 50 + {"mix:1:3:2", 5, 2, 0, 0, 0, 3}, 51 + {"mix:2:0:1:2", 6, 1, 2, 0, 0, 0}, 52 + {"mix:2:0:1:3", 6, 1, 3, 0, 0, 0}, 53 + {"mix:2:0:2:3", 6, 2, 3, 0, 0, 0}, 54 + {"mix:2:1:0:2", 6, 0, 2, 0, 0, 1}, 55 + {"mix:2:1:0:3", 6, 0, 3, 0, 0, 1}, 56 + {"mix:2:1:2:3", 6, 2, 3, 0, 0, 1}, 57 + {"mix:2:2:0:1", 6, 0, 1, 0, 0, 2}, 58 + {"mix:2:2:0:3", 6, 0, 3, 0, 0, 2}, 59 + {"mix:2:2:1:3", 6, 1, 3, 0, 0, 2}, 60 + {"mix:2:3:0:1", 6, 0, 1, 0, 0, 3}, 61 + {"mix:2:3:0:2", 6, 0, 2, 0, 0, 3}, 62 + {"mix:2:3:1:2", 6, 1, 2, 0, 0, 3}, 63 + {"mix:3:0:1:2:3", 7, 1, 2, 3, 0, 0}, 64 + {"mix:3:1:0:2:3", 7, 0, 2, 3, 0, 1}, 65 + {"mix:3:2:0:1:3", 7, 0, 1, 3, 0, 2}, 66 + {"mix:3:3:0:1:2", 7, 0, 1, 2, 0, 3} 67 + }; 68 + 69 + static int lp5812_write(struct lp5812_chip *chip, u16 reg, u8 val) 70 + { 71 + struct device *dev = &chip->client->dev; 72 + struct i2c_msg msg; 73 + u8 buf[LP5812_DATA_LENGTH]; 74 + u8 reg_addr_bit8_9; 75 + int ret; 76 + 77 + /* Extract register address bits 9 and 8 for Address Byte 1 */ 78 + reg_addr_bit8_9 = (reg >> LP5812_REG_ADDR_HIGH_SHIFT) & LP5812_REG_ADDR_BIT_8_9_MASK; 79 + 80 + /* Prepare payload: Address Byte 2 (bits [7:0]) and value to write */ 81 + buf[LP5812_DATA_BYTE_0_IDX] = (u8)(reg & LP5812_REG_ADDR_LOW_MASK); 82 + buf[LP5812_DATA_BYTE_1_IDX] = val; 83 + 84 + /* Construct I2C message for a write operation */ 85 + msg.addr = (chip->client->addr << LP5812_CHIP_ADDR_SHIFT) | reg_addr_bit8_9; 86 + msg.flags = 0; 87 + msg.len = sizeof(buf); 88 + msg.buf = buf; 89 + 90 + ret = i2c_transfer(chip->client->adapter, &msg, 1); 91 + if (ret == 1) 92 + return 0; 93 + 94 + dev_err(dev, "I2C write error, ret=%d\n", ret); 95 + return ret < 0 ? ret : -EIO; 96 + } 97 + 98 + static int lp5812_read(struct lp5812_chip *chip, u16 reg, u8 *val) 99 + { 100 + struct device *dev = &chip->client->dev; 101 + struct i2c_msg msgs[LP5812_READ_MSG_LENGTH]; 102 + u8 ret_val; 103 + u8 reg_addr_bit8_9; 104 + u8 converted_reg; 105 + int ret; 106 + 107 + /* Extract register address bits 9 and 8 for Address Byte 1 */ 108 + reg_addr_bit8_9 = (reg >> LP5812_REG_ADDR_HIGH_SHIFT) & LP5812_REG_ADDR_BIT_8_9_MASK; 109 + 110 + /* Lower 8 bits go in Address Byte 2 */ 111 + converted_reg = (u8)(reg & LP5812_REG_ADDR_LOW_MASK); 112 + 113 + /* Prepare I2C write message to set register address */ 114 + msgs[LP5812_MSG_0_IDX].addr = 115 + (chip->client->addr << LP5812_CHIP_ADDR_SHIFT) | reg_addr_bit8_9; 116 + msgs[LP5812_MSG_0_IDX].flags = 0; 117 + msgs[LP5812_MSG_0_IDX].len = 1; 118 + msgs[LP5812_MSG_0_IDX].buf = &converted_reg; 119 + 120 + /* Prepare I2C read message to retrieve register value */ 121 + msgs[LP5812_MSG_1_IDX].addr = 122 + (chip->client->addr << LP5812_CHIP_ADDR_SHIFT) | reg_addr_bit8_9; 123 + msgs[LP5812_MSG_1_IDX].flags = I2C_M_RD; 124 + msgs[LP5812_MSG_1_IDX].len = 1; 125 + msgs[LP5812_MSG_1_IDX].buf = &ret_val; 126 + 127 + ret = i2c_transfer(chip->client->adapter, msgs, LP5812_READ_MSG_LENGTH); 128 + if (ret == LP5812_READ_MSG_LENGTH) { 129 + *val = ret_val; 130 + return 0; 131 + } 132 + 133 + dev_err(dev, "I2C read error, ret=%d\n", ret); 134 + *val = 0; 135 + return ret < 0 ? ret : -EIO; 136 + } 137 + 138 + static int lp5812_read_tsd_config_status(struct lp5812_chip *chip, u8 *reg_val) 139 + { 140 + return lp5812_read(chip, LP5812_TSD_CONFIG_STATUS, reg_val); 141 + } 142 + 143 + static int lp5812_update_regs_config(struct lp5812_chip *chip) 144 + { 145 + u8 reg_val; 146 + int ret; 147 + 148 + ret = lp5812_write(chip, LP5812_CMD_UPDATE, LP5812_UPDATE_CMD_VAL); 149 + if (ret) 150 + return ret; 151 + 152 + ret = lp5812_read_tsd_config_status(chip, &reg_val); 153 + if (ret) 154 + return ret; 155 + 156 + return reg_val & LP5812_CFG_ERR_STATUS_MASK; 157 + } 158 + 159 + static ssize_t parse_drive_mode(struct lp5812_chip *chip, const char *str) 160 + { 161 + int i; 162 + 163 + chip->drive_mode.bits.mix_sel_led_0 = false; 164 + chip->drive_mode.bits.mix_sel_led_1 = false; 165 + chip->drive_mode.bits.mix_sel_led_2 = false; 166 + chip->drive_mode.bits.mix_sel_led_3 = false; 167 + 168 + if (sysfs_streq(str, LP5812_MODE_DIRECT_NAME)) { 169 + chip->drive_mode.bits.led_mode = LP5812_MODE_DIRECT_VALUE; 170 + return 0; 171 + } 172 + 173 + for (i = 0; i < ARRAY_SIZE(chip_mode_map); i++) { 174 + if (!sysfs_streq(str, chip_mode_map[i].mode_name)) 175 + continue; 176 + 177 + chip->drive_mode.bits.led_mode = chip_mode_map[i].mode; 178 + chip->scan_order.bits.order0 = chip_mode_map[i].scan_order_0; 179 + chip->scan_order.bits.order1 = chip_mode_map[i].scan_order_1; 180 + chip->scan_order.bits.order2 = chip_mode_map[i].scan_order_2; 181 + chip->scan_order.bits.order3 = chip_mode_map[i].scan_order_3; 182 + 183 + switch (chip_mode_map[i].selection_led) { 184 + case LP5812_MODE_MIX_SELECT_LED_0: 185 + chip->drive_mode.bits.mix_sel_led_0 = true; 186 + break; 187 + case LP5812_MODE_MIX_SELECT_LED_1: 188 + chip->drive_mode.bits.mix_sel_led_1 = true; 189 + break; 190 + case LP5812_MODE_MIX_SELECT_LED_2: 191 + chip->drive_mode.bits.mix_sel_led_2 = true; 192 + break; 193 + case LP5812_MODE_MIX_SELECT_LED_3: 194 + chip->drive_mode.bits.mix_sel_led_3 = true; 195 + break; 196 + default: 197 + return -EINVAL; 198 + } 199 + 200 + return 0; 201 + } 202 + 203 + return -EINVAL; 204 + } 205 + 206 + static int lp5812_set_drive_mode_scan_order(struct lp5812_chip *chip) 207 + { 208 + u8 val; 209 + int ret; 210 + 211 + val = chip->drive_mode.val; 212 + ret = lp5812_write(chip, LP5812_DEV_CONFIG1, val); 213 + if (ret) 214 + return ret; 215 + 216 + val = chip->scan_order.val; 217 + ret = lp5812_write(chip, LP5812_DEV_CONFIG2, val); 218 + 219 + return ret; 220 + } 221 + 222 + static int lp5812_set_led_mode(struct lp5812_chip *chip, int led_number, 223 + enum control_mode mode) 224 + { 225 + u8 reg_val; 226 + u16 reg; 227 + int ret; 228 + 229 + /* 230 + * Select device configuration register. 231 + * Reg3 for LED_0–LED_3, LED_A0–A2, LED_B0 232 + * Reg4 for LED_B1–B2, LED_C0–C2, LED_D0–D2 233 + */ 234 + if (led_number < LP5812_NUMBER_LED_IN_REG) 235 + reg = LP5812_DEV_CONFIG3; 236 + else 237 + reg = LP5812_DEV_CONFIG4; 238 + 239 + ret = lp5812_read(chip, reg, &reg_val); 240 + if (ret) 241 + return ret; 242 + 243 + if (mode == LP5812_MODE_MANUAL) 244 + reg_val &= ~(LP5812_ENABLE << (led_number % LP5812_NUMBER_LED_IN_REG)); 245 + else 246 + reg_val |= (LP5812_ENABLE << (led_number % LP5812_NUMBER_LED_IN_REG)); 247 + 248 + ret = lp5812_write(chip, reg, reg_val); 249 + if (ret) 250 + return ret; 251 + 252 + ret = lp5812_update_regs_config(chip); 253 + 254 + return ret; 255 + } 256 + 257 + static int lp5812_manual_dc_pwm_control(struct lp5812_chip *chip, int led_number, 258 + u8 val, enum dimming_type dimming_type) 259 + { 260 + u16 led_base_reg; 261 + int ret; 262 + 263 + if (dimming_type == LP5812_DIMMING_ANALOG) 264 + led_base_reg = LP5812_MANUAL_DC_BASE; 265 + else 266 + led_base_reg = LP5812_MANUAL_PWM_BASE; 267 + 268 + ret = lp5812_write(chip, led_base_reg + led_number, val); 269 + 270 + return ret; 271 + } 272 + 273 + static int lp5812_multicolor_brightness(struct lp5812_led *led) 274 + { 275 + struct lp5812_chip *chip = led->chip; 276 + int ret, i; 277 + 278 + guard(mutex)(&chip->lock); 279 + for (i = 0; i < led->mc_cdev.num_colors; i++) { 280 + ret = lp5812_manual_dc_pwm_control(chip, led->mc_cdev.subled_info[i].channel, 281 + led->mc_cdev.subled_info[i].brightness, 282 + LP5812_DIMMING_PWM); 283 + if (ret) 284 + return ret; 285 + } 286 + 287 + return 0; 288 + } 289 + 290 + static int lp5812_led_brightness(struct lp5812_led *led) 291 + { 292 + struct lp5812_chip *chip = led->chip; 293 + struct lp5812_led_config *led_cfg; 294 + int ret; 295 + 296 + led_cfg = &chip->led_config[led->chan_nr]; 297 + 298 + guard(mutex)(&chip->lock); 299 + ret = lp5812_manual_dc_pwm_control(chip, led_cfg->led_id[0], 300 + led->brightness, LP5812_DIMMING_PWM); 301 + 302 + return ret; 303 + } 304 + 305 + static int lp5812_set_brightness(struct led_classdev *cdev, 306 + enum led_brightness brightness) 307 + { 308 + struct lp5812_led *led = container_of(cdev, struct lp5812_led, cdev); 309 + 310 + led->brightness = (u8)brightness; 311 + 312 + return lp5812_led_brightness(led); 313 + } 314 + 315 + static int lp5812_set_mc_brightness(struct led_classdev *cdev, 316 + enum led_brightness brightness) 317 + { 318 + struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev); 319 + struct lp5812_led *led = container_of(mc_dev, struct lp5812_led, mc_cdev); 320 + 321 + led_mc_calc_color_components(&led->mc_cdev, brightness); 322 + 323 + return lp5812_multicolor_brightness(led); 324 + } 325 + 326 + static int lp5812_init_led(struct lp5812_led *led, struct lp5812_chip *chip, int chan) 327 + { 328 + struct device *dev = &chip->client->dev; 329 + struct mc_subled *mc_led_info; 330 + struct led_classdev *led_cdev; 331 + int i, ret; 332 + 333 + if (chip->led_config[chan].name) { 334 + led->cdev.name = chip->led_config[chan].name; 335 + } else { 336 + led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:channel%d", 337 + chip->label ? : chip->client->name, chan); 338 + if (!led->cdev.name) 339 + return -ENOMEM; 340 + } 341 + 342 + if (!chip->led_config[chan].is_sc_led) { 343 + mc_led_info = devm_kcalloc(dev, chip->led_config[chan].num_colors, 344 + sizeof(*mc_led_info), GFP_KERNEL); 345 + if (!mc_led_info) 346 + return -ENOMEM; 347 + 348 + led_cdev = &led->mc_cdev.led_cdev; 349 + led_cdev->name = led->cdev.name; 350 + led_cdev->brightness_set_blocking = lp5812_set_mc_brightness; 351 + led->mc_cdev.num_colors = chip->led_config[chan].num_colors; 352 + 353 + for (i = 0; i < led->mc_cdev.num_colors; i++) { 354 + mc_led_info[i].color_index = chip->led_config[chan].color_id[i]; 355 + mc_led_info[i].channel = chip->led_config[chan].led_id[i]; 356 + } 357 + 358 + led->mc_cdev.subled_info = mc_led_info; 359 + } else { 360 + led->cdev.brightness_set_blocking = lp5812_set_brightness; 361 + } 362 + 363 + led->chan_nr = chan; 364 + 365 + if (chip->led_config[chan].is_sc_led) { 366 + ret = devm_led_classdev_register(dev, &led->cdev); 367 + if (ret == 0) 368 + led->cdev.dev->platform_data = led; 369 + } else { 370 + ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev); 371 + if (ret == 0) 372 + led->mc_cdev.led_cdev.dev->platform_data = led; 373 + } 374 + 375 + return ret; 376 + } 377 + 378 + static int lp5812_register_leds(struct lp5812_led *leds, struct lp5812_chip *chip) 379 + { 380 + struct lp5812_led *led; 381 + int num_channels = chip->num_channels; 382 + u8 reg_val; 383 + u16 reg; 384 + int ret, i, j; 385 + 386 + for (i = 0; i < num_channels; i++) { 387 + led = &leds[i]; 388 + ret = lp5812_init_led(led, chip, i); 389 + if (ret) 390 + goto err_init_led; 391 + 392 + led->chip = chip; 393 + 394 + for (j = 0; j < chip->led_config[i].num_colors; j++) { 395 + ret = lp5812_write(chip, 396 + LP5812_AUTO_DC_BASE + chip->led_config[i].led_id[j], 397 + chip->led_config[i].max_current[j]); 398 + if (ret) 399 + goto err_init_led; 400 + 401 + ret = lp5812_manual_dc_pwm_control(chip, chip->led_config[i].led_id[j], 402 + chip->led_config[i].max_current[j], 403 + LP5812_DIMMING_ANALOG); 404 + if (ret) 405 + goto err_init_led; 406 + 407 + ret = lp5812_set_led_mode(chip, chip->led_config[i].led_id[j], 408 + LP5812_MODE_MANUAL); 409 + if (ret) 410 + goto err_init_led; 411 + 412 + reg = (chip->led_config[i].led_id[j] < LP5812_NUMBER_LED_IN_REG) ? 413 + LP5812_LED_EN_1 : LP5812_LED_EN_2; 414 + 415 + ret = lp5812_read(chip, reg, &reg_val); 416 + if (ret) 417 + goto err_init_led; 418 + 419 + reg_val |= (LP5812_ENABLE << (chip->led_config[i].led_id[j] % 420 + LP5812_NUMBER_LED_IN_REG)); 421 + 422 + ret = lp5812_write(chip, reg, reg_val); 423 + if (ret) 424 + goto err_init_led; 425 + } 426 + } 427 + 428 + return 0; 429 + 430 + err_init_led: 431 + return ret; 432 + } 433 + 434 + static int lp5812_init_device(struct lp5812_chip *chip) 435 + { 436 + int ret; 437 + 438 + usleep_range(LP5812_WAIT_DEVICE_STABLE_MIN, LP5812_WAIT_DEVICE_STABLE_MAX); 439 + 440 + ret = lp5812_write(chip, LP5812_REG_ENABLE, LP5812_ENABLE); 441 + if (ret) { 442 + dev_err(&chip->client->dev, "failed to enable LP5812 device\n"); 443 + return ret; 444 + } 445 + 446 + ret = lp5812_write(chip, LP5812_DEV_CONFIG12, LP5812_LSD_LOD_START_UP); 447 + if (ret) { 448 + dev_err(&chip->client->dev, "failed to configure device safety thresholds\n"); 449 + return ret; 450 + } 451 + 452 + ret = parse_drive_mode(chip, chip->scan_mode); 453 + if (ret) 454 + return ret; 455 + 456 + ret = lp5812_set_drive_mode_scan_order(chip); 457 + if (ret) 458 + return ret; 459 + 460 + ret = lp5812_update_regs_config(chip); 461 + if (ret) { 462 + dev_err(&chip->client->dev, "failed to apply configuration updates\n"); 463 + return ret; 464 + } 465 + 466 + return 0; 467 + } 468 + 469 + static void lp5812_deinit_device(struct lp5812_chip *chip) 470 + { 471 + lp5812_write(chip, LP5812_LED_EN_1, LP5812_DISABLE); 472 + lp5812_write(chip, LP5812_LED_EN_2, LP5812_DISABLE); 473 + lp5812_write(chip, LP5812_REG_ENABLE, LP5812_DISABLE); 474 + } 475 + 476 + static int lp5812_parse_led_channel(struct device_node *np, 477 + struct lp5812_led_config *cfg, 478 + int color_number) 479 + { 480 + int color_id, reg, ret; 481 + u32 max_cur; 482 + 483 + ret = of_property_read_u32(np, "reg", &reg); 484 + if (ret) 485 + return ret; 486 + 487 + cfg->led_id[color_number] = reg; 488 + 489 + ret = of_property_read_u32(np, "led-max-microamp", &max_cur); 490 + if (ret) 491 + max_cur = 0; 492 + /* Convert microamps to driver units */ 493 + cfg->max_current[color_number] = max_cur / 100; 494 + 495 + ret = of_property_read_u32(np, "color", &color_id); 496 + if (ret) 497 + color_id = 0; 498 + cfg->color_id[color_number] = color_id; 499 + 500 + return 0; 501 + } 502 + 503 + static int lp5812_parse_led(struct device_node *np, 504 + struct lp5812_led_config *cfg, 505 + int led_index) 506 + { 507 + int num_colors, ret; 508 + 509 + of_property_read_string(np, "label", &cfg[led_index].name); 510 + 511 + ret = of_property_read_u32(np, "reg", &cfg[led_index].chan_nr); 512 + if (ret) 513 + return ret; 514 + 515 + num_colors = 0; 516 + for_each_available_child_of_node_scoped(np, child) { 517 + ret = lp5812_parse_led_channel(child, &cfg[led_index], num_colors); 518 + if (ret) 519 + return ret; 520 + 521 + num_colors++; 522 + } 523 + 524 + if (num_colors == 0) { 525 + ret = lp5812_parse_led_channel(np, &cfg[led_index], 0); 526 + if (ret) 527 + return ret; 528 + 529 + num_colors = 1; 530 + cfg[led_index].is_sc_led = true; 531 + } else { 532 + cfg[led_index].is_sc_led = false; 533 + } 534 + 535 + cfg[led_index].num_colors = num_colors; 536 + 537 + return 0; 538 + } 539 + 540 + static int lp5812_of_probe(struct device *dev, 541 + struct device_node *np, 542 + struct lp5812_chip *chip) 543 + { 544 + struct lp5812_led_config *cfg; 545 + int num_channels, i = 0, ret; 546 + 547 + num_channels = of_get_available_child_count(np); 548 + if (num_channels == 0) { 549 + dev_err(dev, "no LED channels\n"); 550 + return -EINVAL; 551 + } 552 + 553 + cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL); 554 + if (!cfg) 555 + return -ENOMEM; 556 + 557 + chip->led_config = &cfg[0]; 558 + chip->num_channels = num_channels; 559 + 560 + for_each_available_child_of_node_scoped(np, child) { 561 + ret = lp5812_parse_led(child, cfg, i); 562 + if (ret) 563 + return -EINVAL; 564 + i++; 565 + } 566 + 567 + ret = of_property_read_string(np, "ti,scan-mode", &chip->scan_mode); 568 + if (ret) 569 + chip->scan_mode = LP5812_MODE_DIRECT_NAME; 570 + 571 + of_property_read_string(np, "label", &chip->label); 572 + 573 + return 0; 574 + } 575 + 576 + static int lp5812_probe(struct i2c_client *client) 577 + { 578 + struct lp5812_chip *chip; 579 + struct device_node *np = dev_of_node(&client->dev); 580 + struct lp5812_led *leds; 581 + int ret; 582 + 583 + if (!np) 584 + return -EINVAL; 585 + 586 + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 587 + if (!chip) 588 + return -ENOMEM; 589 + 590 + ret = lp5812_of_probe(&client->dev, np, chip); 591 + if (ret) 592 + return ret; 593 + 594 + leds = devm_kcalloc(&client->dev, chip->num_channels, sizeof(*leds), GFP_KERNEL); 595 + if (!leds) 596 + return -ENOMEM; 597 + 598 + chip->client = client; 599 + mutex_init(&chip->lock); 600 + i2c_set_clientdata(client, chip); 601 + 602 + ret = lp5812_init_device(chip); 603 + if (ret) 604 + return ret; 605 + 606 + ret = lp5812_register_leds(leds, chip); 607 + if (ret) 608 + goto err_out; 609 + 610 + return 0; 611 + 612 + err_out: 613 + lp5812_deinit_device(chip); 614 + return ret; 615 + } 616 + 617 + static void lp5812_remove(struct i2c_client *client) 618 + { 619 + struct lp5812_chip *chip = i2c_get_clientdata(client); 620 + 621 + lp5812_deinit_device(chip); 622 + } 623 + 624 + static const struct of_device_id of_lp5812_match[] = { 625 + { .compatible = "ti,lp5812" }, 626 + { /* sentinel */ } 627 + }; 628 + MODULE_DEVICE_TABLE(of, of_lp5812_match); 629 + 630 + static struct i2c_driver lp5812_driver = { 631 + .driver = { 632 + .name = "lp5812", 633 + .of_match_table = of_lp5812_match, 634 + }, 635 + .probe = lp5812_probe, 636 + .remove = lp5812_remove, 637 + }; 638 + module_i2c_driver(lp5812_driver); 639 + 640 + MODULE_DESCRIPTION("Texas Instruments LP5812 LED Driver"); 641 + MODULE_AUTHOR("Jared Zhou <jared-zhou@ti.com>"); 642 + MODULE_LICENSE("GPL");
+172
drivers/leds/rgb/leds-lp5812.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * LP5812 Driver Header 4 + * 5 + * Copyright (C) 2025 Texas Instruments 6 + * 7 + * Author: Jared Zhou <jared-zhou@ti.com> 8 + */ 9 + 10 + #ifndef _LP5812_H_ 11 + #define _LP5812_H_ 12 + 13 + #include <linux/delay.h> 14 + #include <linux/i2c.h> 15 + #include <linux/kernel.h> 16 + #include <linux/led-class-multicolor.h> 17 + #include <linux/leds.h> 18 + #include <linux/mutex.h> 19 + #include <linux/sysfs.h> 20 + #include <linux/types.h> 21 + 22 + #define LP5812_REG_ENABLE 0x0000 23 + #define LP5812_REG_RESET 0x0023 24 + #define LP5812_DEV_CONFIG0 0x0001 25 + #define LP5812_DEV_CONFIG1 0x0002 26 + #define LP5812_DEV_CONFIG2 0x0003 27 + #define LP5812_DEV_CONFIG3 0x0004 28 + #define LP5812_DEV_CONFIG4 0x0005 29 + #define LP5812_DEV_CONFIG5 0x0006 30 + #define LP5812_DEV_CONFIG6 0x0007 31 + #define LP5812_DEV_CONFIG7 0x0008 32 + #define LP5812_DEV_CONFIG8 0x0009 33 + #define LP5812_DEV_CONFIG9 0x000A 34 + #define LP5812_DEV_CONFIG10 0x000B 35 + #define LP5812_DEV_CONFIG11 0x000c 36 + #define LP5812_DEV_CONFIG12 0x000D 37 + #define LP5812_CMD_UPDATE 0x0010 38 + #define LP5812_LED_EN_1 0x0020 39 + #define LP5812_LED_EN_2 0x0021 40 + #define LP5812_FAULT_CLEAR 0x0022 41 + #define LP5812_MANUAL_DC_BASE 0x0030 42 + #define LP5812_AUTO_DC_BASE 0x0050 43 + #define LP5812_MANUAL_PWM_BASE 0x0040 44 + 45 + #define LP5812_TSD_CONFIG_STATUS 0x0300 46 + #define LP5812_LOD_STATUS 0x0301 47 + #define LP5812_LSD_STATUS 0x0303 48 + 49 + #define LP5812_ENABLE 0x01 50 + #define LP5812_DISABLE 0x00 51 + #define FAULT_CLEAR_ALL 0x07 52 + #define TSD_CLEAR_VAL 0x04 53 + #define LSD_CLEAR_VAL 0x02 54 + #define LOD_CLEAR_VAL 0x01 55 + #define LP5812_RESET 0x66 56 + #define LP5812_DEV_CONFIG12_DEFAULT 0x08 57 + 58 + #define LP5812_UPDATE_CMD_VAL 0x55 59 + #define LP5812_REG_ADDR_HIGH_SHIFT 8 60 + #define LP5812_REG_ADDR_BIT_8_9_MASK 0x03 61 + #define LP5812_REG_ADDR_LOW_MASK 0xFF 62 + #define LP5812_CHIP_ADDR_SHIFT 2 63 + #define LP5812_DATA_LENGTH 2 64 + #define LP5812_DATA_BYTE_0_IDX 0 65 + #define LP5812_DATA_BYTE_1_IDX 1 66 + 67 + #define LP5812_READ_MSG_LENGTH 2 68 + #define LP5812_MSG_0_IDX 0 69 + #define LP5812_MSG_1_IDX 1 70 + #define LP5812_CFG_ERR_STATUS_MASK 0x01 71 + #define LP5812_CFG_TSD_STATUS_SHIFT 1 72 + #define LP5812_CFG_TSD_STATUS_MASK 0x01 73 + 74 + #define LP5812_FAULT_CLEAR_LOD 0 75 + #define LP5812_FAULT_CLEAR_LSD 1 76 + #define LP5812_FAULT_CLEAR_TSD 2 77 + #define LP5812_FAULT_CLEAR_ALL 3 78 + #define LP5812_NUMBER_LED_IN_REG 8 79 + 80 + #define LP5812_WAIT_DEVICE_STABLE_MIN 1000 81 + #define LP5812_WAIT_DEVICE_STABLE_MAX 1100 82 + 83 + #define LP5812_LSD_LOD_START_UP 0x0B 84 + #define LP5812_MODE_NAME_MAX_LEN 20 85 + #define LP5812_MODE_DIRECT_NAME "direct_mode" 86 + #define LP5812_MODE_DIRECT_VALUE 0 87 + #define LP5812_MODE_MIX_SELECT_LED_0 0 88 + #define LP5812_MODE_MIX_SELECT_LED_1 1 89 + #define LP5812_MODE_MIX_SELECT_LED_2 2 90 + #define LP5812_MODE_MIX_SELECT_LED_3 3 91 + 92 + enum control_mode { 93 + LP5812_MODE_MANUAL = 0, 94 + LP5812_MODE_AUTONOMOUS 95 + }; 96 + 97 + enum dimming_type { 98 + LP5812_DIMMING_ANALOG, 99 + LP5812_DIMMING_PWM 100 + }; 101 + 102 + union lp5812_scan_order { 103 + struct { 104 + u8 order0:2; 105 + u8 order1:2; 106 + u8 order2:2; 107 + u8 order3:2; 108 + } bits; 109 + u8 val; 110 + }; 111 + 112 + union lp5812_drive_mode { 113 + struct { 114 + u8 mix_sel_led_0:1; 115 + u8 mix_sel_led_1:1; 116 + u8 mix_sel_led_2:1; 117 + u8 mix_sel_led_3:1; 118 + u8 led_mode:3; 119 + u8 pwm_fre:1; 120 + } bits; 121 + u8 val; 122 + }; 123 + 124 + struct lp5812_reg { 125 + u16 addr; 126 + union { 127 + u8 val; 128 + u8 mask; 129 + u8 shift; 130 + }; 131 + }; 132 + 133 + struct lp5812_mode_mapping { 134 + char mode_name[LP5812_MODE_NAME_MAX_LEN]; 135 + u8 mode; 136 + u8 scan_order_0; 137 + u8 scan_order_1; 138 + u8 scan_order_2; 139 + u8 scan_order_3; 140 + u8 selection_led; 141 + }; 142 + 143 + struct lp5812_led_config { 144 + bool is_sc_led; 145 + const char *name; 146 + u8 color_id[LED_COLOR_ID_MAX]; 147 + u32 max_current[LED_COLOR_ID_MAX]; 148 + int chan_nr; 149 + int num_colors; 150 + int led_id[LED_COLOR_ID_MAX]; 151 + }; 152 + 153 + struct lp5812_chip { 154 + u8 num_channels; 155 + struct i2c_client *client; 156 + struct mutex lock; /* Protects register access */ 157 + struct lp5812_led_config *led_config; 158 + const char *label; 159 + const char *scan_mode; 160 + union lp5812_scan_order scan_order; 161 + union lp5812_drive_mode drive_mode; 162 + }; 163 + 164 + struct lp5812_led { 165 + u8 brightness; 166 + int chan_nr; 167 + struct led_classdev cdev; 168 + struct led_classdev_mc mc_cdev; 169 + struct lp5812_chip *chip; 170 + }; 171 + 172 + #endif /*_LP5812_H_*/
+5 -3
drivers/leds/rgb/leds-qcom-lpg.c
··· 369 369 { 370 370 unsigned int idx; 371 371 u16 val; 372 - int i; 372 + int i, ret; 373 373 374 374 idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, 375 375 0, len, 0); ··· 379 379 for (i = 0; i < len; i++) { 380 380 val = pattern[i].brightness; 381 381 382 - regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i), 383 - &val, sizeof(val)); 382 + ret = regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i), 383 + &val, sizeof(val)); 384 + if (ret) 385 + return ret; 384 386 } 385 387 386 388 bitmap_set(lpg->lut_bitmap, idx, len);
-3
include/linux/leds-expresswire.h
··· 30 30 31 31 void expresswire_power_off(struct expresswire_common_props *props); 32 32 void expresswire_enable(struct expresswire_common_props *props); 33 - void expresswire_start(struct expresswire_common_props *props); 34 - void expresswire_end(struct expresswire_common_props *props); 35 - void expresswire_set_bit(struct expresswire_common_props *props, bool bit); 36 33 void expresswire_write_u8(struct expresswire_common_props *props, u8 val); 37 34 38 35 #endif /* _LEDS_EXPRESSWIRE_H */