- Core Frameworks

- Introduce ExpressWire library
 
  - New Drivers
    - Add support for ON Semiconductor NCP5623 RGB LED Driver
 
  - New Device Support
    - Add support for PM660L to Qualcomm's LPG driver
 
  - New Functionality
    - Dynamically load modules required for the default-trigger
    - Add some support for suspend and resume
    - Allow LEDs to remain lit during suspend
 
  - Fix-ups
    - Device Tree binding adaptions/conversions/creation
    - Fix include lists; alphabetise, remove unused, explicitly add used
    - Add new led_match_default_trigger to avoid duplication
    - Add module alias' to aid auto-loading
    - Default to hw_control if no others are specified
    - De-bloat the supported link speed attribute lists
    - Remove superfluous code and simplify overall
    - Constify some variables
 
  - Bug Fixes
    - Prevent kernel panic when renaming the net interface
    - Fix Kconfig related build errors
    - Ensure mutexes are unlocked prior to destroying them
    - Provide clean-up between state changes to avoid invalid state
    - Fix some broken kernel-doc headers
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmXzCvUACgkQUa+KL4f8
 d2Fh0A/+KQiuzluglEsnyG7gfb2771x9dQ9pIwaR68UkCwThNH8ico+NqUDs8Jur
 6jtfYfKcWIz3i5kbnWBDGJfEEiVuDGu8Zv9UFxzQViyWQqawkJWNMsYqL3KtfI4i
 Ujj2Ja1MsoqO7COngry9I+3sT6rEwdQJMrVfNAdvOYjlXwr3O8Z2NipPACEqutUr
 0gxKAEEbGOj3+s3UGInrGi9RGuOVBe9UNA2etmtie1kxkdowTxCNY94ukUf9tnvC
 WVXF8iOByUgVAxMh1ugSc27CTCV+VcDMYKKr9ABVhskI/pT3zMFoUCYY1EqhaOTF
 Q40+yFX8ERomNTgy1tbNf06PkzaN+NJ4P/SHFU79madfy4OM6QobpTSt7bBpaSEP
 gm3zuI1a353NPfAUZiIsTgv8jCh18w/adphTNsXY/4PnmkKF0+Pm57PJf8BDhlY3
 KiScK7WXhGS9G3wNpLH+7QBdWiON3oWYJhVK4ijEfgRpEDofv+W16GzcETkUsyQ1
 5DLu/W8wHN9zxHj1YXaitmnRjX3IMoltcIix8FI3YUKrx3m3twm2Vj5ZLziaPm83
 7rBBPyePXwIamLokiTPCfXOxygO7Qv6VAp1aCR6400R/rtjykziboqvT2o6OMpkS
 W/88631VIuL9jAaP/zdUHrle1NpKDiHs2MF0Rtj+0OOoMJyOC7k=
 =m2vY
 -----END PGP SIGNATURE-----

Merge tag 'leds-next-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds

Pull LED updates from Lee Jones:
 "Core Framework:
   - Introduce ExpressWire library

  New Drivers:
   - Add support for ON Semiconductor NCP5623 RGB LED Driver

  New Device Support:
   - Add support for PM660L to Qualcomm's LPG driver

  New Functionality:
   - Dynamically load modules required for the default-trigger
   - Add some support for suspend and resume
   - Allow LEDs to remain lit during suspend

  Fix-ups:
   - Device Tree binding adaptions/conversions/creation
   - Fix include lists; alphabetise, remove unused, explicitly add used
   - Add new led_match_default_trigger to avoid duplication
   - Add module alias' to aid auto-loading
   - Default to hw_control if no others are specified
   - De-bloat the supported link speed attribute lists
   - Remove superfluous code and simplify overall
   - Constify some variables

  Bug Fixes:
   - Prevent kernel panic when renaming the net interface
   - Fix Kconfig related build errors
   - Ensure mutexes are unlocked prior to destroying them
   - Provide clean-up between state changes to avoid invalid state
   - Fix some broken kernel-doc headers"

* tag 'leds-next-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (41 commits)
  leds: ncp5623: Add MS suffix to time defines
  leds: Add NCP5623 multi-led driver
  dt-bindings: leds: Add NCP5623 multi-LED Controller
  leds: mlxreg: Drop an excess struct mlxreg_led_data member
  leds: leds-mlxcpld: Fix struct mlxcpld_led_priv member name
  leds: lm3601x: Fix struct lm3601_led kernel-doc warnings
  leds: Fix ifdef check for gpio_led_register_device()
  dt-bindings: leds: qcom-lpg: Narrow nvmem for other variants
  dt-bindings: leds: qcom-lpg: Drop redundant qcom,pm8550-pwm in if:then:
  dt-bindings: leds: Add LED_FUNCTION_WAN_ONLINE for Internet access
  leds: sgm3140: Add missing timer cleanup and flash gpio control
  leds: expresswire: Don't depend on NEW_LEDS
  Revert "leds: Only descend into leds directory when CONFIG_NEW_LEDS is set"
  leds: aw2013: Unlock mutex before destroying it
  leds: qcom-lpg: Add QCOM_PBS dependency
  leds: rgb: leds-group-multicolor: Allow LEDs to stay on in suspend
  leds: trigger: netdev: Fix kernel panic on interface rename trig notify
  leds: qcom-lpg: Add PM660L configuration and compatible
  leds: spi-byte: Use devm_led_classdev_register_ext()
  leds: pca963x: Add support for suspend and resume
  ...
This commit is contained in:
Linus Torvalds 2024-03-14 10:38:25 -07:00
commit f5c31bcf60
33 changed files with 1033 additions and 188 deletions

View File

@ -88,6 +88,8 @@ Description:
speed of 10MBps of the named network device.
Setting this value also immediately changes the LED state.
Present only if the named network device supports 10Mbps link speed.
What: /sys/class/leds/<led>/link_100
Date: Jun 2023
KernelVersion: 6.5
@ -101,6 +103,8 @@ Description:
speed of 100Mbps of the named network device.
Setting this value also immediately changes the LED state.
Present only if the named network device supports 100Mbps link speed.
What: /sys/class/leds/<led>/link_1000
Date: Jun 2023
KernelVersion: 6.5
@ -114,6 +118,8 @@ Description:
speed of 1000Mbps of the named network device.
Setting this value also immediately changes the LED state.
Present only if the named network device supports 1000Mbps link speed.
What: /sys/class/leds/<led>/link_2500
Date: Nov 2023
KernelVersion: 6.8
@ -127,6 +133,8 @@ Description:
speed of 2500Mbps of the named network device.
Setting this value also immediately changes the LED state.
Present only if the named network device supports 2500Mbps link speed.
What: /sys/class/leds/<led>/link_5000
Date: Nov 2023
KernelVersion: 6.8
@ -140,6 +148,8 @@ Description:
speed of 5000Mbps of the named network device.
Setting this value also immediately changes the LED state.
Present only if the named network device supports 5000Mbps link speed.
What: /sys/class/leds/<led>/link_10000
Date: Nov 2023
KernelVersion: 6.8
@ -153,6 +163,8 @@ Description:
speed of 10000Mbps of the named network device.
Setting this value also immediately changes the LED state.
Present only if the named network device supports 10000Mbps link speed.
What: /sys/class/leds/<led>/half_duplex
Date: Jun 2023
KernelVersion: 6.5

View File

@ -1,11 +1,11 @@
What: /sys/class/leds/<led>/ttyname
What: /sys/class/leds/<tty_led>/ttyname
Date: Dec 2020
KernelVersion: 5.10
Contact: linux-leds@vger.kernel.org
Description:
Specifies the tty device name of the triggering tty
What: /sys/class/leds/<led>/rx
What: /sys/class/leds/<tty_led>/rx
Date: February 2024
KernelVersion: 6.8
Description:
@ -13,7 +13,7 @@ Description:
If set to 0, the LED will not blink on reception.
If set to 1 (default), the LED will blink on reception.
What: /sys/class/leds/<led>/tx
What: /sys/class/leds/<tty_led>/tx
Date: February 2024
KernelVersion: 6.8
Description:
@ -21,7 +21,7 @@ Description:
If set to 0, the LED will not blink on transmission.
If set to 1 (default), the LED will blink on transmission.
What: /sys/class/leds/<led>/cts
What: /sys/class/leds/<tty_led>/cts
Date: February 2024
KernelVersion: 6.8
Description:
@ -31,7 +31,7 @@ Description:
If set to 0 (default), the LED will not evaluate CTS.
If set to 1, the LED will evaluate CTS.
What: /sys/class/leds/<led>/dsr
What: /sys/class/leds/<tty_led>/dsr
Date: February 2024
KernelVersion: 6.8
Description:
@ -41,7 +41,7 @@ Description:
If set to 0 (default), the LED will not evaluate DSR.
If set to 1, the LED will evaluate DSR.
What: /sys/class/leds/<led>/dcd
What: /sys/class/leds/<tty_led>/dcd
Date: February 2024
KernelVersion: 6.8
Description:
@ -51,7 +51,7 @@ Description:
If set to 0 (default), the LED will not evaluate CAR (DCD).
If set to 1, the LED will evaluate CAR (DCD).
What: /sys/class/leds/<led>/rng
What: /sys/class/leds/<tty_led>/rng
Date: February 2024
KernelVersion: 6.8
Description:

