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

Merge tag 'leds-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds

Pull LED updates from Pavel Machek:
"Most significant here is the driver for Qualcomm LPG. Apparently it
drives backlight on some boards, so it is quite important for some
people"

* tag 'leds-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds:
leds: qcom-lpg: Require pattern to follow documentation
leds: lp50xx: Remove duplicated error reporting in .remove()
leds: qcom-lpg: add missing PWM dependency
leds: ktd2692: Make aux-gpios optional
dt-bindings: leds: convert ktd2692 bindings to yaml
leds: ktd2692: Avoid duplicate error messages on probe deferral
leds: is31fl32xx: Improve error reporting in .remove()
leds: Move pwm-multicolor driver into rgb directory
leds: Add PWM multicolor driver
dt-bindings: leds: Add multicolor PWM LED bindings
dt-bindings: leds: Optional multi-led unit address
leds: regulator: Make probeable from device tree
leds: regulator: Add dev helper variable
dt-bindings: leds: Add regulator-led binding
leds: pca9532: Make pca9532_destroy_devices() return void
leds: Add pm8350c support to Qualcomm LPG driver
dt-bindings: leds: Add pm8350c pmic support
leds: Add driver for Qualcomm LPG
dt-bindings: leds: Add Qualcomm Light Pulse Generator binding

