1
0

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

Pull LED updates from Lee Jones:
 "This submission is a mix of new features, a number of fixes, some
  clean-up/refactoring and a few Device Tree binding updates. The most
  significant changes include new device support for the IS31FL3236A and
  QNAP's status LEDs, a number of fixes for the Qualcomm flash, LP55xx,
  MAX77705 and QNAP MCU drivers, along with some generic LED consumer
  binding documentation and unification.

  New Support & Features:
   - Add support for the IS31FL3236A LED driver, including an optional
     22kHz PWM frequency setting.
   - Add support for the red and green status LEDs on QNAP MCU devices.

  Improvements & Fixes:
   - Fix an issue in the MAX77705 driver where an error value was
     assigned but not returned.
   - Correctly update the torch current clamp setting in the Qualcomm
     flash LED driver to prevent unexpected clamping.
   - Add a separate register map for the PMI8998 to the Qualcomm flash
     LED driver to handle its unique register layout.
   - Fix the state numbering for the USB LED in the QNAP MCU driver to
     avoid command conflicts.
   - Correct the memory programming address calculation for non-paged
     devices like the LP5562 in the LP55xx driver.
   - Add Device Tree support to the generic led_get() function, allowing
     LEDs to be looked up by name.

  Cleanups & Refactoring:
   - Fix a spelling mistake in the Kconfig help text for the BD2606MVV
     driver.
   - Use devm_mutex_init() in the IS31FL319x driver to simplify code.

  Device Tree Bindings Updates:
   - Drop the redundant '-db' suffix from the IS31FL319x binding.
   - Introduce generic LED consumer bindings for referencing LEDs via
     phandles.
   - Unify the 'leds' property across several bindings to use the new
     generic consumer binding.
   - Convert the AMS AS3645A LED controller binding to the DT schema
     format"

* tag 'leds-next-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds:
  dt-bindings: leds: as3645: Convert to DT schema
  leds: led-class: Add Device Tree support to led_get()
  dt-bindings: leds: Unify 'leds' property
  dt-bindings: leds: Add generic LED consumer documentation
  leds: is31fl319x: Use devm_mutex_init()
  leds: leds-lp55xx: Use correct address for memory programming
  dt-bindings: leds: issi,is31fl319x: Drop 'db' suffix duplicating dtschema
  leds: qnap-mcu: Add support for the red and green status LEDs
  leds: qnap-mcu: Fix state numbering for USB LED
  leds: flash: leds-qcom-flash: Add a separate register map for PMI8998
  leds: flash: leds-qcom-flash: Update torch current clamp setting
  leds: max77705: Function return instead of variable assignment
  leds: Kconfig: Fix spelling mistake "limitiation" -> "limitation"
  leds: leds-is31fl32xx: Add support for is31fl3236a
This commit is contained in:
Linus Torvalds
2025-10-01 12:42:13 -07:00
15 changed files with 497 additions and 145 deletions

View File

@@ -1,85 +0,0 @@
Analog devices AS3645A device tree bindings
The AS3645A flash LED controller can drive two LEDs, one high current
flash LED and one indicator LED. The high current flash LED can be
used in torch mode as well.
Ranges below noted as [a, b] are closed ranges between a and b, i.e. a
and b are included in the range.
Please also see common.txt in the same directory.
Required properties
===================
compatible : Must be "ams,as3645a".
reg : The I2C address of the device. Typically 0x30.
#address-cells : 1
#size-cells : 0
Required properties of the flash child node (0)
===============================================
reg: 0
flash-timeout-us: Flash timeout in microseconds. The value must be in
the range [100000, 850000] and divisible by 50000.
flash-max-microamp: Maximum flash current in microamperes. Has to be
in the range between [200000, 500000] and
divisible by 20000.
led-max-microamp: Maximum torch (assist) current in microamperes. The
value must be in the range between [20000, 160000] and
divisible by 20000.
ams,input-max-microamp: Maximum flash controller input current. The
value must be in the range [1250000, 2000000]
and divisible by 50000.
Optional properties of the flash child node
===========================================
function : See Documentation/devicetree/bindings/leds/common.txt.
color : See Documentation/devicetree/bindings/leds/common.txt.
label : See Documentation/devicetree/bindings/leds/common.txt (deprecated).
Required properties of the indicator child node (1)
===================================================
reg: 1
led-max-microamp: Maximum indicator current. The allowed values are
2500, 5000, 7500 and 10000.
Optional properties of the indicator child node
===============================================
function : See Documentation/devicetree/bindings/leds/common.txt.
color : See Documentation/devicetree/bindings/leds/common.txt.
label : See Documentation/devicetree/bindings/leds/common.txt (deprecated).
Example
=======
#include <dt-bindings/leds/common.h>
as3645a@30 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x30>;
compatible = "ams,as3645a";
led@0 {
reg = <0x0>;
flash-timeout-us = <150000>;
flash-max-microamp = <320000>;
led-max-microamp = <60000>;
ams,input-max-microamp = <1750000>;
function = LED_FUNCTION_FLASH;
};
led@1 {
reg = <0x1>;
led-max-microamp = <10000>;
function = LED_FUNCTION_INDICATOR;
};
};

