mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-04 04:04:19 +00:00
- Core Frameworks
- Add new framework to support Group Multi-Color (GMC) LEDs - Offer an 'optional' API for non-essential LEDs - Support obtaining 'max brightness' values from Device Tree - Provide new led_classdev member 'color' (settable via DT and SYFS) - Stop TTY Trigger from using the old LED_ON constraints - Statically allocate leds_class - New Drivers - Add support for NXP PCA995x I2C Constant Current LED Driver - New Device Support - Add support for Siemens Simatic IPC BX-21 to Simatic IPC - Fix-ups - Some dependency / Kconfig tweaking - Move final probe() functions back over from .probe_new() - Simplify obtaining resources (memory, device data) using unified API helpers - Bunch of Device Tree additions, conversions and adaptions - Fix trivial styling issues; comments - Ensure correct includes are present and remove some that are not required - Omit the use of redundant casts and if relevant replace with better ones - Use purpose-built APIs for various actions; sysfs_emit(), module_led_trigger() - Remove a bunch of superfluous locking - Bug Fixes - Ensure error codes are correctly propagated back up the call chain - Fix incorrect error values from being returned (missing '-') - Ensure get'ed resources are put'ed to prevent leaks - Use correct class when exporting module resources - Fixing rounding (or lack there of) issues - Fix 'always false' LED_COLOR_ID_MULTI BUG() check -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmT15DUACgkQUa+KL4f8 d2ER0BAAoejCCnb24JOrYQy7Pq3/ZaaT6PWOB+/U+6s5jyj9ikwHqqH+g/E+FqkE AHvhkBalgw1l+QRCvrZMEcsiQmjK5ZR010Ju3lCbvUDa1tsm2Fn/K2IZZxI1j9O8 mGnPjYrX3FRfejscw0a+MA75o3DuN97V9mHNk6lyRnm/d0ronu+vv/fRYFON04iY v2e166/9wfJWvDwOLo+phDS2aCkjLkxzj2lcTtcvuzYqQjOPDqiFTJsft9gOpC9r hKOqUkexKebShbi08A+x0fmiB9LNs5kZ3xWWLaxVDT58Plkqv905oLVrWARTGhIG BDAhW2/TLV30bhSiTb/bX0eNEMCU6q6WIPwc3otUYpkDeLgbxLD7Rlhp49oud7j9 5vWX8bxSczIYRir42TKZ+s/d6pKKSvMrsNRDplFb1ahZTv3ssDxuxAA/FY2UPqTt C83j0hJOwVfj6ric01R+yedPSW8Po8xqEIzjoxzYtsu3IYGiz6ddjd2ZnhNF5YfP AN7GDuja9r7+Fa8JcTFssHe6HtuzkfpJvrpjV3BxL79q6s1z+lT9szTw5Z65zGmO k/z4xz95RHUTbpR4rVT+A1pdbQ8OsbVthdy1XnJoYK3NJYJrPldj3Ur3W4wRecP6 32RWF8ZF6WARsSyyACLo/MP6qxwAE2xk5TjmpLqCeK0ObVmlwDM= =wNmv -----END PGP SIGNATURE----- Merge tag 'leds-next-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds Pull LED updates from Lee Jones: "Core Frameworks: - Add new framework to support Group Multi-Color (GMC) LEDs - Offer an 'optional' API for non-essential LEDs - Support obtaining 'max brightness' values from Device Tree - Provide new led_classdev member 'color' (settable via DT and SYFS) - Stop TTY Trigger from using the old LED_ON constraints - Statically allocate leds_class New Drivers: - Add support for NXP PCA995x I2C Constant Current LED Driver New Device Support: - Add support for Siemens Simatic IPC BX-21 to Simatic IPC Fix-ups: - Some dependency / Kconfig tweaking - Move final probe() functions back over from .probe_new() - Simplify obtaining resources (memory, device data) using unified API helpers - Bunch of Device Tree additions, conversions and adaptions - Fix trivial styling issues; comments - Ensure correct includes are present and remove some that are not required - Omit the use of redundant casts and if relevant replace with better ones - Use purpose-built APIs for various actions; sysfs_emit(), module_led_trigger() - Remove a bunch of superfluous locking Bug Fixes: - Ensure error codes are correctly propagated back up the call chain - Fix incorrect error values from being returned (missing '-') - Ensure get'ed resources are put'ed to prevent leaks - Use correct class when exporting module resources - Fixing rounding (or lack there of) issues - Fix 'always false' LED_COLOR_ID_MULTI BUG() check" * tag 'leds-next-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (40 commits) leds: aw2013: Enable pull-up supply for interrupt and I2C dt-bindings: leds: Document pull-up supply for interrupt and I2C dt-bindings: leds: aw2013: Document interrupt leds: uleds: Use module_misc_device macro to simplify the code leds: trigger: netdev: Use module_led_trigger macro to simplify the code dt-bindings: leds: Fix reference to definition of default-state leds: turris-omnia: Drop unnecessary mutex locking leds: turris-omnia: Use sysfs_emit() instead of sprintf() leds: Make leds_class a static const structure leds: Remove redundant of_match_ptr() dt-bindings: leds: Add gpio-line-names to PCA9532 GPIO leds: trigger: tty: Do not use LED_ON/OFF constants, use led_blink_set_oneshot instead dt-bindings: leds: rohm,bd71828: Drop select:false leds: Fix BUG_ON check for LED_COLOR_ID_MULTI that is always false leds: multicolor: Use rounded division when calculating color components leds: rgb: Add a multicolor LED driver to group monochromatic LEDs dt-bindings: leds: Add binding for a multicolor group of LEDs leds: class: Store the color index in struct led_classdev leds: Provide devm_of_led_get_optional() leds: pca995x: Fix MODULE_DEVICE_TABLE for OF ...
This commit is contained in:
commit
2be6bc48df
@ -59,6 +59,15 @@ Description:
|
||||
brightness. Reading this file when no hw brightness change
|
||||
event has happened will return an ENODATA error.
|
||||
|
||||
What: /sys/class/leds/<led>/color
|
||||
Date: June 2023
|
||||
KernelVersion: 6.5
|
||||
Description:
|
||||
Color of the LED.
|
||||
|
||||
This is a read-only file. Reading this file returns the color
|
||||
of the LED as a string (e.g: "red", "green", "multicolor").
|
||||
|
||||
What: /sys/class/leds/<led>/trigger
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
|
@ -83,8 +83,7 @@ properties:
|
||||
- enum:
|
||||
# LED will act as a back-light, controlled by the framebuffer system
|
||||
- backlight
|
||||
# LED will turn on (but for leds-gpio see "default-state" property in
|
||||
# Documentation/devicetree/bindings/leds/leds-gpio.yaml)
|
||||
# LED will turn on (see also "default-state" property)
|
||||
- default-on
|
||||
# LED "double" flashes at a load average based rate
|
||||
- heartbeat
|
||||
@ -158,6 +157,18 @@ properties:
|
||||
For flash LED controllers with configurable current this property is
|
||||
mandatory for the LEDs in the non-flash modes (e.g. torch or indicator).
|
||||
|
||||
max-brightness:
|
||||
description:
|
||||
Normally, the maximum brightness is determined by the hardware, and this
|
||||
property is not required. This property is used to set a software limit.
|
||||
It could happen that an LED is made so bright that it gets damaged or
|
||||
causes damage due to restrictions in a specific system, such as mounting
|
||||
conditions.
|
||||
Note that this flag is mainly used for PWM-LEDs, where it is not possible
|
||||
to map brightness to current. Drivers for other controllers should use
|
||||
led-max-microamp.
|
||||
$ref: /schemas/types.yaml#definitions/uint32
|
||||
|
||||
panic-indicator:
|
||||
description:
|
||||
This property specifies that the LED should be used, if at all possible,
|
||||
|
@ -1,55 +0,0 @@
|
||||
* Panasonic AN30259A 3-channel LED driver
|
||||
|
||||
The AN30259A is a LED controller capable of driving three LEDs independently. It supports
|
||||
constant current output and sloping current output modes. The chip is connected over I2C.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "panasonic,an30259a".
|
||||
- reg: I2C slave address.
|
||||
- #address-cells: Must be 1.
|
||||
- #size-cells: Must be 0.
|
||||
|
||||
Each LED is represented as a sub-node of the panasonic,an30259a node.
|
||||
|
||||
Required sub-node properties:
|
||||
- reg: Pin that the LED is connected to. Must be 1, 2, or 3.
|
||||
|
||||
Optional sub-node properties:
|
||||
- function :
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
- color :
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
- label :
|
||||
see Documentation/devicetree/bindings/leds/common.txt (deprecated)
|
||||
- linux,default-trigger :
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
led-controller@30 {
|
||||
compatible = "panasonic,an30259a";
|
||||
reg = <0x30>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
linux,default-trigger = "heartbeat";
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
@ -20,9 +20,20 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: Open-drain, low active interrupt pin "INTN".
|
||||
Used to report completion of operations (power up, LED breath effects).
|
||||
|
||||
vcc-supply:
|
||||
description: Regulator providing power to the "VCC" pin.
|
||||
|
||||
vio-supply:
|
||||
description: Regulator providing power for pull-up of the I/O lines.
|
||||
"VIO1" in the typical application circuit example of the datasheet.
|
||||
Note that this regulator does not directly connect to AW2013, but is
|
||||
needed for the correct operation of the interrupt and I2C lines.
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
@ -52,6 +63,7 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
@ -61,6 +73,7 @@ examples:
|
||||
led-controller@45 {
|
||||
compatible = "awinic,aw2013";
|
||||
reg = <0x45>;
|
||||
interrupts = <42 IRQ_TYPE_LEVEL_LOW>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -0,0 +1,64 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/leds-group-multicolor.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Multi-color LED built with monochromatic LEDs
|
||||
|
||||
maintainers:
|
||||
- Jean-Jacques Hiblot <jjhiblot@traphandler.com>
|
||||
|
||||
description: |
|
||||
This driver combines several monochromatic LEDs into one multi-color
|
||||
LED using the multicolor LED class.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: leds-group-multicolor
|
||||
|
||||
leds:
|
||||
description:
|
||||
An aray of monochromatic leds
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
|
||||
required:
|
||||
- leds
|
||||
|
||||
allOf:
|
||||
- $ref: leds-class-multicolor.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
monochromatic-leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
led0: led-0 {
|
||||
gpios = <&mcu_pio 0 GPIO_ACTIVE_LOW>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led1: led-1 {
|
||||
gpios = <&mcu_pio 1 GPIO_ACTIVE_HIGH>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led2: led-2 {
|
||||
gpios = <&mcu_pio 2 GPIO_ACTIVE_HIGH>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
|
||||
multi-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
leds = <&led0>, <&led1>, <&led2>;
|
||||
};
|
||||
|
||||
...
|
@ -29,6 +29,10 @@ properties:
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
gpio-line-names:
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
|
81
Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
Normal file
81
Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
Normal file
@ -0,0 +1,81 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/nxp,pca995x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP PCA995x LED controllers
|
||||
|
||||
maintainers:
|
||||
- Isai Gaspar <isaiezequiel.gaspar@nxp.com>
|
||||
- Marek Vasut <marex@denx.de>
|
||||
|
||||
description:
|
||||
The NXP PCA9952/PCA9955B are programmable LED controllers connected via I2C
|
||||
that can drive 16 separate lines. Each of them can be individually switched
|
||||
on and off, and brightness can be controlled via individual PWM.
|
||||
|
||||
Datasheets are available at
|
||||
https://www.nxp.com/docs/en/data-sheet/PCA9952_PCA9955.pdf
|
||||
https://www.nxp.com/docs/en/data-sheet/PCA9955B.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,pca9952
|
||||
- nxp,pca9955b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@1 {
|
||||
compatible = "nxp,pca9955b";
|
||||
reg = <0x01>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
function = LED_FUNCTION_POWER;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -0,0 +1,84 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/panasonic,an30259a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Panasonic AN30259A 3-channel LED controller
|
||||
|
||||
maintainers:
|
||||
- Iskren Chernev <me@iskren.info>
|
||||
|
||||
description:
|
||||
The AN30259A is a LED controller capable of driving three LEDs independently.
|
||||
It supports constant current output and sloping current output modes. The chip
|
||||
is connected over I2C.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: panasonic,an30259a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^led@[1-3]$":
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
enum: [ 1, 2, 3 ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@30 {
|
||||
compatible = "panasonic,an30259a";
|
||||
reg = <0x30>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
linux,default-trigger = "heartbeat";
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -35,7 +35,7 @@ properties:
|
||||
description: GPIO pin to enable/disable the device.
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-6]$":
|
||||
"^led@[0-5]$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
@ -43,7 +43,7 @@ patternProperties:
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 6
|
||||
maximum: 5
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
@ -18,8 +18,6 @@ description: |
|
||||
|
||||
The device has two LED outputs referred as GRNLED and AMBLED in data-sheet.
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rohm,bd71828-leds
|
||||
|
@ -521,6 +521,15 @@ config LEDS_PCA963X
|
||||
LED driver chip accessed via the I2C bus. Supported
|
||||
devices include PCA9633 and PCA9634
|
||||
|
||||
config LEDS_PCA995X
|
||||
tristate "LED Support for PCA995x I2C chips"
|
||||
depends on LEDS_CLASS
|
||||
depends on I2C
|
||||
help
|
||||
This option enables support for LEDs connected to PCA995x
|
||||
LED driver chips accessed via the I2C bus. Supported
|
||||
devices include PCA9955BTW, PCA9952TW and PCA9955TW.
|
||||
|
||||
config LEDS_WM831X_STATUS
|
||||
tristate "LED support for status LEDs on WM831x PMICs"
|
||||
depends on LEDS_CLASS
|
||||
|
@ -72,6 +72,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o
|
||||
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
|
||||
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
|
||||
obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o
|
||||
obj-$(CONFIG_LEDS_PCA995X) += leds-pca995x.o
|
||||
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
|
||||
obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
|
||||
obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
|
||||
|
@ -1,10 +1,10 @@
|
||||
config LEDS_BCM63138
|
||||
tristate "LED Support for Broadcom BCM63138 SoC"
|
||||
depends on LEDS_CLASS
|
||||
depends on ARCH_BCM4908 || ARCH_BCM_5301X || BCM63XX || COMPILE_TEST
|
||||
depends on ARCH_BCMBCA || ARCH_BCM_5301X || BCM63XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
default ARCH_BCM4908
|
||||
default ARCH_BCMBCA
|
||||
help
|
||||
This option enables support for LED controller that is part of
|
||||
BCM63138 SoC. The same hardware block is known to be also used
|
||||
|
@ -89,6 +89,8 @@ config LEDS_QCOM_FLASH
|
||||
the total LED current will be split symmetrically on each channel and
|
||||
they will be enabled/disabled at the same time.
|
||||
|
||||
This driver can be built as a module, it will be called "leds-qcom-flash".
|
||||
|
||||
config LEDS_RT4505
|
||||
tristate "LED support for RT4505 flashlight controller"
|
||||
depends on I2C && OF
|
||||
|
@ -309,6 +309,10 @@ static int qcom_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool stat
|
||||
struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev);
|
||||
int rc;
|
||||
|
||||
rc = set_flash_strobe(led, SW_STROBE, false);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -745,6 +749,7 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
release:
|
||||
fwnode_handle_put(child);
|
||||
while (flash_data->v4l2_flash[flash_data->leds_count] && flash_data->leds_count)
|
||||
v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]);
|
||||
return rc;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -19,9 +20,10 @@ int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mcled_cdev->num_colors; i++)
|
||||
mcled_cdev->subled_info[i].brightness = brightness *
|
||||
mcled_cdev->subled_info[i].intensity /
|
||||
led_cdev->max_brightness;
|
||||
mcled_cdev->subled_info[i].brightness =
|
||||
DIV_ROUND_CLOSEST(brightness *
|
||||
mcled_cdev->subled_info[i].intensity,
|
||||
led_cdev->max_brightness);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <linux/of.h>
|
||||
#include "leds.h"
|
||||
|
||||
static struct class *leds_class;
|
||||
static DEFINE_MUTEX(leds_lookup_lock);
|
||||
static LIST_HEAD(leds_lookup_list);
|
||||
|
||||
@ -76,6 +75,19 @@ static ssize_t max_brightness_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(max_brightness);
|
||||
|
||||
static ssize_t color_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const char *color_text = "invalid";
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
||||
if (led_cdev->color < LED_COLOR_ID_MAX)
|
||||
color_text = led_colors[led_cdev->color];
|
||||
|
||||
return sysfs_emit(buf, "%s\n", color_text);
|
||||
}
|
||||
static DEVICE_ATTR_RO(color);
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0);
|
||||
static struct bin_attribute *led_trigger_bin_attrs[] = {
|
||||
@ -90,6 +102,7 @@ static const struct attribute_group led_trigger_group = {
|
||||
static struct attribute *led_class_attrs[] = {
|
||||
&dev_attr_brightness.attr,
|
||||
&dev_attr_max_brightness.attr,
|
||||
&dev_attr_color.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -234,6 +247,12 @@ static struct led_classdev *led_module_get(struct device *led_dev)
|
||||
return led_cdev;
|
||||
}
|
||||
|
||||
static const struct class leds_class = {
|
||||
.name = "leds",
|
||||
.dev_groups = led_groups,
|
||||
.pm = &leds_class_dev_pm_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* of_led_get() - request a LED device via the LED framework
|
||||
* @np: device node to get the LED device from
|
||||
@ -251,7 +270,7 @@ struct led_classdev *of_led_get(struct device_node *np, int index)
|
||||
if (!led_node)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
led_dev = class_find_device_by_of_node(leds_class, led_node);
|
||||
led_dev = class_find_device_by_of_node(&leds_class, led_node);
|
||||
of_node_put(led_node);
|
||||
put_device(led_dev);
|
||||
|
||||
@ -346,7 +365,7 @@ struct led_classdev *led_get(struct device *dev, char *con_id)
|
||||
if (!provider)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
led_dev = class_find_device_by_name(leds_class, provider);
|
||||
led_dev = class_find_device_by_name(&leds_class, provider);
|
||||
kfree_const(provider);
|
||||
|
||||
return led_module_get(led_dev);
|
||||
@ -402,6 +421,31 @@ void led_remove_lookup(struct led_lookup_data *led_lookup)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_remove_lookup);
|
||||
|
||||
/**
|
||||
* devm_of_led_get_optional - Resource-managed request of an optional LED device
|
||||
* @dev: LED consumer
|
||||
* @index: index of the LED to obtain in the consumer
|
||||
*
|
||||
* The device node of the device is parsed to find the requested LED device.
|
||||
* The LED device returned from this function is automatically released
|
||||
* on driver detach.
|
||||
*
|
||||
* @return a pointer to a LED device, ERR_PTR(errno) on failure and NULL if the
|
||||
* led was not found.
|
||||
*/
|
||||
struct led_classdev *__must_check devm_of_led_get_optional(struct device *dev,
|
||||
int index)
|
||||
{
|
||||
struct led_classdev *led;
|
||||
|
||||
led = devm_of_led_get(dev, index);
|
||||
if (IS_ERR(led) && PTR_ERR(led) == -ENOENT)
|
||||
return NULL;
|
||||
|
||||
return led;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_led_get_optional);
|
||||
|
||||
static int led_classdev_next_name(const char *init_name, char *name,
|
||||
size_t len)
|
||||
{
|
||||
@ -412,7 +456,7 @@ static int led_classdev_next_name(const char *init_name, char *name,
|
||||
strscpy(name, init_name, len);
|
||||
|
||||
while ((ret < len) &&
|
||||
(dev = class_find_device_by_name(leds_class, name))) {
|
||||
(dev = class_find_device_by_name(&leds_class, name))) {
|
||||
put_device(dev);
|
||||
ret = snprintf(name, len, "%s_%u", init_name, ++i);
|
||||
}
|
||||
@ -457,6 +501,14 @@ int led_classdev_register_ext(struct device *parent,
|
||||
if (fwnode_property_present(init_data->fwnode,
|
||||
"retain-state-shutdown"))
|
||||
led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
|
||||
|
||||
fwnode_property_read_u32(init_data->fwnode,
|
||||
"max-brightness",
|
||||
&led_cdev->max_brightness);
|
||||
|
||||
if (fwnode_property_present(init_data->fwnode, "color"))
|
||||
fwnode_property_read_u32(init_data->fwnode, "color",
|
||||
&led_cdev->color);
|
||||
}
|
||||
} else {
|
||||
proposed_name = led_cdev->name;
|
||||
@ -466,10 +518,13 @@ int led_classdev_register_ext(struct device *parent,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (led_cdev->color >= LED_COLOR_ID_MAX)
|
||||
dev_warn(parent, "LED %s color identifier out of range\n", final_name);
|
||||
|
||||
mutex_init(&led_cdev->led_access);
|
||||
mutex_lock(&led_cdev->led_access);
|
||||
led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
|
||||
led_cdev, led_cdev->groups, "%s", final_name);
|
||||
led_cdev->dev = device_create_with_groups(&leds_class, parent, 0,
|
||||
led_cdev, led_cdev->groups, "%s", final_name);
|
||||
if (IS_ERR(led_cdev->dev)) {
|
||||
mutex_unlock(&led_cdev->led_access);
|
||||
return PTR_ERR(led_cdev->dev);
|
||||
@ -626,17 +681,12 @@ EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
|
||||
|
||||
static int __init leds_init(void)
|
||||
{
|
||||
leds_class = class_create("leds");
|
||||
if (IS_ERR(leds_class))
|
||||
return PTR_ERR(leds_class);
|
||||
leds_class->pm = &leds_class_dev_pm_ops;
|
||||
leds_class->dev_groups = led_groups;
|
||||
return 0;
|
||||
return class_register(&leds_class);
|
||||
}
|
||||
|
||||
static void __exit leds_exit(void)
|
||||
{
|
||||
class_destroy(leds_class);
|
||||
class_unregister(&leds_class);
|
||||
}
|
||||
|
||||
subsys_initcall(leds_init);
|
||||
|
@ -474,15 +474,15 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data,
|
||||
struct fwnode_handle *fwnode = init_data->fwnode;
|
||||
const char *devicename = init_data->devicename;
|
||||
|
||||
/* We want to label LEDs that can produce full range of colors
|
||||
* as RGB, not multicolor */
|
||||
BUG_ON(props.color == LED_COLOR_ID_MULTI);
|
||||
|
||||
if (!led_classdev_name)
|
||||
return -EINVAL;
|
||||
|
||||
led_parse_fwnode_props(dev, fwnode, &props);
|
||||
|
||||
/* We want to label LEDs that can produce full range of colors
|
||||
* as RGB, not multicolor */
|
||||
BUG_ON(props.color == LED_COLOR_ID_MULTI);
|
||||
|
||||
if (props.label) {
|
||||
/*
|
||||
* If init_data.devicename is NULL, then it indicates that
|
||||
|
@ -344,7 +344,7 @@ MODULE_DEVICE_TABLE(i2c, an30259a_id);
|
||||
static struct i2c_driver an30259a_driver = {
|
||||
.driver = {
|
||||
.name = "leds-an30259a",
|
||||
.of_match_table = of_match_ptr(an30259a_match_table),
|
||||
.of_match_table = an30259a_match_table,
|
||||
},
|
||||
.probe = an30259a_probe,
|
||||
.remove = an30259a_remove,
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
enum ec_index {
|
||||
EC_BLUE_LED = 0x01,
|
||||
|
@ -368,7 +368,7 @@ static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip)
|
||||
|
||||
if (!chip->display_rows ||
|
||||
chip->display_rows > chip->cdef->display_size_rows_max) {
|
||||
return dev_err_probe(dev, ret,
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid leds display size %u\n",
|
||||
chip->display_rows);
|
||||
}
|
||||
@ -583,7 +583,7 @@ static struct i2c_driver aw200xx_driver = {
|
||||
.name = "aw200xx",
|
||||
.of_match_table = aw200xx_match_table,
|
||||
},
|
||||
.probe_new = aw200xx_probe,
|
||||
.probe = aw200xx_probe,
|
||||
.remove = aw200xx_remove,
|
||||
.id_table = aw200xx_id,
|
||||
};
|
||||
|
@ -62,7 +62,7 @@ struct aw2013_led {
|
||||
|
||||
struct aw2013 {
|
||||
struct mutex mutex; /* held when writing to registers */
|
||||
struct regulator *vcc_regulator;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct i2c_client *client;
|
||||
struct aw2013_led leds[AW2013_MAX_LEDS];
|
||||
struct regmap *regmap;
|
||||
@ -106,10 +106,11 @@ static void aw2013_chip_disable(struct aw2013 *chip)
|
||||
|
||||
regmap_write(chip->regmap, AW2013_GCR, 0);
|
||||
|
||||
ret = regulator_disable(chip->vcc_regulator);
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
|
||||
chip->regulators);
|
||||
if (ret) {
|
||||
dev_err(&chip->client->dev,
|
||||
"Failed to disable regulator: %d\n", ret);
|
||||
"Failed to disable regulators: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -123,10 +124,11 @@ static int aw2013_chip_enable(struct aw2013 *chip)
|
||||
if (chip->enabled)
|
||||
return 0;
|
||||
|
||||
ret = regulator_enable(chip->vcc_regulator);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators),
|
||||
chip->regulators);
|
||||
if (ret) {
|
||||
dev_err(&chip->client->dev,
|
||||
"Failed to enable regulator: %d\n", ret);
|
||||
"Failed to enable regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
chip->enabled = true;
|
||||
@ -348,19 +350,23 @@ static int aw2013_probe(struct i2c_client *client)
|
||||
goto error;
|
||||
}
|
||||
|
||||
chip->vcc_regulator = devm_regulator_get(&client->dev, "vcc");
|
||||
ret = PTR_ERR_OR_ZERO(chip->vcc_regulator);
|
||||
if (ret) {
|
||||
chip->regulators[0].supply = "vcc";
|
||||
chip->regulators[1].supply = "vio";
|
||||
ret = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(chip->regulators),
|
||||
chip->regulators);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&client->dev,
|
||||
"Failed to request regulator: %d\n", ret);
|
||||
"Failed to request regulators: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = regulator_enable(chip->vcc_regulator);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators),
|
||||
chip->regulators);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable regulator: %d\n", ret);
|
||||
"Failed to enable regulators: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -382,10 +388,11 @@ static int aw2013_probe(struct i2c_client *client)
|
||||
if (ret < 0)
|
||||
goto error_reg;
|
||||
|
||||
ret = regulator_disable(chip->vcc_regulator);
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
|
||||
chip->regulators);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to disable regulator: %d\n", ret);
|
||||
"Failed to disable regulators: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -394,7 +401,8 @@ static int aw2013_probe(struct i2c_client *client)
|
||||
return 0;
|
||||
|
||||
error_reg:
|
||||
regulator_disable(chip->vcc_regulator);
|
||||
regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
|
||||
chip->regulators);
|
||||
|
||||
error:
|
||||
mutex_destroy(&chip->mutex);
|
||||
@ -420,7 +428,7 @@ MODULE_DEVICE_TABLE(of, aw2013_match_table);
|
||||
static struct i2c_driver aw2013_driver = {
|
||||
.driver = {
|
||||
.name = "leds-aw2013",
|
||||
.of_match_table = of_match_ptr(aw2013_match_table),
|
||||
.of_match_table = aw2013_match_table,
|
||||
},
|
||||
.probe = aw2013_probe,
|
||||
.remove = aw2013_remove,
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <linux/mfd/motorola-cpcap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
|
@ -27,22 +27,16 @@ static void ip30led_set(struct led_classdev *led_cdev,
|
||||
|
||||
static int ip30led_create(struct platform_device *pdev, int num)
|
||||
{
|
||||
struct resource *res;
|
||||
struct ip30_led *data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, num);
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
data->reg = devm_platform_ioremap_resource(pdev, num);
|
||||
if (IS_ERR(data->reg))
|
||||
return PTR_ERR(data->reg);
|
||||
|
||||
|
||||
switch (num) {
|
||||
case IP30_LED_SYSTEM:
|
||||
data->cdev.name = "white:power";
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
/* Used to indicate a device has no such register */
|
||||
#define IS31FL32XX_REG_NONE 0xFF
|
||||
|
@ -594,18 +594,17 @@ static const struct i2c_device_id lp5521_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lp5521_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_lp5521_leds_match[] = {
|
||||
{ .compatible = "national,lp5521", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_lp5521_leds_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver lp5521_driver = {
|
||||
.driver = {
|
||||
.name = "lp5521",
|
||||
.of_match_table = of_match_ptr(of_lp5521_leds_match),
|
||||
.of_match_table = of_lp5521_leds_match,
|
||||
},
|
||||
.probe = lp5521_probe,
|
||||
.remove = lp5521_remove,
|
||||
|
@ -972,7 +972,6 @@ static const struct i2c_device_id lp5523_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, lp5523_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_lp5523_leds_match[] = {
|
||||
{ .compatible = "national,lp5523", },
|
||||
{ .compatible = "ti,lp55231", },
|
||||
@ -980,12 +979,11 @@ static const struct of_device_id of_lp5523_leds_match[] = {
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_lp5523_leds_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver lp5523_driver = {
|
||||
.driver = {
|
||||
.name = "lp5523x",
|
||||
.of_match_table = of_match_ptr(of_lp5523_leds_match),
|
||||
.of_match_table = of_lp5523_leds_match,
|
||||
},
|
||||
.probe = lp5523_probe,
|
||||
.remove = lp5523_remove,
|
||||
|
@ -589,19 +589,17 @@ static const struct i2c_device_id lp5562_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lp5562_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_lp5562_leds_match[] = {
|
||||
{ .compatible = "ti,lp5562", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver lp5562_driver = {
|
||||
.driver = {
|
||||
.name = "lp5562",
|
||||
.of_match_table = of_match_ptr(of_lp5562_leds_match),
|
||||
.of_match_table = of_lp5562_leds_match,
|
||||
},
|
||||
.probe = lp5562_probe,
|
||||
.remove = lp5562_remove,
|
||||
|
@ -380,19 +380,17 @@ static const struct i2c_device_id lp8501_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lp8501_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_lp8501_leds_match[] = {
|
||||
{ .compatible = "ti,lp8501", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver lp8501_driver = {
|
||||
.driver = {
|
||||
.name = "lp8501",
|
||||
.of_match_table = of_match_ptr(of_lp8501_leds_match),
|
||||
.of_match_table = of_lp8501_leds_match,
|
||||
},
|
||||
.probe = lp8501_probe,
|
||||
.remove = lp8501_remove,
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_data/mlxreg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -247,7 +247,7 @@ static int ns2_led_probe(struct platform_device *pdev)
|
||||
if (!count)
|
||||
return -ENODEV;
|
||||
|
||||
leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
|
||||
leds = devm_kcalloc(dev, count, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/leds-pca9532.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
/* m = num_leds*/
|
||||
#define PCA9532_REG_INPUT(i) ((i) >> 3)
|
||||
|
204
drivers/leds/leds-pca995x.c
Normal file
204
drivers/leds/leds-pca995x.c
Normal file
@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* LED driver for PCA995x I2C LED drivers
|
||||
*
|
||||
* Copyright 2011 bct electronic GmbH
|
||||
* Copyright 2013 Qtechnology/AS
|
||||
* Copyright 2022 NXP
|
||||
* Copyright 2023 Marek Vasut
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Register definition */
|
||||
#define PCA995X_MODE1 0x00
|
||||
#define PCA995X_MODE2 0x01
|
||||
#define PCA995X_LEDOUT0 0x02
|
||||
#define PCA9955B_PWM0 0x08
|
||||
#define PCA9952_PWM0 0x0A
|
||||
#define PCA9952_IREFALL 0x43
|
||||
#define PCA9955B_IREFALL 0x45
|
||||
|
||||
/* Auto-increment disabled. Normal mode */
|
||||
#define PCA995X_MODE1_CFG 0x00
|
||||
|
||||
/* LED select registers determine the source that drives LED outputs */
|
||||
#define PCA995X_LED_OFF 0x0
|
||||
#define PCA995X_LED_ON 0x1
|
||||
#define PCA995X_LED_PWM_MODE 0x2
|
||||
#define PCA995X_LDRX_MASK 0x3
|
||||
#define PCA995X_LDRX_BITS 2
|
||||
|
||||
#define PCA995X_MAX_OUTPUTS 16
|
||||
#define PCA995X_OUTPUTS_PER_REG 4
|
||||
|
||||
#define PCA995X_IREFALL_FULL_CFG 0xFF
|
||||
#define PCA995X_IREFALL_HALF_CFG (PCA995X_IREFALL_FULL_CFG / 2)
|
||||
|
||||
#define PCA995X_TYPE_NON_B 0
|
||||
#define PCA995X_TYPE_B 1
|
||||
|
||||
#define ldev_to_led(c) container_of(c, struct pca995x_led, ldev)
|
||||
|
||||
struct pca995x_led {
|
||||
unsigned int led_no;
|
||||
struct led_classdev ldev;
|
||||
struct pca995x_chip *chip;
|
||||
};
|
||||
|
||||
struct pca995x_chip {
|
||||
struct regmap *regmap;
|
||||
struct pca995x_led leds[PCA995X_MAX_OUTPUTS];
|
||||
int btype;
|
||||
};
|
||||
|
||||
static int pca995x_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct pca995x_led *led = ldev_to_led(led_cdev);
|
||||
struct pca995x_chip *chip = led->chip;
|
||||
u8 ledout_addr, pwmout_addr;
|
||||
int shift, ret;
|
||||
|
||||
pwmout_addr = (chip->btype ? PCA9955B_PWM0 : PCA9952_PWM0) + led->led_no;
|
||||
ledout_addr = PCA995X_LEDOUT0 + (led->led_no / PCA995X_OUTPUTS_PER_REG);
|
||||
shift = PCA995X_LDRX_BITS * (led->led_no % PCA995X_OUTPUTS_PER_REG);
|
||||
|
||||
switch (brightness) {
|
||||
case LED_FULL:
|
||||
return regmap_update_bits(chip->regmap, ledout_addr,
|
||||
PCA995X_LDRX_MASK << shift,
|
||||
PCA995X_LED_ON << shift);
|
||||
case LED_OFF:
|
||||
return regmap_update_bits(chip->regmap, ledout_addr,
|
||||
PCA995X_LDRX_MASK << shift, 0);
|
||||
default:
|
||||
/* Adjust brightness as per user input by changing individual PWM */
|
||||
ret = regmap_write(chip->regmap, pwmout_addr, brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Change LDRx configuration to individual brightness via PWM.
|
||||
* LED will stop blinking if it's doing so.
|
||||
*/
|
||||
return regmap_update_bits(chip->regmap, ledout_addr,
|
||||
PCA995X_LDRX_MASK << shift,
|
||||
PCA995X_LED_PWM_MODE << shift);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config pca995x_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x49,
|
||||
};
|
||||
|
||||
static int pca995x_probe(struct i2c_client *client)
|
||||
{
|
||||
struct fwnode_handle *led_fwnodes[PCA995X_MAX_OUTPUTS] = { 0 };
|
||||
struct fwnode_handle *np, *child;
|
||||
struct device *dev = &client->dev;
|
||||
struct pca995x_chip *chip;
|
||||
struct pca995x_led *led;
|
||||
int i, btype, reg, ret;
|
||||
|
||||
btype = (unsigned long)device_get_match_data(&client->dev);
|
||||
|
||||
np = dev_fwnode(dev);
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->btype = btype;
|
||||
chip->regmap = devm_regmap_init_i2c(client, &pca995x_regmap);
|
||||
if (IS_ERR(chip->regmap))
|
||||
return PTR_ERR(chip->regmap);
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
fwnode_for_each_available_child_node(np, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg]) {
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led = &chip->leds[reg];
|
||||
led_fwnodes[reg] = child;
|
||||
led->chip = chip;
|
||||
led->led_no = reg;
|
||||
led->ldev.brightness_set_blocking = pca995x_brightness_set;
|
||||
led->ldev.max_brightness = 255;
|
||||
}
|
||||
|
||||
for (i = 0; i < PCA995X_MAX_OUTPUTS; i++) {
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
if (!led_fwnodes[i])
|
||||
continue;
|
||||
|
||||
init_data.fwnode = led_fwnodes[i];
|
||||
|
||||
ret = devm_led_classdev_register_ext(dev,
|
||||
&chip->leds[i].ldev,
|
||||
&init_data);
|
||||
if (ret < 0) {
|
||||
fwnode_handle_put(child);
|
||||
return dev_err_probe(dev, ret,
|
||||
"Could not register LED %s\n",
|
||||
chip->leds[i].ldev.name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable LED all-call address and set normal mode */
|
||||
ret = regmap_write(chip->regmap, PCA995X_MODE1, PCA995X_MODE1_CFG);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IREF Output current value for all LEDn outputs */
|
||||
return regmap_write(chip->regmap,
|
||||
btype ? PCA9955B_IREFALL : PCA9952_IREFALL,
|
||||
PCA995X_IREFALL_HALF_CFG);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pca995x_id[] = {
|
||||
{ "pca9952", .driver_data = (kernel_ulong_t)PCA995X_TYPE_NON_B },
|
||||
{ "pca9955b", .driver_data = (kernel_ulong_t)PCA995X_TYPE_B },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca995x_id);
|
||||
|
||||
static const struct of_device_id pca995x_of_match[] = {
|
||||
{ .compatible = "nxp,pca9952", .data = (void *)PCA995X_TYPE_NON_B },
|
||||
{ .compatible = "nxp,pca9955b", .data = (void *)PCA995X_TYPE_B },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pca995x_of_match);
|
||||
|
||||
static struct i2c_driver pca995x_driver = {
|
||||
.driver = {
|
||||
.name = "leds-pca995x",
|
||||
.of_match_table = pca995x_of_match,
|
||||
},
|
||||
.probe = pca995x_probe,
|
||||
.id_table = pca995x_id,
|
||||
};
|
||||
module_i2c_driver(pca995x_driver);
|
||||
|
||||
MODULE_AUTHOR("Isai Gaspar <isaiezequiel.gaspar@nxp.com>");
|
||||
MODULE_DESCRIPTION("PCA995x LED driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -4,7 +4,6 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pwm.h>
|
||||
@ -146,7 +146,7 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv)
|
||||
led.name = to_of_node(fwnode)->name;
|
||||
|
||||
if (!led.name) {
|
||||
ret = EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_child_out;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <uapi/linux/uleds.h>
|
||||
|
@ -7,8 +7,7 @@
|
||||
*/
|
||||
#include <linux/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/leds-ti-lmu-common.h>
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -156,24 +156,20 @@ static ssize_t brightness_show(struct device *dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct omnia_leds *leds = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&leds->lock);
|
||||
ret = i2c_smbus_read_byte_data(client, CMD_LED_GET_BRIGHTNESS);
|
||||
mutex_unlock(&leds->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", ret);
|
||||
return sysfs_emit(buf, "%d\n", ret);
|
||||
}
|
||||
|
||||
static ssize_t brightness_store(struct device *dev, struct device_attribute *a,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct omnia_leds *leds = i2c_get_clientdata(client);
|
||||
unsigned long brightness;
|
||||
int ret;
|
||||
|
||||
@ -183,15 +179,10 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a,
|
||||
if (brightness > 100)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&leds->lock);
|
||||
ret = i2c_smbus_write_byte_data(client, CMD_LED_SET_BRIGHTNESS,
|
||||
(u8)brightness);
|
||||
mutex_unlock(&leds->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(brightness);
|
||||
|
||||
|
@ -2,6 +2,18 @@
|
||||
|
||||
if LEDS_CLASS_MULTICOLOR
|
||||
|
||||
config LEDS_GROUP_MULTICOLOR
|
||||
tristate "LEDs group multi-color support"
|
||||
depends on OF || COMPILE_TEST
|
||||
help
|
||||
This option enables support for monochrome LEDs that are grouped
|
||||
into multicolor LEDs which is useful in the case where LEDs of
|
||||
different colors are physically grouped in a single multi-color LED
|
||||
and driven by a controller that doesn't have multi-color support.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called leds-group-multicolor.
|
||||
|
||||
config LEDS_PWM_MULTICOLOR
|
||||
tristate "PWM driven multi-color LED Support"
|
||||
depends on PWM
|
||||
|
@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o
|
||||
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
|
||||
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
|
||||
obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o
|
||||
|
169
drivers/leds/rgb/leds-group-multicolor.c
Normal file
169
drivers/leds/rgb/leds-group-multicolor.c
Normal file
@ -0,0 +1,169 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Multi-color LED built with monochromatic LED devices
|
||||
*
|
||||
* This driver groups several monochromatic LED devices in a single multicolor LED device.
|
||||
*
|
||||
* Compared to handling this grouping in user-space, the benefits are:
|
||||
* - The state of the monochromatic LED relative to each other is always consistent.
|
||||
* - The sysfs interface of the LEDs can be used for the group as a whole.
|
||||
*
|
||||
* Copyright 2023 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
struct leds_multicolor {
|
||||
struct led_classdev_mc mc_cdev;
|
||||
struct led_classdev **monochromatics;
|
||||
};
|
||||
|
||||
static int leds_gmc_set(struct led_classdev *cdev, enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
|
||||
struct leds_multicolor *priv = container_of(mc_cdev, struct leds_multicolor, mc_cdev);
|
||||
const unsigned int group_max_brightness = mc_cdev->led_cdev.max_brightness;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mc_cdev->num_colors; i++) {
|
||||
struct led_classdev *mono = priv->monochromatics[i];
|
||||
const unsigned int mono_max_brightness = mono->max_brightness;
|
||||
unsigned int intensity = mc_cdev->subled_info[i].intensity;
|
||||
int mono_brightness;
|
||||
|
||||
/*
|
||||
* Scale the brightness according to relative intensity of the
|
||||
* color AND the max brightness of the monochromatic LED.
|
||||
*/
|
||||
mono_brightness = DIV_ROUND_CLOSEST(brightness * intensity * mono_max_brightness,
|
||||
group_max_brightness * group_max_brightness);
|
||||
|
||||
led_set_brightness(mono, mono_brightness);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void restore_sysfs_write_access(void *data)
|
||||
{
|
||||
struct led_classdev *led_cdev = data;
|
||||
|
||||
/* Restore the write acccess to the LED */
|
||||
mutex_lock(&led_cdev->led_access);
|
||||
led_sysfs_enable(led_cdev);
|
||||
mutex_unlock(&led_cdev->led_access);
|
||||
}
|
||||
|
||||
static int leds_gmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
struct mc_subled *subled;
|
||||
struct leds_multicolor *priv;
|
||||
unsigned int max_brightness = 0;
|
||||
int i, ret, count = 0;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
for (;;) {
|
||||
struct led_classdev *led_cdev;
|
||||
|
||||
led_cdev = devm_of_led_get_optional(dev, count);
|
||||
if (IS_ERR(led_cdev))
|
||||
return dev_err_probe(dev, PTR_ERR(led_cdev), "Unable to get LED #%d",
|
||||
count);
|
||||
if (!led_cdev)
|
||||
break;
|
||||
|
||||
priv->monochromatics = devm_krealloc_array(dev, priv->monochromatics,
|
||||
count + 1, sizeof(*priv->monochromatics),
|
||||
GFP_KERNEL);
|
||||
if (!priv->monochromatics)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->monochromatics[count] = led_cdev;
|
||||
|
||||
max_brightness = max(max_brightness, led_cdev->max_brightness);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
subled = devm_kcalloc(dev, count, sizeof(*subled), GFP_KERNEL);
|
||||
if (!subled)
|
||||
return -ENOMEM;
|
||||
priv->mc_cdev.subled_info = subled;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct led_classdev *led_cdev = priv->monochromatics[i];
|
||||
|
||||
subled[i].color_index = led_cdev->color;
|
||||
|
||||
/* Configure the LED intensity to its maximum */
|
||||
subled[i].intensity = max_brightness;
|
||||
}
|
||||
|
||||
/* Initialise the multicolor's LED class device */
|
||||
cdev = &priv->mc_cdev.led_cdev;
|
||||
cdev->flags = LED_CORE_SUSPENDRESUME;
|
||||
cdev->brightness_set_blocking = leds_gmc_set;
|
||||
cdev->max_brightness = max_brightness;
|
||||
cdev->color = LED_COLOR_ID_MULTI;
|
||||
priv->mc_cdev.num_colors = count;
|
||||
|
||||
init_data.fwnode = dev_fwnode(dev);
|
||||
ret = devm_led_classdev_multicolor_register_ext(dev, &priv->mc_cdev, &init_data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to register multicolor LED for %s.\n",
|
||||
cdev->name);
|
||||
|
||||
ret = leds_gmc_set(cdev, cdev->brightness);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to set LED value for %s.", cdev->name);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct led_classdev *led_cdev = priv->monochromatics[i];
|
||||
|
||||
/*
|
||||
* Make the individual LED sysfs interface read-only to prevent the user
|
||||
* to change the brightness of the individual LEDs of the group.
|
||||
*/
|
||||
mutex_lock(&led_cdev->led_access);
|
||||
led_sysfs_disable(led_cdev);
|
||||
mutex_unlock(&led_cdev->led_access);
|
||||
|
||||
/* Restore the write access to the LED sysfs when the group is destroyed */
|
||||
devm_add_action_or_reset(dev, restore_sysfs_write_access, led_cdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_leds_group_multicolor_match[] = {
|
||||
{ .compatible = "leds-group-multicolor" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_leds_group_multicolor_match);
|
||||
|
||||
static struct platform_driver leds_group_multicolor_driver = {
|
||||
.probe = leds_gmc_probe,
|
||||
.driver = {
|
||||
.name = "leds_group_multicolor",
|
||||
.of_match_table = of_leds_group_multicolor_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(leds_group_multicolor_driver);
|
||||
|
||||
MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
|
||||
MODULE_DESCRIPTION("LEDs group multicolor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:leds-group-multicolor");
|
@ -9,7 +9,6 @@
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -1093,7 +1092,6 @@ static int lpg_add_pwm(struct lpg *lpg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lpg->pwm.base = -1;
|
||||
lpg->pwm.dev = lpg->dev;
|
||||
lpg->pwm.npwm = lpg->num_channels;
|
||||
lpg->pwm.ops = &lpg_pwm_ops;
|
||||
|
@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config LEDS_SIEMENS_SIMATIC_IPC
|
||||
tristate "LED driver for Siemens Simatic IPCs"
|
||||
depends on LEDS_CLASS
|
||||
depends on SIEMENS_SIMATIC_IPC
|
||||
default y
|
||||
help
|
||||
@ -35,3 +36,16 @@ config LEDS_SIEMENS_SIMATIC_IPC_F7188X
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called simatic-ipc-leds-gpio-f7188x.
|
||||
|
||||
config LEDS_SIEMENS_SIMATIC_IPC_ELKHARTLAKE
|
||||
tristate "LED driver for Siemens Simatic IPCs based on Intel Elkhart Lake GPIO"
|
||||
depends on LEDS_GPIO
|
||||
depends on PINCTRL_ELKHARTLAKE
|
||||
depends on SIEMENS_SIMATIC_IPC
|
||||
default LEDS_SIEMENS_SIMATIC_IPC
|
||||
help
|
||||
This option enables support for the LEDs of several Industrial PCs
|
||||
from Siemens based on Elkhart Lake GPIO i.e. BX-21A.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called simatic-ipc-leds-gpio-elkhartlake.
|
||||
|
@ -2,3 +2,4 @@
|
||||
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
|
||||
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-apollolake.o
|
||||
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_F7188X) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-f7188x.o
|
||||
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_ELKHARTLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-elkhartlake.o
|
||||
|
@ -57,6 +57,7 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev,
|
||||
switch (plat->devmode) {
|
||||
case SIMATIC_IPC_DEVICE_127E:
|
||||
case SIMATIC_IPC_DEVICE_227G:
|
||||
case SIMATIC_IPC_DEVICE_BX_21A:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
@ -72,6 +73,9 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!table_extra)
|
||||
return 0;
|
||||
|
||||
table_extra->dev_id = dev_name(dev);
|
||||
gpiod_add_lookup_table(table_extra);
|
||||
|
||||
|
57
drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c
Normal file
57
drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Siemens SIMATIC IPC driver for GPIO based LEDs
|
||||
*
|
||||
* Copyright (c) Siemens AG, 2023
|
||||
*
|
||||
* Author:
|
||||
* Henning Schild <henning.schild@siemens.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/x86/simatic-ipc-base.h>
|
||||
|
||||
#include "simatic-ipc-leds-gpio.h"
|
||||
|
||||
static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
GPIO_LOOKUP_IDX("INTC1020:04", 72, NULL, 0, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX("INTC1020:04", 77, NULL, 1, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX("INTC1020:04", 78, NULL, 2, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX("INTC1020:04", 58, NULL, 3, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX("INTC1020:04", 60, NULL, 4, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX("INTC1020:04", 62, NULL, 5, GPIO_ACTIVE_HIGH),
|
||||
{} /* Terminating entry */
|
||||
},
|
||||
};
|
||||
|
||||
static int simatic_ipc_leds_gpio_elkhartlake_probe(struct platform_device *pdev)
|
||||
{
|
||||
return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev)
|
||||
{
|
||||
return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static struct platform_driver simatic_ipc_led_gpio_elkhartlake_driver = {
|
||||
.probe = simatic_ipc_leds_gpio_elkhartlake_probe,
|
||||
.remove = simatic_ipc_leds_gpio_elkhartlake_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
};
|
||||
module_platform_driver(simatic_ipc_led_gpio_elkhartlake_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" KBUILD_MODNAME);
|
||||
MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:elkhartlake-pinctrl");
|
||||
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Siemens SIMATIC IPC driver for GPIO based LEDs
|
||||
*
|
||||
|
@ -609,18 +609,7 @@ static struct led_trigger netdev_led_trigger = {
|
||||
.groups = netdev_trig_groups,
|
||||
};
|
||||
|
||||
static int __init netdev_trig_init(void)
|
||||
{
|
||||
return led_trigger_register(&netdev_led_trigger);
|
||||
}
|
||||
|
||||
static void __exit netdev_trig_exit(void)
|
||||
{
|
||||
led_trigger_unregister(&netdev_led_trigger);
|
||||
}
|
||||
|
||||
module_init(netdev_trig_init);
|
||||
module_exit(netdev_trig_exit);
|
||||
module_led_trigger(netdev_led_trigger);
|
||||
|
||||
MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
|
||||
MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <linux/tty.h>
|
||||
#include <uapi/linux/serial.h>
|
||||
|
||||
#define LEDTRIG_TTY_INTERVAL 50
|
||||
|
||||
struct ledtrig_tty_data {
|
||||
struct led_classdev *led_cdev;
|
||||
struct delayed_work dwork;
|
||||
@ -122,17 +124,19 @@ static void ledtrig_tty_work(struct work_struct *work)
|
||||
|
||||
if (icount.rx != trigger_data->rx ||
|
||||
icount.tx != trigger_data->tx) {
|
||||
led_set_brightness_sync(trigger_data->led_cdev, LED_ON);
|
||||
unsigned long interval = LEDTRIG_TTY_INTERVAL;
|
||||
|
||||
led_blink_set_oneshot(trigger_data->led_cdev, &interval,
|
||||
&interval, 0);
|
||||
|
||||
trigger_data->rx = icount.rx;
|
||||
trigger_data->tx = icount.tx;
|
||||
} else {
|
||||
led_set_brightness_sync(trigger_data->led_cdev, LED_OFF);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&trigger_data->mutex);
|
||||
schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(100));
|
||||
schedule_delayed_work(&trigger_data->dwork,
|
||||
msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2));
|
||||
}
|
||||
|
||||
static struct attribute *ledtrig_tty_attrs[] = {
|
||||
|
@ -209,17 +209,7 @@ static struct miscdevice uleds_misc = {
|
||||
.name = ULEDS_NAME,
|
||||
};
|
||||
|
||||
static int __init uleds_init(void)
|
||||
{
|
||||
return misc_register(&uleds_misc);
|
||||
}
|
||||
module_init(uleds_init);
|
||||
|
||||
static void __exit uleds_exit(void)
|
||||
{
|
||||
misc_deregister(&uleds_misc);
|
||||
}
|
||||
module_exit(uleds_exit);
|
||||
module_misc_device(uleds_misc);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
|
||||
MODULE_DESCRIPTION("Userspace driver for the LED subsystem");
|
||||
|
@ -100,6 +100,7 @@ struct led_classdev {
|
||||
const char *name;
|
||||
unsigned int brightness;
|
||||
unsigned int max_brightness;
|
||||
unsigned int color;
|
||||
int flags;
|
||||
|
||||
/* Lower 16 bits reflect status */
|
||||
@ -313,6 +314,8 @@ extern struct led_classdev *of_led_get(struct device_node *np, int index);
|
||||
extern void led_put(struct led_classdev *led_cdev);
|
||||
struct led_classdev *__must_check devm_of_led_get(struct device *dev,
|
||||
int index);
|
||||
struct led_classdev *__must_check devm_of_led_get_optional(struct device *dev,
|
||||
int index);
|
||||
|
||||
/**
|
||||
* led_blink_set - set blinking with software fallback
|
||||
|
Loading…
Reference in New Issue
Block a user