View File

@ -11,7 +11,7 @@ maintainers:
description: >
The Qualcomm Light Pulse Generator consists of three different hardware blocks;
a ramp generator with lookup table, the light pulse generator and a three
a ramp generator with lookup table (LUT), the light pulse generator and a three
channel current sink. These blocks are found in a wide range of Qualcomm PMICs.
properties:
@ -63,6 +63,29 @@ properties:
- description: dtest line to attach
- description: flags for the attachment
nvmem:
description: >
This property is required for PMICs that supports PPG, which is when a
PMIC stores LPG per-channel data and pattern LUT in SDAM modules instead
of in a LUT peripheral. For PMICs, such as PM8350C, per-channel data
and pattern LUT is separated into 2 SDAM modules. In that case, phandles
to both SDAM modules need to be specified.
minItems: 1
maxItems: 2
nvmem-names:
minItems: 1
items:
- const: lpg_chan_sdam
- const: lut_sdam
qcom,pbs:
$ref: /schemas/types.yaml#/definitions/phandle
description: >
Phandle of the Qualcomm Programmable Boot Sequencer node (PBS).
PBS node is used to trigger LPG pattern sequences for PMICs that support
single SDAM PPG.
multi-led:
type: object
$ref: leds-class-multicolor.yaml#
@ -106,6 +129,52 @@ required:
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
enum:
- qcom,pm660l-lpg
- qcom,pm8150b-lpg
- qcom,pm8150l-lpg
- qcom,pm8916-pwm
- qcom,pm8941-lpg
- qcom,pm8994-lpg
- qcom,pmc8180c-lpg
- qcom,pmi8994-lpg
- qcom,pmi8998-lpg
- qcom,pmk8550-pwm
then:
properties:
nvmem: false
nvmem-names: false
- if:
properties:
compatible:
contains:
const: qcom,pmi632-lpg
then:
properties:
nvmem:
maxItems: 1
nvmem-names:
maxItems: 1
- if:
properties:
compatible:
contains:
enum:
- qcom,pm8350c-pwm
then:
properties:
nvmem:
minItems: 2
nvmem-names:
minItems: 2
examples:
- |
#include <dt-bindings/leds/common.h>
@ -191,4 +260,35 @@ examples:
compatible = "qcom,pm8916-pwm";
#pwm-cells = <2>;
};
- |
#include <dt-bindings/leds/common.h>
led-controller {
compatible = "qcom,pmi632-lpg";
#address-cells = <1>;
#size-cells = <0>;
#pwm-cells = <2>;
nvmem-names = "lpg_chan_sdam";
nvmem = <&pmi632_sdam_7>;
qcom,pbs = <&pmi632_pbs_client3>;
led@1 {
reg = <1>;
color = <LED_COLOR_ID_RED>;
label = "red";
};
led@2 {
reg = <2>;
color = <LED_COLOR_ID_GREEN>;
label = "green";
};
led@3 {
reg = <3>;
color = <LED_COLOR_ID_BLUE>;
label = "blue";
};
};
...

View File

@ -0,0 +1,96 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/onnn,ncp5623.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ON Semiconductor NCP5623 multi-LED Driver
maintainers:
- Abdel Alkuor <alkuor@gmail.com>
description:
NCP5623 Triple Output I2C Controlled LED Driver.
https://www.onsemi.com/pdf/datasheet/ncp5623-d.pdf
properties:
compatible:
enum:
- onnn,ncp5623
reg:
const: 0x38
multi-led:
type: object
$ref: leds-class-multicolor.yaml#
unevaluatedProperties: false
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^led@[0-2]$":
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
minimum: 0
maximum: 2
required:
- reg
- color
required:
- "#address-cells"
- "#size-cells"
required:
- compatible
- reg
- multi-led
additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
led-controller@38 {
compatible = "onnn,ncp5623";
reg = <0x38>;
multi-led {
color = <LED_COLOR_ID_RGB>;
#address-cells = <1>;
#size-cells = <0>;
led@0 {
reg = <0>;
color = <LED_COLOR_ID_RED>;
};
led@1 {
reg = <1>;
color = <LED_COLOR_ID_GREEN>;
};
led@2 {
reg = <2>;
color = <LED_COLOR_ID_BLUE>;
};
};
};
};

View File

@ -135,7 +135,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-y += mmc/
obj-y += ufs/
obj-$(CONFIG_MEMSTICK) += memstick/
obj-$(CONFIG_NEW_LEDS) += leds/
obj-y += leds/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/

View File

@ -6,6 +6,12 @@ config LEDS_GPIO_REGISTER
As this function is used by arch code it must not be compiled as a
module.
# This library does not depend on NEW_LEDS and must be independent so it can be
# selected from other subsystems (specifically backlight).
config LEDS_EXPRESSWIRE
bool
depends on GPIOLIB
menuconfig NEW_LEDS
bool "LED Support"
help
@ -399,7 +405,7 @@ config LEDS_LP3952
config LEDS_LP50XX
tristate "LED Support for TI LP5036/30/24/18/12/09 LED driver chip"
depends on LEDS_CLASS && REGMAP_I2C
depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
depends on LEDS_CLASS_MULTICOLOR
help
If you say yes here you get support for the Texas Instruments
LP5036, LP5030, LP5024, LP5018, LP5012 and LP5009 LED driver.
@ -410,7 +416,7 @@ config LEDS_LP50XX
config LEDS_LP55XX_COMMON
tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
depends on LEDS_CLASS
depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
depends on LEDS_CLASS_MULTICOLOR
depends on OF
depends on I2C
select FW_LOADER

View File

@ -52,8 +52,8 @@ config LEDS_MAX77693
config LEDS_MT6360
tristate "LED Support for Mediatek MT6360 PMIC"
depends on LEDS_CLASS && OF
depends on LEDS_CLASS_FLASH || !LEDS_CLASS_FLASH
depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
depends on LEDS_CLASS_FLASH
depends on LEDS_CLASS_MULTICOLOR
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
depends on MFD_MT6360
help

View File

@ -70,12 +70,11 @@ enum lm3601x_type {
};
/**
* struct lm3601x_led -
* struct lm3601x_led - private lm3601x LED data
* @fled_cdev: flash LED class device pointer
* @client: Pointer to the I2C client
* @regmap: Devices register map
* @lock: Lock for reading/writing the device
* @led_name: LED label for the Torch or IR LED
* @flash_timeout: the timeout for the flash
* @last_flag: last known flags register value
* @torch_current_max: maximum current for the torch

View File

@ -114,8 +114,11 @@ static int sgm3140_brightness_set(struct led_classdev *led_cdev,
"failed to enable regulator: %d\n", ret);
return ret;
}
gpiod_set_value_cansleep(priv->flash_gpio, 0);
gpiod_set_value_cansleep(priv->enable_gpio, 1);
} else {
del_timer_sync(&priv->powerdown_timer);
gpiod_set_value_cansleep(priv->flash_gpio, 0);
gpiod_set_value_cansleep(priv->enable_gpio, 0);
ret = regulator_disable(priv->vin_regulator);
if (ret) {

View File

@ -552,6 +552,12 @@ int led_classdev_register_ext(struct device *parent,
led_init_core(led_cdev);
#ifdef CONFIG_LEDS_TRIGGERS
/*
* If no default trigger was given and hw_control_trigger is set,
* make it the default trigger.
*/
if (!led_cdev->default_trigger && led_cdev->hw_control_trigger)
led_cdev->default_trigger = led_cdev->hw_control_trigger;
led_trigger_set_default(led_cdev);
#endif

View File