View File

@@ -0,0 +1,130 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/ams,as3645a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AS3645A LED Controller
maintainers:
- Sakari Ailus <sakari.ailus@iki.fi>
description:
The AS3645A flash LED controller can drive two LEDs, one
high current flash LED and one indicator LED. The high
current flash LED can be used in torch mode as well.
properties:
compatible:
const: ams,as3645a
"#address-cells":
const: 1
"#size-cells":
const: 0
reg:
maxItems: 1
led@0:
description: led0 describes the 'flash' feature
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
const: 0
flash-timeout-us:
minimum: 100000
maximum: 850000
multipleOf: 50000
flash-max-microamp:
minimum: 200000
maximum: 500000
multipleOf: 20000
led-max-microamp:
minimum: 20000
maximum: 160000
multipleOf: 20000
description:
Maximum current when in torch (assist) mode.
ams,input-max-microamp:
minimum: 1250000
maximum: 2000000
multipleOf: 50000
required:
- reg
- flash-timeout-us
- flash-max-microamp
- led-max-microamp
- ams,input-max-microamp
led@1:
description: led1 describes the 'indicator' feature
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
const: 1
led-max-microamp:
enum:
- 2500
- 5000
- 7500
- 10000
description:
Maximum indicator current.
required:
- reg
- led-max-microamp
required:
- compatible
- reg
- "#size-cells"
- "#address-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
i2c{
#address-cells = <1>;
#size-cells = <0>;
led-controller@30 {
compatible = "ams,as3645a";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x30>;
led@0 {
reg = <0>;
flash-timeout-us = <150000>;
flash-max-microamp = <320000>;
led-max-microamp = <60000>;
ams,input-max-microamp = <1750000>;
function = LED_FUNCTION_FLASH;
};
led@1 {
reg = <1>;
led-max-microamp = <10000>;
function = LED_FUNCTION_INDICATOR;
};
};
};
...

View File

@@ -23,11 +23,7 @@ properties:
compatible:
const: led-backlight
leds:
description: A list of LED nodes
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
maxItems: 1
leds: true
required:
- compatible

View File

@@ -42,7 +42,6 @@ properties:
description: GPIO attached to the SDB pin.
audio-gain-db:
$ref: /schemas/types.yaml#/definitions/uint32
default: 0
description: Audio gain selection for external analog modulation input.
enum: [0, 3, 6, 9, 12, 15, 18, 21]

View File

@@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/leds-consumer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Common leds consumer
maintainers:
- Aleksandrs Vinarskis <alex@vinarskis.com>
description:
Some LED defined in DT are required by other DT consumers, for example
v4l2 subnode may require privacy or flash LED. Unlike trigger-source
approach which is typically used as 'soft' binding, referencing LED
devices by phandle makes things simpler when 'hard' binding is desired.
Document LED properties that its consumers may define.
select: true
properties:
leds:
oneOf:
- type: object
- $ref: /schemas/types.yaml#/definitions/phandle-array
description:
A list of LED device(s) required by a particular consumer.
items:
maxItems: 1
led-names:
description:
A list of device name(s). Used to map LED devices to their respective
functions, when consumer requires more than one LED.
additionalProperties: true
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
leds {
compatible = "gpio-leds";
privacy_led: privacy-led {
color = <LED_COLOR_ID_RED>;
default-state = "off";
function = LED_FUNCTION_INDICATOR;
gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>;
};
};
i2c {
#address-cells = <1>;
#size-cells = <0>;
v4l2_node: camera@36 {
reg = <0x36>;
leds = <&privacy_led>;
led-names = "privacy";
};
};
...