+2193 -93
+87
Documentation/devicetree/bindings/leds/kinetic,ktd2692.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/kinetic,ktd2692.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: KTD2692 Flash LED Driver from Kinetic Technologies 8 + 9 + maintainers: 10 + - Markuss Broks <markuss.broks@gmail.com> 11 + 12 + description: | 13 + KTD2692 is the ideal power solution for high-power flash LEDs. 14 + It uses ExpressWire single-wire programming for maximum flexibility. 15 + 16 + The ExpressWire interface through CTRL pin can control LED on/off and 17 + enable/disable the IC, Movie(max 1/3 of Flash current) / Flash mode current, 18 + Flash timeout, LVP(low voltage protection). 19 + 20 + Also, When the AUX pin is pulled high while CTRL pin is high, 21 + LED current will be ramped up to the flash-mode current level. 22 + 23 + properties: 24 + compatible: 25 + const: kinetic,ktd2692 26 + 27 + ctrl-gpios: 28 + maxItems: 1 29 + description: Specifier of the GPIO connected to CTRL pin. 30 + 31 + aux-gpios: 32 + maxItems: 1 33 + description: Specifier of the GPIO connected to CTRL pin. 34 + 35 + vin-supply: 36 + description: LED supply (2.7V to 5.5V). 37 + 38 + led: 39 + type: object 40 + $ref: common.yaml# 41 + description: Properties for the LED. 42 + properties: 43 + function: true 44 + color: true 45 + flash-max-timeout-us: 46 + description: Flash LED maximum timeout. 47 + 48 + led-max-microamp: 49 + maximum: 300000 50 + description: Minimum Threshold for Timer protection 51 + is defined internally (Maximum 300mA). 52 + 53 + flash-max-microamp: 54 + maximum: 300000 55 + description: Flash LED maximum current 56 + Formula - I(uA) = 15000000 / Rset. 57 + 58 + additionalProperties: false 59 + 60 + required: 61 + - compatible 62 + - ctrl-gpios 63 + - led 64 + 65 + additionalProperties: false 66 + 67 + examples: 68 + - | 69 + #include <dt-bindings/gpio/gpio.h> 70 + #include <dt-bindings/leds/common.h> 71 + 72 + ktd2692 { 73 + compatible = "kinetic,ktd2692"; 74 + ctrl-gpios = <&gpc0 1 0>; 75 + aux-gpios = <&gpc0 2 0>; 76 + vin-supply = <&vbat>; 77 + 78 + led { 79 + function = LED_FUNCTION_FLASH; 80 + color = <LED_COLOR_ID_WHITE>; 81 + flash-max-timeout-us = <250000>; 82 + flash-max-microamp = <150000>; 83 + led-max-microamp = <25000>; 84 + }; 85 + }; 86 + 87 + ...
+1 -1
Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml
··· 20 20 within this documentation directory. 21 21 22 22 patternProperties: 23 - "^multi-led@([0-9a-f])$": 23 + "^multi-led(@[0-9a-f])?$": 24 24 type: object 25 25 description: Represents the LEDs that are to be grouped. 26 26 properties:
-50
Documentation/devicetree/bindings/leds/leds-ktd2692.txt
··· 1 - * Kinetic Technologies - KTD2692 Flash LED Driver 2 - 3 - KTD2692 is the ideal power solution for high-power flash LEDs. 4 - It uses ExpressWire single-wire programming for maximum flexibility. 5 - 6 - The ExpressWire interface through CTRL pin can control LED on/off and 7 - enable/disable the IC, Movie(max 1/3 of Flash current) / Flash mode current, 8 - Flash timeout, LVP(low voltage protection). 9 - 10 - Also, When the AUX pin is pulled high while CTRL pin is high, 11 - LED current will be ramped up to the flash-mode current level. 12 - 13 - Required properties: 14 - - compatible : Should be "kinetic,ktd2692". 15 - - ctrl-gpios : Specifier of the GPIO connected to CTRL pin. 16 - - aux-gpios : Specifier of the GPIO connected to AUX pin. 17 - 18 - Optional properties: 19 - - vin-supply : "vin" LED supply (2.7V to 5.5V). 20 - See Documentation/devicetree/bindings/regulator/regulator.txt 21 - 22 - A discrete LED element connected to the device must be represented by a child 23 - node - See Documentation/devicetree/bindings/leds/common.txt 24 - 25 - Required properties for flash LED child nodes: 26 - See Documentation/devicetree/bindings/leds/common.txt 27 - - led-max-microamp : Minimum Threshold for Timer protection 28 - is defined internally (Maximum 300mA). 29 - - flash-max-microamp : Flash LED maximum current 30 - Formula : I(mA) = 15000 / Rset. 31 - - flash-max-timeout-us : Flash LED maximum timeout. 32 - 33 - Optional properties for flash LED child nodes: 34 - - label : See Documentation/devicetree/bindings/leds/common.txt 35 - 36 - Example: 37 - 38 - ktd2692 { 39 - compatible = "kinetic,ktd2692"; 40 - ctrl-gpios = <&gpc0 1 0>; 41 - aux-gpios = <&gpc0 2 0>; 42 - vin-supply = <&vbat>; 43 - 44 - flash-led { 45 - label = "ktd2692-flash"; 46 - led-max-microamp = <300000>; 47 - flash-max-microamp = <1500000>; 48 - flash-max-timeout-us = <1835000>; 49 - }; 50 - };
+79
Documentation/devicetree/bindings/leds/leds-pwm-multicolor.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-pwm-multicolor.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Multi-color LEDs connected to PWM 8 + 9 + maintainers: 10 + - Sven Schwermer <sven.schwermer@disruptive-technologies.com> 11 + 12 + description: | 13 + This driver combines several monochrome PWM LEDs into one multi-color 14 + LED using the multicolor LED class. 15 + 16 + properties: 17 + compatible: 18 + const: pwm-leds-multicolor 19 + 20 + multi-led: 21 + type: object 22 + 23 + patternProperties: 24 + "^led-[0-9a-z]+$": 25 + type: object 26 + $ref: common.yaml# 27 + 28 + additionalProperties: false 29 + 30 + properties: 31 + pwms: 32 + maxItems: 1 33 + 34 + pwm-names: true 35 + 36 + color: true 37 + 38 + required: 39 + - pwms 40 + - color 41 + 42 + required: 43 + - compatible 44 + 45 + allOf: 46 + - $ref: leds-class-multicolor.yaml# 47 + 48 + additionalProperties: false 49 + 50 + examples: 51 + - | 52 + #include <dt-bindings/leds/common.h> 53 + 54 + led-controller { 55 + compatible = "pwm-leds-multicolor"; 56 + 57 + multi-led { 58 + color = <LED_COLOR_ID_RGB>; 59 + function = LED_FUNCTION_INDICATOR; 60 + max-brightness = <65535>; 61 + 62 + led-red { 63 + pwms = <&pwm1 0 1000000>; 64 + color = <LED_COLOR_ID_RED>; 65 + }; 66 + 67 + led-green { 68 + pwms = <&pwm2 0 1000000>; 69 + color = <LED_COLOR_ID_GREEN>; 70 + }; 71 + 72 + led-blue { 73 + pwms = <&pwm3 0 1000000>; 74 + color = <LED_COLOR_ID_BLUE>; 75 + }; 76 + }; 77 + }; 78 + 79 + ...
+174
Documentation/devicetree/bindings/leds/leds-qcom-lpg.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-qcom-lpg.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Qualcomm Light Pulse Generator 8 + 9 + maintainers: 10 + - Bjorn Andersson <bjorn.andersson@linaro.org> 11 + 12 + description: > 13 + The Qualcomm Light Pulse Generator consists of three different hardware blocks; 14 + a ramp generator with lookup table, the light pulse generator and a three 15 + channel current sink. These blocks are found in a wide range of Qualcomm PMICs. 16 + 17 + properties: 18 + compatible: 19 + enum: 20 + - qcom,pm8150b-lpg 21 + - qcom,pm8150l-lpg 22 + - qcom,pm8350c-pwm 23 + - qcom,pm8916-pwm 24 + - qcom,pm8941-lpg 25 + - qcom,pm8994-lpg 26 + - qcom,pmc8180c-lpg 27 + - qcom,pmi8994-lpg 28 + - qcom,pmi8998-lpg 29 + 30 + "#pwm-cells": 31 + const: 2 32 + 33 + "#address-cells": 34 + const: 1 35 + 36 + "#size-cells": 37 + const: 0 38 + 39 + qcom,power-source: 40 + $ref: /schemas/types.yaml#/definitions/uint32 41 + description: 42 + power-source used to drive the output, as defined in the datasheet. 43 + Should be specified if the TRILED block is present 44 + enum: [0, 1, 3] 45 + 46 + qcom,dtest: 47 + $ref: /schemas/types.yaml#/definitions/uint32-matrix 48 + description: > 49 + A list of integer pairs, where each pair represent the dtest line the 50 + particular channel should be connected to and the flags denoting how the 51 + value should be outputed, as defined in the datasheet. The number of 52 + pairs should be the same as the number of channels. 53 + items: 54 + items: 55 + - description: dtest line to attach 56 + - description: flags for the attachment 57 + 58 + multi-led: 59 + type: object 60 + $ref: leds-class-multicolor.yaml# 61 + properties: 62 + "#address-cells": 63 + const: 1 64 + 65 + "#size-cells": 66 + const: 0 67 + 68 + patternProperties: 69 + "^led@[0-9a-f]$": 70 + type: object 71 + $ref: common.yaml# 72 + 73 + patternProperties: 74 + "^led@[0-9a-f]$": 75 + type: object 76 + $ref: common.yaml# 77 + 78 + properties: 79 + reg: true 80 + 81 + required: 82 + - reg 83 + 84 + required: 85 + - compatible 86 + 87 + additionalProperties: false 88 + 89 + examples: 90 + - | 91 + #include <dt-bindings/leds/common.h> 92 + 93 + led-controller { 94 + compatible = "qcom,pmi8994-lpg"; 95 + 96 + #address-cells = <1>; 97 + #size-cells = <0>; 98 + 99 + qcom,power-source = <1>; 100 + 101 + qcom,dtest = <0 0>, 102 + <0 0>, 103 + <0 0>, 104 + <4 1>; 105 + 106 + led@1 { 107 + reg = <1>; 108 + color = <LED_COLOR_ID_GREEN>; 109 + function = LED_FUNCTION_INDICATOR; 110 + function-enumerator = <1>; 111 + }; 112 + 113 + led@2 { 114 + reg = <2>; 115 + color = <LED_COLOR_ID_GREEN>; 116 + function = LED_FUNCTION_INDICATOR; 117 + function-enumerator = <0>; 118 + default-state = "on"; 119 + }; 120 + 121 + led@3 { 122 + reg = <3>; 123 + color = <LED_COLOR_ID_GREEN>; 124 + function = LED_FUNCTION_INDICATOR; 125 + function-enumerator = <2>; 126 + }; 127 + 128 + led@4 { 129 + reg = <4>; 130 + color = <LED_COLOR_ID_GREEN>; 131 + function = LED_FUNCTION_INDICATOR; 132 + function-enumerator = <3>; 133 + }; 134 + }; 135 + - | 136 + #include <dt-bindings/leds/common.h> 137 + 138 + led-controller { 139 + compatible = "qcom,pmi8994-lpg"; 140 + 141 + #address-cells = <1>; 142 + #size-cells = <0>; 143 + 144 + qcom,power-source = <1>; 145 + 146 + multi-led { 147 + color = <LED_COLOR_ID_RGB>; 148 + function = LED_FUNCTION_STATUS; 149 + 150 + #address-cells = <1>; 151 + #size-cells = <0>; 152 + 153 + led@1 { 154 + reg = <1>; 155 + color = <LED_COLOR_ID_RED>; 156 + }; 157 + 158 + led@2 { 159 + reg = <2>; 160 + color = <LED_COLOR_ID_GREEN>; 161 + }; 162 + 163 + led@3 { 164 + reg = <3>; 165 + color = <LED_COLOR_ID_BLUE>; 166 + }; 167 + }; 168 + }; 169 + - | 170 + pwm-controller { 171 + compatible = "qcom,pm8916-pwm"; 172 + #pwm-cells = <2>; 173 + }; 174 + ...
+55
Documentation/devicetree/bindings/leds/regulator-led.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/regulator-led.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Device Tree Bindings for Regulator LEDs 8 + 9 + maintainers: 10 + - Linus Walleij <linus.walleij@linaro.org> 11 + 12 + description: | 13 + Regulator LEDs are powered by a single regulator such that they can 14 + be turned on or off by enabling or disabling the regulator. The available 15 + brightness settings will be inferred from the available voltages on the 16 + regulator, and any constraints on the voltage or current will need to be 17 + specified on the regulator. 18 + 19 + allOf: 20 + - $ref: common.yaml# 21 + 22 + properties: 23 + $nodename: 24 + pattern: '^led.*$' 25 + 26 + compatible: 27 + const: regulator-led 28 + 29 + vled-supply: 30 + description: 31 + The regulator controlling the current to the LED. 32 + 33 + function: true 34 + color: true 35 + linux,default-trigger: true 36 + default-state: true 37 + 38 + required: 39 + - compatible 40 + - vled-supply 41 + 42 + additionalProperties: false 43 + 44 + examples: 45 + - | 46 + #include <dt-bindings/leds/common.h> 47 + 48 + led-heartbeat { 49 + compatible = "regulator-led"; 50 + vled-supply = <&regulator>; 51 + function = LED_FUNCTION_STATUS; 52 + color = <LED_COLOR_ID_BLUE>; 53 + linux,default-trigger = "heartbeat"; 54 + }; 55 + ...
+78
Documentation/leds/leds-qcom-lpg.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ============================== 4 + Kernel driver for Qualcomm LPG 5 + ============================== 6 + 7 + Description 8 + ----------- 9 + 10 + The Qualcomm LPG can be found in a variety of Qualcomm PMICs and consists of a 11 + number of PWM channels, a programmable pattern lookup table and a RGB LED 12 + current sink. 13 + 14 + To facilitate the various use cases, the LPG channels can be exposed as 15 + individual LEDs, grouped together as RGB LEDs or otherwise be accessed as PWM 16 + channels. The output of each PWM channel is routed to other hardware 17 + blocks, such as the RGB current sink, GPIO pins etc. 18 + 19 + The each PWM channel can operate with a period between 27us and 384 seconds and 20 + has a 9 bit resolution of the duty cycle. 21 + 22 + In order to provide support for status notifications with the CPU subsystem in 23 + deeper idle states the LPG provides pattern support. This consists of a shared 24 + lookup table of brightness values and per channel properties to select the 25 + range within the table to use, the rate and if the pattern should repeat. 26 + 27 + The pattern for a channel can be programmed using the "pattern" trigger, using 28 + the hw_pattern attribute. 29 + 30 + /sys/class/leds/<led>/hw_pattern 31 + -------------------------------- 32 + 33 + Specify a hardware pattern for a Qualcomm LPG LED. 34 + 35 + The pattern is a series of brightness and hold-time pairs, with the hold-time 36 + expressed in milliseconds. The hold time is a property of the pattern and must 37 + therefor be identical for each element in the pattern (except for the pauses 38 + described below). As the LPG hardware is not able to perform the linear 39 + transitions expected by the leds-trigger-pattern format, each entry in the 40 + pattern must be followed a zero-length entry of the same brightness. 41 + 42 + Simple pattern:: 43 + 44 + "255 500 255 0 0 500 0 0" 45 + 46 + ^ 47 + | 48 + 255 +----+ +----+ 49 + | | | | ... 50 + 0 | +----+ +---- 51 + +----------------------> 52 + 0 5 10 15 time (100ms) 53 + 54 + The LPG supports specifying a longer hold-time for the first and last element 55 + in the pattern, the so called "low pause" and "high pause". 56 + 57 + Low-pause pattern:: 58 + 59 + "255 1000 255 0 0 500 0 0 255 500 255 0 0 500 0 0" 60 + 61 + ^ 62 + | 63 + 255 +--------+ +----+ +----+ +--------+ 64 + | | | | | | | | ... 65 + 0 | +----+ +----+ +----+ +---- 66 + +-----------------------------> 67 + 0 5 10 15 20 25 time (100ms) 68 + 69 + Similarily, the last entry can be stretched by using a higher hold-time on the 70 + last entry. 71 + 72 + In order to save space in the shared lookup table the LPG supports "ping-pong" 73 + mode, in which case each run through the pattern is performed by first running 74 + the pattern forward, then backwards. This mode is automatically used by the 75 + driver when the given pattern is a palindrome. In this case the "high pause" 76 + denotes the wait time before the pattern is run in reverse and as such the 77 + specified hold-time of the middle item in the pattern is allowed to have a 78 + different hold-time.
+3
drivers/leds/Kconfig
··· 869 869 comment "Flash and Torch LED drivers" 870 870 source "drivers/leds/flash/Kconfig" 871 871 872 + comment "RGB LED drivers" 873 + source "drivers/leds/rgb/Kconfig" 874 + 872 875 comment "LED Triggers" 873 876 source "drivers/leds/trigger/Kconfig" 874 877
+3
drivers/leds/Makefile
··· 99 99 # Flash and Torch LED Drivers 100 100 obj-$(CONFIG_LEDS_CLASS_FLASH) += flash/ 101 101 102 + # RGB LED Drivers 103 + obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += rgb/ 104 + 102 105 # LED Triggers 103 106 obj-$(CONFIG_LEDS_TRIGGERS) += trigger/ 104 107
+5 -10
drivers/leds/flash/leds-ktd2692.c
··· 279 279 280 280 led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS); 281 281 ret = PTR_ERR_OR_ZERO(led->ctrl_gpio); 282 - if (ret) { 283 - dev_err(dev, "cannot get ctrl-gpios %d\n", ret); 284 - return ret; 285 - } 282 + if (ret) 283 + return dev_err_probe(dev, ret, "cannot get ctrl-gpios\n"); 286 284 287 - led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS); 288 - ret = PTR_ERR_OR_ZERO(led->aux_gpio); 289 - if (ret) { 290 - dev_err(dev, "cannot get aux-gpios %d\n", ret); 291 - return ret; 292 - } 285 + led->aux_gpio = devm_gpiod_get_optional(dev, "aux", GPIOD_ASIS); 286 + if (IS_ERR(led->aux_gpio)) 287 + return dev_err_probe(dev, PTR_ERR(led->aux_gpio), "cannot get aux-gpios\n"); 293 288 294 289 led->regulator = devm_regulator_get(dev, "vin"); 295 290 if (IS_ERR(led->regulator))
+7 -1
drivers/leds/leds-is31fl32xx.c
··· 460 460 static int is31fl32xx_remove(struct i2c_client *client) 461 461 { 462 462 struct is31fl32xx_priv *priv = i2c_get_clientdata(client); 463 + int ret; 463 464 464 - return is31fl32xx_reset_regs(priv); 465 + ret = is31fl32xx_reset_regs(priv); 466 + if (ret) 467 + dev_err(&client->dev, "Failed to reset registers on removal (%pe)\n", 468 + ERR_PTR(ret)); 469 + 470 + return 0; 465 471 } 466 472 467 473 /*
+1 -3
drivers/leds/leds-lp50xx.c
··· 569 569 int ret; 570 570 571 571 ret = lp50xx_enable_disable(led, 0); 572 - if (ret) { 572 + if (ret) 573 573 dev_err(led->dev, "Failed to disable chip\n"); 574 - return ret; 575 - } 576 574 577 575 if (led->regulator) { 578 576 ret = regulator_disable(led->regulator);
+4 -7
drivers/leds/leds-pca9532.c
··· 318 318 } 319 319 #endif /* CONFIG_LEDS_PCA9532_GPIO */ 320 320 321 - static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs) 321 + static void pca9532_destroy_devices(struct pca9532_data *data, int n_devs) 322 322 { 323 323 int i = n_devs; 324 - 325 - if (!data) 326 - return -EINVAL; 327 324 328 325 while (--i >= 0) { 329 326 switch (data->leds[i].type) { ··· 343 346 if (data->gpio.parent) 344 347 gpiochip_remove(&data->gpio); 345 348 #endif 346 - 347 - return 0; 348 349 } 349 350 350 351 static int pca9532_configure(struct i2c_client *client, ··· 550 555 { 551 556 struct pca9532_data *data = i2c_get_clientdata(client); 552 557 553 - return pca9532_destroy_devices(data, data->chip_info->num_leds); 558 + pca9532_destroy_devices(data, data->chip_info->num_leds); 559 + 560 + return 0; 554 561 } 555 562 556 563 module_i2c_driver(pca9532_driver);
+26 -21
drivers/leds/leds-regulator.c
··· 8 8 */ 9 9 10 10 #include <linux/module.h> 11 + #include <linux/mod_devicetable.h> 11 12 #include <linux/err.h> 12 13 #include <linux/slab.h> 13 14 #include <linux/leds.h> ··· 124 123 { 125 124 struct led_regulator_platform_data *pdata = 126 125 dev_get_platdata(&pdev->dev); 126 + struct device *dev = &pdev->dev; 127 + struct led_init_data init_data = {}; 127 128 struct regulator_led *led; 128 129 struct regulator *vcc; 129 130 int ret = 0; 130 131 131 - if (pdata == NULL) { 132 - dev_err(&pdev->dev, "no platform data\n"); 133 - return -ENODEV; 134 - } 135 - 136 - vcc = devm_regulator_get_exclusive(&pdev->dev, "vled"); 132 + vcc = devm_regulator_get_exclusive(dev, "vled"); 137 133 if (IS_ERR(vcc)) { 138 - dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); 134 + dev_err(dev, "Cannot get vcc\n"); 139 135 return PTR_ERR(vcc); 140 136 } 141 137 142 - led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); 138 + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 143 139 if (led == NULL) 144 140 return -ENOMEM; 145 141 142 + init_data.fwnode = dev->fwnode; 143 + 146 144 led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); 147 - if (pdata->brightness > led->cdev.max_brightness) { 148 - dev_err(&pdev->dev, "Invalid default brightness %d\n", 145 + /* Legacy platform data label assignment */ 146 + if (pdata) { 147 + if (pdata->brightness > led->cdev.max_brightness) { 148 + dev_err(dev, "Invalid default brightness %d\n", 149 149 pdata->brightness); 150 - return -EINVAL; 150 + return -EINVAL; 151 + } 152 + led->cdev.brightness = pdata->brightness; 153 + init_data.default_label = pdata->name; 151 154 } 152 155 153 156 led->cdev.brightness_set_blocking = regulator_led_brightness_set; 154 - led->cdev.name = pdata->name; 155 157 led->cdev.flags |= LED_CORE_SUSPENDRESUME; 156 158 led->vcc = vcc; 157 159 ··· 166 162 167 163 platform_set_drvdata(pdev, led); 168 164 169 - ret = led_classdev_register(&pdev->dev, &led->cdev); 165 + ret = led_classdev_register_ext(dev, &led->cdev, &init_data); 170 166 if (ret < 0) 171 167 return ret; 172 - 173 - /* to expose the default value to userspace */ 174 - led->cdev.brightness = pdata->brightness; 175 - 176 - /* Set the default led status */ 177 - regulator_led_brightness_set(&led->cdev, led->cdev.brightness); 178 168 179 169 return 0; 180 170 } ··· 182 184 return 0; 183 185 } 184 186 187 + static const struct of_device_id regulator_led_of_match[] = { 188 + { .compatible = "regulator-led", }, 189 + {} 190 + }; 191 + MODULE_DEVICE_TABLE(of, regulator_led_of_match); 192 + 185 193 static struct platform_driver regulator_led_driver = { 186 194 .driver = { 187 - .name = "leds-regulator", 188 - }, 195 + .name = "leds-regulator", 196 + .of_match_table = regulator_led_of_match, 197 + }, 189 198 .probe = regulator_led_probe, 190 199 .remove = regulator_led_remove, 191 200 };
+29
drivers/leds/rgb/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + if LEDS_CLASS_MULTICOLOR 4 + 5 + config LEDS_PWM_MULTICOLOR 6 + tristate "PWM driven multi-color LED Support" 7 + depends on PWM 8 + help 9 + This option enables support for PWM driven monochrome LEDs that are 10 + grouped into multicolor LEDs. 11 + 12 + To compile this driver as a module, choose M here: the module 13 + will be called leds-pwm-multicolor. 14 + 15 + config LEDS_QCOM_LPG 16 + tristate "LED support for Qualcomm LPG" 17 + depends on OF 18 + depends on PWM 19 + depends on SPMI 20 + help 21 + This option enables support for the Light Pulse Generator found in a 22 + wide variety of Qualcomm PMICs. The LPG consists of a number of PWM 23 + channels and typically a shared pattern lookup table and a current 24 + sink, intended to drive RGB LEDs. Each channel can either be used as 25 + a LED, grouped to represent a RGB LED or exposed as PWM channels. 26 + 27 + If compiled as a module, the module will be named leds-qcom-lpg. 28 + 29 + endif # LEDS_CLASS_MULTICOLOR
+4
drivers/leds/rgb/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o 4 + obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
+186
drivers/leds/rgb/leds-pwm-multicolor.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * PWM-based multi-color LED control 4 + * 5 + * Copyright 2022 Sven Schwermer <sven.schwermer@disruptive-technologies.com> 6 + */ 7 + 8 + #include <linux/err.h> 9 + #include <linux/kernel.h> 10 + #include <linux/led-class-multicolor.h> 11 + #include <linux/leds.h> 12 + #include <linux/mod_devicetable.h> 13 + #include <linux/module.h> 14 + #include <linux/mutex.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/property.h> 17 + #include <linux/pwm.h> 18 + 19 + struct pwm_led { 20 + struct pwm_device *pwm; 21 + struct pwm_state state; 22 + }; 23 + 24 + struct pwm_mc_led { 25 + struct led_classdev_mc mc_cdev; 26 + struct mutex lock; 27 + struct pwm_led leds[]; 28 + }; 29 + 30 + static int led_pwm_mc_set(struct led_classdev *cdev, 31 + enum led_brightness brightness) 32 + { 33 + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); 34 + struct pwm_mc_led *priv = container_of(mc_cdev, struct pwm_mc_led, mc_cdev); 35 + unsigned long long duty; 36 + int ret = 0; 37 + int i; 38 + 39 + led_mc_calc_color_components(mc_cdev, brightness); 40 + 41 + mutex_lock(&priv->lock); 42 + 43 + for (i = 0; i < mc_cdev->num_colors; i++) { 44 + duty = priv->leds[i].state.period; 45 + duty *= mc_cdev->subled_info[i].brightness; 46 + do_div(duty, cdev->max_brightness); 47 + 48 + priv->leds[i].state.duty_cycle = duty; 49 + priv->leds[i].state.enabled = duty > 0; 50 + ret = pwm_apply_state(priv->leds[i].pwm, 51 + &priv->leds[i].state); 52 + if (ret) 53 + break; 54 + } 55 + 56 + mutex_unlock(&priv->lock); 57 + 58 + return ret; 59 + } 60 + 61 + static int iterate_subleds(struct device *dev, struct pwm_mc_led *priv, 62 + struct fwnode_handle *mcnode) 63 + { 64 + struct mc_subled *subled = priv->mc_cdev.subled_info; 65 + struct fwnode_handle *fwnode; 66 + struct pwm_led *pwmled; 67 + u32 color; 68 + int ret; 69 + 70 + /* iterate over the nodes inside the multi-led node */ 71 + fwnode_for_each_child_node(mcnode, fwnode) { 72 + pwmled = &priv->leds[priv->mc_cdev.num_colors]; 73 + pwmled->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL); 74 + if (IS_ERR(pwmled->pwm)) { 75 + ret = PTR_ERR(pwmled->pwm); 76 + dev_err(dev, "unable to request PWM: %d\n", ret); 77 + goto release_fwnode; 78 + } 79 + pwm_init_state(pwmled->pwm, &pwmled->state); 80 + 81 + ret = fwnode_property_read_u32(fwnode, "color", &color); 82 + if (ret) { 83 + dev_err(dev, "cannot read color: %d\n", ret); 84 + goto release_fwnode; 85 + } 86 + 87 + subled[priv->mc_cdev.num_colors].color_index = color; 88 + priv->mc_cdev.num_colors++; 89 + } 90 + 91 + return 0; 92 + 93 + release_fwnode: 94 + fwnode_handle_put(fwnode); 95 + return ret; 96 + } 97 + 98 + static int led_pwm_mc_probe(struct platform_device *pdev) 99 + { 100 + struct fwnode_handle *mcnode, *fwnode; 101 + struct led_init_data init_data = {}; 102 + struct led_classdev *cdev; 103 + struct mc_subled *subled; 104 + struct pwm_mc_led *priv; 105 + int count = 0; 106 + int ret = 0; 107 + 108 + mcnode = device_get_named_child_node(&pdev->dev, "multi-led"); 109 + if (!mcnode) 110 + return dev_err_probe(&pdev->dev, -ENODEV, 111 + "expected multi-led node\n"); 112 + 113 + /* count the nodes inside the multi-led node */ 114 + fwnode_for_each_child_node(mcnode, fwnode) 115 + count++; 116 + 117 + priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, count), 118 + GFP_KERNEL); 119 + if (!priv) { 120 + ret = -ENOMEM; 121 + goto release_mcnode; 122 + } 123 + mutex_init(&priv->lock); 124 + 125 + subled = devm_kcalloc(&pdev->dev, count, sizeof(*subled), GFP_KERNEL); 126 + if (!subled) { 127 + ret = -ENOMEM; 128 + goto release_mcnode; 129 + } 130 + priv->mc_cdev.subled_info = subled; 131 + 132 + /* init the multicolor's LED class device */ 133 + cdev = &priv->mc_cdev.led_cdev; 134 + fwnode_property_read_u32(mcnode, "max-brightness", 135 + &cdev->max_brightness); 136 + cdev->flags = LED_CORE_SUSPENDRESUME; 137 + cdev->brightness_set_blocking = led_pwm_mc_set; 138 + 139 + ret = iterate_subleds(&pdev->dev, priv, mcnode); 140 + if (ret) 141 + goto release_mcnode; 142 + 143 + init_data.fwnode = mcnode; 144 + ret = devm_led_classdev_multicolor_register_ext(&pdev->dev, 145 + &priv->mc_cdev, 146 + &init_data); 147 + if (ret) { 148 + dev_err(&pdev->dev, 149 + "failed to register multicolor PWM led for %s: %d\n", 150 + cdev->name, ret); 151 + goto release_mcnode; 152 + } 153 + 154 + ret = led_pwm_mc_set(cdev, cdev->brightness); 155 + if (ret) 156 + return dev_err_probe(&pdev->dev, ret, 157 + "failed to set led PWM value for %s: %d", 158 + cdev->name, ret); 159 + 160 + platform_set_drvdata(pdev, priv); 161 + return 0; 162 + 163 + release_mcnode: 164 + fwnode_handle_put(mcnode); 165 + return ret; 166 + } 167 + 168 + static const struct of_device_id of_pwm_leds_mc_match[] = { 169 + { .compatible = "pwm-leds-multicolor", }, 170 + {} 171 + }; 172 + MODULE_DEVICE_TABLE(of, of_pwm_leds_mc_match); 173 + 174 + static struct platform_driver led_pwm_mc_driver = { 175 + .probe = led_pwm_mc_probe, 176 + .driver = { 177 + .name = "leds_pwm_multicolor", 178 + .of_match_table = of_pwm_leds_mc_match, 179 + }, 180 + }; 181 + module_platform_driver(led_pwm_mc_driver); 182 + 183 + MODULE_AUTHOR("Sven Schwermer <sven.schwermer@disruptive-technologies.com>"); 184 + MODULE_DESCRIPTION("multi-color PWM LED driver"); 185 + MODULE_LICENSE("GPL v2"); 186 + MODULE_ALIAS("platform:leds-pwm-multicolor");
+1451
drivers/leds/rgb/leds-qcom-lpg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2017-2022 Linaro Ltd 4 + * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. 5 + */ 6 + #include <linux/bits.h> 7 + #include <linux/bitfield.h> 8 + #include <linux/led-class-multicolor.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/of_device.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/pwm.h> 14 + #include <linux/regmap.h> 15 + #include <linux/slab.h> 16 + 17 + #define LPG_SUBTYPE_REG 0x05 18 + #define LPG_SUBTYPE_LPG 0x2 19 + #define LPG_SUBTYPE_PWM 0xb 20 + #define LPG_SUBTYPE_LPG_LITE 0x11 21 + #define LPG_PATTERN_CONFIG_REG 0x40 22 + #define LPG_SIZE_CLK_REG 0x41 23 + #define PWM_CLK_SELECT_MASK GENMASK(1, 0) 24 + #define LPG_PREDIV_CLK_REG 0x42 25 + #define PWM_FREQ_PRE_DIV_MASK GENMASK(6, 5) 26 + #define PWM_FREQ_EXP_MASK GENMASK(2, 0) 27 + #define PWM_TYPE_CONFIG_REG 0x43 28 + #define PWM_VALUE_REG 0x44 29 + #define PWM_ENABLE_CONTROL_REG 0x46 30 + #define PWM_SYNC_REG 0x47 31 + #define LPG_RAMP_DURATION_REG 0x50 32 + #define LPG_HI_PAUSE_REG 0x52 33 + #define LPG_LO_PAUSE_REG 0x54 34 + #define LPG_HI_IDX_REG 0x56 35 + #define LPG_LO_IDX_REG 0x57 36 + #define PWM_SEC_ACCESS_REG 0xd0 37 + #define PWM_DTEST_REG(x) (0xe2 + (x) - 1) 38 + 39 + #define TRI_LED_SRC_SEL 0x45 40 + #define TRI_LED_EN_CTL 0x46 41 + #define TRI_LED_ATC_CTL 0x47 42 + 43 + #define LPG_LUT_REG(x) (0x40 + (x) * 2) 44 + #define RAMP_CONTROL_REG 0xc8 45 + 46 + #define LPG_RESOLUTION 512 47 + #define LPG_MAX_M 7 48 + 49 + struct lpg_channel; 50 + struct lpg_data; 51 + 52 + /** 53 + * struct lpg - LPG device context 54 + * @dev: pointer to LPG device 55 + * @map: regmap for register access 56 + * @lock: used to synchronize LED and pwm callback requests 57 + * @pwm: PWM-chip object, if operating in PWM mode 58 + * @data: reference to version specific data 59 + * @lut_base: base address of the LUT block (optional) 60 + * @lut_size: number of entries in the LUT block 61 + * @lut_bitmap: allocation bitmap for LUT entries 62 + * @triled_base: base address of the TRILED block (optional) 63 + * @triled_src: power-source for the TRILED 64 + * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register 65 + * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register 66 + * @channels: list of PWM channels 67 + * @num_channels: number of @channels 68 + */ 69 + struct lpg { 70 + struct device *dev; 71 + struct regmap *map; 72 + 73 + struct mutex lock; 74 + 75 + struct pwm_chip pwm; 76 + 77 + const struct lpg_data *data; 78 + 79 + u32 lut_base; 80 + u32 lut_size; 81 + unsigned long *lut_bitmap; 82 + 83 + u32 triled_base; 84 + u32 triled_src; 85 + bool triled_has_atc_ctl; 86 + bool triled_has_src_sel; 87 + 88 + struct lpg_channel *channels; 89 + unsigned int num_channels; 90 + }; 91 + 92 + /** 93 + * struct lpg_channel - per channel data 94 + * @lpg: reference to parent lpg 95 + * @base: base address of the PWM channel 96 + * @triled_mask: mask in TRILED to enable this channel 97 + * @lut_mask: mask in LUT to start pattern generator for this channel 98 + * @subtype: PMIC hardware block subtype 99 + * @in_use: channel is exposed to LED framework 100 + * @color: color of the LED attached to this channel 101 + * @dtest_line: DTEST line for output, or 0 if disabled 102 + * @dtest_value: DTEST line configuration 103 + * @pwm_value: duty (in microseconds) of the generated pulses, overridden by LUT 104 + * @enabled: output enabled? 105 + * @period: period (in nanoseconds) of the generated pulses 106 + * @clk_sel: reference clock frequency selector 107 + * @pre_div_sel: divider selector of the reference clock 108 + * @pre_div_exp: exponential divider of the reference clock 109 + * @ramp_enabled: duty cycle is driven by iterating over lookup table 110 + * @ramp_ping_pong: reverse through pattern, rather than wrapping to start 111 + * @ramp_oneshot: perform only a single pass over the pattern 112 + * @ramp_reverse: iterate over pattern backwards 113 + * @ramp_tick_ms: length (in milliseconds) of one step in the pattern 114 + * @ramp_lo_pause_ms: pause (in milliseconds) before iterating over pattern 115 + * @ramp_hi_pause_ms: pause (in milliseconds) after iterating over pattern 116 + * @pattern_lo_idx: start index of associated pattern 117 + * @pattern_hi_idx: last index of associated pattern 118 + */ 119 + struct lpg_channel { 120 + struct lpg *lpg; 121 + 122 + u32 base; 123 + unsigned int triled_mask; 124 + unsigned int lut_mask; 125 + unsigned int subtype; 126 + 127 + bool in_use; 128 + 129 + int color; 130 + 131 + u32 dtest_line; 132 + u32 dtest_value; 133 + 134 + u16 pwm_value; 135 + bool enabled; 136 + 137 + u64 period; 138 + unsigned int clk_sel; 139 + unsigned int pre_div_sel; 140 + unsigned int pre_div_exp; 141 + 142 + bool ramp_enabled; 143 + bool ramp_ping_pong; 144 + bool ramp_oneshot; 145 + bool ramp_reverse; 146 + unsigned short ramp_tick_ms; 147 + unsigned long ramp_lo_pause_ms; 148 + unsigned long ramp_hi_pause_ms; 149 + 150 + unsigned int pattern_lo_idx; 151 + unsigned int pattern_hi_idx; 152 + }; 153 + 154 + /** 155 + * struct lpg_led - logical LED object 156 + * @lpg: lpg context reference 157 + * @cdev: LED class device 158 + * @mcdev: Multicolor LED class device 159 + * @num_channels: number of @channels 160 + * @channels: list of channels associated with the LED 161 + */ 162 + struct lpg_led { 163 + struct lpg *lpg; 164 + 165 + struct led_classdev cdev; 166 + struct led_classdev_mc mcdev; 167 + 168 + unsigned int num_channels; 169 + struct lpg_channel *channels[]; 170 + }; 171 + 172 + /** 173 + * struct lpg_channel_data - per channel initialization data 174 + * @base: base address for PWM channel registers 175 + * @triled_mask: bitmask for controlling this channel in TRILED 176 + */ 177 + struct lpg_channel_data { 178 + unsigned int base; 179 + u8 triled_mask; 180 + }; 181 + 182 + /** 183 + * struct lpg_data - initialization data 184 + * @lut_base: base address of LUT block 185 + * @lut_size: number of entries in LUT 186 + * @triled_base: base address of TRILED 187 + * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register 188 + * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register 189 + * @num_channels: number of channels in LPG 190 + * @channels: list of channel initialization data 191 + */ 192 + struct lpg_data { 193 + unsigned int lut_base; 194 + unsigned int lut_size; 195 + unsigned int triled_base; 196 + bool triled_has_atc_ctl; 197 + bool triled_has_src_sel; 198 + int num_channels; 199 + const struct lpg_channel_data *channels; 200 + }; 201 + 202 + static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable) 203 + { 204 + /* Skip if we don't have a triled block */ 205 + if (!lpg->triled_base) 206 + return 0; 207 + 208 + return regmap_update_bits(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, 209 + mask, enable); 210 + } 211 + 212 + static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern, 213 + size_t len, unsigned int *lo_idx, unsigned int *hi_idx) 214 + { 215 + unsigned int idx; 216 + u16 val; 217 + int i; 218 + 219 + idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, 220 + 0, len, 0); 221 + if (idx >= lpg->lut_size) 222 + return -ENOMEM; 223 + 224 + for (i = 0; i < len; i++) { 225 + val = pattern[i].brightness; 226 + 227 + regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i), 228 + &val, sizeof(val)); 229 + } 230 + 231 + bitmap_set(lpg->lut_bitmap, idx, len); 232 + 233 + *lo_idx = idx; 234 + *hi_idx = idx + len - 1; 235 + 236 + return 0; 237 + } 238 + 239 + static void lpg_lut_free(struct lpg *lpg, unsigned int lo_idx, unsigned int hi_idx) 240 + { 241 + int len; 242 + 243 + len = hi_idx - lo_idx + 1; 244 + if (len == 1) 245 + return; 246 + 247 + bitmap_clear(lpg->lut_bitmap, lo_idx, len); 248 + } 249 + 250 + static int lpg_lut_sync(struct lpg *lpg, unsigned int mask) 251 + { 252 + return regmap_write(lpg->map, lpg->lut_base + RAMP_CONTROL_REG, mask); 253 + } 254 + 255 + static const unsigned int lpg_clk_rates[] = {0, 1024, 32768, 19200000}; 256 + static const unsigned int lpg_pre_divs[] = {1, 3, 5, 6}; 257 + 258 + static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period) 259 + { 260 + unsigned int clk_sel, best_clk = 0; 261 + unsigned int div, best_div = 0; 262 + unsigned int m, best_m = 0; 263 + unsigned int error; 264 + unsigned int best_err = UINT_MAX; 265 + u64 best_period = 0; 266 + u64 max_period; 267 + 268 + /* 269 + * The PWM period is determined by: 270 + * 271 + * resolution * pre_div * 2^M 272 + * period = -------------------------- 273 + * refclk 274 + * 275 + * With resolution fixed at 2^9 bits, pre_div = {1, 3, 5, 6} and 276 + * M = [0..7]. 277 + * 278 + * This allows for periods between 27uS and 384s, as the PWM framework 279 + * wants a period of equal or lower length than requested, reject 280 + * anything below 27uS. 281 + */ 282 + if (period <= (u64)NSEC_PER_SEC * LPG_RESOLUTION / 19200000) 283 + return -EINVAL; 284 + 285 + /* Limit period to largest possible value, to avoid overflows */ 286 + max_period = (u64)NSEC_PER_SEC * LPG_RESOLUTION * 6 * (1 << LPG_MAX_M) / 1024; 287 + if (period > max_period) 288 + period = max_period; 289 + 290 + /* 291 + * Search for the pre_div, refclk and M by solving the rewritten formula 292 + * for each refclk and pre_div value: 293 + * 294 + * period * refclk 295 + * M = log2 ------------------------------------- 296 + * NSEC_PER_SEC * pre_div * resolution 297 + */ 298 + for (clk_sel = 1; clk_sel < ARRAY_SIZE(lpg_clk_rates); clk_sel++) { 299 + u64 numerator = period * lpg_clk_rates[clk_sel]; 300 + 301 + for (div = 0; div < ARRAY_SIZE(lpg_pre_divs); div++) { 302 + u64 denominator = (u64)NSEC_PER_SEC * lpg_pre_divs[div] * LPG_RESOLUTION; 303 + u64 actual; 304 + u64 ratio; 305 + 306 + if (numerator < denominator) 307 + continue; 308 + 309 + ratio = div64_u64(numerator, denominator); 310 + m = ilog2(ratio); 311 + if (m > LPG_MAX_M) 312 + m = LPG_MAX_M; 313 + 314 + actual = DIV_ROUND_UP_ULL(denominator * (1 << m), lpg_clk_rates[clk_sel]); 315 + 316 + error = period - actual; 317 + if (error < best_err) { 318 + best_err = error; 319 + 320 + best_div = div; 321 + best_m = m; 322 + best_clk = clk_sel; 323 + best_period = actual; 324 + } 325 + } 326 + } 327 + 328 + chan->clk_sel = best_clk; 329 + chan->pre_div_sel = best_div; 330 + chan->pre_div_exp = best_m; 331 + chan->period = best_period; 332 + 333 + return 0; 334 + } 335 + 336 + static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty) 337 + { 338 + unsigned int max = LPG_RESOLUTION - 1; 339 + unsigned int val; 340 + 341 + val = div64_u64(duty * lpg_clk_rates[chan->clk_sel], 342 + (u64)NSEC_PER_SEC * lpg_pre_divs[chan->pre_div_sel] * (1 << chan->pre_div_exp)); 343 + 344 + chan->pwm_value = min(val, max); 345 + } 346 + 347 + static void lpg_apply_freq(struct lpg_channel *chan) 348 + { 349 + unsigned long val; 350 + struct lpg *lpg = chan->lpg; 351 + 352 + if (!chan->enabled) 353 + return; 354 + 355 + val = chan->clk_sel; 356 + 357 + /* Specify 9bit resolution, based on the subtype of the channel */ 358 + switch (chan->subtype) { 359 + case LPG_SUBTYPE_LPG: 360 + val |= GENMASK(5, 4); 361 + break; 362 + case LPG_SUBTYPE_PWM: 363 + val |= BIT(2); 364 + break; 365 + case LPG_SUBTYPE_LPG_LITE: 366 + default: 367 + val |= BIT(4); 368 + break; 369 + } 370 + 371 + regmap_write(lpg->map, chan->base + LPG_SIZE_CLK_REG, val); 372 + 373 + val = FIELD_PREP(PWM_FREQ_PRE_DIV_MASK, chan->pre_div_sel) | 374 + FIELD_PREP(PWM_FREQ_EXP_MASK, chan->pre_div_exp); 375 + regmap_write(lpg->map, chan->base + LPG_PREDIV_CLK_REG, val); 376 + } 377 + 378 + #define LPG_ENABLE_GLITCH_REMOVAL BIT(5) 379 + 380 + static void lpg_enable_glitch(struct lpg_channel *chan) 381 + { 382 + struct lpg *lpg = chan->lpg; 383 + 384 + regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, 385 + LPG_ENABLE_GLITCH_REMOVAL, 0); 386 + } 387 + 388 + static void lpg_disable_glitch(struct lpg_channel *chan) 389 + { 390 + struct lpg *lpg = chan->lpg; 391 + 392 + regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, 393 + LPG_ENABLE_GLITCH_REMOVAL, 394 + LPG_ENABLE_GLITCH_REMOVAL); 395 + } 396 + 397 + static void lpg_apply_pwm_value(struct lpg_channel *chan) 398 + { 399 + struct lpg *lpg = chan->lpg; 400 + u16 val = chan->pwm_value; 401 + 402 + if (!chan->enabled) 403 + return; 404 + 405 + regmap_bulk_write(lpg->map, chan->base + PWM_VALUE_REG, &val, sizeof(val)); 406 + } 407 + 408 + #define LPG_PATTERN_CONFIG_LO_TO_HI BIT(4) 409 + #define LPG_PATTERN_CONFIG_REPEAT BIT(3) 410 + #define LPG_PATTERN_CONFIG_TOGGLE BIT(2) 411 + #define LPG_PATTERN_CONFIG_PAUSE_HI BIT(1) 412 + #define LPG_PATTERN_CONFIG_PAUSE_LO BIT(0) 413 + 414 + static void lpg_apply_lut_control(struct lpg_channel *chan) 415 + { 416 + struct lpg *lpg = chan->lpg; 417 + unsigned int hi_pause; 418 + unsigned int lo_pause; 419 + unsigned int conf = 0; 420 + unsigned int lo_idx = chan->pattern_lo_idx; 421 + unsigned int hi_idx = chan->pattern_hi_idx; 422 + u16 step = chan->ramp_tick_ms; 423 + 424 + if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) 425 + return; 426 + 427 + hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, step); 428 + lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, step); 429 + 430 + if (!chan->ramp_reverse) 431 + conf |= LPG_PATTERN_CONFIG_LO_TO_HI; 432 + if (!chan->ramp_oneshot) 433 + conf |= LPG_PATTERN_CONFIG_REPEAT; 434 + if (chan->ramp_ping_pong) 435 + conf |= LPG_PATTERN_CONFIG_TOGGLE; 436 + if (chan->ramp_hi_pause_ms) 437 + conf |= LPG_PATTERN_CONFIG_PAUSE_HI; 438 + if (chan->ramp_lo_pause_ms) 439 + conf |= LPG_PATTERN_CONFIG_PAUSE_LO; 440 + 441 + regmap_write(lpg->map, chan->base + LPG_PATTERN_CONFIG_REG, conf); 442 + regmap_write(lpg->map, chan->base + LPG_HI_IDX_REG, hi_idx); 443 + regmap_write(lpg->map, chan->base + LPG_LO_IDX_REG, lo_idx); 444 + 445 + regmap_bulk_write(lpg->map, chan->base + LPG_RAMP_DURATION_REG, &step, sizeof(step)); 446 + regmap_write(lpg->map, chan->base + LPG_HI_PAUSE_REG, hi_pause); 447 + regmap_write(lpg->map, chan->base + LPG_LO_PAUSE_REG, lo_pause); 448 + } 449 + 450 + #define LPG_ENABLE_CONTROL_OUTPUT BIT(7) 451 + #define LPG_ENABLE_CONTROL_BUFFER_TRISTATE BIT(5) 452 + #define LPG_ENABLE_CONTROL_SRC_PWM BIT(2) 453 + #define LPG_ENABLE_CONTROL_RAMP_GEN BIT(1) 454 + 455 + static void lpg_apply_control(struct lpg_channel *chan) 456 + { 457 + unsigned int ctrl; 458 + struct lpg *lpg = chan->lpg; 459 + 460 + ctrl = LPG_ENABLE_CONTROL_BUFFER_TRISTATE; 461 + 462 + if (chan->enabled) 463 + ctrl |= LPG_ENABLE_CONTROL_OUTPUT; 464 + 465 + if (chan->pattern_lo_idx != chan->pattern_hi_idx) 466 + ctrl |= LPG_ENABLE_CONTROL_RAMP_GEN; 467 + else 468 + ctrl |= LPG_ENABLE_CONTROL_SRC_PWM; 469 + 470 + regmap_write(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, ctrl); 471 + 472 + /* 473 + * Due to LPG hardware bug, in the PWM mode, having enabled PWM, 474 + * We have to write PWM values one more time. 475 + */ 476 + if (chan->enabled) 477 + lpg_apply_pwm_value(chan); 478 + } 479 + 480 + #define LPG_SYNC_PWM BIT(0) 481 + 482 + static void lpg_apply_sync(struct lpg_channel *chan) 483 + { 484 + struct lpg *lpg = chan->lpg; 485 + 486 + regmap_write(lpg->map, chan->base + PWM_SYNC_REG, LPG_SYNC_PWM); 487 + } 488 + 489 + static int lpg_parse_dtest(struct lpg *lpg) 490 + { 491 + struct lpg_channel *chan; 492 + struct device_node *np = lpg->dev->of_node; 493 + int count; 494 + int ret; 495 + int i; 496 + 497 + count = of_property_count_u32_elems(np, "qcom,dtest"); 498 + if (count == -EINVAL) { 499 + return 0; 500 + } else if (count < 0) { 501 + ret = count; 502 + goto err_malformed; 503 + } else if (count != lpg->data->num_channels * 2) { 504 + dev_err(lpg->dev, "qcom,dtest needs to be %d items\n", 505 + lpg->data->num_channels * 2); 506 + return -EINVAL; 507 + } 508 + 509 + for (i = 0; i < lpg->data->num_channels; i++) { 510 + chan = &lpg->channels[i]; 511 + 512 + ret = of_property_read_u32_index(np, "qcom,dtest", i * 2, 513 + &chan->dtest_line); 514 + if (ret) 515 + goto err_malformed; 516 + 517 + ret = of_property_read_u32_index(np, "qcom,dtest", i * 2 + 1, 518 + &chan->dtest_value); 519 + if (ret) 520 + goto err_malformed; 521 + } 522 + 523 + return 0; 524 + 525 + err_malformed: 526 + dev_err(lpg->dev, "malformed qcom,dtest\n"); 527 + return ret; 528 + } 529 + 530 + static void lpg_apply_dtest(struct lpg_channel *chan) 531 + { 532 + struct lpg *lpg = chan->lpg; 533 + 534 + if (!chan->dtest_line) 535 + return; 536 + 537 + regmap_write(lpg->map, chan->base + PWM_SEC_ACCESS_REG, 0xa5); 538 + regmap_write(lpg->map, chan->base + PWM_DTEST_REG(chan->dtest_line), 539 + chan->dtest_value); 540 + } 541 + 542 + static void lpg_apply(struct lpg_channel *chan) 543 + { 544 + lpg_disable_glitch(chan); 545 + lpg_apply_freq(chan); 546 + lpg_apply_pwm_value(chan); 547 + lpg_apply_control(chan); 548 + lpg_apply_sync(chan); 549 + lpg_apply_lut_control(chan); 550 + lpg_enable_glitch(chan); 551 + } 552 + 553 + static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, 554 + struct mc_subled *subleds) 555 + { 556 + enum led_brightness brightness; 557 + struct lpg_channel *chan; 558 + unsigned int triled_enabled = 0; 559 + unsigned int triled_mask = 0; 560 + unsigned int lut_mask = 0; 561 + unsigned int duty; 562 + struct lpg *lpg = led->lpg; 563 + int i; 564 + 565 + for (i = 0; i < led->num_channels; i++) { 566 + chan = led->channels[i]; 567 + brightness = subleds[i].brightness; 568 + 569 + if (brightness == LED_OFF) { 570 + chan->enabled = false; 571 + chan->ramp_enabled = false; 572 + } else if (chan->pattern_lo_idx != chan->pattern_hi_idx) { 573 + lpg_calc_freq(chan, NSEC_PER_MSEC); 574 + 575 + chan->enabled = true; 576 + chan->ramp_enabled = true; 577 + 578 + lut_mask |= chan->lut_mask; 579 + triled_enabled |= chan->triled_mask; 580 + } else { 581 + lpg_calc_freq(chan, NSEC_PER_MSEC); 582 + 583 + duty = div_u64(brightness * chan->period, cdev->max_brightness); 584 + lpg_calc_duty(chan, duty); 585 + chan->enabled = true; 586 + chan->ramp_enabled = false; 587 + 588 + triled_enabled |= chan->triled_mask; 589 + } 590 + 591 + triled_mask |= chan->triled_mask; 592 + 593 + lpg_apply(chan); 594 + } 595 + 596 + /* Toggle triled lines */ 597 + if (triled_mask) 598 + triled_set(lpg, triled_mask, triled_enabled); 599 + 600 + /* Trigger start of ramp generator(s) */ 601 + if (lut_mask) 602 + lpg_lut_sync(lpg, lut_mask); 603 + } 604 + 605 + static void lpg_brightness_single_set(struct led_classdev *cdev, 606 + enum led_brightness value) 607 + { 608 + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); 609 + struct mc_subled info; 610 + 611 + mutex_lock(&led->lpg->lock); 612 + 613 + info.brightness = value; 614 + lpg_brightness_set(led, cdev, &info); 615 + 616 + mutex_unlock(&led->lpg->lock); 617 + } 618 + 619 + static void lpg_brightness_mc_set(struct led_classdev *cdev, 620 + enum led_brightness value) 621 + { 622 + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); 623 + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); 624 + 625 + mutex_lock(&led->lpg->lock); 626 + 627 + led_mc_calc_color_components(mc, value); 628 + lpg_brightness_set(led, cdev, mc->subled_info); 629 + 630 + mutex_unlock(&led->lpg->lock); 631 + } 632 + 633 + static int lpg_blink_set(struct lpg_led *led, 634 + unsigned long *delay_on, unsigned long *delay_off) 635 + { 636 + struct lpg_channel *chan; 637 + unsigned int period; 638 + unsigned int triled_mask = 0; 639 + struct lpg *lpg = led->lpg; 640 + u64 duty; 641 + int i; 642 + 643 + if (!*delay_on && !*delay_off) { 644 + *delay_on = 500; 645 + *delay_off = 500; 646 + } 647 + 648 + duty = *delay_on * NSEC_PER_MSEC; 649 + period = (*delay_on + *delay_off) * NSEC_PER_MSEC; 650 + 651 + for (i = 0; i < led->num_channels; i++) { 652 + chan = led->channels[i]; 653 + 654 + lpg_calc_freq(chan, period); 655 + lpg_calc_duty(chan, duty); 656 + 657 + chan->enabled = true; 658 + chan->ramp_enabled = false; 659 + 660 + triled_mask |= chan->triled_mask; 661 + 662 + lpg_apply(chan); 663 + } 664 + 665 + /* Enable triled lines */ 666 + triled_set(lpg, triled_mask, triled_mask); 667 + 668 + chan = led->channels[0]; 669 + duty = div_u64(chan->pwm_value * chan->period, LPG_RESOLUTION); 670 + *delay_on = div_u64(duty, NSEC_PER_MSEC); 671 + *delay_off = div_u64(chan->period - duty, NSEC_PER_MSEC); 672 + 673 + return 0; 674 + } 675 + 676 + static int lpg_blink_single_set(struct led_classdev *cdev, 677 + unsigned long *delay_on, unsigned long *delay_off) 678 + { 679 + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); 680 + int ret; 681 + 682 + mutex_lock(&led->lpg->lock); 683 + 684 + ret = lpg_blink_set(led, delay_on, delay_off); 685 + 686 + mutex_unlock(&led->lpg->lock); 687 + 688 + return ret; 689 + } 690 + 691 + static int lpg_blink_mc_set(struct led_classdev *cdev, 692 + unsigned long *delay_on, unsigned long *delay_off) 693 + { 694 + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); 695 + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); 696 + int ret; 697 + 698 + mutex_lock(&led->lpg->lock); 699 + 700 + ret = lpg_blink_set(led, delay_on, delay_off); 701 + 702 + mutex_unlock(&led->lpg->lock); 703 + 704 + return ret; 705 + } 706 + 707 + static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, 708 + u32 len, int repeat) 709 + { 710 + struct lpg_channel *chan; 711 + struct lpg *lpg = led->lpg; 712 + struct led_pattern *pattern; 713 + unsigned int brightness_a; 714 + unsigned int brightness_b; 715 + unsigned int actual_len; 716 + unsigned int hi_pause; 717 + unsigned int lo_pause; 718 + unsigned int delta_t; 719 + unsigned int lo_idx; 720 + unsigned int hi_idx; 721 + unsigned int i; 722 + bool ping_pong = true; 723 + int ret = -EINVAL; 724 + 725 + /* Hardware only support oneshot or indefinite loops */ 726 + if (repeat != -1 && repeat != 1) 727 + return -EINVAL; 728 + 729 + /* 730 + * The standardized leds-trigger-pattern format defines that the 731 + * brightness of the LED follows a linear transition from one entry 732 + * in the pattern to the next, over the given delta_t time. It 733 + * describes that the way to perform instant transitions a zero-length 734 + * entry should be added following a pattern entry. 735 + * 736 + * The LPG hardware is only able to perform the latter (no linear 737 + * transitions), so require each entry in the pattern to be followed by 738 + * a zero-length transition. 739 + */ 740 + if (len % 2) 741 + return -EINVAL; 742 + 743 + pattern = kcalloc(len / 2, sizeof(*pattern), GFP_KERNEL); 744 + if (!pattern) 745 + return -ENOMEM; 746 + 747 + for (i = 0; i < len; i += 2) { 748 + if (led_pattern[i].brightness != led_pattern[i + 1].brightness) 749 + goto out_free_pattern; 750 + if (led_pattern[i + 1].delta_t != 0) 751 + goto out_free_pattern; 752 + 753 + pattern[i / 2].brightness = led_pattern[i].brightness; 754 + pattern[i / 2].delta_t = led_pattern[i].delta_t; 755 + } 756 + 757 + len /= 2; 758 + 759 + /* 760 + * Specifying a pattern of length 1 causes the hardware to iterate 761 + * through the entire LUT, so prohibit this. 762 + */ 763 + if (len < 2) 764 + goto out_free_pattern; 765 + 766 + /* 767 + * The LPG plays patterns with at a fixed pace, a "low pause" can be 768 + * used to stretch the first delay of the pattern and a "high pause" 769 + * the last one. 770 + * 771 + * In order to save space the pattern can be played in "ping pong" 772 + * mode, in which the pattern is first played forward, then "high 773 + * pause" is applied, then the pattern is played backwards and finally 774 + * the "low pause" is applied. 775 + * 776 + * The middle elements of the pattern are used to determine delta_t and 777 + * the "low pause" and "high pause" multipliers are derrived from this. 778 + * 779 + * The first element in the pattern is used to determine "low pause". 780 + * 781 + * If the specified pattern is a palindrome the ping pong mode is 782 + * enabled. In this scenario the delta_t of the middle entry (i.e. the 783 + * last in the programmed pattern) determines the "high pause". 784 + */ 785 + 786 + /* Detect palindromes and use "ping pong" to reduce LUT usage */ 787 + for (i = 0; i < len / 2; i++) { 788 + brightness_a = pattern[i].brightness; 789 + brightness_b = pattern[len - i - 1].brightness; 790 + 791 + if (brightness_a != brightness_b) { 792 + ping_pong = false; 793 + break; 794 + } 795 + } 796 + 797 + /* The pattern length to be written to the LUT */ 798 + if (ping_pong) 799 + actual_len = (len + 1) / 2; 800 + else 801 + actual_len = len; 802 + 803 + /* 804 + * Validate that all delta_t in the pattern are the same, with the 805 + * exception of the middle element in case of ping_pong. 806 + */ 807 + delta_t = pattern[1].delta_t; 808 + for (i = 2; i < len; i++) { 809 + if (pattern[i].delta_t != delta_t) { 810 + /* 811 + * Allow last entry in the full or shortened pattern to 812 + * specify hi pause. Reject other variations. 813 + */ 814 + if (i != actual_len - 1) 815 + goto out_free_pattern; 816 + } 817 + } 818 + 819 + /* LPG_RAMP_DURATION_REG is a 9bit */ 820 + if (delta_t >= BIT(9)) 821 + goto out_free_pattern; 822 + 823 + /* Find "low pause" and "high pause" in the pattern */ 824 + lo_pause = pattern[0].delta_t; 825 + hi_pause = pattern[actual_len - 1].delta_t; 826 + 827 + mutex_lock(&lpg->lock); 828 + ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx); 829 + if (ret < 0) 830 + goto out_unlock; 831 + 832 + for (i = 0; i < led->num_channels; i++) { 833 + chan = led->channels[i]; 834 + 835 + chan->ramp_tick_ms = delta_t; 836 + chan->ramp_ping_pong = ping_pong; 837 + chan->ramp_oneshot = repeat != -1; 838 + 839 + chan->ramp_lo_pause_ms = lo_pause; 840 + chan->ramp_hi_pause_ms = hi_pause; 841 + 842 + chan->pattern_lo_idx = lo_idx; 843 + chan->pattern_hi_idx = hi_idx; 844 + } 845 + 846 + out_unlock: 847 + mutex_unlock(&lpg->lock); 848 + out_free_pattern: 849 + kfree(pattern); 850 + 851 + return ret; 852 + } 853 + 854 + static int lpg_pattern_single_set(struct led_classdev *cdev, 855 + struct led_pattern *pattern, u32 len, 856 + int repeat) 857 + { 858 + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); 859 + int ret; 860 + 861 + ret = lpg_pattern_set(led, pattern, len, repeat); 862 + if (ret < 0) 863 + return ret; 864 + 865 + lpg_brightness_single_set(cdev, LED_FULL); 866 + 867 + return 0; 868 + } 869 + 870 + static int lpg_pattern_mc_set(struct led_classdev *cdev, 871 + struct led_pattern *pattern, u32 len, 872 + int repeat) 873 + { 874 + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); 875 + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); 876 + int ret; 877 + 878 + ret = lpg_pattern_set(led, pattern, len, repeat); 879 + if (ret < 0) 880 + return ret; 881 + 882 + led_mc_calc_color_components(mc, LED_FULL); 883 + lpg_brightness_set(led, cdev, mc->subled_info); 884 + 885 + return 0; 886 + } 887 + 888 + static int lpg_pattern_clear(struct lpg_led *led) 889 + { 890 + struct lpg_channel *chan; 891 + struct lpg *lpg = led->lpg; 892 + int i; 893 + 894 + mutex_lock(&lpg->lock); 895 + 896 + chan = led->channels[0]; 897 + lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); 898 + 899 + for (i = 0; i < led->num_channels; i++) { 900 + chan = led->channels[i]; 901 + chan->pattern_lo_idx = 0; 902 + chan->pattern_hi_idx = 0; 903 + } 904 + 905 + mutex_unlock(&lpg->lock); 906 + 907 + return 0; 908 + } 909 + 910 + static int lpg_pattern_single_clear(struct led_classdev *cdev) 911 + { 912 + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); 913 + 914 + return lpg_pattern_clear(led); 915 + } 916 + 917 + static int lpg_pattern_mc_clear(struct led_classdev *cdev) 918 + { 919 + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); 920 + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); 921 + 922 + return lpg_pattern_clear(led); 923 + } 924 + 925 + static int lpg_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 926 + { 927 + struct lpg *lpg = container_of(chip, struct lpg, pwm); 928 + struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; 929 + 930 + return chan->in_use ? -EBUSY : 0; 931 + } 932 + 933 + /* 934 + * Limitations: 935 + * - Updating both duty and period is not done atomically, so the output signal 936 + * will momentarily be a mix of the settings. 937 + * - Changed parameters takes effect immediately. 938 + * - A disabled channel outputs a logical 0. 939 + */ 940 + static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 941 + const struct pwm_state *state) 942 + { 943 + struct lpg *lpg = container_of(chip, struct lpg, pwm); 944 + struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; 945 + int ret = 0; 946 + 947 + if (state->polarity != PWM_POLARITY_NORMAL) 948 + return -EINVAL; 949 + 950 + mutex_lock(&lpg->lock); 951 + 952 + if (state->enabled) { 953 + ret = lpg_calc_freq(chan, state->period); 954 + if (ret < 0) 955 + goto out_unlock; 956 + 957 + lpg_calc_duty(chan, state->duty_cycle); 958 + } 959 + chan->enabled = state->enabled; 960 + 961 + lpg_apply(chan); 962 + 963 + triled_set(lpg, chan->triled_mask, chan->enabled ? chan->triled_mask : 0); 964 + 965 + out_unlock: 966 + mutex_unlock(&lpg->lock); 967 + 968 + return ret; 969 + } 970 + 971 + static void lpg_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 972 + struct pwm_state *state) 973 + { 974 + struct lpg *lpg = container_of(chip, struct lpg, pwm); 975 + struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; 976 + unsigned int pre_div; 977 + unsigned int refclk; 978 + unsigned int val; 979 + unsigned int m; 980 + u16 pwm_value; 981 + int ret; 982 + 983 + ret = regmap_read(lpg->map, chan->base + LPG_SIZE_CLK_REG, &val); 984 + if (ret) 985 + return; 986 + 987 + refclk = lpg_clk_rates[val & PWM_CLK_SELECT_MASK]; 988 + if (refclk) { 989 + ret = regmap_read(lpg->map, chan->base + LPG_PREDIV_CLK_REG, &val); 990 + if (ret) 991 + return; 992 + 993 + pre_div = lpg_pre_divs[FIELD_GET(PWM_FREQ_PRE_DIV_MASK, val)]; 994 + m = FIELD_GET(PWM_FREQ_EXP_MASK, val); 995 + 996 + ret = regmap_bulk_read(lpg->map, chan->base + PWM_VALUE_REG, &pwm_value, sizeof(pwm_value)); 997 + if (ret) 998 + return; 999 + 1000 + state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * LPG_RESOLUTION * pre_div * (1 << m), refclk); 1001 + state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pwm_value * pre_div * (1 << m), refclk); 1002 + } else { 1003 + state->period = 0; 1004 + state->duty_cycle = 0; 1005 + } 1006 + 1007 + ret = regmap_read(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, &val); 1008 + if (ret) 1009 + return; 1010 + 1011 + state->enabled = FIELD_GET(LPG_ENABLE_CONTROL_OUTPUT, val); 1012 + state->polarity = PWM_POLARITY_NORMAL; 1013 + 1014 + if (state->duty_cycle > state->period) 1015 + state->duty_cycle = state->period; 1016 + } 1017 + 1018 + static const struct pwm_ops lpg_pwm_ops = { 1019 + .request = lpg_pwm_request, 1020 + .apply = lpg_pwm_apply, 1021 + .get_state = lpg_pwm_get_state, 1022 + .owner = THIS_MODULE, 1023 + }; 1024 + 1025 + static int lpg_add_pwm(struct lpg *lpg) 1026 + { 1027 + int ret; 1028 + 1029 + lpg->pwm.base = -1; 1030 + lpg->pwm.dev = lpg->dev; 1031 + lpg->pwm.npwm = lpg->num_channels; 1032 + lpg->pwm.ops = &lpg_pwm_ops; 1033 + 1034 + ret = pwmchip_add(&lpg->pwm); 1035 + if (ret) 1036 + dev_err(lpg->dev, "failed to add PWM chip: ret %d\n", ret); 1037 + 1038 + return ret; 1039 + } 1040 + 1041 + static int lpg_parse_channel(struct lpg *lpg, struct device_node *np, 1042 + struct lpg_channel **channel) 1043 + { 1044 + struct lpg_channel *chan; 1045 + u32 color = LED_COLOR_ID_GREEN; 1046 + u32 reg; 1047 + int ret; 1048 + 1049 + ret = of_property_read_u32(np, "reg", &reg); 1050 + if (ret || !reg || reg > lpg->num_channels) { 1051 + dev_err(lpg->dev, "invalid \"reg\" of %pOFn\n", np); 1052 + return -EINVAL; 1053 + } 1054 + 1055 + chan = &lpg->channels[reg - 1]; 1056 + chan->in_use = true; 1057 + 1058 + ret = of_property_read_u32(np, "color", &color); 1059 + if (ret < 0 && ret != -EINVAL) { 1060 + dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); 1061 + return ret; 1062 + } 1063 + 1064 + chan->color = color; 1065 + 1066 + *channel = chan; 1067 + 1068 + return 0; 1069 + } 1070 + 1071 + static int lpg_add_led(struct lpg *lpg, struct device_node *np) 1072 + { 1073 + struct led_init_data init_data = {}; 1074 + struct led_classdev *cdev; 1075 + struct device_node *child; 1076 + struct mc_subled *info; 1077 + struct lpg_led *led; 1078 + const char *state; 1079 + int num_channels; 1080 + u32 color = 0; 1081 + int ret; 1082 + int i; 1083 + 1084 + ret = of_property_read_u32(np, "color", &color); 1085 + if (ret < 0 && ret != -EINVAL) { 1086 + dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); 1087 + return ret; 1088 + } 1089 + 1090 + if (color == LED_COLOR_ID_RGB) 1091 + num_channels = of_get_available_child_count(np); 1092 + else 1093 + num_channels = 1; 1094 + 1095 + led = devm_kzalloc(lpg->dev, struct_size(led, channels, num_channels), GFP_KERNEL); 1096 + if (!led) 1097 + return -ENOMEM; 1098 + 1099 + led->lpg = lpg; 1100 + led->num_channels = num_channels; 1101 + 1102 + if (color == LED_COLOR_ID_RGB) { 1103 + info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL); 1104 + if (!info) 1105 + return -ENOMEM; 1106 + i = 0; 1107 + for_each_available_child_of_node(np, child) { 1108 + ret = lpg_parse_channel(lpg, child, &led->channels[i]); 1109 + if (ret < 0) 1110 + return ret; 1111 + 1112 + info[i].color_index = led->channels[i]->color; 1113 + info[i].intensity = 0; 1114 + i++; 1115 + } 1116 + 1117 + led->mcdev.subled_info = info; 1118 + led->mcdev.num_colors = num_channels; 1119 + 1120 + cdev = &led->mcdev.led_cdev; 1121 + cdev->brightness_set = lpg_brightness_mc_set; 1122 + cdev->blink_set = lpg_blink_mc_set; 1123 + 1124 + /* Register pattern accessors only if we have a LUT block */ 1125 + if (lpg->lut_base) { 1126 + cdev->pattern_set = lpg_pattern_mc_set; 1127 + cdev->pattern_clear = lpg_pattern_mc_clear; 1128 + } 1129 + } else { 1130 + ret = lpg_parse_channel(lpg, np, &led->channels[0]); 1131 + if (ret < 0) 1132 + return ret; 1133 + 1134 + cdev = &led->cdev; 1135 + cdev->brightness_set = lpg_brightness_single_set; 1136 + cdev->blink_set = lpg_blink_single_set; 1137 + 1138 + /* Register pattern accessors only if we have a LUT block */ 1139 + if (lpg->lut_base) { 1140 + cdev->pattern_set = lpg_pattern_single_set; 1141 + cdev->pattern_clear = lpg_pattern_single_clear; 1142 + } 1143 + } 1144 + 1145 + cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL); 1146 + cdev->max_brightness = LPG_RESOLUTION - 1; 1147 + 1148 + if (!of_property_read_string(np, "default-state", &state) && 1149 + !strcmp(state, "on")) 1150 + cdev->brightness = cdev->max_brightness; 1151 + else 1152 + cdev->brightness = LED_OFF; 1153 + 1154 + cdev->brightness_set(cdev, cdev->brightness); 1155 + 1156 + init_data.fwnode = of_fwnode_handle(np); 1157 + 1158 + if (color == LED_COLOR_ID_RGB) 1159 + ret = devm_led_classdev_multicolor_register_ext(lpg->dev, &led->mcdev, &init_data); 1160 + else 1161 + ret = devm_led_classdev_register_ext(lpg->dev, &led->cdev, &init_data); 1162 + if (ret) 1163 + dev_err(lpg->dev, "unable to register %s\n", cdev->name); 1164 + 1165 + return ret; 1166 + } 1167 + 1168 + static int lpg_init_channels(struct lpg *lpg) 1169 + { 1170 + const struct lpg_data *data = lpg->data; 1171 + struct lpg_channel *chan; 1172 + int i; 1173 + 1174 + lpg->num_channels = data->num_channels; 1175 + lpg->channels = devm_kcalloc(lpg->dev, data->num_channels, 1176 + sizeof(struct lpg_channel), GFP_KERNEL); 1177 + if (!lpg->channels) 1178 + return -ENOMEM; 1179 + 1180 + for (i = 0; i < data->num_channels; i++) { 1181 + chan = &lpg->channels[i]; 1182 + 1183 + chan->lpg = lpg; 1184 + chan->base = data->channels[i].base; 1185 + chan->triled_mask = data->channels[i].triled_mask; 1186 + chan->lut_mask = BIT(i); 1187 + 1188 + regmap_read(lpg->map, chan->base + LPG_SUBTYPE_REG, &chan->subtype); 1189 + } 1190 + 1191 + return 0; 1192 + } 1193 + 1194 + static int lpg_init_triled(struct lpg *lpg) 1195 + { 1196 + struct device_node *np = lpg->dev->of_node; 1197 + int ret; 1198 + 1199 + /* Skip initialization if we don't have a triled block */ 1200 + if (!lpg->data->triled_base) 1201 + return 0; 1202 + 1203 + lpg->triled_base = lpg->data->triled_base; 1204 + lpg->triled_has_atc_ctl = lpg->data->triled_has_atc_ctl; 1205 + lpg->triled_has_src_sel = lpg->data->triled_has_src_sel; 1206 + 1207 + if (lpg->triled_has_src_sel) { 1208 + ret = of_property_read_u32(np, "qcom,power-source", &lpg->triled_src); 1209 + if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) { 1210 + dev_err(lpg->dev, "invalid power source\n"); 1211 + return -EINVAL; 1212 + } 1213 + } 1214 + 1215 + /* Disable automatic trickle charge LED */ 1216 + if (lpg->triled_has_atc_ctl) 1217 + regmap_write(lpg->map, lpg->triled_base + TRI_LED_ATC_CTL, 0); 1218 + 1219 + /* Configure power source */ 1220 + if (lpg->triled_has_src_sel) 1221 + regmap_write(lpg->map, lpg->triled_base + TRI_LED_SRC_SEL, lpg->triled_src); 1222 + 1223 + /* Default all outputs to off */ 1224 + regmap_write(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, 0); 1225 + 1226 + return 0; 1227 + } 1228 + 1229 + static int lpg_init_lut(struct lpg *lpg) 1230 + { 1231 + const struct lpg_data *data = lpg->data; 1232 + 1233 + if (!data->lut_base) 1234 + return 0; 1235 + 1236 + lpg->lut_base = data->lut_base; 1237 + lpg->lut_size = data->lut_size; 1238 + 1239 + lpg->lut_bitmap = devm_bitmap_zalloc(lpg->dev, lpg->lut_size, GFP_KERNEL); 1240 + if (!lpg->lut_bitmap) 1241 + return -ENOMEM; 1242 + 1243 + return 0; 1244 + } 1245 + 1246 + static int lpg_probe(struct platform_device *pdev) 1247 + { 1248 + struct device_node *np; 1249 + struct lpg *lpg; 1250 + int ret; 1251 + int i; 1252 + 1253 + lpg = devm_kzalloc(&pdev->dev, sizeof(*lpg), GFP_KERNEL); 1254 + if (!lpg) 1255 + return -ENOMEM; 1256 + 1257 + lpg->data = of_device_get_match_data(&pdev->dev); 1258 + if (!lpg->data) 1259 + return -EINVAL; 1260 + 1261 + platform_set_drvdata(pdev, lpg); 1262 + 1263 + lpg->dev = &pdev->dev; 1264 + mutex_init(&lpg->lock); 1265 + 1266 + lpg->map = dev_get_regmap(pdev->dev.parent, NULL); 1267 + if (!lpg->map) 1268 + return dev_err_probe(&pdev->dev, -ENXIO, "parent regmap unavailable\n"); 1269 + 1270 + ret = lpg_init_channels(lpg); 1271 + if (ret < 0) 1272 + return ret; 1273 + 1274 + ret = lpg_parse_dtest(lpg); 1275 + if (ret < 0) 1276 + return ret; 1277 + 1278 + ret = lpg_init_triled(lpg); 1279 + if (ret < 0) 1280 + return ret; 1281 + 1282 + ret = lpg_init_lut(lpg); 1283 + if (ret < 0) 1284 + return ret; 1285 + 1286 + for_each_available_child_of_node(pdev->dev.of_node, np) { 1287 + ret = lpg_add_led(lpg, np); 1288 + if (ret) 1289 + return ret; 1290 + } 1291 + 1292 + for (i = 0; i < lpg->num_channels; i++) 1293 + lpg_apply_dtest(&lpg->channels[i]); 1294 + 1295 + return lpg_add_pwm(lpg); 1296 + } 1297 + 1298 + static int lpg_remove(struct platform_device *pdev) 1299 + { 1300 + struct lpg *lpg = platform_get_drvdata(pdev); 1301 + 1302 + pwmchip_remove(&lpg->pwm); 1303 + 1304 + return 0; 1305 + } 1306 + 1307 + static const struct lpg_data pm8916_pwm_data = { 1308 + .num_channels = 1, 1309 + .channels = (const struct lpg_channel_data[]) { 1310 + { .base = 0xbc00 }, 1311 + }, 1312 + }; 1313 + 1314 + static const struct lpg_data pm8941_lpg_data = { 1315 + .lut_base = 0xb000, 1316 + .lut_size = 64, 1317 + 1318 + .triled_base = 0xd000, 1319 + .triled_has_atc_ctl = true, 1320 + .triled_has_src_sel = true, 1321 + 1322 + .num_channels = 8, 1323 + .channels = (const struct lpg_channel_data[]) { 1324 + { .base = 0xb100 }, 1325 + { .base = 0xb200 }, 1326 + { .base = 0xb300 }, 1327 + { .base = 0xb400 }, 1328 + { .base = 0xb500, .triled_mask = BIT(5) }, 1329 + { .base = 0xb600, .triled_mask = BIT(6) }, 1330 + { .base = 0xb700, .triled_mask = BIT(7) }, 1331 + { .base = 0xb800 }, 1332 + }, 1333 + }; 1334 + 1335 + static const struct lpg_data pm8994_lpg_data = { 1336 + .lut_base = 0xb000, 1337 + .lut_size = 64, 1338 + 1339 + .num_channels = 6, 1340 + .channels = (const struct lpg_channel_data[]) { 1341 + { .base = 0xb100 }, 1342 + { .base = 0xb200 }, 1343 + { .base = 0xb300 }, 1344 + { .base = 0xb400 }, 1345 + { .base = 0xb500 }, 1346 + { .base = 0xb600 }, 1347 + }, 1348 + }; 1349 + 1350 + static const struct lpg_data pmi8994_lpg_data = { 1351 + .lut_base = 0xb000, 1352 + .lut_size = 24, 1353 + 1354 + .triled_base = 0xd000, 1355 + .triled_has_atc_ctl = true, 1356 + .triled_has_src_sel = true, 1357 + 1358 + .num_channels = 4, 1359 + .channels = (const struct lpg_channel_data[]) { 1360 + { .base = 0xb100, .triled_mask = BIT(5) }, 1361 + { .base = 0xb200, .triled_mask = BIT(6) }, 1362 + { .base = 0xb300, .triled_mask = BIT(7) }, 1363 + { .base = 0xb400 }, 1364 + }, 1365 + }; 1366 + 1367 + static const struct lpg_data pmi8998_lpg_data = { 1368 + .lut_base = 0xb000, 1369 + .lut_size = 49, 1370 + 1371 + .triled_base = 0xd000, 1372 + 1373 + .num_channels = 6, 1374 + .channels = (const struct lpg_channel_data[]) { 1375 + { .base = 0xb100 }, 1376 + { .base = 0xb200 }, 1377 + { .base = 0xb300, .triled_mask = BIT(5) }, 1378 + { .base = 0xb400, .triled_mask = BIT(6) }, 1379 + { .base = 0xb500, .triled_mask = BIT(7) }, 1380 + { .base = 0xb600 }, 1381 + }, 1382 + }; 1383 + 1384 + static const struct lpg_data pm8150b_lpg_data = { 1385 + .lut_base = 0xb000, 1386 + .lut_size = 24, 1387 + 1388 + .triled_base = 0xd000, 1389 + 1390 + .num_channels = 2, 1391 + .channels = (const struct lpg_channel_data[]) { 1392 + { .base = 0xb100, .triled_mask = BIT(7) }, 1393 + { .base = 0xb200, .triled_mask = BIT(6) }, 1394 + }, 1395 + }; 1396 + 1397 + static const struct lpg_data pm8150l_lpg_data = { 1398 + .lut_base = 0xb000, 1399 + .lut_size = 48, 1400 + 1401 + .triled_base = 0xd000, 1402 + 1403 + .num_channels = 5, 1404 + .channels = (const struct lpg_channel_data[]) { 1405 + { .base = 0xb100, .triled_mask = BIT(7) }, 1406 + { .base = 0xb200, .triled_mask = BIT(6) }, 1407 + { .base = 0xb300, .triled_mask = BIT(5) }, 1408 + { .base = 0xbc00 }, 1409 + { .base = 0xbd00 }, 1410 + 1411 + }, 1412 + }; 1413 + 1414 + static const struct lpg_data pm8350c_pwm_data = { 1415 + .triled_base = 0xef00, 1416 + 1417 + .num_channels = 4, 1418 + .channels = (const struct lpg_channel_data[]) { 1419 + { .base = 0xe800, .triled_mask = BIT(7) }, 1420 + { .base = 0xe900, .triled_mask = BIT(6) }, 1421 + { .base = 0xea00, .triled_mask = BIT(5) }, 1422 + { .base = 0xeb00 }, 1423 + }, 1424 + }; 1425 + 1426 + static const struct of_device_id lpg_of_table[] = { 1427 + { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data }, 1428 + { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data }, 1429 + { .compatible = "qcom,pm8350c-pwm", .data = &pm8350c_pwm_data }, 1430 + { .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data }, 1431 + { .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data }, 1432 + { .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data }, 1433 + { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data }, 1434 + { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data }, 1435 + { .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data }, 1436 + {} 1437 + }; 1438 + MODULE_DEVICE_TABLE(of, lpg_of_table); 1439 + 1440 + static struct platform_driver lpg_driver = { 1441 + .probe = lpg_probe, 1442 + .remove = lpg_remove, 1443 + .driver = { 1444 + .name = "qcom-spmi-lpg", 1445 + .of_match_table = lpg_of_table, 1446 + }, 1447 + }; 1448 + module_platform_driver(lpg_driver); 1449 + 1450 + MODULE_DESCRIPTION("Qualcomm LPG LED driver"); 1451 + MODULE_LICENSE("GPL v2");