@ -23,7 +23,7 @@
* Nests outside led_cdev->trigger_lock
*/
static DECLARE_RWSEM(triggers_list_lock);
LIST_HEAD(trigger_list);
static LIST_HEAD(trigger_list);
/* Used by LED Class */
@ -247,9 +247,23 @@ void led_trigger_remove(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_trigger_remove);
static bool led_match_default_trigger(struct led_classdev *led_cdev,
struct led_trigger *trig)
{
if (!strcmp(led_cdev->default_trigger, trig->name) &&
trigger_relevant(led_cdev, trig)) {
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
led_trigger_set(led_cdev, trig);
return true;
}
return false;
}
void led_trigger_set_default(struct led_classdev *led_cdev)
{
struct led_trigger *trig;
bool found = false;
if (!led_cdev->default_trigger)
return;
@ -257,15 +271,19 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
down_read(&triggers_list_lock);
down_write(&led_cdev->trigger_lock);
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(led_cdev->default_trigger, trig->name) &&
trigger_relevant(led_cdev, trig)) {
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
led_trigger_set(led_cdev, trig);
found = led_match_default_trigger(led_cdev, trig);
if (found)
break;
}
}
up_write(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
/*
* If default trigger wasn't found, maybe trigger module isn't loaded yet.
* Once loaded it will re-probe with all led_cdev's.
*/
if (!found)
request_module_nowait("ledtrig:%s", led_cdev->default_trigger);
}
EXPORT_SYMBOL_GPL(led_trigger_set_default);
@ -297,12 +315,8 @@ int led_trigger_register(struct led_trigger *trig)
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (!led_cdev->trigger && led_cdev->default_trigger &&
!strcmp(led_cdev->default_trigger, trig->name) &&
trigger_relevant(led_cdev, trig)) {
led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
led_trigger_set(led_cdev, trig);
}
if (!led_cdev->trigger && led_cdev->default_trigger)
led_match_default_trigger(led_cdev, trig);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);

View File

@ -282,7 +282,7 @@ static int aw200xx_set_imax(const struct aw200xx *const chip,
u32 led_imax_uA)
{
u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA);
u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16};
static const u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16};
u32 gccr_imax = UINT_MAX;
u32 cur_imax = 0;
int i;

View File

@ -405,6 +405,7 @@ error_reg:
chip->regulators);
error:
mutex_unlock(&chip->mutex);
mutex_destroy(&chip->mutex);
return ret;
}

View File