View File

@@ -17,10 +17,7 @@ properties:
compatible:
const: leds-group-multicolor
leds:
description:
An aray of monochromatic leds
$ref: /schemas/types.yaml#/definitions/phandle-array
leds: true
required:
- leds

View File

@@ -17,6 +17,14 @@ properties:
An array of phandles, each referring to a flash LED, a sub-node of the LED
driver device node.
leds:
minItems: 1
maxItems: 1
led-names:
enum:
- privacy
lens-focus:
$ref: /schemas/types.yaml#/definitions/phandle
description:

View File

@@ -674,7 +674,7 @@ config LEDS_BD2606MVV
help
This option enables support for BD2606MVV LED driver chips
accessed via the I2C bus. It supports setting brightness, with
the limitiation that there are groups of two channels sharing
the limitation that there are groups of two channels sharing
a brightness setting, but not the on/off setting.
To compile this driver as a module, choose M here: the module will

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/bitfield.h>
@@ -114,36 +114,55 @@ enum {
REG_THERM_THRSH1,
REG_THERM_THRSH2,
REG_THERM_THRSH3,
REG_TORCH_CLAMP,
REG_MAX_COUNT,
};
static const struct reg_field mvflash_3ch_pmi8998_regs[REG_MAX_COUNT] = {
[REG_STATUS1] = REG_FIELD(0x08, 0, 5),
[REG_STATUS2] = REG_FIELD(0x09, 0, 7),
[REG_STATUS3] = REG_FIELD(0x0a, 0, 7),
[REG_CHAN_TIMER] = REG_FIELD_ID(0x40, 0, 7, 3, 1),
[REG_ITARGET] = REG_FIELD_ID(0x43, 0, 6, 3, 1),
[REG_MODULE_EN] = REG_FIELD(0x46, 7, 7),
[REG_IRESOLUTION] = REG_FIELD(0x47, 0, 5),
[REG_CHAN_STROBE] = REG_FIELD_ID(0x49, 0, 2, 3, 1),
[REG_CHAN_EN] = REG_FIELD(0x4c, 0, 2),
[REG_THERM_THRSH1] = REG_FIELD(0x56, 0, 2),
[REG_THERM_THRSH2] = REG_FIELD(0x57, 0, 2),
[REG_THERM_THRSH3] = REG_FIELD(0x58, 0, 2),
[REG_TORCH_CLAMP] = REG_FIELD(0xea, 0, 6),
};
static const struct reg_field mvflash_3ch_regs[REG_MAX_COUNT] = {
REG_FIELD(0x08, 0, 7), /* status1 */
REG_FIELD(0x09, 0, 7), /* status2 */
REG_FIELD(0x0a, 0, 7), /* status3 */
REG_FIELD_ID(0x40, 0, 7, 3, 1), /* chan_timer */
REG_FIELD_ID(0x43, 0, 6, 3, 1), /* itarget */
REG_FIELD(0x46, 7, 7), /* module_en */
REG_FIELD(0x47, 0, 5), /* iresolution */
REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */
REG_FIELD(0x4c, 0, 2), /* chan_en */
REG_FIELD(0x56, 0, 2), /* therm_thrsh1 */
REG_FIELD(0x57, 0, 2), /* therm_thrsh2 */
REG_FIELD(0x58, 0, 2), /* therm_thrsh3 */
[REG_STATUS1] = REG_FIELD(0x08, 0, 7),
[REG_STATUS2] = REG_FIELD(0x09, 0, 7),
[REG_STATUS3] = REG_FIELD(0x0a, 0, 7),
[REG_CHAN_TIMER] = REG_FIELD_ID(0x40, 0, 7, 3, 1),
[REG_ITARGET] = REG_FIELD_ID(0x43, 0, 6, 3, 1),
[REG_MODULE_EN] = REG_FIELD(0x46, 7, 7),
[REG_IRESOLUTION] = REG_FIELD(0x47, 0, 5),
[REG_CHAN_STROBE] = REG_FIELD_ID(0x49, 0, 2, 3, 1),
[REG_CHAN_EN] = REG_FIELD(0x4c, 0, 2),
[REG_THERM_THRSH1] = REG_FIELD(0x56, 0, 2),
[REG_THERM_THRSH2] = REG_FIELD(0x57, 0, 2),
[REG_THERM_THRSH3] = REG_FIELD(0x58, 0, 2),
[REG_TORCH_CLAMP] = REG_FIELD(0xec, 0, 6),
};
static const struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
REG_FIELD(0x06, 0, 7), /* status1 */
REG_FIELD(0x07, 0, 6), /* status2 */
REG_FIELD(0x09, 0, 7), /* status3 */
REG_FIELD_ID(0x3e, 0, 7, 4, 1), /* chan_timer */
REG_FIELD_ID(0x42, 0, 6, 4, 1), /* itarget */
REG_FIELD(0x46, 7, 7), /* module_en */
REG_FIELD(0x49, 0, 3), /* iresolution */
REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */
REG_FIELD(0x4e, 0, 3), /* chan_en */
REG_FIELD(0x7a, 0, 2), /* therm_thrsh1 */
REG_FIELD(0x78, 0, 2), /* therm_thrsh2 */
[REG_STATUS1] = REG_FIELD(0x06, 0, 7),
[REG_STATUS2] = REG_FIELD(0x07, 0, 6),
[REG_STATUS3] = REG_FIELD(0x09, 0, 7),
[REG_CHAN_TIMER] = REG_FIELD_ID(0x3e, 0, 7, 4, 1),
[REG_ITARGET] = REG_FIELD_ID(0x42, 0, 6, 4, 1),
[REG_MODULE_EN] = REG_FIELD(0x46, 7, 7),
[REG_IRESOLUTION] = REG_FIELD(0x49, 0, 3),
[REG_CHAN_STROBE] = REG_FIELD_ID(0x4a, 0, 6, 4, 1),
[REG_CHAN_EN] = REG_FIELD(0x4e, 0, 3),
[REG_THERM_THRSH1] = REG_FIELD(0x7a, 0, 2),
[REG_THERM_THRSH2] = REG_FIELD(0x78, 0, 2),
[REG_TORCH_CLAMP] = REG_FIELD(0xed, 0, 6),
};
struct qcom_flash_data {
@@ -156,6 +175,7 @@ struct qcom_flash_data {
u8 max_channels;
u8 chan_en_bits;
u8 revision;
u8 torch_clamp;
};
struct qcom_flash_led {
@@ -702,6 +722,7 @@ static int qcom_flash_register_led_device(struct device *dev,
u32 current_ua, timeout_us;
u32 channels[4];
int i, rc, count;
u8 torch_clamp;
count = fwnode_property_count_u32(node, "led-sources");
if (count <= 0) {
@@ -751,6 +772,12 @@ static int qcom_flash_register_led_device(struct device *dev,
current_ua = min_t(u32, current_ua, TORCH_CURRENT_MAX_UA * led->chan_count);
led->max_torch_current_ma = current_ua / UA_PER_MA;
torch_clamp = (current_ua / led->chan_count) / TORCH_IRES_UA;
if (torch_clamp != 0)
torch_clamp--;
flash_data->torch_clamp = max_t(u8, flash_data->torch_clamp, torch_clamp);
if (fwnode_property_present(node, "flash-max-microamp")) {
flash->led_cdev.flags |= LED_DEV_CAP_FLASH;
@@ -851,13 +878,20 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
return rc;
}
if (val == FLASH_SUBTYPE_3CH_PM8150_VAL || val == FLASH_SUBTYPE_3CH_PMI8998_VAL) {
if (val == FLASH_SUBTYPE_3CH_PM8150_VAL) {
flash_data->hw_type = QCOM_MVFLASH_3CH;
flash_data->max_channels = 3;
regs = devm_kmemdup(dev, mvflash_3ch_regs, sizeof(mvflash_3ch_regs),
GFP_KERNEL);
if (!regs)
return -ENOMEM;
} else if (val == FLASH_SUBTYPE_3CH_PMI8998_VAL) {
flash_data->hw_type = QCOM_MVFLASH_3CH;
flash_data->max_channels = 3;
regs = devm_kmemdup(dev, mvflash_3ch_pmi8998_regs,
sizeof(mvflash_3ch_pmi8998_regs), GFP_KERNEL);
if (!regs)
return -ENOMEM;
} else if (val == FLASH_SUBTYPE_4CH_VAL) {
flash_data->hw_type = QCOM_MVFLASH_4CH;
flash_data->max_channels = 4;
@@ -917,8 +951,7 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
flash_data->leds_count++;
}
return 0;
return regmap_field_write(flash_data->r_fields[REG_TORCH_CLAMP], flash_data->torch_clamp);
release:
while (flash_data->v4l2_flash[flash_data->leds_count] && flash_data->leds_count)
v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]);

View File

@@ -252,15 +252,23 @@ static const struct class leds_class = {
* of_led_get() - request a LED device via the LED framework
* @np: device node to get the LED device from
* @index: the index of the LED
* @name: the name of the LED used to map it to its function, if present
*
* Returns the LED device parsed from the phandle specified in the "leds"
* property of a device tree node or a negative error-code on failure.
*/
static struct led_classdev *of_led_get(struct device_node *np, int index)
static struct led_classdev *of_led_get(struct device_node *np, int index,
const char *name)
{
struct device *led_dev;
struct device_node *led_node;
/*
* For named LEDs, first look up the name in the "led-names" property.
* If it cannot be found, then of_parse_phandle() will propagate the error.
*/
if (name)
index = of_property_match_string(np, "led-names", name);
led_node = of_parse_phandle(np, "leds", index);
if (!led_node)
return ERR_PTR(-ENOENT);
@@ -324,7 +332,7 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
if (!dev)
return ERR_PTR(-EINVAL);
led = of_led_get(dev->of_node, index);
led = of_led_get(dev->of_node, index, NULL);
if (IS_ERR(led))
return led;
@@ -342,9 +350,14 @@ EXPORT_SYMBOL_GPL(devm_of_led_get);
struct led_classdev *led_get(struct device *dev, char *con_id)
{
struct led_lookup_data *lookup;
struct led_classdev *led_cdev;
const char *provider = NULL;
struct device *led_dev;
led_cdev = of_led_get(dev->of_node, -1, con_id);
if (!IS_ERR(led_cdev) || PTR_ERR(led_cdev) != -ENOENT)
return led_cdev;
mutex_lock(&leds_lookup_lock);
list_for_each_entry(lookup, &leds_lookup_list, list) {
if (!strcmp(lookup->dev_id, dev_name(dev)) &&

View File

@@ -483,11 +483,6 @@ static inline int is31fl3196_db_to_gain(u32 dezibel)
return dezibel / IS31FL3196_AUDIO_GAIN_DB_STEP;
}
static void is31f1319x_mutex_destroy(void *lock)
{
mutex_destroy(lock);
}
static int is31fl319x_probe(struct i2c_client *client)
{
struct is31fl319x_chip *is31;
@@ -503,8 +498,7 @@ static int is31fl319x_probe(struct i2c_client *client)
if (!is31)
return -ENOMEM;
mutex_init(&is31->lock);
err = devm_add_action_or_reset(dev, is31f1319x_mutex_destroy, &is31->lock);
err = devm_mutex_init(dev, &is31->lock);
if (err)
return err;

View File

@@ -32,6 +32,8 @@
#define IS31FL3216_CONFIG_SSD_ENABLE BIT(7)
#define IS31FL3216_CONFIG_SSD_DISABLE 0
#define IS31FL32XX_PWM_FREQUENCY_22KHZ 0x01
struct is31fl32xx_priv;
struct is31fl32xx_led_data {
struct led_classdev cdev;
@@ -53,6 +55,7 @@ struct is31fl32xx_priv {
* @pwm_update_reg : address of PWM Update register
* @global_control_reg : address of Global Control register (optional)
* @reset_reg : address of Reset register (optional)
* @output_frequency_setting_reg: address of output frequency register (optional)
* @pwm_register_base : address of first PWM register
* @pwm_registers_reversed: : true if PWM registers count down instead of up
* @led_control_register_base : address of first LED control register (optional)
@@ -76,6 +79,7 @@ struct is31fl32xx_chipdef {
u8 pwm_update_reg;
u8 global_control_reg;
u8 reset_reg;
u8 output_frequency_setting_reg;
u8 pwm_register_base;
bool pwm_registers_reversed;
u8 led_control_register_base;
@@ -90,6 +94,19 @@ static const struct is31fl32xx_chipdef is31fl3236_cdef = {
.pwm_update_reg = 0x25,
.global_control_reg = 0x4a,
.reset_reg = 0x4f,
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
.pwm_register_base = 0x01,
.led_control_register_base = 0x26,
.enable_bits_per_led_control_register = 1,
};
static const struct is31fl32xx_chipdef is31fl3236a_cdef = {
.channels = 36,
.shutdown_reg = 0x00,
.pwm_update_reg = 0x25,
.global_control_reg = 0x4a,
.reset_reg = 0x4f,
.output_frequency_setting_reg = 0x4b,
.pwm_register_base = 0x01,
.led_control_register_base = 0x26,
.enable_bits_per_led_control_register = 1,
@@ -101,6 +118,7 @@ static const struct is31fl32xx_chipdef is31fl3235_cdef = {
.pwm_update_reg = 0x25,
.global_control_reg = 0x4a,
.reset_reg = 0x4f,
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
.pwm_register_base = 0x05,
.led_control_register_base = 0x2a,
.enable_bits_per_led_control_register = 1,
@@ -112,6 +130,7 @@ static const struct is31fl32xx_chipdef is31fl3218_cdef = {
.pwm_update_reg = 0x16,
.global_control_reg = IS31FL32XX_REG_NONE,
.reset_reg = 0x17,
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
.pwm_register_base = 0x01,
.led_control_register_base = 0x13,
.enable_bits_per_led_control_register = 6,
@@ -126,6 +145,7 @@ static const struct is31fl32xx_chipdef is31fl3216_cdef = {
.pwm_update_reg = 0xB0,
.global_control_reg = IS31FL32XX_REG_NONE,
.reset_reg = IS31FL32XX_REG_NONE,
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
.pwm_register_base = 0x10,
.pwm_registers_reversed = true,
.led_control_register_base = 0x01,
@@ -363,8 +383,21 @@ static struct is31fl32xx_led_data *is31fl32xx_find_led_data(
static int is31fl32xx_parse_dt(struct device *dev,
struct is31fl32xx_priv *priv)
{
const struct is31fl32xx_chipdef *cdef = priv->cdef;
int ret = 0;
if ((cdef->output_frequency_setting_reg != IS31FL32XX_REG_NONE) &&
of_property_read_bool(dev_of_node(dev), "issi,22khz-pwm")) {
ret = is31fl32xx_write(priv, cdef->output_frequency_setting_reg,
IS31FL32XX_PWM_FREQUENCY_22KHZ);
if (ret) {
dev_err(dev, "Failed to write output PWM frequency register\n");
return ret;
}
}
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
struct led_init_data init_data = {};
struct is31fl32xx_led_data *led_data =
@@ -404,12 +437,13 @@ static int is31fl32xx_parse_dt(struct device *dev,
}
static const struct of_device_id of_is31fl32xx_match[] = {
{ .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
{ .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
{ .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, },
{ .compatible = "si-en,sn3218", .data = &is31fl3218_cdef, },
{ .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, },
{ .compatible = "si-en,sn3216", .data = &is31fl3216_cdef, },
{ .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
{ .compatible = "issi,is31fl3236a", .data = &is31fl3236a_cdef, },
{ .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
{ .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, },
{ .compatible = "si-en,sn3218", .data = &is31fl3218_cdef, },
{ .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, },
{ .compatible = "si-en,sn3216", .data = &is31fl3216_cdef, },
{},
};
@@ -466,6 +500,7 @@ static void is31fl32xx_remove(struct i2c_client *client)
*/
static const struct i2c_device_id is31fl32xx_id[] = {
{ "is31fl3236" },
{ "is31fl3236a" },
{ "is31fl3235" },
{ "is31fl3218" },
{ "sn3218" },

View File

@@ -212,7 +212,7 @@ int lp55xx_update_program_memory(struct lp55xx_chip *chip,
* For LED chip that support page, PAGE is already set in load_engine.
*/
if (!cfg->pages_per_engine)
start_addr += LP55xx_BYTES_PER_PAGE * idx;
start_addr += LP55xx_BYTES_PER_PAGE * (idx - 1);
for (page = 0; page < program_length / LP55xx_BYTES_PER_PAGE; page++) {
/* Write to the next page each 32 bytes (if supported) */

View File

@@ -180,7 +180,7 @@ static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fw
ret = fwnode_property_read_u32(np, "reg", &reg);
if (ret || reg >= MAX77705_LED_NUM_LEDS)
ret = -EINVAL;
return -EINVAL;
info = devm_kcalloc(dev, num_channels, sizeof(*info), GFP_KERNEL);
if (!info)

View File

@@ -104,9 +104,9 @@ static int qnap_mcu_register_err_led(struct device *dev, struct qnap_mcu *mcu, i
}
enum qnap_mcu_usb_led_mode {
QNAP_MCU_USB_LED_ON = 1,
QNAP_MCU_USB_LED_OFF = 3,
QNAP_MCU_USB_LED_BLINK = 2,
QNAP_MCU_USB_LED_ON = 0,
QNAP_MCU_USB_LED_OFF = 2,
QNAP_MCU_USB_LED_BLINK = 1,
};
struct qnap_mcu_usb_led {
@@ -137,7 +137,7 @@ static int qnap_mcu_usb_led_set(struct led_classdev *led_cdev,
* Byte 3 is shared between the usb led target on/off/blink
* and also the buzzer control (in the input driver)
*/
cmd[2] = 'D' + usb_led->mode;
cmd[2] = 'E' + usb_led->mode;
return qnap_mcu_exec_with_ack(usb_led->mcu, cmd, sizeof(cmd));
}
@@ -161,7 +161,7 @@ static int qnap_mcu_usb_led_blink_set(struct led_classdev *led_cdev,
* Byte 3 is shared between the USB LED target on/off/blink
* and also the buzzer control (in the input driver)
*/
cmd[2] = 'D' + usb_led->mode;
cmd[2] = 'E' + usb_led->mode;
return qnap_mcu_exec_with_ack(usb_led->mcu, cmd, sizeof(cmd));
}
@@ -190,6 +190,166 @@ static int qnap_mcu_register_usb_led(struct device *dev, struct qnap_mcu *mcu)
return qnap_mcu_usb_led_set(&usb_led->cdev, 0);
}
enum qnap_mcu_status_led_mode {
QNAP_MCU_STATUS_LED_OFF = 0,
QNAP_MCU_STATUS_LED_ON = 1,
QNAP_MCU_STATUS_LED_BLINK_FAST = 2, /* 500ms / 500ms */
QNAP_MCU_STATUS_LED_BLINK_SLOW = 3, /* 1s / 1s */
};
struct qnap_mcu_status_led {
struct led_classdev cdev;
struct qnap_mcu_status_led *red;
u8 mode;
};
struct qnap_mcu_status {
struct qnap_mcu *mcu;
struct qnap_mcu_status_led red;
struct qnap_mcu_status_led green;
};
static inline struct qnap_mcu_status_led *cdev_to_qnap_mcu_status_led(struct led_classdev *led_cdev)
{
return container_of(led_cdev, struct qnap_mcu_status_led, cdev);
}
static inline struct qnap_mcu_status *statusled_to_qnap_mcu_status(struct qnap_mcu_status_led *led)
{
return container_of(led->red, struct qnap_mcu_status, red);
}
static u8 qnap_mcu_status_led_encode(struct qnap_mcu_status *status)
{
if (status->red.mode == QNAP_MCU_STATUS_LED_OFF) {
switch (status->green.mode) {
case QNAP_MCU_STATUS_LED_OFF:
return '9';
case QNAP_MCU_STATUS_LED_ON:
return '6';
case QNAP_MCU_STATUS_LED_BLINK_FAST:
return '5';
case QNAP_MCU_STATUS_LED_BLINK_SLOW:
return 'A';
}
} else if (status->green.mode == QNAP_MCU_STATUS_LED_OFF) {
switch (status->red.mode) {
case QNAP_MCU_STATUS_LED_OFF:
return '9';
case QNAP_MCU_STATUS_LED_ON:
return '7';
case QNAP_MCU_STATUS_LED_BLINK_FAST:
return '4';
case QNAP_MCU_STATUS_LED_BLINK_SLOW:
return 'B';
}
} else if (status->green.mode == QNAP_MCU_STATUS_LED_ON &&
status->red.mode == QNAP_MCU_STATUS_LED_ON) {
return 'D';
} else if (status->green.mode == QNAP_MCU_STATUS_LED_BLINK_SLOW &&
status->red.mode == QNAP_MCU_STATUS_LED_BLINK_SLOW) {
return 'C';
}
/*
* Here both LEDs are on in some fashion, either both blinking fast,
* or in different speeds, so default to fast blinking for both.
*/
return '8';
}
static int qnap_mcu_status_led_update(struct qnap_mcu *mcu,
struct qnap_mcu_status *status)
{
u8 cmd[] = { '@', 'C', 0 };
cmd[2] = qnap_mcu_status_led_encode(status);
return qnap_mcu_exec_with_ack(mcu, cmd, sizeof(cmd));
}
static int qnap_mcu_status_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct qnap_mcu_status_led *status_led = cdev_to_qnap_mcu_status_led(led_cdev);
struct qnap_mcu_status *base = statusled_to_qnap_mcu_status(status_led);
/* Don't disturb a possible set blink-mode if LED stays on */
if (brightness != 0 && status_led->mode >= QNAP_MCU_STATUS_LED_BLINK_FAST)
return 0;
status_led->mode = brightness ? QNAP_MCU_STATUS_LED_ON :
QNAP_MCU_STATUS_LED_OFF;
return qnap_mcu_status_led_update(base->mcu, base);
}
static int qnap_mcu_status_led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct qnap_mcu_status_led *status_led = cdev_to_qnap_mcu_status_led(led_cdev);
struct qnap_mcu_status *base = statusled_to_qnap_mcu_status(status_led);
if (status_led->mode == QNAP_MCU_STATUS_LED_OFF)
return 0;
if (*delay_on <= 500) {
*delay_on = 500;
*delay_off = 500;
status_led->mode = QNAP_MCU_STATUS_LED_BLINK_FAST;
} else {
*delay_on = 1000;
*delay_off = 1000;
status_led->mode = QNAP_MCU_STATUS_LED_BLINK_SLOW;
}
return qnap_mcu_status_led_update(base->mcu, base);
}
static int qnap_mcu_register_status_leds(struct device *dev, struct qnap_mcu *mcu)
{
struct qnap_mcu_status *status;
int ret;
status = devm_kzalloc(dev, sizeof(*status), GFP_KERNEL);
if (!status)
return -ENOMEM;
status->mcu = mcu;
/*
* point to the red led, so that statusled_to_qnap_mcu_status
* can resolve the main status struct containing both leds
*/
status->red.red = &status->red;
status->green.red = &status->red;
status->red.mode = QNAP_MCU_STATUS_LED_OFF;
status->red.cdev.name = "red:status";
status->red.cdev.brightness_set_blocking = qnap_mcu_status_led_set;
status->red.cdev.blink_set = qnap_mcu_status_led_blink_set;
status->red.cdev.brightness = 0;
status->red.cdev.max_brightness = 1;
status->green.mode = QNAP_MCU_STATUS_LED_OFF;
status->green.cdev.name = "green:status";
status->green.cdev.brightness_set_blocking = qnap_mcu_status_led_set;
status->green.cdev.blink_set = qnap_mcu_status_led_blink_set;
status->green.cdev.brightness = 0;
status->green.cdev.max_brightness = 1;
ret = devm_led_classdev_register(dev, &status->red.cdev);
if (ret)
return ret;
ret = devm_led_classdev_register(dev, &status->green.cdev);
if (ret)
return ret;
return qnap_mcu_status_led_update(status->mcu, status);
}
static int qnap_mcu_leds_probe(struct platform_device *pdev)
{
struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
@@ -210,6 +370,11 @@ static int qnap_mcu_leds_probe(struct platform_device *pdev)
"failed to register USB LED\n");
}
ret = qnap_mcu_register_status_leds(&pdev->dev, mcu);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to register status LEDs\n");
return 0;
}