mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
LEDs for v7.4
- New Support & Features
- Core: Add `led_trigger_notify_hw_control_changed()` to allow drivers to notify the
core of autonomous hardware control transitions
- Core: Add the `hw_offloaded()` trigger callback and `trigger_may_offload_to_hw` sysfs
attribute to expose hardware-offloaded triggers
- Netdev Trigger: Implement the `hw_offloaded()` callback to report hardware control
state to the LED core
- Analog Devices LTC3208: Add support for the LTC3208 7-channel multi-display I2C LED
driver
- ChromeOS EC: Implement the `hw_offloaded()` callback for the `chromeos-auto` trigger
- CZ.NIC Turris Omnia: Implement the `hw_offloaded()` callback and declare `omnia-mcu`
as a hardware control trigger
- STMicroelectronics ST1202: Add hardware-accelerated blink support via `blink_set()`
- Improvements & Fixes
- Class: Always hold the `led_access` mutex in `brightness_show()` to avoid exposing
transient registration states
- Class: Deactivate active hardware control triggers when writing to the `brightness`
sysfs attribute
- Core: Enforce strict validation in `led_trigger_is_hw_controlled()` and warn on legacy
fallback paths
- Flash Class: Initialize the fault variable in `flash_fault_show()` to prevent
undefined behavior when callbacks perform bitwise ORs
- Kinetic KTD2692: Pass the driver context directly to `regulator_disable_action()` to
prevent a NULL pointer dereference during error unwinding
- Maxim MAX77705: Use `fwnode_for_each_child_node_scoped()` in `max77705_add_led()` to
prevent child node reference leaks on error paths
- NXP PCA9532: Check the return value of `i2c_smbus_read_byte_data()` in
`pca9532_getled()` to avoid integer underflow on read errors
- Qualcomm WLED: Fix a NULL pointer dereference in `wled_remove()` by setting platform
driver data in `wled_probe()`
- Qualcomm WLED: Fix the WLED3 brightness register stride to prevent consecutive string
writes from overlapping
- Qualcomm WLED: Read back the hardware-programmed brightness at probe, expose it via
`get_brightness()`, and prevent unbalanced OVP IRQ enabling
- STMicroelectronics ST1202: Clear unused PWM pattern slots in `pattern_set()` and
`blink_set()` to prevent unintended output when sharing the global sequencer
- STMicroelectronics ST1202: Program and cap the channel current according to
`max_brightness` across brightness, blink, and hardware pattern modes, and scale
pattern brightness to the 12-bit PWM range
- STMicroelectronics ST1202: Retain a reference to each child LED firmware node for the
lifetime of the device
- STMicroelectronics ST1202: Use only the blocking `brightness_set_blocking()` callback
to avoid sleeping in atomic context during I2C transfers
- TI LM3692x: Check the final register write in `lm3692x_leds_enable()` to prevent
regulator/GPIO leaks and an inconsistent enabled state
- TI LP8860: Fix a `device_node` reference leak in `lp8860_probe()` using
`__free(device_node)`
- Cleanups & Refactoring
- Core: Move `led_trigger_is_hw_controlled()` and `led_trigger_group` from `led-class.c`
into `led-triggers.c`
- Core: Replace conditional `set_bit()`/`clear_bit()` patterns with `assign_bit()` and
`__assign_bit()`
- Flash Class: Simplify `flash_fault_show()` using `sysfs_emit_at()` and omit the
trailing space
- Maintainers: Add Griffin Kroah-Hartman as an LED subsystem reviewer
- Intel SS4200: Fix a spelling typo in a comment
- ISSI IS31FL32xx: Convert DT-specific parsing helpers to generic device and fwnode
property APIs
- NXP PCA955x: Drop redundant intermediate platform data and allocate the LED array
using a flexible array member
- Qualcomm LPG: Convert LED and channel parsing to fwnode APIs and avoid leaving
`default_trigger` uninitialized
- Skyworks AAT1290: Use `ARRAY_SIZE()` when allocating `mm_current_scale` to match the
target pointer type for type-aware allocators
- STMicroelectronics ST1202: Introduce a `st1202_iled_max()` helper to unify channel
current ceiling calculations
- STMicroelectronics ST1202: Standardize variable and macro naming, fix helper spelling,
and use `u8` consistently instead of `uint8_t`
- Syscon: Convert from OF-specific property helpers to the generic device property and
fwnode APIs
- Device Tree Binding Updates
- Core: Allow PHY RX triggers (`phy[0-9]+rx`) in `linux,default-trigger`
- Core: Standardize example indentation across multiple LED and backlight binding
schemas
- Analog Devices LTC3208: Document the LTC3208 multi-display LED driver
- LaCie netxbig: Convert LED bindings to DT schema
- Motorola CPCAP: Convert LED bindings to DT schemaLEDs for v7.4
- New Support & Features
- Core: Add `led_trigger_notify_hw_control_changed()` to allow drivers to notify the
core of autonomous hardware control transitions
- Core: Add the `hw_offloaded()` trigger callback and `trigger_may_offload_to_hw` sysfs
attribute to expose hardware-offloaded triggers
- Netdev Trigger: Implement the `hw_offloaded()` callback to report hardware control
state to the LED core
- Analog Devices LTC3208: Add support for the LTC3208 7-channel multi-display I2C LED
driver
- ChromeOS EC: Implement the `hw_offloaded()` callback for the `chromeos-auto` trigger
- CZ.NIC Turris Omnia: Implement the `hw_offloaded()` callback and declare `omnia-mcu`
as a hardware control trigger
- STMicroelectronics ST1202: Add hardware-accelerated blink support via `blink_set()`
- Improvements & Fixes
- Class: Always hold the `led_access` mutex in `brightness_show()` to avoid exposing
transient registration states
- Class: Deactivate active hardware control triggers when writing to the `brightness`
sysfs attribute
- Core: Enforce strict validation in `led_trigger_is_hw_controlled()` and warn on legacy
fallback paths
- Flash Class: Initialize the fault variable in `flash_fault_show()` to prevent
undefined behavior when callbacks perform bitwise ORs
- Kinetic KTD2692: Pass the driver context directly to `regulator_disable_action()` to
prevent a NULL pointer dereference during error unwinding
- Maxim MAX77705: Use `fwnode_for_each_child_node_scoped()` in `max77705_add_led()` to
prevent child node reference leaks on error paths
- NXP PCA9532: Check the return value of `i2c_smbus_read_byte_data()` in
`pca9532_getled()` to avoid integer underflow on read errors
- Qualcomm WLED: Fix a NULL pointer dereference in `wled_remove()` by setting platform
driver data in `wled_probe()`
- Qualcomm WLED: Fix the WLED3 brightness register stride to prevent consecutive string
writes from overlapping
- Qualcomm WLED: Read back the hardware-programmed brightness at probe, expose it via
`get_brightness()`, and prevent unbalanced OVP IRQ enabling
- STMicroelectronics ST1202: Clear unused PWM pattern slots in `pattern_set()` and
`blink_set()` to prevent unintended output when sharing the global sequencer
- STMicroelectronics ST1202: Program and cap the channel current according to
`max_brightness` across brightness, blink, and hardware pattern modes, and scale
pattern brightness to the 12-bit PWM range
- STMicroelectronics ST1202: Retain a reference to each child LED firmware node for the
lifetime of the device
- STMicroelectronics ST1202: Use only the blocking `brightness_set_blocking()` callback
to avoid sleeping in atomic context during I2C transfers
- TI LM3692x: Check the final register write in `lm3692x_leds_enable()` to prevent
regulator/GPIO leaks and an inconsistent enabled state
- TI LP8860: Fix a `device_node` reference leak in `lp8860_probe()` using
`__free(device_node)`
- Cleanups & Refactoring
- Core: Move `led_trigger_is_hw_controlled()` and `led_trigger_group` from `led-class.c`
into `led-triggers.c`
- Core: Replace conditional `set_bit()`/`clear_bit()` patterns with `assign_bit()` and
`__assign_bit()`
- Flash Class: Simplify `flash_fault_show()` using `sysfs_emit_at()` and omit the
trailing space
- Maintainers: Add Griffin Kroah-Hartman as an LED subsystem reviewer
- Intel SS4200: Fix a spelling typo in a comment
- ISSI IS31FL32xx: Convert DT-specific parsing helpers to generic device and fwnode
property APIs
- NXP PCA955x: Drop redundant intermediate platform data and allocate the LED array
using a flexible array member
- Qualcomm LPG: Convert LED and channel parsing to fwnode APIs and avoid leaving
`default_trigger` uninitialized
- Skyworks AAT1290: Use `ARRAY_SIZE()` when allocating `mm_current_scale` to match the
target pointer type for type-aware allocators
- STMicroelectronics ST1202: Introduce a `st1202_iled_max()` helper to unify channel
current ceiling calculations
- STMicroelectronics ST1202: Standardize variable and macro naming, fix helper spelling,
and use `u8` consistently instead of `uint8_t`
- Syscon: Convert from OF-specific property helpers to the generic device property and
fwnode APIs
- Device Tree Binding Updates
- Core: Allow PHY RX triggers (`phy[0-9]+rx`) in `linux,default-trigger`
- Core: Standardize example indentation across multiple LED and backlight binding
schemas
- Analog Devices LTC3208: Document the LTC3208 multi-display LED driver
- LaCie netxbig: Convert LED bindings to DT schema
- Motorola CPCAP: Convert LED bindings to DT schema
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmrfbg4ACgkQUa+KL4f8
d2EYqg/+IH3k8Jm0Et7WGte+Ej4cmQJ3/xJNNu+ezGeaZu1WcxT4xXLCGhE89GyC
4hqlyHM7mSDFBwVoiEqznu/rMQ9gCxQZbDPZkBJFrnAIvWYd2veMAuzC5l3vnvUD
DuOyN+i++KzoX5F5dNd802CElKmhhB03V+FbU4QUyHT6I6zjUtR/6WXYv6Wyvca4
gQSYKcyHem7VQaVmk85kvvH9Z8wIvAlzaMHOz7vyfZNarb5K+RXgvCjZfncR/c6P
FYGh4RRl0zJV/x+8FZ7Ehwck4YjOJf0hySmRQ9DeABV2CFDYFrSEck5UfMPDO+c0
tpdHHQp66lrLjM1MZns1zP7SnucXt1G6y9b5DmMd/bneMlDa5enBLIFbS106hdIh
NDu9rKQz/AW1V/fPV+db+AgjL7Sr7oKTNbRuclLj57Zj7TEbtgeUOXh/FKRxPKfu
mzLPpjdggiuXbTCSNxyW+jKm9a7wthlk2dSsNOWgVea6d3Et/Y1oYOpbOX+s+CqF
WShv55jkpyqpazorpzdNdv8mdsXRItf13UZy8izmwufve0JrnAlbqTt5TEzs7zgj
cuDYreg90RNckLWNv/Tdgjxw1aIIWAC3Z3gVVJt0sbdPGS56r6aHu3Rt4Usurxx5
GHcgIOZcmK5bPk/2gH/chvQj3MOsxrr38Q1Gazv55yVTOnp/39Q=
=x9D5
-----END PGP SIGNATURE-----
Merge tag 'leds-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
Pull LED updates from Lee Jones:
"New Support & Features:
- Core: Add `led_trigger_notify_hw_control_changed()` to allow
drivers to notify the core of autonomous hardware control
transitions
- Core: Add the `hw_offloaded()` trigger callback and
`trigger_may_offload_to_hw` sysfs attribute to expose
hardware-offloaded triggers
- Netdev Trigger: Implement the `hw_offloaded()` callback to report
hardware control state to the LED core
- Analog Devices LTC3208: Add support for the LTC3208 7-channel
multi-display I2C LED driver
- ChromeOS EC: Implement the `hw_offloaded()` callback for the
`chromeos-auto` trigger
- CZ.NIC Turris Omnia: Implement the `hw_offloaded()` callback and
declare `omnia-mcu` as a hardware control trigger
- STMicroelectronics ST1202: Add hardware-accelerated blink support
via `blink_set()`
Improvements & Fixes:
- Class: Always hold the `led_access` mutex in `brightness_show()` to
avoid exposing transient registration states
- Class: Deactivate active hardware control triggers when writing to
the `brightness` sysfs attribute
- Core: Enforce strict validation in `led_trigger_is_hw_controlled()`
and warn on legacy fallback paths
- Flash Class: Initialize the fault variable in `flash_fault_show()`
to prevent undefined behavior when callbacks perform bitwise ORs
- Kinetic KTD2692: Pass the driver context directly to
`regulator_disable_action()` to prevent a NULL pointer dereference
during error unwinding
- Maxim MAX77705: Use `fwnode_for_each_child_node_scoped()` in
`max77705_add_led()` to prevent child node reference leaks on error
paths
- NXP PCA9532: Check the return value of `i2c_smbus_read_byte_data()`
in `pca9532_getled()` to avoid integer underflow on read errors
- Qualcomm WLED: Fix a NULL pointer dereference in `wled_remove()` by
setting platform driver data in `wled_probe()`
- Qualcomm WLED: Fix the WLED3 brightness register stride to prevent
consecutive string writes from overlapping
- Qualcomm WLED: Read back the hardware-programmed brightness at
probe, expose it via `get_brightness()`, and prevent unbalanced OVP
IRQ enabling
- STMicroelectronics ST1202: Clear unused PWM pattern slots in
`pattern_set()` and `blink_set()` to prevent unintended output when
sharing the global sequencer
- STMicroelectronics ST1202: Program and cap the channel current
according to `max_brightness` across brightness, blink, and
hardware pattern modes, and scale pattern brightness to the 12-bit
PWM range
- STMicroelectronics ST1202: Retain a reference to each child LED
firmware node for the lifetime of the device
- STMicroelectronics ST1202: Use only the blocking
`brightness_set_blocking()` callback to avoid sleeping in atomic
context during I2C transfers
- TI LM3692x: Check the final register write in
`lm3692x_leds_enable()` to prevent regulator/GPIO leaks and an
inconsistent enabled state
- TI LP8860: Fix a `device_node` reference leak in `lp8860_probe()`
using `__free(device_node)`
Cleanups & Refactoring:
- Core: Move `led_trigger_is_hw_controlled()` and `led_trigger_group`
from `led-class.c` into `led-triggers.c`
- Core: Replace conditional `set_bit()`/`clear_bit()` patterns with
`assign_bit()` and `__assign_bit()`
- Flash Class: Simplify `flash_fault_show()` using `sysfs_emit_at()`
and omit the trailing space
- Maintainers: Add Griffin Kroah-Hartman as an LED subsystem reviewer
- Intel SS4200: Fix a spelling typo in a comment
- ISSI IS31FL32xx: Convert DT-specific parsing helpers to generic
device and fwnode property APIs
- NXP PCA955x: Drop redundant intermediate platform data and allocate
the LED array using a flexible array member
- Qualcomm LPG: Convert LED and channel parsing to fwnode APIs and
avoid leaving `default_trigger` uninitialized
- Skyworks AAT1290: Use `ARRAY_SIZE()` when allocating
`mm_current_scale` to match the target pointer type for type-aware
allocators
- STMicroelectronics ST1202: Introduce a `st1202_iled_max()` helper
to unify channel current ceiling calculations
- STMicroelectronics ST1202: Standardize variable and macro naming,
fix helper spelling, and use `u8` consistently instead of `uint8_t`
- Syscon: Convert from OF-specific property helpers to the generic
device property and fwnode APIs
Device Tree Binding Updates:
- Core: Allow PHY RX triggers (`phy[0-9]+rx`) in
`linux,default-trigger`
- Core: Standardize example indentation across multiple LED and
backlight binding schemas
- Analog Devices LTC3208: Document the LTC3208 multi-display LED
driver
- LaCie netxbig: Convert LED bindings to DT schema
- Motorola CPCAP: Convert LED bindings to DT schemaLEDs for v7.4"
* tag 'leds-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (49 commits)
dt-bindings: leds: lacie,netxbig-leds: Convert to DT schema
leds: pca955x: Drop platform data and allocate LEDs with the device
dt-bindings: leds: Use consistent indentation in the example
backlight: qcom-wled: Read back the programmed brightness at probe
backlight: qcom-wled: Fix unbalanced OVP IRQ enable at probe
backlight: qcom-wled: Fix WLED3 brightness register stride
backlight: qcom-wled: Fix NULL pointer dereference in wled_remove()
dt-bindings: leds: Allow PHY RX triggers
MAINTAINERS: Add myself as a LED SUBSYSTEM reviewer
leds: trigger: Add led_trigger_notify_hw_control_changed() interface
leds: trigger: Enforce strict checks in led_trigger_is_hw_controlled()
leds: trigger: netdev: Implement hw_offloaded() callback
leds: turris-omnia: Implement hw_offloaded() trigger callback and declare hw_control_trigger
leds: cros_ec: Implement hw_offloaded() trigger callback
leds: trigger: Add hw_offloaded() callback and provide trigger_may_offload_to_hw attribute
leds: trigger: Move led_trigger_group to the right place
leds: class: Remove hardware control trigger when writing brightness
leds: trigger: Move led_trigger_is_hw_controlled() to the right place
leds: class: Always protect brightness_show() with led_access
dt-bindings: leds: leds-cpcap: Convert to DT schema
...
This commit is contained in:
commit
5b93dc1c0c
|
|
@ -28,7 +28,9 @@ Description:
|
|||
Writing 0 to this file clears active trigger.
|
||||
|
||||
Writing non-zero to this file while trigger is active changes the
|
||||
top brightness trigger is going to use.
|
||||
top brightness trigger is going to use, unless the current
|
||||
trigger is offloaded to hardware (in which case writing any
|
||||
value will deactivate the trigger).
|
||||
|
||||
|
||||
|
||||
|
|
@ -78,6 +80,30 @@ Description:
|
|||
(which would often be configured in the device tree for the
|
||||
hardware).
|
||||
|
||||
What: /sys/class/leds/<led>/trigger_may_offload_to_hw
|
||||
Date: September 2026
|
||||
KernelVersion: 7.4
|
||||
Contact: linux-leds@vger.kernel.org
|
||||
Description:
|
||||
Names and states of triggers that may be offloaded to hardware.
|
||||
Such triggers are also called "hardware control trigger" in some
|
||||
context.
|
||||
|
||||
Only exists when the LED supports trigger offload.
|
||||
|
||||
Reading this file returns a list of triggers that are capable to
|
||||
be offloaded. The optional brackets around the trigger name
|
||||
indicate the state of the current trigger:
|
||||
|
||||
- `foo_trigger`: the trigger is not selected, or selected but
|
||||
falls back to software blink for some reasons, e.g.,
|
||||
incompatible trigger parameters.
|
||||
- `[foo_trigger]`: the trigger is selected and offloaded to
|
||||
hardware.
|
||||
|
||||
The "netdev" trigger also provides a custom attribute to
|
||||
indicate its state, see `/sys/class/leds/<led>/offloaded`.
|
||||
|
||||
What: /sys/class/leds/<led>/inverted
|
||||
Date: January 2011
|
||||
KernelVersion: 2.6.38
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ Description:
|
|||
If 1, the LED blinking in requested mode is offloaded to
|
||||
hardware.
|
||||
|
||||
LED trigger core also provides a generic attribute for this
|
||||
purpose, see `/sys/class/leds/<led>/trigger_may_offload_to_hw`.
|
||||
|
||||
What: /sys/class/leds/<led>/link_10
|
||||
Date: Jun 2023
|
||||
KernelVersion: 6.5
|
||||
|
|
|
|||
183
Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
Normal file
183
Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (c) 2026 Analog Devices, Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/adi,ltc3208.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LTC3208 Multidisplay LED Controller from Linear Technologies (Now Analog Devices)
|
||||
|
||||
maintainers:
|
||||
- Jan Carlo Roleda <jancarlo.roleda@analog.com>
|
||||
|
||||
description:
|
||||
The LTC3208 is a multidisplay LED controller that can support up to 1A to all
|
||||
connected LEDs.
|
||||
|
||||
The datasheet for this device can be found in
|
||||
https://www.analog.com/en/products/ltc3208.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: adi,ltc3208
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
adi,disable-camhl-pin:
|
||||
type: boolean
|
||||
description:
|
||||
Configures whether the external CAMHL pin is disabled.
|
||||
If disabled then the output pins associated with CAM will always select
|
||||
the CAM register's high half-byte brightness.
|
||||
|
||||
adi,cfg-enrgbs-pin:
|
||||
type: boolean
|
||||
description:
|
||||
Configures which channel the ENRGBS pin toggles when it receives a signal.
|
||||
ENRGBS pin controls the SUB channel's output pins if this is set,
|
||||
or RGB channel's output pins if this is unset.
|
||||
|
||||
adi,disable-rgb-aux4-dropout:
|
||||
type: boolean
|
||||
description:
|
||||
Configures the RGB and AUX4 dropout signals to be disabled.
|
||||
|
||||
adi,aux1-channel:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
LED Channel that the AUX1 output pin mirrors its brightness level from.
|
||||
enum: [aux, main, sub, cam]
|
||||
default: aux
|
||||
|
||||
adi,aux2-channel:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
LED Channel that the AUX2 output pin mirrors its brightness level from.
|
||||
enum: [aux, main, sub, cam]
|
||||
default: aux
|
||||
|
||||
adi,aux3-channel:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
LED Channel that the AUX3 output pin mirrors its brightness level from.
|
||||
enum: [aux, main, sub, cam]
|
||||
default: aux
|
||||
|
||||
adi,aux4-channel:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
LED Channel that the AUX4 output pin mirrors its brightness level from.
|
||||
enum: [aux, main, sub, cam]
|
||||
default: aux
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-7]$":
|
||||
type: object
|
||||
$ref: /schemas/leds/common.yaml#
|
||||
unevaluatedProperties: false
|
||||
properties:
|
||||
reg:
|
||||
description:
|
||||
LED Channel Number. each channel maps to a specific channel group used
|
||||
to configure the brightness level of the output pins corresponding to
|
||||
the channel.
|
||||
enum:
|
||||
- 0 # Main Channel (8-bit brightness)
|
||||
- 1 # Sub Channel (8-bit brightness)
|
||||
- 2 # AUX Channel (4-bit brightness)
|
||||
- 3 # Camera Channel, Low-side byte (4-bit brightness)
|
||||
- 4 # Camera Channel, High-side byte (4-bit brightness)
|
||||
- 5 # Red Channel (4-bit brightness)
|
||||
- 6 # Blue Channel (4-bit brightness)
|
||||
- 7 # Green Channel (4-bit brightness)
|
||||
required:
|
||||
- reg
|
||||
|
||||
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@1b {
|
||||
compatible = "adi,ltc3208";
|
||||
reg = <0x1b>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adi,disable-camhl-pin;
|
||||
adi,cfg-enrgbs-pin;
|
||||
adi,disable-rgb-aux4-dropout;
|
||||
|
||||
/* MAIN */
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
function = LED_FUNCTION_ACTIVITY;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
};
|
||||
|
||||
/* SUB */
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
function = LED_FUNCTION_ACTIVITY;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
};
|
||||
|
||||
/* AUX */
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
function = LED_FUNCTION_ACTIVITY;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
};
|
||||
|
||||
/* CAMLO */
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
function = LED_FUNCTION_FLASH;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
};
|
||||
|
||||
/* CAMHI */
|
||||
led@4 {
|
||||
reg = <4>;
|
||||
function = LED_FUNCTION_FLASH;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
};
|
||||
|
||||
/* RED */
|
||||
led@5 {
|
||||
reg = <5>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
/* BLUE */
|
||||
led@6 {
|
||||
reg = <6>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
|
||||
/* GREEN */
|
||||
led@7 {
|
||||
reg = <7>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -99,20 +99,20 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
led-controller@38 {
|
||||
compatible = "ti,lm3630a";
|
||||
reg = <0x38>;
|
||||
enable-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>;
|
||||
compatible = "ti,lm3630a";
|
||||
reg = <0x38>;
|
||||
enable-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
led-sources = <0 1>;
|
||||
label = "lcd-backlight";
|
||||
default-brightness = <200>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
led-sources = <0 1>;
|
||||
label = "lcd-backlight";
|
||||
default-brightness = <200>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
|
|
@ -121,22 +121,22 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
led-controller@38 {
|
||||
compatible = "ti,lm3630a";
|
||||
reg = <0x38>;
|
||||
compatible = "ti,lm3630a";
|
||||
reg = <0x38>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
default-brightness = <150>;
|
||||
ti,linear-mapping-mode;
|
||||
};
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
default-brightness = <150>;
|
||||
ti,linear-mapping-mode;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
default-brightness = <225>;
|
||||
ti,linear-mapping-mode;
|
||||
};
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
default-brightness = <225>;
|
||||
ti,linear-mapping-mode;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -95,20 +95,20 @@ examples:
|
|||
|
||||
/* 4V OV, 4 output LED0 string enabled */
|
||||
rom-14h {
|
||||
rom-addr = /bits/ 8 <0x14>;
|
||||
rom-val = /bits/ 8 <0xcf>;
|
||||
rom-addr = /bits/ 8 <0x14>;
|
||||
rom-val = /bits/ 8 <0xcf>;
|
||||
};
|
||||
|
||||
/* Heavy smoothing, 24ms ramp time step */
|
||||
rom-15h {
|
||||
rom-addr = /bits/ 8 <0x15>;
|
||||
rom-val = /bits/ 8 <0xc7>;
|
||||
rom-addr = /bits/ 8 <0x15>;
|
||||
rom-val = /bits/ 8 <0xc7>;
|
||||
};
|
||||
|
||||
/* 4 output LED1 string enabled */
|
||||
rom-19h {
|
||||
rom-addr = /bits/ 8 <0x19>;
|
||||
rom-val = /bits/ 8 <0x0f>;
|
||||
rom-addr = /bits/ 8 <0x19>;
|
||||
rom-val = /bits/ 8 <0x0f>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -125,7 +125,7 @@ examples:
|
|||
dev-ctrl = /bits/ 8 <0x85>;
|
||||
init-brt = /bits/ 8 <0x10>;
|
||||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
|
|
@ -142,8 +142,8 @@ examples:
|
|||
|
||||
/* 4V OV, 4 output LED string enabled */
|
||||
rom-14h {
|
||||
rom-addr = /bits/ 8 <0x14>;
|
||||
rom-val = /bits/ 8 <0xcf>;
|
||||
rom-addr = /bits/ 8 <0x14>;
|
||||
rom-val = /bits/ 8 <0xcf>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -280,4 +280,4 @@ examples:
|
|||
qcom,ovp = <29>;
|
||||
qcom,num-strings = <2>;
|
||||
qcom,enabled-strings = <0 1>;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ properties:
|
|||
# LED is triggered by SD/MMC activity
|
||||
- pattern: "^mmc[0-9]+$"
|
||||
# LED is triggered by WLAN activity
|
||||
- pattern: "^phy[0-9]+tx$"
|
||||
- pattern: "^phy[0-9]+(rx|tx)$"
|
||||
|
||||
led-pattern:
|
||||
description: |
|
||||
|
|
|
|||
136
Documentation/devicetree/bindings/leds/lacie,netxbig-leds.yaml
Normal file
136
Documentation/devicetree/bindings/leds/lacie,netxbig-leds.yaml
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/lacie,netxbig-leds.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LaCie/Seagate netxbig CPLD LEDs
|
||||
|
||||
maintainers:
|
||||
- Simon Guinot <simon.guinot@sequanux.org>
|
||||
|
||||
description: |
|
||||
Binding for the CPLD LEDs (GPIO extension bus) found on some LaCie/Seagate
|
||||
boards (Example: 2Big/5Big Network v2, 2Big NAS).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: lacie,netxbig-leds
|
||||
|
||||
gpio-ext:
|
||||
description: Phandle for the gpio-ext bus.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
timers:
|
||||
description:
|
||||
Timer array. Each timer entry is represented by three integers -
|
||||
Mode (gpio-ext bus), delay_on and delay_off.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
patternProperties:
|
||||
"^led-[a-z0-9]+$":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
mode-addr:
|
||||
description: Mode register address on gpio-ext bus.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
mode-val:
|
||||
description:
|
||||
Mode to value mapping. Each entry is represented by two integers -
|
||||
a mode and the corresponding value on the gpio-ext bus.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
bright-addr:
|
||||
description: Brightness register address on gpio-ext bus.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
max-brightness:
|
||||
description: Maximum brightness value.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
required:
|
||||
- mode-addr
|
||||
- mode-val
|
||||
- bright-addr
|
||||
- max-brightness
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- gpio-ext
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/leds-netxbig.h>
|
||||
|
||||
leds {
|
||||
compatible = "lacie,netxbig-leds";
|
||||
|
||||
gpio-ext = <&netxbig_gpio_ext>;
|
||||
|
||||
timers = <NETXBIG_LED_TIMER1 500 500
|
||||
NETXBIG_LED_TIMER2 500 1000>;
|
||||
|
||||
led-0 {
|
||||
label = "netxbig:blue:power";
|
||||
mode-addr = <0>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 1
|
||||
NETXBIG_LED_TIMER1 3
|
||||
NETXBIG_LED_TIMER2 7>;
|
||||
bright-addr = <1>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
led-1 {
|
||||
label = "netxbig:red:power";
|
||||
mode-addr = <0>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 2
|
||||
NETXBIG_LED_TIMER1 4>;
|
||||
bright-addr = <1>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
led-2 {
|
||||
label = "netxbig:blue:sata0";
|
||||
mode-addr = <3>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 7
|
||||
NETXBIG_LED_SATA 1
|
||||
NETXBIG_LED_TIMER1 3>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
led-3 {
|
||||
label = "netxbig:red:sata0";
|
||||
mode-addr = <3>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 2
|
||||
NETXBIG_LED_TIMER1 4>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
led-4 {
|
||||
label = "netxbig:blue:sata1";
|
||||
mode-addr = <4>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 7
|
||||
NETXBIG_LED_SATA 1
|
||||
NETXBIG_LED_TIMER1 3>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
led-5 {
|
||||
label = "netxbig:red:sata1";
|
||||
mode-addr = <4>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 2
|
||||
NETXBIG_LED_TIMER1 4>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
};
|
||||
|
|
@ -53,15 +53,15 @@ examples:
|
|||
};
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
v4l2_node: camera@36 {
|
||||
reg = <0x36>;
|
||||
v4l2_node: camera@36 {
|
||||
reg = <0x36>;
|
||||
|
||||
leds = <&privacy_led>;
|
||||
led-names = "privacy";
|
||||
};
|
||||
leds = <&privacy_led>;
|
||||
led-names = "privacy";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
Motorola CPCAP PMIC LEDs
|
||||
------------------------
|
||||
|
||||
This module is part of the CPCAP. For more details about the whole
|
||||
chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
|
||||
|
||||
Requires node properties:
|
||||
- compatible: should be one of
|
||||
* "motorola,cpcap-led-mdl" (Main Display Lighting)
|
||||
* "motorola,cpcap-led-kl" (Keyboard Lighting)
|
||||
* "motorola,cpcap-led-adl" (Aux Display Lighting)
|
||||
* "motorola,cpcap-led-red" (Red Triode)
|
||||
* "motorola,cpcap-led-green" (Green Triode)
|
||||
* "motorola,cpcap-led-blue" (Blue Triode)
|
||||
* "motorola,cpcap-led-cf" (Camera Flash)
|
||||
* "motorola,cpcap-led-bt" (Bluetooth)
|
||||
* "motorola,cpcap-led-cp" (Camera Privacy LED)
|
||||
- label: see Documentation/devicetree/bindings/leds/common.txt
|
||||
- vdd-supply: A phandle to the regulator powering the LED
|
||||
|
||||
Example:
|
||||
|
||||
&cpcap {
|
||||
cpcap_led_red: red-led {
|
||||
compatible = "motorola,cpcap-led-red";
|
||||
label = "cpcap:red";
|
||||
vdd-supply = <&sw5>;
|
||||
};
|
||||
};
|
||||
|
|
@ -121,18 +121,18 @@ examples:
|
|||
function = LED_FUNCTION_CHARGING;
|
||||
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <0x1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
reg = <0x1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -100,18 +100,18 @@ examples:
|
|||
color = <LED_COLOR_ID_RGB>;
|
||||
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
reg = <0x0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <0x1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
reg = <0x1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
Binding for the CPLD LEDs (GPIO extension bus) found on some LaCie/Seagate
|
||||
boards (Example: 2Big/5Big Network v2, 2Big NAS).
|
||||
|
||||
Required properties:
|
||||
- compatible: "lacie,netxbig-leds".
|
||||
- gpio-ext: Phandle for the gpio-ext bus.
|
||||
|
||||
Optional properties:
|
||||
- timers: Timer array. Each timer entry is represented by three integers:
|
||||
Mode (gpio-ext bus), delay_on and delay_off.
|
||||
|
||||
Each LED is represented as a sub-node of the netxbig-leds device.
|
||||
|
||||
Required sub-node properties:
|
||||
- mode-addr: Mode register address on gpio-ext bus.
|
||||
- mode-val: Mode to value mapping. Each entry is represented by two integers:
|
||||
A mode and the corresponding value on the gpio-ext bus.
|
||||
- bright-addr: Brightness register address on gpio-ext bus.
|
||||
- max-brightness: Maximum brightness value.
|
||||
|
||||
Optional sub-node properties:
|
||||
- label: Name for this LED. If omitted, the label is taken from the node name.
|
||||
- linux,default-trigger: Trigger assigned to the LED.
|
||||
|
||||
Example:
|
||||
|
||||
netxbig-leds {
|
||||
compatible = "lacie,netxbig-leds";
|
||||
|
||||
gpio-ext = &gpio_ext;
|
||||
|
||||
timers = <NETXBIG_LED_TIMER1 500 500
|
||||
NETXBIG_LED_TIMER2 500 1000>;
|
||||
|
||||
blue-power {
|
||||
label = "netxbig:blue:power";
|
||||
mode-addr = <0>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 1
|
||||
NETXBIG_LED_TIMER1 3
|
||||
NETXBIG_LED_TIMER2 7>;
|
||||
bright-addr = <1>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
red-power {
|
||||
label = "netxbig:red:power";
|
||||
mode-addr = <0>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 2
|
||||
NETXBIG_LED_TIMER1 4>;
|
||||
bright-addr = <1>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
blue-sata0 {
|
||||
label = "netxbig:blue:sata0";
|
||||
mode-addr = <3>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 7
|
||||
NETXBIG_LED_SATA 1
|
||||
NETXBIG_LED_TIMER1 3>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
red-sata0 {
|
||||
label = "netxbig:red:sata0";
|
||||
mode-addr = <3>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 2
|
||||
NETXBIG_LED_TIMER1 4>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
blue-sata1 {
|
||||
label = "netxbig:blue:sata1";
|
||||
mode-addr = <4>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 7
|
||||
NETXBIG_LED_SATA 1
|
||||
NETXBIG_LED_TIMER1 3>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
red-sata1 {
|
||||
label = "netxbig:red:sata1";
|
||||
mode-addr = <4>;
|
||||
mode-val = <NETXBIG_LED_OFF 0
|
||||
NETXBIG_LED_ON 2
|
||||
NETXBIG_LED_TIMER1 4>;
|
||||
bright-addr = <2>;
|
||||
max-brightness = <7>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/motorola,cpcap-leds.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Motorola CPCAP PMIC LEDs
|
||||
|
||||
maintainers:
|
||||
- Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
|
||||
description:
|
||||
This module is part of the Motorola CPCAP MFD device. For more details
|
||||
see Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml. LEDs are
|
||||
represented as sub-nodes of the PMIC node on the device tree.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/leds/common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- motorola,cpcap-led-adl # Display Lighting
|
||||
- motorola,cpcap-led-blue # Blue Triode
|
||||
- motorola,cpcap-led-bt # Bluetooth
|
||||
- motorola,cpcap-led-cf # Camera Flash
|
||||
- motorola,cpcap-led-cp # Camera Privacy LED
|
||||
- motorola,cpcap-led-green # Green Triode
|
||||
- motorola,cpcap-led-kl # Keyboard Lighting
|
||||
- motorola,cpcap-led-mdl # Main Display Lighting
|
||||
- motorola,cpcap-led-red # Red Triode
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- label
|
||||
- vdd-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
...
|
||||
|
|
@ -164,27 +164,27 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_WHITE>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -242,6 +242,9 @@ ops and needs to declare specific support for the supported triggers.
|
|||
|
||||
With hw control we refer to the LED driven by hardware.
|
||||
|
||||
A sysfs attribute `trigger_may_offload_to_hw` is provided for userspace
|
||||
to query supported triggers and their states.
|
||||
|
||||
LED driver must define the following value to support hw control:
|
||||
|
||||
- hw_control_trigger:
|
||||
|
|
@ -298,6 +301,15 @@ LED driver must implement the following API to support hw control:
|
|||
Returns a pointer to a struct device or NULL if nothing
|
||||
is currently attached.
|
||||
|
||||
LED trigger should implement the following API to indicate hw control:
|
||||
- hw_offloaded:
|
||||
return a boolean indicating if the trigger is currently
|
||||
offloaded to hardware.
|
||||
|
||||
If a trigger doesn't implement this callback, the default
|
||||
value will be true for private triggers and false for generic
|
||||
ones.
|
||||
|
||||
LED driver can activate additional modes by default to workaround the
|
||||
impossibility of supporting each different mode on the supported trigger.
|
||||
Examples are hardcoding the blink speed to a set interval, enable special
|
||||
|
|
@ -311,9 +323,69 @@ the end use hw_control_set to activate hw control.
|
|||
A trigger can use hw_control_get to check if a LED is already in hw control
|
||||
and init their flags.
|
||||
|
||||
Alternatively, a private trigger can be implemented along with the LED driver if
|
||||
the LED's hardware control doesn't fit any generic trigger. To associate the
|
||||
private trigger with the LED classdev, their `trigger_type` must be the same. To
|
||||
declare that the private trigger provides hardware control for the associated
|
||||
LED classdev, set the `hw_control_trigger` string to the trigger's name. Since
|
||||
both the LED classdev and the private trigger are in the same LED driver, it's
|
||||
not necessary for them to coordinate via `hw_control_*` callbacks.
|
||||
|
||||
When the LED is in hw control, no software blink is possible and doing so
|
||||
will effectively disable hw control.
|
||||
|
||||
Hardware-initiated trigger transition
|
||||
=====================================
|
||||
|
||||
Some hardware can autonomously activate/deactivate hardware control. After that,
|
||||
the LED hardware notifies the LED driver.
|
||||
|
||||
If the driver can detect such transitions and thus wants to notify the LED core
|
||||
to update the current trigger then the `LED_TRIG_HW_CHANGED` flag must be set in
|
||||
flags before registering. To update the current trigger accordingly, call
|
||||
`led_trigger_notify_hw_control_changed` on the LED classdev.
|
||||
|
||||
This capability is restricted to the LED device's private trigger. The private
|
||||
trigger must have been properly registered (see above) and named after
|
||||
`hw_control_trigger`.
|
||||
|
||||
Only two transitions are defined:
|
||||
|
||||
- "none" => private trigger:
|
||||
This happens when the hardware autonomously activates hardware control
|
||||
and when "none" (i.e., no trigger) is currently active. If the private
|
||||
trigger is already active when the method is called, this is essentially
|
||||
a no-op.
|
||||
|
||||
The activation sequence for the private trigger will be executed as
|
||||
normal.
|
||||
|
||||
The LED driver and its private trigger must be able to handle the
|
||||
activation sequence even if the hardware is currently in hardware
|
||||
control.
|
||||
|
||||
If error occurs in the activation sequence, the LED Trigger core reverts
|
||||
the effective trigger to "none".
|
||||
|
||||
- private trigger => "none"
|
||||
This happens when the hardware autonomously deactivates hardware control
|
||||
and when the private trigger is currently active. If "none" (i.e., no
|
||||
trigger) is active when the method is called, this is essentially a
|
||||
no-op.
|
||||
|
||||
The deactivation sequence for the private trigger will be executed as
|
||||
normal, except that the current LED brightness is retained. The reason
|
||||
for keeping the brightness unchanged is that some hardware may choose a
|
||||
specific brightness instead of simply turning off the LED after
|
||||
autonomously deactivating hardware control.
|
||||
|
||||
The LED driver and its private trigger must be able to handle the
|
||||
deactivation sequence even if the hardware is not currently in hardware
|
||||
control.
|
||||
|
||||
If the current trigger is neither the private trigger nor "none", no transition
|
||||
will be made.
|
||||
|
||||
Known Issues
|
||||
============
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ brightness (PWM) and duration must be written to hw_pattern.
|
|||
- Min pattern duration: 22 ms
|
||||
- Max pattern duration: 5610 ms
|
||||
|
||||
If the device tree sets max-brightness, that value replaces 255 as the
|
||||
upper limit, and a step equal to it drives the channel at full scale.
|
||||
The hardware cannot go above 255, so with a larger max-brightness every
|
||||
step from 255 upwards is full scale.
|
||||
|
||||
The format of the hardware pattern values should be:
|
||||
"brightness duration brightness duration ..."
|
||||
|
||||
|
|
|
|||
|
|
@ -15077,6 +15077,7 @@ F: drivers/scsi/leapraid/
|
|||
LED SUBSYSTEM
|
||||
M: Lee Jones <lee@kernel.org>
|
||||
M: Pavel Machek <pavel@kernel.org>
|
||||
R: Griffin Kroah-Hartman <griffin@kroah.com>
|
||||
L: linux-leds@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git
|
||||
|
|
@ -15821,6 +15822,14 @@ W: https://ez.analog.com/linux-software-drivers
|
|||
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
|
||||
F: drivers/iio/temperature/ltc2983.c
|
||||
|
||||
LTC3208 LED DRIVER
|
||||
M: Jan Carlo Roleda <jancarlo.roleda@analog.com>
|
||||
L: linux-leds@vger.kernel.org
|
||||
S: Maintained
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
|
||||
F: drivers/leds/leds-ltc3208.c
|
||||
|
||||
LTC3220 LED DRIVER
|
||||
M: Edelweise Escala <edelweise.escala@analog.com>
|
||||
L: linux-leds@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -1042,6 +1042,18 @@ config LEDS_ACER_A500
|
|||
This option enables support for the Power Button LED of
|
||||
Acer Iconia Tab A500.
|
||||
|
||||
config LEDS_LTC3208
|
||||
tristate "LED Driver for Analog Devices LTC3208"
|
||||
depends on LEDS_CLASS && I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y to enable the LTC3208 LED driver.
|
||||
This enables the LED device LTC3208, a 7-channel, 17-current source
|
||||
multidisplay high-current LED driver, configured via I2C.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ltc3208.
|
||||
|
||||
source "drivers/leds/blink/Kconfig"
|
||||
|
||||
comment "Flash and Torch LED drivers"
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
|
|||
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
|
||||
obj-$(CONFIG_LEDS_LP8864) += leds-lp8864.o
|
||||
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
|
||||
obj-$(CONFIG_LEDS_LTC3208) += leds-ltc3208.o
|
||||
obj-$(CONFIG_LEDS_LTC3220) += leds-ltc3220.o
|
||||
obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o
|
||||
obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o
|
||||
|
|
|
|||
|
|
@ -311,8 +311,9 @@ static int init_mm_current_scale(struct aat1290_led *led,
|
|||
int i, max_mm_current =
|
||||
AAT1290_MAX_MM_CURRENT(cfg->max_flash_current);
|
||||
|
||||
led->mm_current_scale = devm_kzalloc(&led->pdev->dev,
|
||||
sizeof(max_mm_current_percent),
|
||||
led->mm_current_scale = devm_kcalloc(&led->pdev->dev,
|
||||
ARRAY_SIZE(max_mm_current_percent),
|
||||
sizeof(*led->mm_current_scale),
|
||||
GFP_KERNEL);
|
||||
if (!led->mm_current_scale)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ static const struct expresswire_timing ktd2692_timing = {
|
|||
};
|
||||
|
||||
struct ktd2692_context {
|
||||
struct device *dev;
|
||||
|
||||
/* Common ExpressWire properties (ctrl GPIO and timing) */
|
||||
struct expresswire_common_props props;
|
||||
|
||||
|
|
@ -196,13 +198,12 @@ static void ktd2692_setup(struct ktd2692_context *led)
|
|||
|
||||
static void regulator_disable_action(void *_data)
|
||||
{
|
||||
struct device *dev = _data;
|
||||
struct ktd2692_context *led = dev_get_drvdata(dev);
|
||||
struct ktd2692_context *led = _data;
|
||||
int ret;
|
||||
|
||||
ret = regulator_disable(led->regulator);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to disable supply: %d\n", ret);
|
||||
dev_err(led->dev, "Failed to disable supply: %d\n", ret);
|
||||
}
|
||||
|
||||
static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
|
||||
|
|
@ -233,7 +234,7 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
|
|||
dev_err(dev, "Failed to enable supply: %d\n", ret);
|
||||
} else {
|
||||
ret = devm_add_action_or_reset(dev,
|
||||
regulator_disable_action, dev);
|
||||
regulator_disable_action, led);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -290,6 +291,7 @@ static int ktd2692_probe(struct platform_device *pdev)
|
|||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
led->dev = &pdev->dev;
|
||||
fled_cdev = &led->fled_cdev;
|
||||
led_cdev = &fled_cdev->led_cdev;
|
||||
led->props.timing = ktd2692_timing;
|
||||
|
|
|
|||
|
|
@ -187,26 +187,23 @@ static ssize_t flash_fault_show(struct device *dev,
|
|||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
|
||||
u32 fault, mask = 0x1;
|
||||
char *pbuf = buf;
|
||||
int i, ret, buf_len;
|
||||
u32 fault = 0, mask = 0x1;
|
||||
int i, ret;
|
||||
|
||||
ret = led_get_flash_fault(fled_cdev, &fault);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*buf = '\0';
|
||||
|
||||
for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
|
||||
if (fault & mask) {
|
||||
buf_len = sprintf(pbuf, "%s ",
|
||||
led_flash_fault_names[i]);
|
||||
pbuf += buf_len;
|
||||
}
|
||||
for (ret = 0, i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
|
||||
if (fault & mask)
|
||||
ret += sysfs_emit_at(buf, ret, "%s%s",
|
||||
(ret ? " " : ""),
|
||||
led_flash_fault_names[i]);
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return strlen(strcat(buf, "\n"));
|
||||
ret += sysfs_emit_at(buf, ret, "\n");
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RO(flash_fault);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,29 +27,19 @@ static LIST_HEAD(leds_lookup_list);
|
|||
|
||||
static struct workqueue_struct *leds_wq;
|
||||
|
||||
static bool led_trigger_is_hw_controlled(struct led_classdev *led_cdev)
|
||||
{
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
guard(rwsem_read)(&led_cdev->trigger_lock);
|
||||
return led_cdev->trigger && led_cdev->trigger->trigger_type;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ssize_t brightness_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
unsigned int brightness;
|
||||
|
||||
if (led_trigger_is_hw_controlled(led_cdev))
|
||||
return -ENODATA;
|
||||
scoped_guard(mutex, &led_cdev->led_access) {
|
||||
if (led_trigger_is_hw_controlled(led_cdev))
|
||||
return -ENODATA;
|
||||
|
||||
mutex_lock(&led_cdev->led_access);
|
||||
led_update_brightness(led_cdev);
|
||||
brightness = led_cdev->brightness;
|
||||
mutex_unlock(&led_cdev->led_access);
|
||||
led_update_brightness(led_cdev);
|
||||
brightness = led_cdev->brightness;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%u\n", brightness);
|
||||
}
|
||||
|
|
@ -74,6 +64,9 @@ static ssize_t brightness_store(struct device *dev,
|
|||
|
||||
if (state == LED_OFF)
|
||||
led_trigger_remove(led_cdev);
|
||||
else
|
||||
led_trigger_remove_hw_control(led_cdev);
|
||||
|
||||
led_set_brightness(led_cdev, state);
|
||||
|
||||
ret = size;
|
||||
|
|
@ -97,17 +90,6 @@ static ssize_t max_brightness_show(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR_RO(max_brightness);
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
static const BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0);
|
||||
static const struct bin_attribute *const led_trigger_bin_attrs[] = {
|
||||
&bin_attr_trigger,
|
||||
NULL,
|
||||
};
|
||||
static const struct attribute_group led_trigger_group = {
|
||||
.bin_attrs = led_trigger_bin_attrs,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct attribute *led_class_attrs[] = {
|
||||
&dev_attr_brightness.attr,
|
||||
&dev_attr_max_brightness.attr,
|
||||
|
|
@ -577,6 +559,11 @@ int led_classdev_register_ext(struct device *parent,
|
|||
#endif
|
||||
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
|
||||
led_cdev->brightness_hw_changed = -1;
|
||||
#endif
|
||||
#ifdef CONFIG_LEDS_TRIGGERS_HW_CHANGED
|
||||
if (led_cdev->flags & LED_TRIG_HW_CHANGED)
|
||||
INIT_WORK(&led_cdev->trigger_hw_changed_work,
|
||||
led_trigger_hw_control_changed_worker);
|
||||
#endif
|
||||
if (!led_cdev->max_brightness)
|
||||
led_cdev->max_brightness = LED_FULL;
|
||||
|
|
@ -616,6 +603,11 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
|
|||
if (IS_ERR_OR_NULL(led_cdev->dev))
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS_HW_CHANGED
|
||||
if (led_cdev->flags & LED_TRIG_HW_CHANGED)
|
||||
disable_work_sync(&led_cdev->trigger_hw_changed_work);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
down_write(&led_cdev->trigger_lock);
|
||||
if (led_cdev->trigger)
|
||||
|
|
|
|||
|
|
@ -267,10 +267,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
|
|||
set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
|
||||
clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
|
||||
|
||||
if (invert)
|
||||
set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
|
||||
else
|
||||
clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
|
||||
assign_bit(LED_BLINK_INVERT, &led_cdev->work_flags, invert);
|
||||
|
||||
led_blink_setup(led_cdev, delay_on, delay_off);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,13 @@
|
|||
* Author: Richard Purdie <rpurdie@openedhand.com>
|
||||
*/
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/timer.h>
|
||||
|
|
@ -33,9 +37,38 @@ trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig)
|
|||
return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type;
|
||||
}
|
||||
|
||||
ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
|
||||
const struct bin_attribute *bin_attr, char *buf,
|
||||
loff_t pos, size_t count)
|
||||
static bool __led_trigger_is_hw_controlled(struct led_classdev *led_cdev)
|
||||
{
|
||||
lockdep_assert_held(&led_cdev->trigger_lock);
|
||||
|
||||
if (!led_cdev->trigger)
|
||||
return false;
|
||||
|
||||
if (!led_cdev->hw_control_trigger ||
|
||||
strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name))
|
||||
return false;
|
||||
|
||||
if (led_cdev->trigger->hw_offloaded)
|
||||
return led_cdev->trigger->hw_offloaded(led_cdev);
|
||||
|
||||
dev_warn_once(led_cdev->dev,
|
||||
"Hardware control trigger %s doesn't provide offloaded state\n",
|
||||
led_cdev->trigger->name);
|
||||
|
||||
/* Otherwise assume private triggers are always offloaded. */
|
||||
return led_cdev->trigger->trigger_type;
|
||||
}
|
||||
|
||||
bool led_trigger_is_hw_controlled(struct led_classdev *led_cdev)
|
||||
{
|
||||
guard(rwsem_read)(&led_cdev->trigger_lock);
|
||||
return __led_trigger_is_hw_controlled(led_cdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_is_hw_controlled);
|
||||
|
||||
static ssize_t trigger_write(struct file *filp, struct kobject *kobj,
|
||||
const struct bin_attribute *bin_attr, char *buf,
|
||||
loff_t pos, size_t count)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
|
@ -78,7 +111,6 @@ ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
|
|||
mutex_unlock(&led_cdev->led_access);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_write);
|
||||
|
||||
__printf(3, 4)
|
||||
static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...)
|
||||
|
|
@ -130,9 +162,9 @@ static int led_trigger_format(char *buf, size_t size,
|
|||
* attribute, which is not limited by length. This is _not_ good design, do not
|
||||
* copy it.
|
||||
*/
|
||||
ssize_t led_trigger_read(struct file *filp, struct kobject *kobj,
|
||||
const struct bin_attribute *attr, char *buf,
|
||||
loff_t pos, size_t count)
|
||||
static ssize_t trigger_read(struct file *filp, struct kobject *kobj,
|
||||
const struct bin_attribute *attr, char *buf,
|
||||
loff_t pos, size_t count)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
|
@ -160,10 +192,53 @@ ssize_t led_trigger_read(struct file *filp, struct kobject *kobj,
|
|||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_read);
|
||||
static const BIN_ATTR_RW(trigger, 0);
|
||||
|
||||
static const struct bin_attribute *const led_trigger_bin_attrs[] = {
|
||||
&bin_attr_trigger,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t trigger_may_offload_to_hw_show(struct device *dev,
|
||||
const struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
bool offloaded = led_trigger_is_hw_controlled(led_cdev);
|
||||
|
||||
return sysfs_emit(buf, "%s%s%s\n",
|
||||
offloaded ? "[" : "",
|
||||
led_cdev->hw_control_trigger,
|
||||
offloaded ? "]" : "");
|
||||
}
|
||||
static const DEVICE_ATTR_RO(trigger_may_offload_to_hw);
|
||||
|
||||
static const struct attribute *const led_trigger_attrs[] = {
|
||||
&dev_attr_trigger_may_offload_to_hw.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t led_trigger_is_visible(struct kobject *kobj,
|
||||
const struct attribute *attr, int idx)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
||||
if (attr == &dev_attr_trigger_may_offload_to_hw.attr)
|
||||
return led_cdev->hw_control_trigger ? attr->mode : 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
const struct attribute_group led_trigger_group = {
|
||||
.bin_attrs = led_trigger_bin_attrs,
|
||||
.attrs_const = led_trigger_attrs,
|
||||
.is_visible_const = led_trigger_is_visible,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(led_trigger_group);
|
||||
|
||||
/* Caller must ensure led_cdev->trigger_lock held */
|
||||
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
||||
static int __led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig,
|
||||
bool hw_triggered)
|
||||
{
|
||||
char *event = NULL;
|
||||
char *envp[2];
|
||||
|
|
@ -194,7 +269,21 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
|||
led_cdev->trigger_data = NULL;
|
||||
led_cdev->activated = false;
|
||||
led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
|
||||
led_set_brightness(led_cdev, LED_OFF);
|
||||
|
||||
/*
|
||||
* Hardware may have selected a new brightness level during its
|
||||
* hardware control transition, so only reset brightness if we
|
||||
* are switching to another trigger or if the switching is not
|
||||
* hardware triggered.
|
||||
*
|
||||
* Note that this does not apply to the error path, as running
|
||||
* into the error path implies a none => private trigger
|
||||
* transition. This hints that the LED driver and its private
|
||||
* trigger must have some fundamental bugs, so the error path
|
||||
* always turns off the LED to reset it to a certain state.
|
||||
*/
|
||||
if (trig || !hw_triggered)
|
||||
led_set_brightness(led_cdev, LED_OFF);
|
||||
}
|
||||
if (trig) {
|
||||
spin_lock(&trig->leddev_list_lock);
|
||||
|
|
@ -258,6 +347,11 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
||||
{
|
||||
return __led_trigger_set(led_cdev, trig, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_set);
|
||||
|
||||
void led_trigger_remove(struct led_classdev *led_cdev)
|
||||
|
|
@ -268,6 +362,15 @@ void led_trigger_remove(struct led_classdev *led_cdev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_remove);
|
||||
|
||||
void led_trigger_remove_hw_control(struct led_classdev *led_cdev)
|
||||
{
|
||||
guard(rwsem_write)(&led_cdev->trigger_lock);
|
||||
|
||||
if (__led_trigger_is_hw_controlled(led_cdev))
|
||||
led_trigger_set(led_cdev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_remove_hw_control);
|
||||
|
||||
static bool led_match_default_trigger(struct led_classdev *led_cdev,
|
||||
struct led_trigger *trig)
|
||||
{
|
||||
|
|
@ -403,6 +506,60 @@ int devm_led_trigger_register(struct device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_led_trigger_register);
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS_HW_CHANGED
|
||||
|
||||
static void led_trigger_do_hw_control_transition(struct led_classdev *led_cdev, bool activate,
|
||||
struct led_trigger *hc_trig)
|
||||
{
|
||||
if (activate && !led_cdev->trigger) /* "none" => private trigger. */
|
||||
__led_trigger_set(led_cdev, hc_trig, true);
|
||||
else if (!activate && led_cdev->trigger == hc_trig) /* private trigger => "none". */
|
||||
__led_trigger_set(led_cdev, NULL, true);
|
||||
|
||||
/* Already in the desired state, or another trigger is active, ignore. */
|
||||
}
|
||||
|
||||
void led_trigger_hw_control_changed_worker(struct work_struct *work)
|
||||
{
|
||||
struct led_classdev *led_cdev =
|
||||
container_of(work, struct led_classdev, trigger_hw_changed_work);
|
||||
bool activate = READ_ONCE(led_cdev->trigger_hw_changed);
|
||||
|
||||
scoped_guard(rwsem_read, &triggers_list_lock) {
|
||||
struct led_trigger *trig;
|
||||
|
||||
list_for_each_entry(trig, &trigger_list, next_trig) {
|
||||
if (trig->trigger_type == led_cdev->trigger_type &&
|
||||
!strcmp(trig->name, led_cdev->hw_control_trigger)) {
|
||||
guard(rwsem_write)(&led_cdev->trigger_lock);
|
||||
|
||||
led_trigger_do_hw_control_transition(led_cdev, activate, trig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev_warn(led_cdev->dev,
|
||||
"Private trigger %s is not registered, can't toggle hardware control\n",
|
||||
led_cdev->hw_control_trigger);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_hw_control_changed_worker);
|
||||
|
||||
void led_trigger_notify_hw_control_changed(struct led_classdev *led_cdev, bool activate)
|
||||
{
|
||||
/* Restricted to private triggers. */
|
||||
if (WARN_ON(!(led_cdev->flags & LED_TRIG_HW_CHANGED) ||
|
||||
!led_cdev->hw_control_trigger || !led_cdev->trigger_type))
|
||||
return;
|
||||
|
||||
WRITE_ONCE(led_cdev->trigger_hw_changed, activate);
|
||||
|
||||
schedule_work(&led_cdev->trigger_hw_changed_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_trigger_notify_hw_control_changed);
|
||||
|
||||
#endif /* CONFIG_LEDS_TRIGGERS_HW_CHANGED */
|
||||
|
||||
/* Simple LED Trigger Interface */
|
||||
|
||||
void led_trigger_event(struct led_trigger *trig,
|
||||
|
|
|
|||
|
|
@ -85,12 +85,18 @@ static int cros_ec_led_trigger_activate(struct led_classdev *led_cdev)
|
|||
return cros_ec_led_send_cmd(priv->cros_ec, &arg);
|
||||
}
|
||||
|
||||
static bool cros_ec_led_trigger_hw_offloaded(struct led_classdev *led_cdev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct led_hw_trigger_type cros_ec_led_trigger_type;
|
||||
|
||||
static struct led_trigger cros_ec_led_trigger = {
|
||||
.name = "chromeos-auto",
|
||||
.trigger_type = &cros_ec_led_trigger_type,
|
||||
.activate = cros_ec_led_trigger_activate,
|
||||
.hw_offloaded = cros_ec_led_trigger_hw_offloaded,
|
||||
};
|
||||
|
||||
static int cros_ec_led_brightness_set_blocking(struct led_classdev *led_cdev,
|
||||
|
|
|
|||
|
|
@ -378,23 +378,23 @@ static int is31fl32xx_init_regs(struct is31fl32xx_priv *priv)
|
|||
}
|
||||
|
||||
static int is31fl32xx_parse_child_dt(const struct device *dev,
|
||||
const struct device_node *child,
|
||||
const struct fwnode_handle *child,
|
||||
struct is31fl32xx_led_data *led_data)
|
||||
{
|
||||
struct led_classdev *cdev = &led_data->cdev;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {
|
||||
dev_err(dev,
|
||||
"Child node %pOF does not have a valid reg property\n",
|
||||
"Child node %pfwP does not have a valid reg property\n",
|
||||
child);
|
||||
return -EINVAL;
|
||||
}
|
||||
led_data->channel = reg;
|
||||
|
||||
of_property_read_u32(child, "led-max-microamp", &led_data->max_microamp);
|
||||
fwnode_property_read_u32(child, "led-max-microamp", &led_data->max_microamp);
|
||||
|
||||
cdev->brightness_set_blocking = is31fl32xx_brightness_set;
|
||||
|
||||
|
|
@ -422,7 +422,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
int ret = 0;
|
||||
|
||||
if ((cdef->output_frequency_setting_reg != IS31FL32XX_REG_NONE) &&
|
||||
of_property_read_bool(dev_of_node(dev), "issi,22khz-pwm")) {
|
||||
device_property_read_bool(dev, "issi,22khz-pwm")) {
|
||||
|
||||
ret = is31fl32xx_write(priv, cdef->output_frequency_setting_reg,
|
||||
IS31FL32XX_PWM_FREQUENCY_22KHZ);
|
||||
|
|
@ -433,7 +433,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
struct led_init_data init_data = {};
|
||||
struct is31fl32xx_led_data *led_data =
|
||||
&priv->leds[priv->num_leds];
|
||||
|
|
@ -451,17 +451,17 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
led_data->channel);
|
||||
if (other_led_data) {
|
||||
dev_err(dev,
|
||||
"Node %pOF 'reg' conflicts with another LED\n",
|
||||
"Node %pfwP 'reg' conflicts with another LED\n",
|
||||
child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init_data.fwnode = of_fwnode_handle(child);
|
||||
init_data.fwnode = child;
|
||||
|
||||
ret = devm_led_classdev_register_ext(dev, &led_data->cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register LED for %pOF: %d\n",
|
||||
dev_err(dev, "Failed to register LED for %pfwP: %d\n",
|
||||
child, ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -576,7 +576,7 @@ static int is31fl32xx_probe(struct i2c_client *client)
|
|||
|
||||
cdef = device_get_match_data(dev);
|
||||
|
||||
count = of_get_available_child_count(dev_of_node(dev));
|
||||
count = device_get_child_node_count(dev);
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -276,9 +276,11 @@ static int lm3692x_leds_enable(struct lm3692x_led *led)
|
|||
|
||||
ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK,
|
||||
enable_state | LM3692X_DEVICE_EN);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
led->enabled = true;
|
||||
return ret;
|
||||
return 0;
|
||||
out:
|
||||
dev_err(&led->client->dev, "Fail writing initialization values\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
* Author: Dan Murphy <dmurphy@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
|
|
@ -274,7 +275,6 @@ static int lp8860_probe(struct i2c_client *client)
|
|||
int ret;
|
||||
struct lp8860_led *led;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
struct device_node *child_node;
|
||||
struct led_init_data init_data = {};
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
|
|
@ -282,7 +282,8 @@ static int lp8860_probe(struct i2c_client *client)
|
|||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
child_node = of_get_next_available_child(np, NULL);
|
||||
struct device_node *child_node __free(device_node) =
|
||||
of_get_next_available_child(np, NULL);
|
||||
if (!child_node)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
234
drivers/leds/leds-ltc3208.c
Normal file
234
drivers/leds/leds-ltc3208.c
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* LED driver for Analog Devices LTC3208 Multi-Display Driver
|
||||
*
|
||||
* Copyright 2026 Analog Devices Inc.
|
||||
*
|
||||
* Author: Jan Carlo Roleda <jancarlo.roleda@analog.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Registers */
|
||||
#define LTC3208_REG_A_GRNRED_CURRENT 0x1 /* Green and Red current DAC */
|
||||
#define LTC3208_REG_B_AUXBLU_CURRENT 0x2 /* AUX and Blue current DAC */
|
||||
#define LTC3208_REG_C_MAIN_CURRENT 0x3 /* Main current DAC */
|
||||
#define LTC3208_REG_D_SUB_CURRENT 0x4 /* Sub current DAC */
|
||||
#define LTC3208_REG_E_AUX_SELECT 0x5 /* AUX DAC Select */
|
||||
#define LTC3208_AUX1_MASK GENMASK(1, 0)
|
||||
#define LTC3208_AUX2_MASK GENMASK(3, 2)
|
||||
#define LTC3208_AUX3_MASK GENMASK(5, 4)
|
||||
#define LTC3208_AUX4_MASK GENMASK(7, 6)
|
||||
#define LTC3208_REG_F_CAM_CURRENT 0x6 /* CAM (High and Low) current DAC */
|
||||
#define LTC3208_REG_G_OPT 0x7 /* Device Options */
|
||||
#define LTC3208_OPT_CPO_MASK GENMASK(7, 6)
|
||||
#define LTC3208_OPT_DIS_RGBDROP BIT(3)
|
||||
#define LTC3208_OPT_DIS_CAMHILO BIT(2)
|
||||
#define LTC3208_OPT_EN_RGBS BIT(1)
|
||||
|
||||
#define LTC3208_MAX_BRIGHTNESS_4BIT 0xF
|
||||
#define LTC3208_MAX_BRIGHTNESS_8BIT 0xFF
|
||||
|
||||
#define LTC3208_NUM_AUX_LEDS 4 /* Number of configurable aux channels */
|
||||
#define LTC3208_NUM_REGS 7 /* Number of available registers to access */
|
||||
|
||||
enum ltc3208_aux_channel {
|
||||
LTC3208_AUX_CHAN_AUX = 0,
|
||||
LTC3208_AUX_CHAN_MAIN,
|
||||
LTC3208_AUX_CHAN_SUB,
|
||||
LTC3208_AUX_CHAN_CAM
|
||||
};
|
||||
|
||||
enum ltc3208_channel {
|
||||
LTC3208_CHAN_MAIN = 0,
|
||||
LTC3208_CHAN_SUB,
|
||||
LTC3208_CHAN_AUX,
|
||||
LTC3208_CHAN_CAML,
|
||||
LTC3208_CHAN_CAMH,
|
||||
LTC3208_CHAN_RED,
|
||||
LTC3208_CHAN_BLUE,
|
||||
LTC3208_CHAN_GREEN,
|
||||
LTC3208_CHAN_N_COUNT,
|
||||
};
|
||||
|
||||
static const char *const ltc3208_dt_aux_channels[] = {
|
||||
"adi,aux1-channel",
|
||||
"adi,aux2-channel",
|
||||
"adi,aux3-channel",
|
||||
"adi,aux4-channel"
|
||||
};
|
||||
|
||||
static const char *const ltc3208_aux_opt[] = { "aux", "main", "sub", "cam" };
|
||||
|
||||
struct ltc3208_led {
|
||||
struct led_classdev cdev;
|
||||
struct regmap_field *rfield;
|
||||
};
|
||||
|
||||
static const struct reg_default ltc3208_reg_defaults[] = {
|
||||
{ LTC3208_REG_A_GRNRED_CURRENT, 0 },
|
||||
{ LTC3208_REG_B_AUXBLU_CURRENT, 0 },
|
||||
{ LTC3208_REG_C_MAIN_CURRENT, 0 },
|
||||
{ LTC3208_REG_D_SUB_CURRENT, 0 },
|
||||
{ LTC3208_REG_E_AUX_SELECT, 0 },
|
||||
{ LTC3208_REG_F_CAM_CURRENT, 0 },
|
||||
{ LTC3208_REG_G_OPT, 0 }
|
||||
};
|
||||
|
||||
static const struct regmap_config ltc3208_regmap_cfg = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = LTC3208_REG_G_OPT,
|
||||
.cache_type = REGCACHE_FLAT_S,
|
||||
.reg_defaults = ltc3208_reg_defaults,
|
||||
.num_reg_defaults = LTC3208_NUM_REGS,
|
||||
};
|
||||
|
||||
static const struct reg_field ltc3208_led_reg_field[LTC3208_CHAN_N_COUNT] = {
|
||||
[LTC3208_CHAN_MAIN] = REG_FIELD(LTC3208_REG_C_MAIN_CURRENT, 0, 7),
|
||||
[LTC3208_CHAN_SUB] = REG_FIELD(LTC3208_REG_D_SUB_CURRENT, 0, 7),
|
||||
[LTC3208_CHAN_BLUE] = REG_FIELD(LTC3208_REG_B_AUXBLU_CURRENT, 0, 3),
|
||||
[LTC3208_CHAN_AUX] = REG_FIELD(LTC3208_REG_B_AUXBLU_CURRENT, 4, 7),
|
||||
[LTC3208_CHAN_CAML] = REG_FIELD(LTC3208_REG_F_CAM_CURRENT, 0, 3),
|
||||
[LTC3208_CHAN_CAMH] = REG_FIELD(LTC3208_REG_F_CAM_CURRENT, 4, 7),
|
||||
[LTC3208_CHAN_RED] = REG_FIELD(LTC3208_REG_A_GRNRED_CURRENT, 0, 3),
|
||||
[LTC3208_CHAN_GREEN] = REG_FIELD(LTC3208_REG_A_GRNRED_CURRENT, 4, 7),
|
||||
};
|
||||
|
||||
static int ltc3208_led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness)
|
||||
{
|
||||
struct ltc3208_led *led = container_of(led_cdev, struct ltc3208_led, cdev);
|
||||
u8 current_level = brightness;
|
||||
|
||||
return regmap_field_write(led->rfield, current_level);
|
||||
}
|
||||
|
||||
static int ltc3208_probe(struct i2c_client *client)
|
||||
{
|
||||
enum ltc3208_aux_channel aux_channels[LTC3208_NUM_AUX_LEDS];
|
||||
DECLARE_BITMAP(registered_chans, LTC3208_CHAN_N_COUNT) = { };
|
||||
struct regmap *regmap;
|
||||
bool disable_rgb_aux4_dropout_signal;
|
||||
bool disable_camhl_pin;
|
||||
bool set_sub_control_pin;
|
||||
int ret;
|
||||
u8 dev_options;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, <c3208_regmap_cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(regmap), "Failed to initialize regmap");
|
||||
|
||||
regcache_mark_dirty(regmap);
|
||||
ret = regcache_sync(regmap);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Failed to sync register cache");
|
||||
|
||||
disable_camhl_pin = device_property_read_bool(&client->dev, "adi,disable-camhl-pin");
|
||||
|
||||
set_sub_control_pin = device_property_read_bool(&client->dev, "adi,cfg-enrgbs-pin");
|
||||
|
||||
disable_rgb_aux4_dropout_signal =
|
||||
device_property_read_bool(&client->dev, "adi,disable-rgb-aux4-dropout");
|
||||
|
||||
dev_options = FIELD_PREP(LTC3208_OPT_EN_RGBS, set_sub_control_pin) |
|
||||
FIELD_PREP(LTC3208_OPT_DIS_CAMHILO, disable_camhl_pin) |
|
||||
FIELD_PREP(LTC3208_OPT_CPO_MASK, 0) |
|
||||
FIELD_PREP(LTC3208_OPT_DIS_RGBDROP, disable_rgb_aux4_dropout_signal);
|
||||
|
||||
ret = regmap_write(regmap, LTC3208_REG_G_OPT, dev_options);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Failed to set device options register");
|
||||
|
||||
/* Initialize AUX channel configurations */
|
||||
for (int i = 0; i < LTC3208_NUM_AUX_LEDS; i++) {
|
||||
/* Default value (AUX) if property is not present in DT */
|
||||
if (!device_property_present(&client->dev, ltc3208_dt_aux_channels[i])) {
|
||||
aux_channels[i] = LTC3208_AUX_CHAN_AUX;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = device_property_match_property_string(&client->dev,
|
||||
ltc3208_dt_aux_channels[i],
|
||||
ltc3208_aux_opt, LTC3208_NUM_AUX_LEDS);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&client->dev, ret, "Error reading AUX Channel %d", i);
|
||||
|
||||
aux_channels[i] = ret;
|
||||
}
|
||||
|
||||
dev_options = FIELD_PREP(LTC3208_AUX1_MASK, aux_channels[0]) |
|
||||
FIELD_PREP(LTC3208_AUX2_MASK, aux_channels[1]) |
|
||||
FIELD_PREP(LTC3208_AUX3_MASK, aux_channels[2]) |
|
||||
FIELD_PREP(LTC3208_AUX4_MASK, aux_channels[3]);
|
||||
|
||||
ret = regmap_write(regmap, LTC3208_REG_E_AUX_SELECT, dev_options);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Error writing to aux channel register");
|
||||
|
||||
device_for_each_child_node_scoped(&client->dev, child) {
|
||||
struct ltc3208_led *led;
|
||||
struct led_init_data init_data = { };
|
||||
u32 chan;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", &chan);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Failed to get register value of LED");
|
||||
if (chan >= LTC3208_CHAN_N_COUNT)
|
||||
return dev_err_probe(&client->dev, -EINVAL, "%u is an invalid LED ID",
|
||||
chan);
|
||||
if (test_and_set_bit(chan, registered_chans))
|
||||
return dev_err_probe(&client->dev, -EINVAL, "%u is already registered",
|
||||
chan);
|
||||
|
||||
led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
led->rfield = devm_regmap_field_alloc(&client->dev, regmap,
|
||||
ltc3208_led_reg_field[chan]);
|
||||
if (IS_ERR(led->rfield))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(led->rfield),
|
||||
"Cannot allocate regmap field");
|
||||
led->cdev.brightness_set_blocking = ltc3208_led_set_brightness;
|
||||
led->cdev.max_brightness = LTC3208_MAX_BRIGHTNESS_4BIT;
|
||||
|
||||
if (chan == LTC3208_CHAN_MAIN || chan == LTC3208_CHAN_SUB)
|
||||
led->cdev.max_brightness = LTC3208_MAX_BRIGHTNESS_8BIT;
|
||||
|
||||
init_data.fwnode = child;
|
||||
|
||||
ret = devm_led_classdev_register_ext(&client->dev, &led->cdev, &init_data);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Failed to register LED %u", chan);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ltc3208_match_table[] = {
|
||||
{ .compatible = "adi,ltc3208" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc3208_match_table);
|
||||
|
||||
static struct i2c_driver ltc3208_driver = {
|
||||
.driver = {
|
||||
.name = "ltc3208",
|
||||
.of_match_table = ltc3208_match_table,
|
||||
},
|
||||
.probe = ltc3208_probe,
|
||||
};
|
||||
module_i2c_driver(ltc3208_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jan Carlo Roleda <jancarlo.roleda@analog.com>");
|
||||
MODULE_DESCRIPTION("LTC3208 LED Driver");
|
||||
|
|
@ -160,7 +160,6 @@ static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fw
|
|||
struct max77705_led *led;
|
||||
struct led_classdev *cdev;
|
||||
struct mc_subled *info;
|
||||
struct fwnode_handle *child;
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||
|
|
@ -191,7 +190,7 @@ static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fw
|
|||
cdev->brightness_set_blocking = max77705_led_brightness_set_multi;
|
||||
cdev->blink_set = max77705_rgb_blink;
|
||||
|
||||
fwnode_for_each_child_node(np, child) {
|
||||
fwnode_for_each_child_node_scoped(np, child) {
|
||||
ret = max77705_parse_subled(dev, child, &info[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -298,12 +298,15 @@ static enum pca9532_state pca9532_getled(struct pca9532_led *led)
|
|||
struct i2c_client *client = led->client;
|
||||
struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
u8 maxleds = data->chip_info->num_leds;
|
||||
char reg;
|
||||
enum pca9532_state ret;
|
||||
int reg;
|
||||
enum pca9532_state ret = PCA9532_OFF;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
|
||||
ret = (reg & LED_MASK(led->id)) >> LED_SHIFT(led->id);
|
||||
if (reg >= 0)
|
||||
ret = (reg & LED_MASK(led->id)) >> LED_SHIFT(led->id);
|
||||
else
|
||||
dev_warn(&client->dev, "failed to read LED register: %d\n", reg);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,17 @@ struct pca955x_chipdef {
|
|||
int blink_div; /* PSC divider */
|
||||
};
|
||||
|
||||
struct pca955x_led {
|
||||
struct pca955x *pca955x;
|
||||
struct led_classdev led_cdev;
|
||||
int led_num; /* 0 .. 15 potentially */
|
||||
u32 type;
|
||||
enum led_default_state default_state;
|
||||
struct fwnode_handle *fwnode;
|
||||
};
|
||||
|
||||
#define led_to_pca955x(l) container_of(l, struct pca955x_led, led_cdev)
|
||||
|
||||
static const struct pca955x_chipdef pca955x_chipdefs[] = {
|
||||
[pca9550] = {
|
||||
.bits = 2,
|
||||
|
|
@ -114,7 +125,6 @@ static const struct pca955x_chipdef pca955x_chipdefs[] = {
|
|||
|
||||
struct pca955x {
|
||||
struct mutex lock;
|
||||
struct pca955x_led *leds;
|
||||
const struct pca955x_chipdef *chipdef;
|
||||
struct i2c_client *client;
|
||||
unsigned long active_blink;
|
||||
|
|
@ -123,22 +133,7 @@ struct pca955x {
|
|||
#ifdef CONFIG_LEDS_PCA955X_GPIO
|
||||
struct gpio_chip gpio;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pca955x_led {
|
||||
struct pca955x *pca955x;
|
||||
struct led_classdev led_cdev;
|
||||
int led_num; /* 0 .. 15 potentially */
|
||||
u32 type;
|
||||
enum led_default_state default_state;
|
||||
struct fwnode_handle *fwnode;
|
||||
};
|
||||
|
||||
#define led_to_pca955x(l) container_of(l, struct pca955x_led, led_cdev)
|
||||
|
||||
struct pca955x_platform_data {
|
||||
struct pca955x_led *leds;
|
||||
int num_leds;
|
||||
struct pca955x_led leds[];
|
||||
};
|
||||
|
||||
/* 8 bits per input register */
|
||||
|
|
@ -530,37 +525,27 @@ static int pca955x_gpio_direction_output(struct gpio_chip *gc,
|
|||
}
|
||||
#endif /* CONFIG_LEDS_PCA955X_GPIO */
|
||||
|
||||
static struct pca955x_platform_data *
|
||||
pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
|
||||
static int pca955x_parse_fwnode(struct pca955x *pca955x)
|
||||
{
|
||||
struct pca955x_platform_data *pdata;
|
||||
struct device *dev = &pca955x->client->dev;
|
||||
u8 bits = pca955x->chipdef->bits;
|
||||
struct pca955x_led *led;
|
||||
struct fwnode_handle *child;
|
||||
int count;
|
||||
|
||||
count = device_get_child_node_count(&client->dev);
|
||||
if (count > chip->bits)
|
||||
return ERR_PTR(-ENODEV);
|
||||
count = device_get_child_node_count(dev);
|
||||
if (count > bits)
|
||||
return -ENODEV;
|
||||
|
||||
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->leds = devm_kcalloc(&client->dev,
|
||||
chip->bits, sizeof(struct pca955x_led),
|
||||
GFP_KERNEL);
|
||||
if (!pdata->leds)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
device_for_each_child_node(&client->dev, child) {
|
||||
device_for_each_child_node(dev, child) {
|
||||
u32 reg;
|
||||
int res;
|
||||
|
||||
res = fwnode_property_read_u32(child, "reg", ®);
|
||||
if ((res != 0) || (reg >= chip->bits))
|
||||
if ((res != 0) || (reg >= bits))
|
||||
continue;
|
||||
|
||||
led = &pdata->leds[reg];
|
||||
led = &pca955x->leds[reg];
|
||||
led->type = PCA955X_TYPE_LED;
|
||||
led->fwnode = child;
|
||||
led->default_state = led_init_default_state_get(child);
|
||||
|
|
@ -568,9 +553,7 @@ pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
|
|||
fwnode_property_read_u32(child, "type", &led->type);
|
||||
}
|
||||
|
||||
pdata->num_leds = chip->bits;
|
||||
|
||||
return pdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca955x_probe(struct i2c_client *client)
|
||||
|
|
@ -584,7 +567,6 @@ static int pca955x_probe(struct i2c_client *client)
|
|||
u8 i, nls, psc0;
|
||||
u8 ls1[4];
|
||||
u8 ls2[4];
|
||||
struct pca955x_platform_data *pdata;
|
||||
bool keep_psc0 = false;
|
||||
bool set_default_label = false;
|
||||
char default_label[4];
|
||||
|
|
@ -595,12 +577,6 @@ static int pca955x_probe(struct i2c_client *client)
|
|||
return dev_err_probe(&client->dev, -ENODEV, "unknown chip\n");
|
||||
|
||||
adapter = client->adapter;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (!pdata) {
|
||||
pdata = pca955x_get_pdata(client, chip);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
|
||||
/* Make sure the slave address / chip type combo given is possible */
|
||||
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
|
||||
|
|
@ -616,22 +592,11 @@ static int pca955x_probe(struct i2c_client *client)
|
|||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EIO;
|
||||
|
||||
if (pdata->num_leds != chip->bits) {
|
||||
dev_err(&client->dev,
|
||||
"board info claims %d LEDs on a %u-bit chip\n",
|
||||
pdata->num_leds, chip->bits);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);
|
||||
pca955x = devm_kzalloc(&client->dev,
|
||||
struct_size(pca955x, leds, chip->bits), GFP_KERNEL);
|
||||
if (!pca955x)
|
||||
return -ENOMEM;
|
||||
|
||||
pca955x->leds = devm_kcalloc(&client->dev, chip->bits,
|
||||
sizeof(*pca955x_led), GFP_KERNEL);
|
||||
if (!pca955x->leds)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, pca955x);
|
||||
|
||||
mutex_init(&pca955x->lock);
|
||||
|
|
@ -639,6 +604,10 @@ static int pca955x_probe(struct i2c_client *client)
|
|||
pca955x->chipdef = chip;
|
||||
pca955x->blink_period = PCA955X_BLINK_DEFAULT_MS;
|
||||
|
||||
err = pca955x_parse_fwnode(pca955x);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
init_data.devname_mandatory = false;
|
||||
init_data.devicename = "pca955x";
|
||||
|
||||
|
|
@ -657,7 +626,6 @@ static int pca955x_probe(struct i2c_client *client)
|
|||
pca955x_led = &pca955x->leds[i];
|
||||
pca955x_led->led_num = i;
|
||||
pca955x_led->pca955x = pca955x;
|
||||
pca955x_led->type = pdata->leds[i].type;
|
||||
|
||||
switch (pca955x_led->type) {
|
||||
case PCA955X_TYPE_NONE:
|
||||
|
|
@ -671,16 +639,16 @@ static int pca955x_probe(struct i2c_client *client)
|
|||
led->brightness_get = pca955x_led_get;
|
||||
led->blink_set = pca955x_led_blink;
|
||||
|
||||
if (pdata->leds[i].default_state == LEDS_DEFSTATE_OFF)
|
||||
if (pca955x_led->default_state == LEDS_DEFSTATE_OFF)
|
||||
ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_OFF);
|
||||
else if (pdata->leds[i].default_state == LEDS_DEFSTATE_ON)
|
||||
else if (pca955x_led->default_state == LEDS_DEFSTATE_ON)
|
||||
ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_ON);
|
||||
else if (pca955x_ledstate(ls2[reg], bit) == PCA955X_LS_BLINK0) {
|
||||
keep_psc0 = true;
|
||||
set_bit(i, &pca955x->active_blink);
|
||||
}
|
||||
|
||||
init_data.fwnode = pdata->leds[i].fwnode;
|
||||
init_data.fwnode = pca955x_led->fwnode;
|
||||
|
||||
if (is_of_node(init_data.fwnode)) {
|
||||
if (to_of_node(init_data.fwnode)->name[0] ==
|
||||
|
|
|
|||
|
|
@ -208,10 +208,7 @@ static void pca963x_track_power_state(struct pca963x_led *led, unsigned int led_
|
|||
{
|
||||
unsigned long *leds_on = &led->chip->leds_on;
|
||||
|
||||
if (brightness)
|
||||
set_bit(led_num, leds_on);
|
||||
else
|
||||
clear_bit(led_num, leds_on);
|
||||
assign_bit(led_num, leds_on, brightness);
|
||||
}
|
||||
|
||||
static int pca963x_sync_power_state(struct pca963x_led *led, unsigned long cached_leds)
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ static void ich7_lpc_cleanup(struct device *dev)
|
|||
|
||||
/*
|
||||
* The OS has determined that the LPC of the Intel ICH7 Southbridge is present
|
||||
* so we can retrive the required operational information and prepare the GPIO.
|
||||
* so we can retrieve the required operational information and prepare the GPIO.
|
||||
*/
|
||||
static struct pci_dev *nas_gpio_pci_dev;
|
||||
static int ich7_lpc_probe(struct pci_dev *dev,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,11 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define ST1202_BLINK_DEFAULT_DELAY 500
|
||||
#define ST1202_CHAN_DISABLE_ALL 0x00
|
||||
#define ST1202_CHAN_ENABLE_HIGH 0x03
|
||||
#define ST1202_CHAN_ENABLE_LOW 0x02
|
||||
|
|
@ -23,7 +25,8 @@
|
|||
#define ST1202_CONFIG_REG_PATS BIT(7)
|
||||
/* PATSR: Pattern sequence runs (self-clear when sequence is finished) */
|
||||
#define ST1202_CONFIG_REG_PATSR BIT(6)
|
||||
#define ST1202_CONFIG_REG_SHFT BIT(3)
|
||||
/* SHFT: Phase-shift delay enable */
|
||||
#define ST1202_CONFIG_REG_PHASE_SHIFT BIT(3)
|
||||
#define ST1202_DEV_ENABLE 0x01
|
||||
#define ST1202_DEV_ENABLE_ON BIT(0)
|
||||
#define ST1202_DEV_ENABLE_RESET BIT(7)
|
||||
|
|
@ -57,7 +60,7 @@ static struct st1202_led *cdev_to_st1202_led(struct led_classdev *cdev)
|
|||
return container_of(cdev, struct st1202_led, led_cdev);
|
||||
}
|
||||
|
||||
static int st1202_read_reg(struct st1202_chip *chip, int reg, uint8_t *val)
|
||||
static int st1202_read_reg(struct st1202_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
int ret;
|
||||
|
|
@ -68,11 +71,11 @@ static int st1202_read_reg(struct st1202_chip *chip, int reg, uint8_t *val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
*val = (uint8_t)ret;
|
||||
*val = (u8)ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
|
||||
static int st1202_write_reg(struct st1202_chip *chip, int reg, u8 val)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
int ret;
|
||||
|
|
@ -84,11 +87,27 @@ static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t st1202_prescalar_to_miliseconds(unsigned int value)
|
||||
static u8 st1202_milliseconds_to_prescaler(unsigned int value)
|
||||
{
|
||||
return value / ST1202_MILLIS_PATTERN_DUR_MIN;
|
||||
}
|
||||
|
||||
/* The channel current register is 8 bits wide, whatever max-brightness says */
|
||||
static u8 st1202_iled_max(struct led_classdev *led_cdev)
|
||||
{
|
||||
return min_t(unsigned int, led_cdev->max_brightness, U8_MAX);
|
||||
}
|
||||
|
||||
static u16 st1202_brightness_to_pwm(int brightness, unsigned int max_brightness)
|
||||
{
|
||||
if (!max_brightness)
|
||||
return 0;
|
||||
|
||||
brightness = clamp_t(int, brightness, LED_OFF, max_brightness);
|
||||
|
||||
return brightness * ST1202_PATTERN_PWM_FULL / max_brightness;
|
||||
}
|
||||
|
||||
static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
|
||||
int pattern, unsigned int value)
|
||||
{
|
||||
|
|
@ -125,7 +144,7 @@ static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern,
|
|||
unsigned int value)
|
||||
{
|
||||
return st1202_write_reg(chip, (ST1202_PATTERN_DUR + pattern),
|
||||
st1202_prescalar_to_miliseconds(value));
|
||||
st1202_milliseconds_to_prescaler(value));
|
||||
}
|
||||
|
||||
static int __st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
|
||||
|
|
@ -160,27 +179,6 @@ static int __st1202_channel_set(struct st1202_chip *chip, int led_num, bool acti
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
|
||||
{
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
return __st1202_channel_set(chip, led_num, active);
|
||||
}
|
||||
|
||||
static void st1202_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
|
||||
struct st1202_chip *chip = led->chip;
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++)
|
||||
st1202_pwm_pattern_write(chip, led->led_num, patt, ST1202_PATTERN_PWM_FULL);
|
||||
st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
|
||||
__st1202_channel_set(chip, led->led_num, !!value);
|
||||
}
|
||||
|
||||
static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
|
||||
|
|
@ -197,8 +195,30 @@ static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
|
|||
static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
|
||||
{
|
||||
struct st1202_led *led = cdev_to_st1202_led(ldev);
|
||||
struct st1202_chip *chip = led->chip;
|
||||
int ret;
|
||||
|
||||
return st1202_channel_set(led->chip, led->led_num, !!value);
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
/*
|
||||
* The output of a channel is ILED x Pattern_PWM / 4095. Setting every
|
||||
* PWM slot to full scale makes it equal ILED whatever the state of the
|
||||
* sequencer, so the brightness takes effect without stopping the
|
||||
* sequencer, which is global and would disturb the other channels.
|
||||
*/
|
||||
for (int pattern = 0; pattern < ST1202_MAX_PATTERNS; pattern++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, pattern,
|
||||
ST1202_PATTERN_PWM_FULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num,
|
||||
min_t(unsigned int, value, st1202_iled_max(ldev)));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return __st1202_channel_set(chip, led->led_num, !!value);
|
||||
}
|
||||
|
||||
static int st1202_led_pattern_clear(struct led_classdev *ldev)
|
||||
|
|
@ -209,16 +229,25 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
|
|||
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
|
||||
ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, LED_OFF);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, patt, ST1202_PATTERN_PWM_FULL);
|
||||
ret = __st1202_channel_set(chip, led->led_num, false);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_PHASE_SHIFT);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
for (int pattern = 0; pattern < ST1202_MAX_PATTERNS; pattern++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, pattern,
|
||||
ST1202_PATTERN_PWM_FULL);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + patt, 0);
|
||||
ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + pattern, 0);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -227,34 +256,45 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
|
|||
}
|
||||
|
||||
static int st1202_led_pattern_set(struct led_classdev *ldev,
|
||||
struct led_pattern *pattern,
|
||||
struct led_pattern *patterns,
|
||||
u32 len, int repeat)
|
||||
{
|
||||
struct st1202_led *led = cdev_to_st1202_led(ldev);
|
||||
struct st1202_chip *chip = led->chip;
|
||||
unsigned int max_brightness;
|
||||
int ret;
|
||||
|
||||
max_brightness = st1202_iled_max(ldev);
|
||||
|
||||
if (len > ST1202_MAX_PATTERNS)
|
||||
return -EINVAL;
|
||||
|
||||
for (int patt = 0; patt < len; patt++) {
|
||||
if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
|
||||
pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
|
||||
for (int pattern = 0; pattern < len; pattern++) {
|
||||
if (patterns[pattern].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
|
||||
patterns[pattern].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_PHASE_SHIFT);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
for (int patt = 0; patt < len; patt++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
|
||||
for (int pattern = 0; pattern < len; pattern++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, pattern,
|
||||
st1202_brightness_to_pwm(patterns[pattern].brightness,
|
||||
max_brightness));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = st1202_duration_pattern_write(chip, patt, pattern[patt].delta_t);
|
||||
ret = st1202_duration_pattern_write(chip, pattern, patterns[pattern].delta_t);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int pattern = len; pattern < ST1202_MAX_PATTERNS; pattern++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, pattern, LED_OFF);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -263,18 +303,117 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
|
|||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, max_brightness);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = __st1202_channel_set(chip, led->led_num, true);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR |
|
||||
ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT));
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
|
||||
ST1202_CONFIG_REG_PATSR | ST1202_CONFIG_REG_PATS |
|
||||
ST1202_CONFIG_REG_PHASE_SHIFT);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st1202_blink_set(struct led_classdev *led_cdev,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
|
||||
struct st1202_chip *chip = led->chip;
|
||||
unsigned long on, off;
|
||||
int ret;
|
||||
|
||||
if (!*delay_on)
|
||||
*delay_on = ST1202_BLINK_DEFAULT_DELAY;
|
||||
if (!*delay_off)
|
||||
*delay_off = ST1202_BLINK_DEFAULT_DELAY;
|
||||
|
||||
on = *delay_on;
|
||||
off = *delay_off;
|
||||
|
||||
on = clamp_val(on, ST1202_MILLIS_PATTERN_DUR_MIN, ST1202_MILLIS_PATTERN_DUR_MAX);
|
||||
off = clamp_val(off, ST1202_MILLIS_PATTERN_DUR_MIN, ST1202_MILLIS_PATTERN_DUR_MAX);
|
||||
on = roundup(on, ST1202_MILLIS_PATTERN_DUR_MIN);
|
||||
off = roundup(off, ST1202_MILLIS_PATTERN_DUR_MIN);
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_PHASE_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Zero out PWM for all other active channels to prevent them from blinking */
|
||||
for (int chan = 0; chan < ST1202_MAX_LEDS; chan++) {
|
||||
if (!chip->leds[chan].is_active || chan == led->led_num)
|
||||
continue;
|
||||
|
||||
ret = st1202_pwm_pattern_write(chip, chan, 0, LED_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = st1202_pwm_pattern_write(chip, chan, 1, LED_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, 0, ST1202_PATTERN_PWM_FULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int pattern = 1; pattern < ST1202_MAX_PATTERNS; pattern++) {
|
||||
ret = st1202_pwm_pattern_write(chip, led->led_num, pattern, LED_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st1202_duration_pattern_write(chip, 0, on);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = st1202_duration_pattern_write(chip, 1, off);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int pattern = 2; pattern < ST1202_MAX_PATTERNS; pattern++) {
|
||||
ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + pattern, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_PATTERN_REP, U8_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, st1202_iled_max(led_cdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __st1202_channel_set(chip, led->led_num, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
|
||||
ST1202_CONFIG_REG_PATSR | ST1202_CONFIG_REG_PATS |
|
||||
ST1202_CONFIG_REG_PHASE_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*delay_on = on;
|
||||
*delay_off = off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st1202_fwnode_put(void *data)
|
||||
{
|
||||
fwnode_handle_put(data);
|
||||
}
|
||||
|
||||
static int st1202_dt_init(struct st1202_chip *chip)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
|
|
@ -294,14 +433,18 @@ static int st1202_dt_init(struct st1202_chip *chip)
|
|||
|
||||
led = &chip->leds[reg];
|
||||
led->is_active = true;
|
||||
led->fwnode = of_fwnode_handle(child);
|
||||
led->fwnode = fwnode_handle_get(of_fwnode_handle(child));
|
||||
|
||||
err = devm_add_action_or_reset(dev, st1202_fwnode_put, led->fwnode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
led->led_cdev.max_brightness = U8_MAX;
|
||||
led->led_cdev.brightness_set_blocking = st1202_led_set;
|
||||
led->led_cdev.pattern_set = st1202_led_pattern_set;
|
||||
led->led_cdev.pattern_clear = st1202_led_pattern_clear;
|
||||
led->led_cdev.default_trigger = "pattern";
|
||||
led->led_cdev.brightness_set = st1202_brightness_set;
|
||||
led->led_cdev.blink_set = st1202_blink_set;
|
||||
led->led_cdev.brightness_get = st1202_brightness_get;
|
||||
}
|
||||
|
||||
|
|
@ -330,7 +473,7 @@ static int st1202_setup(struct st1202_chip *chip)
|
|||
return ret;
|
||||
|
||||
/* Enable phase-shift delay feature */
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
|
||||
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_PHASE_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -342,7 +485,7 @@ static int st1202_setup(struct st1202_chip *chip)
|
|||
/* Duration of initialization */
|
||||
usleep_range(6500, 10000);
|
||||
|
||||
/* Deactivate all LEDS (channels) and activate only the ones found in Device Tree */
|
||||
/* Deactivate all LEDs (channels); each is enabled when it is lit */
|
||||
ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, ST1202_CHAN_DISABLE_ALL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -389,11 +532,6 @@ static int st1202_probe(struct i2c_client *client)
|
|||
if (!led->is_active)
|
||||
continue;
|
||||
|
||||
ret = st1202_channel_set(led->chip, led->led_num, true);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Failed to activate LED channel\n");
|
||||
|
||||
ret = st1202_led_pattern_clear(&led->led_cdev);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ static int syscon_led_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev_of_node(dev);
|
||||
struct device *parent;
|
||||
struct regmap *map;
|
||||
struct syscon_led *sled;
|
||||
|
|
@ -81,13 +80,13 @@ static int syscon_led_probe(struct platform_device *pdev)
|
|||
|
||||
sled->map = map;
|
||||
|
||||
if (of_property_read_u32(np, "reg", &sled->offset) &&
|
||||
of_property_read_u32(np, "offset", &sled->offset))
|
||||
if (device_property_read_u32(dev, "reg", &sled->offset) &&
|
||||
device_property_read_u32(dev, "offset", &sled->offset))
|
||||
return -EINVAL;
|
||||
if (of_property_read_u32(np, "mask", &sled->mask))
|
||||
if (device_property_read_u32(dev, "mask", &sled->mask))
|
||||
return -EINVAL;
|
||||
|
||||
init_data.fwnode = of_fwnode_handle(np);
|
||||
init_data.fwnode = dev_fwnode(dev);
|
||||
|
||||
state = led_init_default_state_get(init_data.fwnode);
|
||||
switch (state) {
|
||||
|
|
|
|||
|
|
@ -195,10 +195,16 @@ static void omnia_hwtrig_deactivate(struct led_classdev *cdev)
|
|||
err);
|
||||
}
|
||||
|
||||
static bool omnia_hwtrig_hw_offloaded(struct led_classdev *cdev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct led_trigger omnia_hw_trigger = {
|
||||
.name = "omnia-mcu",
|
||||
.activate = omnia_hwtrig_activate,
|
||||
.deactivate = omnia_hwtrig_deactivate,
|
||||
.hw_offloaded = omnia_hwtrig_hw_offloaded,
|
||||
.trigger_type = &omnia_hw_trigger_type,
|
||||
};
|
||||
|
||||
|
|
@ -251,6 +257,7 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
|
|||
* by LED class from the linux,default-trigger property.
|
||||
*/
|
||||
cdev->default_trigger = omnia_hw_trigger.name;
|
||||
cdev->hw_control_trigger = omnia_hw_trigger.name;
|
||||
|
||||
/* Put the LED into software mode */
|
||||
ret = omnia_cmd_write_u8(client, OMNIA_CMD_LED_MODE, OMNIA_CMD_LED_MODE_LED(led->reg) |
|
||||
|
|
|
|||
|
|
@ -21,14 +21,10 @@ void led_init_core(struct led_classdev *led_cdev);
|
|||
void led_stop_software_blink(struct led_classdev *led_cdev);
|
||||
void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value);
|
||||
void led_set_brightness_nosleep(struct led_classdev *led_cdev, unsigned int value);
|
||||
ssize_t led_trigger_read(struct file *filp, struct kobject *kobj,
|
||||
const struct bin_attribute *attr, char *buf,
|
||||
loff_t pos, size_t count);
|
||||
ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
|
||||
const struct bin_attribute *bin_attr, char *buf,
|
||||
loff_t pos, size_t count);
|
||||
void led_trigger_hw_control_changed_worker(struct work_struct *work);
|
||||
|
||||
extern struct rw_semaphore leds_list_lock;
|
||||
extern struct list_head leds_list;
|
||||
extern const struct attribute_group led_trigger_group;
|
||||
|
||||
#endif /* __LEDS_H_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -1342,7 +1343,7 @@ static int lpg_add_pwm(struct lpg *lpg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int lpg_parse_channel(struct lpg *lpg, struct device_node *np,
|
||||
static int lpg_parse_channel(struct lpg *lpg, struct fwnode_handle *fw,
|
||||
struct lpg_channel **channel)
|
||||
{
|
||||
struct lpg_channel *chan;
|
||||
|
|
@ -1350,17 +1351,18 @@ static int lpg_parse_channel(struct lpg *lpg, struct device_node *np,
|
|||
u32 reg;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "reg", ®);
|
||||
ret = fwnode_property_read_u32(fw, "reg", ®);
|
||||
if (ret || !reg || reg > lpg->num_channels)
|
||||
return dev_err_probe(lpg->dev, -EINVAL, "invalid \"reg\" of %pOFn\n", np);
|
||||
return dev_err_probe(lpg->dev, -EINVAL,
|
||||
"invalid \"reg\" of %s\n", fwnode_get_name(fw));
|
||||
|
||||
chan = &lpg->channels[reg - 1];
|
||||
chan->in_use = true;
|
||||
|
||||
ret = of_property_read_u32(np, "color", &color);
|
||||
ret = fwnode_property_read_u32(fw, "color", &color);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
return dev_err_probe(lpg->dev, ret,
|
||||
"failed to parse \"color\" of %pOF\n", np);
|
||||
"failed to parse \"color\" of %s\n", fwnode_get_name(fw));
|
||||
|
||||
chan->color = color;
|
||||
|
||||
|
|
@ -1369,25 +1371,26 @@ static int lpg_parse_channel(struct lpg *lpg, struct device_node *np,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
||||
static int lpg_add_led(struct lpg *lpg, struct fwnode_handle *fw)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
struct mc_subled *info;
|
||||
struct lpg_led *led;
|
||||
const char *trigger;
|
||||
const char *state;
|
||||
int num_channels;
|
||||
u32 color = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = of_property_read_u32(np, "color", &color);
|
||||
ret = fwnode_property_read_u32(fw, "color", &color);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
return dev_err_probe(lpg->dev, ret,
|
||||
"failed to parse \"color\" of %pOF\n", np);
|
||||
"failed to parse \"color\" of %s\n", fwnode_get_name(fw));
|
||||
|
||||
if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI)
|
||||
num_channels = of_get_available_child_count(np);
|
||||
num_channels = fwnode_get_child_node_count(fw);
|
||||
else
|
||||
num_channels = 1;
|
||||
|
||||
|
|
@ -1403,7 +1406,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
|||
if (!info)
|
||||
return -ENOMEM;
|
||||
i = 0;
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
fwnode_for_each_available_child_node_scoped(fw, child) {
|
||||
ret = lpg_parse_channel(lpg, child, &led->channels[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -1414,7 +1417,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
|||
}
|
||||
|
||||
led->mcdev.subled_info = info;
|
||||
led->mcdev.num_colors = num_channels;
|
||||
led->mcdev.num_colors = i;
|
||||
|
||||
cdev = &led->mcdev.led_cdev;
|
||||
cdev->brightness_set_blocking = lpg_brightness_mc_set;
|
||||
|
|
@ -1426,7 +1429,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
|||
cdev->pattern_clear = lpg_pattern_mc_clear;
|
||||
}
|
||||
} else {
|
||||
ret = lpg_parse_channel(lpg, np, &led->channels[0]);
|
||||
ret = lpg_parse_channel(lpg, fw, &led->channels[0]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1441,14 +1444,15 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
|||
}
|
||||
}
|
||||
|
||||
cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL);
|
||||
if (!fwnode_property_read_string(fw, "linux,default-trigger", &trigger))
|
||||
cdev->default_trigger = trigger;
|
||||
|
||||
if (lpg->lpg_chan_sdam)
|
||||
cdev->max_brightness = PPG_MAX_LED_BRIGHTNESS;
|
||||
else
|
||||
cdev->max_brightness = LPG_RESOLUTION_9BIT - 1;
|
||||
|
||||
if (!of_property_read_string(np, "default-state", &state) &&
|
||||
if (!fwnode_property_read_string(fw, "default-state", &state) &&
|
||||
!strcmp(state, "on"))
|
||||
cdev->brightness = cdev->max_brightness;
|
||||
else
|
||||
|
|
@ -1456,7 +1460,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
|||
|
||||
cdev->brightness_set_blocking(cdev, cdev->brightness);
|
||||
|
||||
init_data.fwnode = of_fwnode_handle(np);
|
||||
init_data.fwnode = fw;
|
||||
|
||||
if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI)
|
||||
ret = devm_led_classdev_multicolor_register_ext(lpg->dev, &led->mcdev, &init_data);
|
||||
|
|
@ -1638,8 +1642,8 @@ static int lpg_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for_each_available_child_of_node_scoped(pdev->dev.of_node, np) {
|
||||
ret = lpg_add_led(lpg, np);
|
||||
device_for_each_child_node_scoped(&pdev->dev, child) {
|
||||
ret = lpg_add_led(lpg, child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,16 @@ menuconfig LEDS_TRIGGERS
|
|||
|
||||
if LEDS_TRIGGERS
|
||||
|
||||
config LEDS_TRIGGERS_HW_CHANGED
|
||||
bool "LED hardware-initiated trigger transition support"
|
||||
default LEDS_BRIGHTNESS_HW_CHANGED
|
||||
help
|
||||
This option enables support for hardware initiated hardware control
|
||||
transitions, where the LED hardware autonomously switches between
|
||||
"none" (i.e., no trigger) and its private trigger.
|
||||
|
||||
See Documentation/leds/leds-class.rst for details.
|
||||
|
||||
config LEDS_TRIGGER_TIMER
|
||||
tristate "LED Timer Trigger"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -414,10 +414,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state)
|
||||
set_bit(bit, &mode);
|
||||
else
|
||||
clear_bit(bit, &mode);
|
||||
assign_bit(bit, &mode, state);
|
||||
|
||||
if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
|
||||
(test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
|
||||
|
|
@ -798,10 +795,18 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev)
|
|||
kfree(trigger_data);
|
||||
}
|
||||
|
||||
static bool netdev_trig_hw_offloaded(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);
|
||||
|
||||
return trigger_data->hw_control;
|
||||
}
|
||||
|
||||
static struct led_trigger netdev_led_trigger = {
|
||||
.name = "netdev",
|
||||
.activate = netdev_trig_activate,
|
||||
.deactivate = netdev_trig_deactivate,
|
||||
.hw_offloaded = netdev_trig_hw_offloaded,
|
||||
.groups = netdev_trig_groups,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
#define WLED3_SINK_REG_CURR_SINK_SHFT 5
|
||||
|
||||
/* WLED3 specific per-'string' registers below */
|
||||
#define WLED3_SINK_REG_BRIGHT(n) (0x40 + n)
|
||||
#define WLED3_SINK_REG_BRIGHT(n) (0x40 + (n * 0x2))
|
||||
|
||||
#define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10))
|
||||
#define WLED3_SINK_REG_STR_MOD_MASK BIT(7)
|
||||
|
|
@ -290,6 +290,50 @@ static int wled5_set_brightness(struct wled *wled, u16 brightness)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int wled_read_brightness(struct wled *wled)
|
||||
{
|
||||
u16 addr, mask;
|
||||
__le16 v;
|
||||
u32 val;
|
||||
int rc;
|
||||
|
||||
rc = regmap_read(wled->regmap, wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
|
||||
&val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (!(val & WLED3_CTRL_REG_MOD_EN_MASK))
|
||||
return 0;
|
||||
|
||||
switch (wled->version) {
|
||||
case 3:
|
||||
addr = wled->sink_addr +
|
||||
WLED3_SINK_REG_BRIGHT(wled->cfg.enabled_strings[0]);
|
||||
mask = WLED3_SINK_REG_BRIGHT_MAX;
|
||||
break;
|
||||
case 4:
|
||||
addr = wled->sink_addr +
|
||||
WLED4_SINK_REG_BRIGHT(wled->cfg.enabled_strings[0]);
|
||||
mask = WLED3_SINK_REG_BRIGHT_MAX;
|
||||
break;
|
||||
case 5:
|
||||
addr = wled->sink_addr + (wled->cfg.mod_sel == MOD_A ?
|
||||
WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB :
|
||||
WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB);
|
||||
mask = WLED5_SINK_REG_BRIGHT_MAX_15B;
|
||||
break;
|
||||
default:
|
||||
dev_err(wled->dev, "Invalid WLED version\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = regmap_bulk_read(wled->regmap, addr, &v, sizeof(v));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return min_t(u32, le16_to_cpu(v) & mask, wled->max_brightness);
|
||||
}
|
||||
|
||||
static void wled_ovp_work(struct work_struct *work)
|
||||
{
|
||||
struct wled *wled = container_of(work,
|
||||
|
|
@ -480,6 +524,18 @@ static int wled_update_status(struct backlight_device *bl)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int wled_get_brightness(struct backlight_device *bl)
|
||||
{
|
||||
struct wled *wled = bl_get_data(bl);
|
||||
int rc;
|
||||
|
||||
mutex_lock(&wled->lock);
|
||||
rc = wled_read_brightness(wled);
|
||||
mutex_unlock(&wled->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int wled4_cabc_config(struct wled *wled, bool enable)
|
||||
{
|
||||
int i, j, rc;
|
||||
|
|
@ -1627,7 +1683,6 @@ static int wled_configure_ovp_irq(struct wled *wled,
|
|||
struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
u32 val;
|
||||
|
||||
wled->ovp_irq = platform_get_irq_byname(pdev, "ovp");
|
||||
if (wled->ovp_irq < 0) {
|
||||
|
|
@ -1643,13 +1698,8 @@ static int wled_configure_ovp_irq(struct wled *wled,
|
|||
return 0;
|
||||
}
|
||||
|
||||
rc = regmap_read(wled->regmap, wled->ctrl_addr +
|
||||
WLED3_CTRL_REG_MOD_EN, &val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Keep OVP irq disabled until module is enabled */
|
||||
if (!(val & WLED3_CTRL_REG_MOD_EN_MASK))
|
||||
/* Keep the OVP irq disabled until the module is enabled */
|
||||
if (!wled->brightness)
|
||||
disable_irq(wled->ovp_irq);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1657,6 +1707,7 @@ static int wled_configure_ovp_irq(struct wled *wled,
|
|||
|
||||
static const struct backlight_ops wled_ops = {
|
||||
.update_status = wled_update_status,
|
||||
.get_brightness = wled_get_brightness,
|
||||
};
|
||||
|
||||
static int wled_probe(struct platform_device *pdev)
|
||||
|
|
@ -1680,6 +1731,7 @@ static int wled_probe(struct platform_device *pdev)
|
|||
|
||||
wled->regmap = regmap;
|
||||
wled->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, wled);
|
||||
|
||||
wled->version = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
if (!wled->version) {
|
||||
|
|
@ -1734,6 +1786,20 @@ static int wled_probe(struct platform_device *pdev)
|
|||
|
||||
INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work);
|
||||
|
||||
val = WLED_DEFAULT_BRIGHTNESS;
|
||||
of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
|
||||
|
||||
/*
|
||||
* The module may already be lit, either by the bootloader or by the
|
||||
* setup above. Start from what the hardware is driving, so that the
|
||||
* first brightness update does not step the brightness.
|
||||
*/
|
||||
rc = wled_read_brightness(wled);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
wled->brightness = rc;
|
||||
|
||||
rc = wled_configure_short_irq(wled, pdev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
|
@ -1742,12 +1808,9 @@ static int wled_probe(struct platform_device *pdev)
|
|||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
val = WLED_DEFAULT_BRIGHTNESS;
|
||||
of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.brightness = val;
|
||||
props.brightness = wled->brightness ?: val;
|
||||
props.max_brightness = wled->max_brightness;
|
||||
bl = devm_backlight_device_register(&pdev->dev, wled->name,
|
||||
&pdev->dev, wled,
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ struct led_classdev {
|
|||
#define LED_INIT_DEFAULT_TRIGGER BIT(23)
|
||||
#define LED_REJECT_NAME_CONFLICT BIT(24)
|
||||
#define LED_MULTI_COLOR BIT(25)
|
||||
#define LED_TRIG_HW_CHANGED BIT(26)
|
||||
|
||||
/* set_brightness_work / blink_timer flags, atomic, private. */
|
||||
unsigned long work_flags;
|
||||
|
|
@ -239,6 +240,11 @@ struct led_classdev {
|
|||
struct kernfs_node *brightness_hw_changed_kn;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS_HW_CHANGED
|
||||
bool trigger_hw_changed;
|
||||
struct work_struct trigger_hw_changed_work;
|
||||
#endif
|
||||
|
||||
/* Ensures consistent access to the LED class device */
|
||||
struct mutex led_access;
|
||||
};
|
||||
|
|
@ -485,6 +491,7 @@ struct led_trigger {
|
|||
const char *name;
|
||||
int (*activate)(struct led_classdev *led_cdev);
|
||||
void (*deactivate)(struct led_classdev *led_cdev);
|
||||
bool (*hw_offloaded)(struct led_classdev *led_cdev);
|
||||
|
||||
/* Brightness set by led_trigger_event */
|
||||
enum led_brightness brightness;
|
||||
|
|
@ -533,6 +540,9 @@ void led_trigger_blink_oneshot(struct led_trigger *trigger,
|
|||
void led_trigger_set_default(struct led_classdev *led_cdev);
|
||||
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger);
|
||||
void led_trigger_remove(struct led_classdev *led_cdev);
|
||||
void led_trigger_remove_hw_control(struct led_classdev *led_cdev);
|
||||
|
||||
bool led_trigger_is_hw_controlled(struct led_classdev *led_cdev);
|
||||
|
||||
static inline void led_set_trigger_data(struct led_classdev *led_cdev,
|
||||
void *trigger_data)
|
||||
|
|
@ -584,6 +594,13 @@ static inline int led_trigger_set(struct led_classdev *led_cdev,
|
|||
}
|
||||
|
||||
static inline void led_trigger_remove(struct led_classdev *led_cdev) {}
|
||||
static inline void led_trigger_remove_hw_control(struct led_classdev *led_cdev) {}
|
||||
|
||||
static inline bool led_trigger_is_hw_controlled(struct led_classdev *led_cdev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void led_set_trigger_data(struct led_classdev *led_cdev) {}
|
||||
static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
|
||||
{
|
||||
|
|
@ -598,6 +615,13 @@ led_trigger_get_brightness(const struct led_trigger *trigger)
|
|||
|
||||
#endif /* CONFIG_LEDS_TRIGGERS */
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS_HW_CHANGED
|
||||
void led_trigger_notify_hw_control_changed(struct led_classdev *led_cdev, bool activate);
|
||||
#else
|
||||
static inline void led_trigger_notify_hw_control_changed(struct led_classdev *led_cdev,
|
||||
bool activate) {}
|
||||
#endif
|
||||
|
||||
/* Trigger specific enum */
|
||||
enum led_trigger_netdev_modes {
|
||||
TRIGGER_NETDEV_LINK = 0,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user