@ -77,7 +77,7 @@ struct mlxcpld_param {
/**
* struct mlxcpld_led_priv - LED private data:
* @cled: LED class device instance
* @cdev: LED class device instance
* @param: LED CPLD access parameters
**/
struct mlxcpld_led_priv {

View File

@ -29,7 +29,6 @@
* @data: led configuration data;
* @led_cdev: led class data;
* @base_color: base led color (other colors have constant offset from base);
* @led_data: led data;
* @data_parent: pointer to private device control data of parent;
* @led_cdev_name: class device name
*/

View File

@ -39,6 +39,7 @@
#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */
#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
#define PCA963X_MODE1_SLEEP 0x04 /* Normal mode or Low Power mode, oscillator off */
#define PCA963X_MODE2_OUTDRV 0x04 /* Open-drain or totem pole */
#define PCA963X_MODE2_INVRT 0x10 /* Normal or inverted direction */
#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */
@ -380,6 +381,32 @@ err:
return ret;
}
static int pca963x_suspend(struct device *dev)
{
struct pca963x *chip = dev_get_drvdata(dev);
u8 reg;
reg = i2c_smbus_read_byte_data(chip->client, PCA963X_MODE1);
reg = reg | BIT(PCA963X_MODE1_SLEEP);
i2c_smbus_write_byte_data(chip->client, PCA963X_MODE1, reg);
return 0;
}
static int pca963x_resume(struct device *dev)
{
struct pca963x *chip = dev_get_drvdata(dev);
u8 reg;
reg = i2c_smbus_read_byte_data(chip->client, PCA963X_MODE1);
reg = reg & ~BIT(PCA963X_MODE1_SLEEP);
i2c_smbus_write_byte_data(chip->client, PCA963X_MODE1, reg);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(pca963x_pm, pca963x_suspend, pca963x_resume);
static const struct of_device_id of_pca963x_match[] = {
{ .compatible = "nxp,pca9632", },
{ .compatible = "nxp,pca9633", },
@ -430,6 +457,7 @@ static struct i2c_driver pca963x_driver = {
.driver = {
.name = "leds-pca963x",
.of_match_table = of_pca963x_match,
.pm = pm_sleep_ptr(&pca963x_pm)
},
.probe = pca963x_probe,
.id_table = pca963x_id,

View File

@ -83,7 +83,7 @@ static int spi_byte_probe(struct spi_device *spi)
struct device_node *child;
struct device *dev = &spi->dev;
struct spi_byte_led *led;
const char *name = "leds-spi-byte::";
struct led_init_data init_data = {};
const char *state;
int ret;
@ -97,12 +97,9 @@ static int spi_byte_probe(struct spi_device *spi)
if (!led)
return -ENOMEM;
of_property_read_string(child, "label", &name);
strscpy(led->name, name, sizeof(led->name));
led->spi = spi;
mutex_init(&led->mutex);
led->cdef = device_get_match_data(dev);
led->ldev.name = led->name;
led->ldev.brightness = LED_OFF;
led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value;
led->ldev.brightness_set_blocking = spi_byte_brightness_set_blocking;
@ -120,7 +117,11 @@ static int spi_byte_probe(struct spi_device *spi)
spi_byte_brightness_set_blocking(&led->ldev,
led->ldev.brightness);
ret = devm_led_classdev_register(&spi->dev, &led->ldev);
init_data.fwnode = of_fwnode_handle(child);
init_data.devicename = "leds-spi-byte";
init_data.default_label = ":";
ret = devm_led_classdev_register_ext(&spi->dev, &led->ldev, &init_data);
if (ret) {
mutex_destroy(&led->mutex);
return ret;

View File

@ -30,7 +30,6 @@ ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
extern struct rw_semaphore leds_list_lock;
extern struct list_head leds_list;
extern struct list_head trigger_list;
extern const char * const led_colors[LED_COLOR_ID_MAX];
#endif /* __LEDS_H_INCLUDED */

View File

@ -27,6 +27,17 @@ config LEDS_KTD202X
To compile this driver as a module, choose M here: the module
will be called leds-ktd202x.
config LEDS_NCP5623
tristate "LED support for NCP5623"
depends on I2C
depends on OF
help
This option enables support for ON semiconductor NCP5623
Triple Output I2C Controlled RGB LED Driver.
To compile this driver as a module, choose M here: the module
will be called leds-ncp5623.
config LEDS_PWM_MULTICOLOR
tristate "PWM driven multi-color LED Support"
depends on PWM
@ -41,6 +52,7 @@ config LEDS_QCOM_LPG
tristate "LED support for Qualcomm LPG"
depends on OF
depends on PWM
depends on QCOM_PBS || !QCOM_PBS
depends on SPMI
help
This option enables support for the Light Pulse Generator found in a

View File

@ -2,6 +2,7 @@
obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o
obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o
obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o

View File

@ -69,7 +69,7 @@ static int leds_gmc_probe(struct platform_device *pdev)
struct mc_subled *subled;
struct leds_multicolor *priv;
unsigned int max_brightness = 0;
int i, ret, count = 0;
int i, ret, count = 0, common_flags = 0;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@ -91,6 +91,7 @@ static int leds_gmc_probe(struct platform_device *pdev)
if (!priv->monochromatics)
return -ENOMEM;
common_flags |= led_cdev->flags;
priv->monochromatics[count] = led_cdev;
max_brightness = max(max_brightness, led_cdev->max_brightness);
@ -114,12 +115,15 @@ static int leds_gmc_probe(struct platform_device *pdev)
/* Initialise the multicolor's LED class device */
cdev = &priv->mc_cdev.led_cdev;
cdev->flags = LED_CORE_SUSPENDRESUME;
cdev->brightness_set_blocking = leds_gmc_set;
cdev->max_brightness = max_brightness;
cdev->color = LED_COLOR_ID_MULTI;
priv->mc_cdev.num_colors = count;
/* we only need suspend/resume if a sub-led requests it */
if (common_flags & LED_CORE_SUSPENDRESUME)
cdev->flags = LED_CORE_SUSPENDRESUME;
init_data.fwnode = dev_fwnode(dev);
ret = devm_led_classdev_multicolor_register_ext(dev, &priv->mc_cdev, &init_data);
if (ret)

View File

@ -0,0 +1,271 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* NCP5623 Multi-LED Driver
*
* Author: Abdel Alkuor <alkuor@gmail.com>
* Datasheet: https://www.onsemi.com/pdf/datasheet/ncp5623-d.pdf
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/led-class-multicolor.h>
#define NCP5623_FUNCTION_OFFSET 0x5
#define NCP5623_REG(x) ((x) << NCP5623_FUNCTION_OFFSET)
#define NCP5623_SHUTDOWN_REG NCP5623_REG(0x0)
#define NCP5623_ILED_REG NCP5623_REG(0x1)
#define NCP5623_PWM_REG(index) NCP5623_REG(0x2 + (index))
#define NCP5623_UPWARD_STEP_REG NCP5623_REG(0x5)
#define NCP5623_DOWNWARD_STEP_REG NCP5623_REG(0x6)
#define NCP5623_DIMMING_TIME_REG NCP5623_REG(0x7)
#define NCP5623_MAX_BRIGHTNESS 0x1f
#define NCP5623_MAX_DIM_TIME_MS 240
#define NCP5623_DIM_STEP_MS 8
struct ncp5623 {
struct i2c_client *client;
struct led_classdev_mc mc_dev;
struct mutex lock;
int current_brightness;
unsigned long delay;
};
static int ncp5623_write(struct i2c_client *client, u8 reg, u8 data)
{
return i2c_smbus_write_byte_data(client, reg | data, 0);
}
static int ncp5623_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
struct ncp5623 *ncp = container_of(mc_cdev, struct ncp5623, mc_dev);
int ret;
guard(mutex)(&ncp->lock);
if (ncp->delay && time_is_after_jiffies(ncp->delay))
return -EBUSY;
ncp->delay = 0;
for (int i = 0; i < mc_cdev->num_colors; i++) {
ret = ncp5623_write(ncp->client,
NCP5623_PWM_REG(mc_cdev->subled_info[i].channel),
min(mc_cdev->subled_info[i].intensity,
NCP5623_MAX_BRIGHTNESS));
if (ret)
return ret;
}
ret = ncp5623_write(ncp->client, NCP5623_DIMMING_TIME_REG, 0);
if (ret)
return ret;
ret = ncp5623_write(ncp->client, NCP5623_ILED_REG, brightness);
if (ret)
return ret;
ncp->current_brightness = brightness;
return 0;
}
static int ncp5623_pattern_set(struct led_classdev *cdev,
struct led_pattern *pattern,
u32 len, int repeat)
{
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
struct ncp5623 *ncp = container_of(mc_cdev, struct ncp5623, mc_dev);
int brightness_diff;
u8 reg;
int ret;
guard(mutex)(&ncp->lock);
if (ncp->delay && time_is_after_jiffies(ncp->delay))
return -EBUSY;
ncp->delay = 0;
if (pattern[0].delta_t > NCP5623_MAX_DIM_TIME_MS ||
(pattern[0].delta_t % NCP5623_DIM_STEP_MS) != 0)
return -EINVAL;
brightness_diff = pattern[0].brightness - ncp->current_brightness;
if (brightness_diff == 0)
return 0;
if (pattern[0].delta_t) {
if (brightness_diff > 0)
reg = NCP5623_UPWARD_STEP_REG;
else
reg = NCP5623_DOWNWARD_STEP_REG;
} else {
reg = NCP5623_ILED_REG;
}
ret = ncp5623_write(ncp->client, reg,
min(pattern[0].brightness, NCP5623_MAX_BRIGHTNESS));
if (ret)
return ret;
ret = ncp5623_write(ncp->client,
NCP5623_DIMMING_TIME_REG,
pattern[0].delta_t / NCP5623_DIM_STEP_MS);
if (ret)
return ret;
/*
* During testing, when the brightness difference is 1, for some
* unknown reason, the time factor it takes to change to the new
* value is the longest time possible. Otherwise, the time factor
* is simply the brightness difference.
*
* For example:
* current_brightness = 20 and new_brightness = 21 then the time it
* takes to set the new brightness increments to the maximum possible
* brightness from 20 then from 0 to 21.
* time_factor = max_brightness - 20 + 21
*/
if (abs(brightness_diff) == 1)
ncp->delay = NCP5623_MAX_BRIGHTNESS + brightness_diff;
else
ncp->delay = abs(brightness_diff);
ncp->delay = msecs_to_jiffies(ncp->delay * pattern[0].delta_t) + jiffies;
ncp->current_brightness = pattern[0].brightness;
return 0;
}
static int ncp5623_pattern_clear(struct led_classdev *led_cdev)
{
return 0;
}
static int ncp5623_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct fwnode_handle *mc_node, *led_node;
struct led_init_data init_data = { };
int num_subleds = 0;
struct ncp5623 *ncp;
struct mc_subled *subled_info;
u32 color_index;
u32 reg;
int ret;
ncp = devm_kzalloc(dev, sizeof(*ncp), GFP_KERNEL);
if (!ncp)
return -ENOMEM;
ncp->client = client;
mc_node = device_get_named_child_node(dev, "multi-led");
if (!mc_node)
return -EINVAL;
fwnode_for_each_child_node(mc_node, led_node)
num_subleds++;
subled_info = devm_kcalloc(dev, num_subleds, sizeof(*subled_info), GFP_KERNEL);
if (!subled_info) {
ret = -ENOMEM;
goto release_mc_node;
}
fwnode_for_each_available_child_node(mc_node, led_node) {
ret = fwnode_property_read_u32(led_node, "color", &color_index);
if (ret) {
fwnode_handle_put(led_node);
goto release_mc_node;
}
ret = fwnode_property_read_u32(led_node, "reg", &reg);
if (ret) {
fwnode_handle_put(led_node);
goto release_mc_node;
}
subled_info[ncp->mc_dev.num_colors].channel = reg;
subled_info[ncp->mc_dev.num_colors++].color_index = color_index;
}
init_data.fwnode = mc_node;
ncp->mc_dev.led_cdev.max_brightness = NCP5623_MAX_BRIGHTNESS;
ncp->mc_dev.subled_info = subled_info;
ncp->mc_dev.led_cdev.brightness_set_blocking = ncp5623_brightness_set;
ncp->mc_dev.led_cdev.pattern_set = ncp5623_pattern_set;
ncp->mc_dev.led_cdev.pattern_clear = ncp5623_pattern_clear;
ncp->mc_dev.led_cdev.default_trigger = "pattern";
mutex_init(&ncp->lock);
i2c_set_clientdata(client, ncp);
ret = led_classdev_multicolor_register_ext(dev, &ncp->mc_dev, &init_data);
if (ret)
goto destroy_lock;
return 0;
destroy_lock:
mutex_destroy(&ncp->lock);
release_mc_node:
fwnode_handle_put(mc_node);
return ret;
}
static void ncp5623_remove(struct i2c_client *client)
{
struct ncp5623 *ncp = i2c_get_clientdata(client);
mutex_lock(&ncp->lock);
ncp->delay = 0;
mutex_unlock(&ncp->lock);
ncp5623_write(client, NCP5623_DIMMING_TIME_REG, 0);
led_classdev_multicolor_unregister(&ncp->mc_dev);
mutex_destroy(&ncp->lock);
}
static void ncp5623_shutdown(struct i2c_client *client)
{
struct ncp5623 *ncp = i2c_get_clientdata(client);
if (!(ncp->mc_dev.led_cdev.flags & LED_RETAIN_AT_SHUTDOWN))
ncp5623_write(client, NCP5623_SHUTDOWN_REG, 0);
mutex_destroy(&ncp->lock);
}
static const struct of_device_id ncp5623_id[] = {
{ .compatible = "onnn,ncp5623" },
{ }
};
MODULE_DEVICE_TABLE(of, ncp5623_id);
static struct i2c_driver ncp5623_i2c_driver = {
.driver = {
.name = "ncp5623",
.of_match_table = ncp5623_id,
},
.probe = ncp5623_probe,
.remove = ncp5623_remove,
.shutdown = ncp5623_shutdown,
};
module_i2c_driver(ncp5623_i2c_driver);
MODULE_AUTHOR("Abdel Alkuor <alkuor@gmail.com>");
MODULE_DESCRIPTION("NCP5623 Multi-LED driver");
MODULE_LICENSE("GPL");

View File

@ -8,11 +8,13 @@
#include <linux/bitfield.h>
#include <linux/led-class-multicolor.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/soc/qcom/qcom-pbs.h>
#define LPG_SUBTYPE_REG 0x05
#define LPG_SUBTYPE_LPG 0x2
@ -39,6 +41,10 @@
#define PWM_SEC_ACCESS_REG 0xd0
#define PWM_DTEST_REG(x) (0xe2 + (x) - 1)
#define SDAM_REG_PBS_SEQ_EN 0x42
#define SDAM_PBS_TRIG_SET 0xe5
#define SDAM_PBS_TRIG_CLR 0xe6
#define TRI_LED_SRC_SEL 0x45
#define TRI_LED_EN_CTL 0x46
#define TRI_LED_ATC_CTL 0x47
@ -48,9 +54,31 @@
#define LPG_RESOLUTION_9BIT BIT(9)
#define LPG_RESOLUTION_15BIT BIT(15)
#define PPG_MAX_LED_BRIGHTNESS 255
#define LPG_MAX_M 7
#define LPG_MAX_PREDIV 6
#define DEFAULT_TICK_DURATION_US 7800
#define RAMP_STEP_DURATION(x) (((x) * 1000 / DEFAULT_TICK_DURATION_US) & 0xff)
#define SDAM_MAX_DEVICES 2
/* LPG common config settings for PPG */
#define SDAM_START_BASE 0x40
#define SDAM_REG_RAMP_STEP_DURATION 0x47
#define SDAM_LUT_SDAM_LUT_PATTERN_OFFSET 0x45
#define SDAM_LPG_SDAM_LUT_PATTERN_OFFSET 0x80
/* LPG per channel config settings for PPG */
#define SDAM_LUT_EN_OFFSET 0x0
#define SDAM_PATTERN_CONFIG_OFFSET 0x1
#define SDAM_END_INDEX_OFFSET 0x3
#define SDAM_START_INDEX_OFFSET 0x4
#define SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET 0x6
#define SDAM_PAUSE_HI_MULTIPLIER_OFFSET 0x8
#define SDAM_PAUSE_LO_MULTIPLIER_OFFSET 0x9
struct lpg_channel;
struct lpg_data;
@ -64,6 +92,10 @@ struct lpg_data;
* @lut_base: base address of the LUT block (optional)
* @lut_size: number of entries in the LUT block
* @lut_bitmap: allocation bitmap for LUT entries
* @pbs_dev: PBS device
* @lpg_chan_sdam: LPG SDAM peripheral device
* @lut_sdam: LUT SDAM peripheral device
* @pbs_en_bitmap: bitmap for tracking PBS triggers
* @triled_base: base address of the TRILED block (optional)
* @triled_src: power-source for the TRILED
* @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register
@ -85,6 +117,11 @@ struct lpg {
u32 lut_size;
unsigned long *lut_bitmap;
struct pbs_dev *pbs_dev;
struct nvmem_device *lpg_chan_sdam;
struct nvmem_device *lut_sdam;
unsigned long pbs_en_bitmap;
u32 triled_base;
u32 triled_src;
bool triled_has_atc_ctl;
@ -101,6 +138,7 @@ struct lpg {
* @triled_mask: mask in TRILED to enable this channel
* @lut_mask: mask in LUT to start pattern generator for this channel
* @subtype: PMIC hardware block subtype
* @sdam_offset: channel offset in LPG SDAM
* @in_use: channel is exposed to LED framework
* @color: color of the LED attached to this channel
* @dtest_line: DTEST line for output, or 0 if disabled
@ -129,6 +167,7 @@ struct lpg_channel {
unsigned int triled_mask;
unsigned int lut_mask;
unsigned int subtype;
u32 sdam_offset;
bool in_use;
@ -178,10 +217,12 @@ struct lpg_led {
/**
* struct lpg_channel_data - per channel initialization data
* @sdam_offset: Channel offset in LPG SDAM
* @base: base address for PWM channel registers
* @triled_mask: bitmask for controlling this channel in TRILED
*/
struct lpg_channel_data {
unsigned int sdam_offset;
unsigned int base;
u8 triled_mask;
};
@ -206,6 +247,65 @@ struct lpg_data {
const struct lpg_channel_data *channels;
};
#define PBS_SW_TRIG_BIT BIT(0)
static int lpg_clear_pbs_trigger(struct lpg *lpg, unsigned int lut_mask)
{
u8 val = 0;
int rc;
lpg->pbs_en_bitmap &= (~lut_mask);
if (!lpg->pbs_en_bitmap) {
rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val);
if (rc < 0)
return rc;
if (lpg->lut_sdam) {
val = PBS_SW_TRIG_BIT;
rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_PBS_TRIG_CLR, 1, &val);
if (rc < 0)
return rc;
}
}
return 0;
}
static int lpg_set_pbs_trigger(struct lpg *lpg, unsigned int lut_mask)
{
u8 val = PBS_SW_TRIG_BIT;
int rc;
if (!lpg->pbs_en_bitmap) {
rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val);
if (rc < 0)
return rc;
if (lpg->lut_sdam) {
rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_PBS_TRIG_SET, 1, &val);
if (rc < 0)
return rc;
} else {
rc = qcom_pbs_trigger_event(lpg->pbs_dev, val);
if (rc < 0)
return rc;
}
}
lpg->pbs_en_bitmap |= lut_mask;
return 0;
}
static int lpg_sdam_configure_triggers(struct lpg_channel *chan, u8 set_trig)
{
u32 addr = SDAM_LUT_EN_OFFSET + chan->sdam_offset;
if (!chan->lpg->lpg_chan_sdam)
return 0;
return nvmem_device_write(chan->lpg->lpg_chan_sdam, addr, 1, &set_trig);
}
static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable)
{
/* Skip if we don't have a triled block */
@ -216,6 +316,47 @@ static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable)
mask, enable);
}
static int lpg_lut_store_sdam(struct lpg *lpg, struct led_pattern *pattern,
size_t len, unsigned int *lo_idx, unsigned int *hi_idx)
{
unsigned int idx;
u8 brightness;
int i, rc;
u16 addr;
if (len > lpg->lut_size) {
dev_err(lpg->dev, "Pattern length (%zu) exceeds maximum pattern length (%d)\n",
len, lpg->lut_size);
return -EINVAL;
}
idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, 0, len, 0);
if (idx >= lpg->lut_size)
return -ENOSPC;
for (i = 0; i < len; i++) {
brightness = pattern[i].brightness;
if (lpg->lut_sdam) {
addr = SDAM_LUT_SDAM_LUT_PATTERN_OFFSET + i + idx;
rc = nvmem_device_write(lpg->lut_sdam, addr, 1, &brightness);
} else {
addr = SDAM_LPG_SDAM_LUT_PATTERN_OFFSET + i + idx;
rc = nvmem_device_write(lpg->lpg_chan_sdam, addr, 1, &brightness);
}
if (rc < 0)
return rc;
}
bitmap_set(lpg->lut_bitmap, idx, len);
*lo_idx = idx;
*hi_idx = idx + len - 1;
return 0;
}
static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern,
size_t len, unsigned int *lo_idx, unsigned int *hi_idx)
{
@ -256,6 +397,9 @@ static void lpg_lut_free(struct lpg *lpg, unsigned int lo_idx, unsigned int hi_i
static int lpg_lut_sync(struct lpg *lpg, unsigned int mask)
{
if (!lpg->lut_base)
return 0;
return regmap_write(lpg->map, lpg->lut_base + RAMP_CONTROL_REG, mask);
}
@ -462,6 +606,49 @@ static void lpg_apply_pwm_value(struct lpg_channel *chan)
#define LPG_PATTERN_CONFIG_PAUSE_HI BIT(1)
#define LPG_PATTERN_CONFIG_PAUSE_LO BIT(0)
static void lpg_sdam_apply_lut_control(struct lpg_channel *chan)
{
struct nvmem_device *lpg_chan_sdam = chan->lpg->lpg_chan_sdam;
unsigned int lo_idx = chan->pattern_lo_idx;
unsigned int hi_idx = chan->pattern_hi_idx;
u8 val = 0, conf = 0, lut_offset = 0;
unsigned int hi_pause, lo_pause;
struct lpg *lpg = chan->lpg;
if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx)
return;
hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, chan->ramp_tick_ms);
lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, chan->ramp_tick_ms);
if (!chan->ramp_oneshot)
conf |= LPG_PATTERN_CONFIG_REPEAT;
if (chan->ramp_hi_pause_ms && lpg->lut_sdam)
conf |= LPG_PATTERN_CONFIG_PAUSE_HI;
if (chan->ramp_lo_pause_ms && lpg->lut_sdam)
conf |= LPG_PATTERN_CONFIG_PAUSE_LO;
if (lpg->lut_sdam) {
lut_offset = SDAM_LUT_SDAM_LUT_PATTERN_OFFSET - SDAM_START_BASE;
hi_idx += lut_offset;
lo_idx += lut_offset;
}
nvmem_device_write(lpg_chan_sdam, SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, 1, &val);
nvmem_device_write(lpg_chan_sdam, SDAM_PATTERN_CONFIG_OFFSET + chan->sdam_offset, 1, &conf);
nvmem_device_write(lpg_chan_sdam, SDAM_END_INDEX_OFFSET + chan->sdam_offset, 1, &hi_idx);
nvmem_device_write(lpg_chan_sdam, SDAM_START_INDEX_OFFSET + chan->sdam_offset, 1, &lo_idx);
val = RAMP_STEP_DURATION(chan->ramp_tick_ms);
nvmem_device_write(lpg_chan_sdam, SDAM_REG_RAMP_STEP_DURATION, 1, &val);
if (lpg->lut_sdam) {
nvmem_device_write(lpg_chan_sdam, SDAM_PAUSE_HI_MULTIPLIER_OFFSET + chan->sdam_offset, 1, &hi_pause);
nvmem_device_write(lpg_chan_sdam, SDAM_PAUSE_LO_MULTIPLIER_OFFSET + chan->sdam_offset, 1, &lo_pause);
}
}
static void lpg_apply_lut_control(struct lpg_channel *chan)
{
struct lpg *lpg = chan->lpg;
@ -596,7 +783,10 @@ static void lpg_apply(struct lpg_channel *chan)
lpg_apply_pwm_value(chan);
lpg_apply_control(chan);
lpg_apply_sync(chan);
lpg_apply_lut_control(chan);
if (chan->lpg->lpg_chan_sdam)
lpg_sdam_apply_lut_control(chan);
else
lpg_apply_lut_control(chan);
lpg_enable_glitch(chan);
}
@ -621,6 +811,7 @@ static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev,
chan->ramp_enabled = false;
} else if (chan->pattern_lo_idx != chan->pattern_hi_idx) {
lpg_calc_freq(chan, NSEC_PER_MSEC);
lpg_sdam_configure_triggers(chan, 1);
chan->enabled = true;
chan->ramp_enabled = true;
@ -648,8 +839,10 @@ static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev,
triled_set(lpg, triled_mask, triled_enabled);
/* Trigger start of ramp generator(s) */
if (lut_mask)
if (lut_mask) {
lpg_lut_sync(lpg, lut_mask);
lpg_set_pbs_trigger(lpg, lut_mask);
}
}
static int lpg_brightness_single_set(struct led_classdev *cdev,
@ -766,9 +959,9 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern,
struct led_pattern *pattern;
unsigned int brightness_a;
unsigned int brightness_b;
unsigned int hi_pause = 0;
unsigned int lo_pause = 0;
unsigned int actual_len;
unsigned int hi_pause;
unsigned int lo_pause;
unsigned int delta_t;
unsigned int lo_idx;
unsigned int hi_idx;
@ -835,18 +1028,24 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern,
* If the specified pattern is a palindrome the ping pong mode is
* enabled. In this scenario the delta_t of the middle entry (i.e. the
* last in the programmed pattern) determines the "high pause".
*
* SDAM-based devices do not support "ping pong", and only supports
* "low pause" and "high pause" with a dedicated SDAM LUT.
*/
/* Detect palindromes and use "ping pong" to reduce LUT usage */
for (i = 0; i < len / 2; i++) {
brightness_a = pattern[i].brightness;
brightness_b = pattern[len - i - 1].brightness;
if (lpg->lut_base) {
for (i = 0; i < len / 2; i++) {
brightness_a = pattern[i].brightness;
brightness_b = pattern[len - i - 1].brightness;
if (brightness_a != brightness_b) {
ping_pong = false;
break;
if (brightness_a != brightness_b) {
ping_pong = false;
break;
}
}
}
} else
ping_pong = false;
/* The pattern length to be written to the LUT */
if (ping_pong)
@ -874,12 +1073,27 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern,
if (delta_t >= BIT(9))
goto out_free_pattern;
/* Find "low pause" and "high pause" in the pattern */
lo_pause = pattern[0].delta_t;
hi_pause = pattern[actual_len - 1].delta_t;
/*
* Find "low pause" and "high pause" in the pattern in the LUT case.
* SDAM-based devices without dedicated LUT SDAM require equal
* duration of all steps.
*/
if (lpg->lut_base || lpg->lut_sdam) {
lo_pause = pattern[0].delta_t;
hi_pause = pattern[actual_len - 1].delta_t;
} else {
if (delta_t != pattern[0].delta_t || delta_t != pattern[actual_len - 1].delta_t)
goto out_free_pattern;
}
mutex_lock(&lpg->lock);
ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx);
if (lpg->lut_base)
ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx);
else
ret = lpg_lut_store_sdam(lpg, pattern, actual_len, &lo_idx, &hi_idx);
if (ret < 0)
goto out_unlock;
@ -927,7 +1141,12 @@ static int lpg_pattern_mc_set(struct led_classdev *cdev,
{
struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
struct lpg_led *led = container_of(mc, struct lpg_led, mcdev);
int ret;
unsigned int triled_mask = 0;
int ret, i;
for (i = 0; i < led->num_channels; i++)
triled_mask |= led->channels[i]->triled_mask;
triled_set(led->lpg, triled_mask, 0);
ret = lpg_pattern_set(led, pattern, len, repeat);
if (ret < 0)
@ -952,6 +1171,8 @@ static int lpg_pattern_clear(struct lpg_led *led)
for (i = 0; i < led->num_channels; i++) {
chan = led->channels[i];
lpg_sdam_configure_triggers(chan, 0);
lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask);
chan->pattern_lo_idx = 0;
chan->pattern_hi_idx = 0;
}
@ -1191,8 +1412,8 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
cdev->brightness_set_blocking = lpg_brightness_mc_set;
cdev->blink_set = lpg_blink_mc_set;
/* Register pattern accessors only if we have a LUT block */
if (lpg->lut_base) {
/* Register pattern accessors if we have a LUT block or when using PPG */
if (lpg->lut_base || lpg->lpg_chan_sdam) {
cdev->pattern_set = lpg_pattern_mc_set;
cdev->pattern_clear = lpg_pattern_mc_clear;
}
@ -1205,15 +1426,19 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
cdev->brightness_set_blocking = lpg_brightness_single_set;
cdev->blink_set = lpg_blink_single_set;
/* Register pattern accessors only if we have a LUT block */
if (lpg->lut_base) {
/* Register pattern accessors if we have a LUT block or when using PPG */
if (lpg->lut_base || lpg->lpg_chan_sdam) {
cdev->pattern_set = lpg_pattern_single_set;
cdev->pattern_clear = lpg_pattern_single_clear;
}
}
cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL);
cdev->max_brightness = LPG_RESOLUTION_9BIT - 1;
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) &&
!strcmp(state, "on"))
@ -1254,6 +1479,7 @@ static int lpg_init_channels(struct lpg *lpg)
chan->base = data->channels[i].base;
chan->triled_mask = data->channels[i].triled_mask;
chan->lut_mask = BIT(i);
chan->sdam_offset = data->channels[i].sdam_offset;
regmap_read(lpg->map, chan->base + LPG_SUBTYPE_REG, &chan->subtype);
}
@ -1299,11 +1525,12 @@ static int lpg_init_lut(struct lpg *lpg)
{
const struct lpg_data *data = lpg->data;
if (!data->lut_base)
if (!data->lut_size)
return 0;
lpg->lut_base = data->lut_base;
lpg->lut_size = data->lut_size;
if (data->lut_base)
lpg->lut_base = data->lut_base;
lpg->lut_bitmap = devm_bitmap_zalloc(lpg->dev, lpg->lut_size, GFP_KERNEL);
if (!lpg->lut_bitmap)
@ -1312,6 +1539,59 @@ static int lpg_init_lut(struct lpg *lpg)
return 0;
}
static int lpg_init_sdam(struct lpg *lpg)
{
int i, sdam_count, rc;
u8 val = 0;
sdam_count = of_property_count_strings(lpg->dev->of_node, "nvmem-names");
if (sdam_count <= 0)
return 0;
if (sdam_count > SDAM_MAX_DEVICES)
return -EINVAL;
/* Get the 1st SDAM device for LPG/LUT config */
lpg->lpg_chan_sdam = devm_nvmem_device_get(lpg->dev, "lpg_chan_sdam");
if (IS_ERR(lpg->lpg_chan_sdam))
return dev_err_probe(lpg->dev, PTR_ERR(lpg->lpg_chan_sdam),
"Failed to get LPG chan SDAM device\n");
if (sdam_count == 1) {
/* Get PBS device node if single SDAM device */
lpg->pbs_dev = get_pbs_client_device(lpg->dev);
if (IS_ERR(lpg->pbs_dev))
return dev_err_probe(lpg->dev, PTR_ERR(lpg->pbs_dev),
"Failed to get PBS client device\n");
} else if (sdam_count == 2) {
/* Get the 2nd SDAM device for LUT pattern */
lpg->lut_sdam = devm_nvmem_device_get(lpg->dev, "lut_sdam");
if (IS_ERR(lpg->lut_sdam))
return dev_err_probe(lpg->dev, PTR_ERR(lpg->lut_sdam),
"Failed to get LPG LUT SDAM device\n");
}
for (i = 0; i < lpg->num_channels; i++) {
struct lpg_channel *chan = &lpg->channels[i];
if (chan->sdam_offset) {
rc = nvmem_device_write(lpg->lpg_chan_sdam,
SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, 1, &val);
if (rc < 0)
return rc;
rc = lpg_sdam_configure_triggers(chan, 0);
if (rc < 0)
return rc;
rc = lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask);
if (rc < 0)
return rc;
}
}
return 0;
}
static int lpg_probe(struct platform_device *pdev)
{
struct device_node *np;
@ -1346,6 +1626,10 @@ static int lpg_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
ret = lpg_init_sdam(lpg);
if (ret < 0)
return ret;
ret = lpg_init_lut(lpg);
if (ret < 0)
return ret;
@ -1364,6 +1648,23 @@ static int lpg_probe(struct platform_device *pdev)
return lpg_add_pwm(lpg);
}
static const struct lpg_data pm660l_lpg_data = {
.lut_base = 0xb000,
.lut_size = 49,
.triled_base = 0xd000,
.triled_has_atc_ctl = true,
.triled_has_src_sel = true,
.num_channels = 4,
.channels = (const struct lpg_channel_data[]) {
{ .base = 0xb100, .triled_mask = BIT(5) },
{ .base = 0xb200, .triled_mask = BIT(6) },
{ .base = 0xb300, .triled_mask = BIT(7) },
{ .base = 0xb400 },
},
};
static const struct lpg_data pm8916_pwm_data = {
.num_channels = 1,
.channels = (const struct lpg_channel_data[]) {
@ -1411,11 +1712,13 @@ static const struct lpg_data pm8994_lpg_data = {
static const struct lpg_data pmi632_lpg_data = {
.triled_base = 0xd000,
.lut_size = 64,
.num_channels = 5,
.channels = (const struct lpg_channel_data[]) {
{ .base = 0xb300, .triled_mask = BIT(7) },
{ .base = 0xb400, .triled_mask = BIT(6) },
{ .base = 0xb500, .triled_mask = BIT(5) },
{ .base = 0xb300, .triled_mask = BIT(7), .sdam_offset = 0x48 },
{ .base = 0xb400, .triled_mask = BIT(6), .sdam_offset = 0x56 },
{ .base = 0xb500, .triled_mask = BIT(5), .sdam_offset = 0x64 },
{ .base = 0xb600 },
{ .base = 0xb700 },
},
@ -1488,11 +1791,13 @@ static const struct lpg_data pm8150l_lpg_data = {
static const struct lpg_data pm8350c_pwm_data = {
.triled_base = 0xef00,
.lut_size = 122,
.num_channels = 4,
.channels = (const struct lpg_channel_data[]) {
{ .base = 0xe800, .triled_mask = BIT(7) },
{ .base = 0xe900, .triled_mask = BIT(6) },
{ .base = 0xea00, .triled_mask = BIT(5) },
{ .base = 0xe800, .triled_mask = BIT(7), .sdam_offset = 0x48 },
{ .base = 0xe900, .triled_mask = BIT(6), .sdam_offset = 0x56 },
{ .base = 0xea00, .triled_mask = BIT(5), .sdam_offset = 0x64 },
{ .base = 0xeb00 },
},
};
@ -1506,6 +1811,7 @@ static const struct lpg_data pmk8550_pwm_data = {
};
static const struct of_device_id lpg_of_table[] = {
{ .compatible = "qcom,pm660l-lpg", .data = &pm660l_lpg_data },
{ .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data },
{ .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data },
{ .compatible = "qcom,pm8350c-pwm", .data = &pm8350c_pwm_data },

View File

@ -63,3 +63,5 @@ module_exit(ledtrig_audio_exit);
MODULE_DESCRIPTION("LED trigger for audio mute control");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("ledtrig:audio-mute");
MODULE_ALIAS("ledtrig:audio-micmute");

View File

@ -28,3 +28,4 @@ module_led_trigger(defon_led_trigger);
MODULE_AUTHOR("Nick Forbes <nick.forbes@incepta.com>");
MODULE_DESCRIPTION("Default-ON LED trigger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("ledtrig:default-on");

View File

@ -18,10 +18,12 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/linkmode.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
#include <linux/phy.h>
#include <linux/rtnetlink.h>
#include <linux/timer.h>
#include "../leds.h"
@ -65,12 +67,15 @@ struct led_netdev_data {
unsigned long mode;
int link_speed;
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes);
u8 duplex;
bool carrier_link_up;
bool hw_control;
};
static const struct attribute_group netdev_trig_link_speed_attrs_group;
static void set_baseline_state(struct led_netdev_data *trigger_data)
{
int current_brightness;
@ -218,13 +223,20 @@ static void get_device_state(struct led_netdev_data *trigger_data)
struct ethtool_link_ksettings cmd;
trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
if (!trigger_data->carrier_link_up)
if (__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
return;
if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) {
if (trigger_data->carrier_link_up) {
trigger_data->link_speed = cmd.base.speed;
trigger_data->duplex = cmd.base.duplex;
}
/*
* Have a local copy of the link speed supported to avoid rtnl lock every time
* modes are refreshed on any change event
*/
linkmode_copy(trigger_data->supported_link_modes, cmd.link_modes.supported);
}
static ssize_t device_name_show(struct device *dev,
@ -277,7 +289,10 @@ static int set_device_name(struct led_netdev_data *trigger_data,
trigger_data->last_activity = 0;
set_baseline_state(trigger_data);
/* Skip if we're called from netdev_trig_activate() and hw_control is true */
if (!trigger_data->hw_control || led_get_trigger_data(trigger_data->led_cdev))
set_baseline_state(trigger_data);
mutex_unlock(&trigger_data->lock);
rtnl_unlock();
@ -295,6 +310,10 @@ static ssize_t device_name_store(struct device *dev,
if (ret < 0)
return ret;
/* Refresh link_speed visibility */
sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group);
return size;
}
@ -458,15 +477,63 @@ static ssize_t offloaded_show(struct device *dev,
static DEVICE_ATTR_RO(offloaded);
static struct attribute *netdev_trig_attrs[] = {
&dev_attr_device_name.attr,
&dev_attr_link.attr,
#define CHECK_LINK_MODE_ATTR(link_speed) \
do { \
if (attr == &dev_attr_link_##link_speed.attr && \
link_ksettings.base.speed == SPEED_##link_speed) \
return attr->mode; \
} while (0)
static umode_t netdev_trig_link_speed_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct led_netdev_data *trigger_data;
unsigned long *supported_link_modes;
u32 mode;
trigger_data = led_trigger_get_drvdata(dev);
supported_link_modes = trigger_data->supported_link_modes;
/*
* Search in the supported link mode mask a matching supported mode.
* Stop at the first matching entry as we care only to check if a particular
* speed is supported and not the kind.
*/
for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) {
struct ethtool_link_ksettings link_ksettings;
ethtool_params_from_link_mode(&link_ksettings, mode);
CHECK_LINK_MODE_ATTR(10);
CHECK_LINK_MODE_ATTR(100);
CHECK_LINK_MODE_ATTR(1000);
CHECK_LINK_MODE_ATTR(2500);
CHECK_LINK_MODE_ATTR(5000);
CHECK_LINK_MODE_ATTR(10000);
}
return 0;
}
static struct attribute *netdev_trig_link_speed_attrs[] = {
&dev_attr_link_10.attr,
&dev_attr_link_100.attr,
&dev_attr_link_1000.attr,
&dev_attr_link_2500.attr,
&dev_attr_link_5000.attr,
&dev_attr_link_10000.attr,
NULL
};
static const struct attribute_group netdev_trig_link_speed_attrs_group = {
.attrs = netdev_trig_link_speed_attrs,
.is_visible = netdev_trig_link_speed_visible,
};
static struct attribute *netdev_trig_attrs[] = {
&dev_attr_device_name.attr,
&dev_attr_link.attr,
&dev_attr_full_duplex.attr,
&dev_attr_half_duplex.attr,
&dev_attr_rx.attr,
@ -475,7 +542,16 @@ static struct attribute *netdev_trig_attrs[] = {
&dev_attr_offloaded.attr,
NULL
};
ATTRIBUTE_GROUPS(netdev_trig);
static const struct attribute_group netdev_trig_attrs_group = {
.attrs = netdev_trig_attrs,
};
static const struct attribute_group *netdev_trig_groups[] = {
&netdev_trig_attrs_group,
&netdev_trig_link_speed_attrs_group,
NULL,
};
static int netdev_trig_notify(struct notifier_block *nb,
unsigned long evt, void *dv)
@ -484,6 +560,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
struct led_netdev_data *trigger_data =
container_of(nb, struct led_netdev_data, notifier);
struct led_classdev *led_cdev = trigger_data->led_cdev;
if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
&& evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
@ -504,12 +581,12 @@ static int netdev_trig_notify(struct notifier_block *nb,
trigger_data->duplex = DUPLEX_UNKNOWN;
switch (evt) {
case NETDEV_CHANGENAME:
get_device_state(trigger_data);
fallthrough;
case NETDEV_REGISTER:
dev_put(trigger_data->net_dev);
dev_hold(dev);
trigger_data->net_dev = dev;
if (evt == NETDEV_CHANGENAME)
get_device_state(trigger_data);
break;
case NETDEV_UNREGISTER:
dev_put(trigger_data->net_dev);
@ -518,6 +595,10 @@ static int netdev_trig_notify(struct notifier_block *nb,
case NETDEV_UP:
case NETDEV_CHANGE:
get_device_state(trigger_data);
/* Refresh link_speed visibility */
if (evt == NETDEV_CHANGE)
sysfs_update_group(&led_cdev->dev->kobj,
&netdev_trig_link_speed_attrs_group);
break;
}
@ -617,8 +698,8 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
if (dev) {
const char *name = dev_name(dev);
set_device_name(trigger_data, name, strlen(name));
trigger_data->hw_control = true;
set_device_name(trigger_data, name, strlen(name));
rc = led_cdev->hw_control_get(led_cdev, &mode);
if (!rc)
@ -663,3 +744,4 @@ MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
MODULE_DESCRIPTION("Netdev LED trigger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("ledtrig:netdev");

View File

@ -21,24 +21,15 @@ static struct led_trigger *trigger;
*/
static void led_trigger_set_panic(struct led_classdev *led_cdev)
{
struct led_trigger *trig;
if (led_cdev->trigger)
list_del(&led_cdev->trig_list);
list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
list_for_each_entry(trig, &trigger_list, next_trig) {
if (strcmp("panic", trig->name))
continue;
if (led_cdev->trigger)
list_del(&led_cdev->trig_list);
list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
/* Avoid the delayed blink path */
led_cdev->blink_delay_on = 0;
led_cdev->blink_delay_off = 0;
/* Avoid the delayed blink path */
led_cdev->blink_delay_on = 0;
led_cdev->blink_delay_off = 0;
led_cdev->trigger = trig;
if (trig->activate)
trig->activate(led_cdev);
break;
}
led_cdev->trigger = trigger;
}
static int led_trigger_panic_notifier(struct notifier_block *nb,

View File

@ -64,7 +64,7 @@ config GREYBUS_HID
config GREYBUS_LIGHT
tristate "Greybus LED Class driver"
depends on LEDS_CLASS
depends on LEDS_CLASS_FLASH
help
Select this option if you have a device that follows the
Greybus LED Class specification.

View File

@ -29,13 +29,9 @@ struct gb_channel {
struct attribute_group *attr_group;
const struct attribute_group **attr_groups;
struct led_classdev *led;
#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH)
struct led_classdev_flash fled;
struct led_flash_setting intensity_uA;
struct led_flash_setting timeout_us;
#else
struct led_classdev cled;
#endif
struct gb_light *light;
bool is_registered;
bool releasing;
@ -84,7 +80,6 @@ static bool is_channel_flash(struct gb_channel *channel)
| GB_CHANNEL_MODE_INDICATOR));
}
#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH)
static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev);
@ -153,22 +148,6 @@ static int __gb_lights_flash_brightness_set(struct gb_channel *channel)
return __gb_lights_flash_intensity_set(channel, intensity);
}
#else
static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev)
{
return container_of(cdev, struct gb_channel, cled);
}
static struct led_classdev *get_channel_cdev(struct gb_channel *channel)
{
return &channel->cled;
}
static int __gb_lights_flash_brightness_set(struct gb_channel *channel)
{
return 0;
}
#endif
static int gb_lights_color_set(struct gb_channel *channel, u32 color);
static int gb_lights_fade_set(struct gb_channel *channel);

View File

@ -100,7 +100,11 @@
#define LED_FUNCTION_TX "tx"
#define LED_FUNCTION_USB "usb"
#define LED_FUNCTION_WAN "wan"
#define LED_FUNCTION_WAN_ONLINE "wan-online"
#define LED_FUNCTION_WLAN "wlan"
#define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz"
#define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz"
#define LED_FUNCTION_WLAN_6GHZ "wlan-6ghz"
#define LED_FUNCTION_WPS "wps"
#endif /* __DT_BINDINGS_LEDS_H */

View File

@ -85,7 +85,6 @@ static inline struct led_classdev_flash *lcdev_to_flcdev(
return container_of(lcdev, struct led_classdev_flash, led_cdev);
}
#if IS_ENABLED(CONFIG_LEDS_CLASS_FLASH)
/**
* led_classdev_flash_register_ext - register a new object of LED class with
* init data and with support for flash LEDs
@ -116,29 +115,6 @@ int devm_led_classdev_flash_register_ext(struct device *parent,
void devm_led_classdev_flash_unregister(struct device *parent,
struct led_classdev_flash *fled_cdev);
#else
static inline int led_classdev_flash_register_ext(struct device *parent,
struct led_classdev_flash *fled_cdev,
struct led_init_data *init_data)
{
return 0;
}
static inline void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) {};
static inline int devm_led_classdev_flash_register_ext(struct device *parent,
struct led_classdev_flash *fled_cdev,
struct led_init_data *init_data)
{
return 0;
}
static inline void devm_led_classdev_flash_unregister(struct device *parent,
struct led_classdev_flash *fled_cdev)
{};
#endif /* IS_ENABLED(CONFIG_LEDS_CLASS_FLASH) */
static inline int led_classdev_flash_register(struct device *parent,
struct led_classdev_flash *fled_cdev)
{

View File

@ -30,7 +30,6 @@ static inline struct led_classdev_mc *lcdev_to_mccdev(
return container_of(led_cdev, struct led_classdev_mc, led_cdev);
}
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
/**
* led_classdev_multicolor_register_ext - register a new object of led_classdev
* class with support for multicolor LEDs
@ -64,34 +63,6 @@ int devm_led_classdev_multicolor_register_ext(struct device *parent,
void devm_led_classdev_multicolor_unregister(struct device *parent,
struct led_classdev_mc *mcled_cdev);
#else
static inline int led_classdev_multicolor_register_ext(struct device *parent,
struct led_classdev_mc *mcled_cdev,
struct led_init_data *init_data)
{
return 0;
}
static inline void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev) {};
static inline int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
enum led_brightness brightness)
{
return 0;
}
static inline int devm_led_classdev_multicolor_register_ext(struct device *parent,
struct led_classdev_mc *mcled_cdev,
struct led_init_data *init_data)
{
return 0;
}
static inline void devm_led_classdev_multicolor_unregister(struct device *parent,
struct led_classdev_mc *mcled_cdev)
{};
#endif /* IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR) */
static inline int led_classdev_multicolor_register(struct device *parent,
struct led_classdev_mc *mcled_cdev)

View File

@ -82,15 +82,7 @@ struct led_init_data {
bool devname_mandatory;
};
#if IS_ENABLED(CONFIG_NEW_LEDS)
enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode);
#else
static inline enum led_default_state
led_init_default_state_get(struct fwnode_handle *fwnode)
{
return LEDS_DEFSTATE_OFF;
}
#endif
struct led_hw_trigger_type {
int dummy;
@ -279,20 +271,9 @@ static inline int led_classdev_register(struct device *parent,
return led_classdev_register_ext(parent, led_cdev, NULL);
}
#if IS_ENABLED(CONFIG_LEDS_CLASS)
int devm_led_classdev_register_ext(struct device *parent,
struct led_classdev *led_cdev,
struct led_init_data *init_data);
#else
static inline int
devm_led_classdev_register_ext(struct device *parent,
struct led_classdev *led_cdev,
struct led_init_data *init_data)
{
return 0;
}
#endif
static inline int devm_led_classdev_register(struct device *parent,
struct led_classdev *led_cdev)
{
@ -658,7 +639,7 @@ struct gpio_led_platform_data {
gpio_blink_set_t gpio_blink_set;
};
#ifdef CONFIG_NEW_LEDS
#ifdef CONFIG_LEDS_GPIO_REGISTER
struct platform_device *gpio_led_register_device(
int id, const struct gpio_led_platform_data *pdata);
#else